From ff1911367744093203071baa9e2ada0aa29d9c37 Mon Sep 17 00:00:00 2001 From: rtdany10 Date: Thu, 2 Sep 2021 19:52:12 +0530 Subject: [PATCH 001/193] feat: currency exchange settings --- .../currency_exchange_settings/__init__.py | 0 .../currency_exchange_settings.js | 8 ++ .../currency_exchange_settings.json | 86 +++++++++++++++++++ .../currency_exchange_settings.py | 38 ++++++++ .../test_currency_exchange_settings.py | 8 ++ .../__init__.py | 0 .../currency_exchange_settings_details.json | 40 +++++++++ .../currency_exchange_settings_details.py | 8 ++ .../__init__.py | 0 ...rency_exchange_settings_extra_details.json | 39 +++++++++ ...urrency_exchange_settings_extra_details.py | 8 ++ 11 files changed, 235 insertions(+) create mode 100644 erpnext/setup/doctype/currency_exchange_settings/__init__.py create mode 100644 erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.js create mode 100644 erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.json create mode 100644 erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py create mode 100644 erpnext/setup/doctype/currency_exchange_settings/test_currency_exchange_settings.py create mode 100644 erpnext/setup/doctype/currency_exchange_settings_details/__init__.py create mode 100644 erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json create mode 100644 erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.py create mode 100644 erpnext/setup/doctype/currency_exchange_settings_extra_details/__init__.py create mode 100644 erpnext/setup/doctype/currency_exchange_settings_extra_details/currency_exchange_settings_extra_details.json create mode 100644 erpnext/setup/doctype/currency_exchange_settings_extra_details/currency_exchange_settings_extra_details.py diff --git a/erpnext/setup/doctype/currency_exchange_settings/__init__.py b/erpnext/setup/doctype/currency_exchange_settings/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.js b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.js new file mode 100644 index 0000000000..6b0ccb7713 --- /dev/null +++ b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.js @@ -0,0 +1,8 @@ +// Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Currency Exchange Settings', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.json b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.json new file mode 100644 index 0000000000..b010437271 --- /dev/null +++ b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.json @@ -0,0 +1,86 @@ +{ + "actions": [], + "creation": "2021-09-02 14:53:50.923529", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "api_details_section", + "api_endpoint", + "column_break_3", + "result_key", + "section_break_2", + "req_params", + "column_break_4", + "extra_params" + ], + "fields": [ + { + "fieldname": "api_endpoint", + "fieldtype": "Data", + "in_list_view": 1, + "label": "API Endpoint", + "reqd": 1 + }, + { + "fieldname": "section_break_2", + "fieldtype": "Section Break", + "label": "Request Parameters" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "req_params", + "fieldtype": "Table", + "label": "Mandatory Parameters", + "options": "Currency Exchange Settings Details", + "reqd": 1 + }, + { + "fieldname": "extra_params", + "fieldtype": "Table", + "label": "Additional Parameters", + "options": "Currency Exchange Settings Extra Details" + }, + { + "fieldname": "api_details_section", + "fieldtype": "Section Break", + "label": "API Details" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "result_key", + "fieldtype": "Data", + "label": "Result Key", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "issingle": 1, + "links": [], + "modified": "2021-09-02 15:18:29.198210", + "modified_by": "Administrator", + "module": "Setup", + "name": "Currency Exchange Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py new file mode 100644 index 0000000000..b726d755e4 --- /dev/null +++ b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py @@ -0,0 +1,38 @@ +# Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe import _ +from frappe.model.document import Document + +class CurrencyExchangeSettings(Document): + def validate(self): + if len(self.req_params) != 3: + frappe.throw(_("Make sure all the three mandatory parameters are filled.")) + req_params = { + 'transaction_date': '2021-08-01', + 'from_currency': 'USD', + 'to_currency': 'INR' + } + params = {} + for row in self.req_params: + try: + params[row.key] = req_params[row.value] + req_params.pop(row.value) + except: + frappe.throw(_("Make sure all the three mandatory parameters are filled.")) + import requests + api_url = self.api_endpoint + try: + response = requests.get(api_url, params=params) + except requests.exceptions.RequestException as e: + frappe.throw("Error: " + str(e)) + response.raise_for_status() + value = response.json() + try: + rate = value[str(self.result_key)] + except KeyError: + frappe.throw(_("Invalid result key.")) + if not isinstance(rate, (int, float)): + frappe.throw(_("Returned exchange rate is neither integer not float.")) + frappe.msgprint(_("Exchange rate of USD to INR on 01-08-2021 is ") + str(rate)) diff --git a/erpnext/setup/doctype/currency_exchange_settings/test_currency_exchange_settings.py b/erpnext/setup/doctype/currency_exchange_settings/test_currency_exchange_settings.py new file mode 100644 index 0000000000..80a9a1768d --- /dev/null +++ b/erpnext/setup/doctype/currency_exchange_settings/test_currency_exchange_settings.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + +class TestCurrencyExchangeSettings(unittest.TestCase): + pass diff --git a/erpnext/setup/doctype/currency_exchange_settings_details/__init__.py b/erpnext/setup/doctype/currency_exchange_settings_details/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json b/erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json new file mode 100644 index 0000000000..9d49daa1eb --- /dev/null +++ b/erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json @@ -0,0 +1,40 @@ +{ + "actions": [], + "creation": "2021-09-02 14:54:49.033512", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "key", + "value" + ], + "fields": [ + { + "fieldname": "key", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Key", + "reqd": 1 + }, + { + "fieldname": "value", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Value", + "options": "\ntransaction_date\nfrom_currency\nto_currency", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-09-02 15:24:24.675019", + "modified_by": "Administrator", + "module": "Setup", + "name": "Currency Exchange Settings Details", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.py b/erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.py new file mode 100644 index 0000000000..f870b11c5a --- /dev/null +++ b/erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class CurrencyExchangeSettingsDetails(Document): + pass diff --git a/erpnext/setup/doctype/currency_exchange_settings_extra_details/__init__.py b/erpnext/setup/doctype/currency_exchange_settings_extra_details/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/setup/doctype/currency_exchange_settings_extra_details/currency_exchange_settings_extra_details.json b/erpnext/setup/doctype/currency_exchange_settings_extra_details/currency_exchange_settings_extra_details.json new file mode 100644 index 0000000000..fb85bb167d --- /dev/null +++ b/erpnext/setup/doctype/currency_exchange_settings_extra_details/currency_exchange_settings_extra_details.json @@ -0,0 +1,39 @@ +{ + "actions": [], + "creation": "2021-09-02 15:18:17.888667", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "key", + "value" + ], + "fields": [ + { + "fieldname": "key", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Key", + "reqd": 1 + }, + { + "fieldname": "value", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Value", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-09-02 15:18:17.888667", + "modified_by": "Administrator", + "module": "Setup", + "name": "Currency Exchange Settings Extra Details", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/setup/doctype/currency_exchange_settings_extra_details/currency_exchange_settings_extra_details.py b/erpnext/setup/doctype/currency_exchange_settings_extra_details/currency_exchange_settings_extra_details.py new file mode 100644 index 0000000000..ee51533091 --- /dev/null +++ b/erpnext/setup/doctype/currency_exchange_settings_extra_details/currency_exchange_settings_extra_details.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class CurrencyExchangeSettingsExtraDetails(Document): + pass From e093863c9e41d97a169afb6b58d4a3f33edcb41a Mon Sep 17 00:00:00 2001 From: rtdany10 Date: Fri, 3 Sep 2021 15:03:47 +0530 Subject: [PATCH 002/193] feat: fetch api details from settings --- .../currency_exchange_settings.json | 5 +-- .../currency_exchange_settings.py | 32 +++++++++++++------ .../__init__.py | 0 .../currency_exchange_settings_result.json | 31 ++++++++++++++++++ .../currency_exchange_settings_result.py | 8 +++++ erpnext/setup/utils.py | 30 +++++++++++++---- 6 files changed, 88 insertions(+), 18 deletions(-) create mode 100644 erpnext/setup/doctype/currency_exchange_settings_result/__init__.py create mode 100644 erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.json create mode 100644 erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.py diff --git a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.json b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.json index b010437271..a0dfe73504 100644 --- a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.json +++ b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.json @@ -55,15 +55,16 @@ }, { "fieldname": "result_key", - "fieldtype": "Data", + "fieldtype": "Table", "label": "Result Key", + "options": "Currency Exchange Settings Result", "reqd": 1 } ], "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-09-02 15:18:29.198210", + "modified": "2021-09-03 13:21:16.397695", "modified_by": "Administrator", "module": "Setup", "name": "Currency Exchange Settings", diff --git a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py index b726d755e4..1993c9bab8 100644 --- a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py +++ b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py @@ -9,10 +9,13 @@ class CurrencyExchangeSettings(Document): def validate(self): if len(self.req_params) != 3: frappe.throw(_("Make sure all the three mandatory parameters are filled.")) - req_params = { - 'transaction_date': '2021-08-01', - 'from_currency': 'USD', - 'to_currency': 'INR' + transaction_date = '2021-08-01' + from_currency = 'USD' + to_currency = 'INR' + req_params={ + "transaction_date": transaction_date, + "from_currency": from_currency, + "to_currency": to_currency } params = {} for row in self.req_params: @@ -21,8 +24,14 @@ class CurrencyExchangeSettings(Document): req_params.pop(row.value) except: frappe.throw(_("Make sure all the three mandatory parameters are filled.")) + for eparam in self.extra_params: + params[eparam.key] = eparam.value import requests - api_url = self.api_endpoint + api_url = self.api_endpoint.format( + transaction_date=transaction_date, + to_currency=to_currency, + from_currency=from_currency + ) try: response = requests.get(api_url, params=params) except requests.exceptions.RequestException as e: @@ -30,9 +39,14 @@ class CurrencyExchangeSettings(Document): response.raise_for_status() value = response.json() try: - rate = value[str(self.result_key)] + for key in self.result_key: + value = value[str(key.key).format( + transaction_date=transaction_date, + to_currency=to_currency, + from_currency=from_currency + )] except KeyError: - frappe.throw(_("Invalid result key.")) - if not isinstance(rate, (int, float)): + frappe.throw(_("Invalid result key. Response: ") + response.text) + if not isinstance(value, (int, float)): frappe.throw(_("Returned exchange rate is neither integer not float.")) - frappe.msgprint(_("Exchange rate of USD to INR on 01-08-2021 is ") + str(rate)) + frappe.msgprint(_("Exchange rate of USD to INR on 01-08-2021 is ") + str(value)) diff --git a/erpnext/setup/doctype/currency_exchange_settings_result/__init__.py b/erpnext/setup/doctype/currency_exchange_settings_result/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.json b/erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.json new file mode 100644 index 0000000000..4203b802fd --- /dev/null +++ b/erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.json @@ -0,0 +1,31 @@ +{ + "actions": [], + "creation": "2021-09-03 13:17:22.088259", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "key" + ], + "fields": [ + { + "fieldname": "key", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Key", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-09-03 13:17:22.088259", + "modified_by": "Administrator", + "module": "Setup", + "name": "Currency Exchange Settings Result", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.py b/erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.py new file mode 100644 index 0000000000..2c4fb61ab9 --- /dev/null +++ b/erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class CurrencyExchangeSettingsResult(Document): + pass diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index 409f776a4e..76e52cdea3 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -98,15 +98,31 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No if not value: import requests - api_url = "https://api.exchangerate.host/convert" - response = requests.get(api_url, params={ - "date": transaction_date, - "from": from_currency, - "to": to_currency - }) + settings = frappe.get_single('Currency Exchange Settings') + req_params={ + "transaction_date": transaction_date, + "from_currency": from_currency, + "to_currency": to_currency + } + params = {} + for row in settings.req_params: + params[row.key] = req_params[row.value] + for eparam in settings.extra_params: + params[eparam.key] = eparam.value + response = requests.get(settings.api_endpoint.format( + transaction_date=transaction_date, + to_currency=to_currency, + from_currency=from_currency + ), params=params) # expire in 6 hours response.raise_for_status() - value = response.json()["result"] + value = response.json() + for res_key in settings.result_key: + value = value[str(res_key.key).format( + transaction_date=transaction_date, + to_currency=to_currency, + from_currency=from_currency + )] cache.setex(name=key, time=21600, value=flt(value)) return flt(value) except Exception: From d8b8032de15c53ec484260f6d72c1f4041adfd68 Mon Sep 17 00:00:00 2001 From: rtdany10 Date: Fri, 3 Sep 2021 15:50:05 +0530 Subject: [PATCH 003/193] feat: init CES default values --- .../currency_exchange_settings.py | 8 ++++---- .../currency_exchange_settings_result.json | 5 +++-- erpnext/setup/install.py | 8 +++++++- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py index 1993c9bab8..92828e60b1 100644 --- a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py +++ b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py @@ -7,8 +7,8 @@ from frappe.model.document import Document class CurrencyExchangeSettings(Document): def validate(self): - if len(self.req_params) != 3: - frappe.throw(_("Make sure all the three mandatory parameters are filled.")) + if len(self.req_params) > 3: + frappe.throw(_("Make sure no mandatory parameters are repeated.")) transaction_date = '2021-08-01' from_currency = 'USD' to_currency = 'INR' @@ -23,7 +23,7 @@ class CurrencyExchangeSettings(Document): params[row.key] = req_params[row.value] req_params.pop(row.value) except: - frappe.throw(_("Make sure all the three mandatory parameters are filled.")) + frappe.throw(_("Make sure no mandatory parameters are repeated.")) for eparam in self.extra_params: params[eparam.key] = eparam.value import requests @@ -42,7 +42,7 @@ class CurrencyExchangeSettings(Document): for key in self.result_key: value = value[str(key.key).format( transaction_date=transaction_date, - to_currency=to_currency, + to_currency=to_currency, from_currency=from_currency )] except KeyError: diff --git a/erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.json b/erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.json index 4203b802fd..1b2c623867 100644 --- a/erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.json +++ b/erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.json @@ -13,13 +13,14 @@ "fieldtype": "Data", "in_list_view": 1, "label": "Key", - "reqd": 1 + "reqd": 1, + "unique": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-09-03 13:17:22.088259", + "modified": "2021-09-03 15:41:09.997576", "modified_by": "Administrator", "module": "Setup", "name": "Currency Exchange Settings Result", diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index bbee74cafb..239733a48d 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -57,7 +57,13 @@ def set_single_defaults(): pass except frappe.ValidationError: pass - + ces = frappe.get_single('Currency Exchange Settings') + ces.api_endpoint = "https://api.exchangerate.host/convert" + ces.append('result_key', {'key': 'result'}) + ces.append('req_params', {'key': 'date', 'value': 'transaction_date'}) + ces.append('req_params', {'key': 'from', 'value': 'from_currency'}) + ces.append('req_params', {'key': 'to', 'value': 'to_currency'}) + ces.save() frappe.db.set_default("date_format", "dd-mm-yyyy") From a86d9c91d0661f741bba9f3e051c9888a6207f87 Mon Sep 17 00:00:00 2001 From: rtdany10 Date: Fri, 3 Sep 2021 19:21:06 +0530 Subject: [PATCH 004/193] chore: code clean up --- .../currency_exchange_settings.json | 9 +++++++- .../currency_exchange_settings.py | 13 +++++++---- .../currency_exchange_settings_details.json | 5 ++-- ...rency_exchange_settings_extra_details.json | 5 ++-- erpnext/setup/utils.py | 23 +++++++++---------- 5 files changed, 34 insertions(+), 21 deletions(-) diff --git a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.json b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.json index a0dfe73504..bd862429ac 100644 --- a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.json +++ b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.json @@ -7,6 +7,7 @@ "field_order": [ "api_details_section", "api_endpoint", + "url", "column_break_3", "result_key", "section_break_2", @@ -59,12 +60,18 @@ "label": "Result Key", "options": "Currency Exchange Settings Result", "reqd": 1 + }, + { + "fieldname": "url", + "fieldtype": "Data", + "label": "URL", + "read_only": 1 } ], "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-09-03 13:21:16.397695", + "modified": "2021-09-03 19:09:02.741016", "modified_by": "Administrator", "module": "Setup", "name": "Currency Exchange Settings", diff --git a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py index 92828e60b1..1ecb0ffe31 100644 --- a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py +++ b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py @@ -12,7 +12,7 @@ class CurrencyExchangeSettings(Document): transaction_date = '2021-08-01' from_currency = 'USD' to_currency = 'INR' - req_params={ + req_params = { "transaction_date": transaction_date, "from_currency": from_currency, "to_currency": to_currency @@ -25,7 +25,11 @@ class CurrencyExchangeSettings(Document): except: frappe.throw(_("Make sure no mandatory parameters are repeated.")) for eparam in self.extra_params: - params[eparam.key] = eparam.value + params[eparam.key] = eparam.value.format( + transaction_date=transaction_date, + to_currency=to_currency, + from_currency=from_currency + ) import requests api_url = self.api_endpoint.format( transaction_date=transaction_date, @@ -46,7 +50,8 @@ class CurrencyExchangeSettings(Document): from_currency=from_currency )] except KeyError: - frappe.throw(_("Invalid result key. Response: ") + response.text) + frappe.throw("Invalid result key. Response: " + response.text) if not isinstance(value, (int, float)): frappe.throw(_("Returned exchange rate is neither integer not float.")) - frappe.msgprint(_("Exchange rate of USD to INR on 01-08-2021 is ") + str(value)) + self.url = response.url + frappe.msgprint("Exchange rate of USD to INR on 01-08-2021 is " + str(value)) diff --git a/erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json b/erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json index 9d49daa1eb..dbb886fe60 100644 --- a/erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json +++ b/erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json @@ -14,7 +14,8 @@ "fieldtype": "Data", "in_list_view": 1, "label": "Key", - "reqd": 1 + "reqd": 1, + "unique": 1 }, { "fieldname": "value", @@ -28,7 +29,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-09-02 15:24:24.675019", + "modified": "2021-09-03 18:50:47.145457", "modified_by": "Administrator", "module": "Setup", "name": "Currency Exchange Settings Details", diff --git a/erpnext/setup/doctype/currency_exchange_settings_extra_details/currency_exchange_settings_extra_details.json b/erpnext/setup/doctype/currency_exchange_settings_extra_details/currency_exchange_settings_extra_details.json index fb85bb167d..f21f9cef0b 100644 --- a/erpnext/setup/doctype/currency_exchange_settings_extra_details/currency_exchange_settings_extra_details.json +++ b/erpnext/setup/doctype/currency_exchange_settings_extra_details/currency_exchange_settings_extra_details.json @@ -14,7 +14,8 @@ "fieldtype": "Data", "in_list_view": 1, "label": "Key", - "reqd": 1 + "reqd": 1, + "unique": 1 }, { "fieldname": "value", @@ -27,7 +28,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-09-02 15:18:17.888667", + "modified": "2021-09-03 18:50:28.482851", "modified_by": "Administrator", "module": "Setup", "name": "Currency Exchange Settings Extra Details", diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index c54f62d09b..9146566f65 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -101,7 +101,7 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No if not value: import requests settings = frappe.get_single('Currency Exchange Settings') - req_params={ + req_params = { "transaction_date": transaction_date, "from_currency": from_currency, "to_currency": to_currency @@ -110,21 +110,13 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No for row in settings.req_params: params[row.key] = req_params[row.value] for eparam in settings.extra_params: - params[eparam.key] = eparam.value - response = requests.get(settings.api_endpoint.format( - transaction_date=transaction_date, - to_currency=to_currency, - from_currency=from_currency - ), params=params) + params[eparam.key] = format_ces_api(eparam.value, req_params) + response = requests.get(format_ces_api(settings.api_endpoint, req_params), params=params) # expire in 6 hours response.raise_for_status() value = response.json() for res_key in settings.result_key: - value = value[str(res_key.key).format( - transaction_date=transaction_date, - to_currency=to_currency, - from_currency=from_currency - )] + value = value[format_ces_api(str(res_key.key), req_params)] cache.setex(name=key, time=21600, value=flt(value)) return flt(value) except Exception: @@ -132,6 +124,13 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No frappe.msgprint(_("Unable to find exchange rate for {0} to {1} for key date {2}. Please create a Currency Exchange record manually").format(from_currency, to_currency, transaction_date)) return 0.0 +def format_ces_api(data="", param={}): + return data.format( + transaction_date=param["transaction_date"], + to_currency=param["to_currency"], + from_currency=param["from_currency"] + ) + def enable_all_roles_and_domains(): """ enable all roles and domain for testing """ # add all roles to users From 227466c36539f5c6448233e3ea51c72a7367ed30 Mon Sep 17 00:00:00 2001 From: rtdany10 Date: Sat, 4 Sep 2021 14:04:56 +0530 Subject: [PATCH 005/193] chore: clean up layout and code --- .../currency_exchange_settings.json | 22 +++++----- .../currency_exchange_settings.py | 20 ++-------- .../currency_exchange_settings_details.json | 5 +-- .../__init__.py | 0 ...rency_exchange_settings_extra_details.json | 40 ------------------- ...urrency_exchange_settings_extra_details.py | 8 ---- erpnext/setup/install.py | 19 +++++---- erpnext/setup/utils.py | 4 +- 8 files changed, 29 insertions(+), 89 deletions(-) delete mode 100644 erpnext/setup/doctype/currency_exchange_settings_extra_details/__init__.py delete mode 100644 erpnext/setup/doctype/currency_exchange_settings_extra_details/currency_exchange_settings_extra_details.json delete mode 100644 erpnext/setup/doctype/currency_exchange_settings_extra_details/currency_exchange_settings_extra_details.py diff --git a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.json b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.json index bd862429ac..d3d23043c4 100644 --- a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.json +++ b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.json @@ -9,11 +9,11 @@ "api_endpoint", "url", "column_break_3", - "result_key", + "help", "section_break_2", "req_params", "column_break_4", - "extra_params" + "result_key" ], "fields": [ { @@ -35,16 +35,10 @@ { "fieldname": "req_params", "fieldtype": "Table", - "label": "Mandatory Parameters", + "label": "Parameters", "options": "Currency Exchange Settings Details", "reqd": 1 }, - { - "fieldname": "extra_params", - "fieldtype": "Table", - "label": "Additional Parameters", - "options": "Currency Exchange Settings Extra Details" - }, { "fieldname": "api_details_section", "fieldtype": "Section Break", @@ -64,14 +58,20 @@ { "fieldname": "url", "fieldtype": "Data", - "label": "URL", + "label": "Example URL", "read_only": 1 + }, + { + "fieldname": "help", + "fieldtype": "HTML", + "label": "Help", + "options": "

Currency Exchange Settings Help

\n

There are 3 variables that could be used within the endpoint, result key and in values of the parameter.

\n

Exchange rate between {from_currency} and {to_currency} on {transaction_date} is fetched by the API.

\n

Example: If your endpoint is exchange.com/2021-08-01, then, you will have to input exchange.com/{transaction_date}

" } ], "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-09-03 19:09:02.741016", + "modified": "2021-09-04 11:41:34.375637", "modified_by": "Administrator", "module": "Setup", "name": "Currency Exchange Settings", diff --git a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py index 1ecb0ffe31..badd14f159 100644 --- a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py +++ b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py @@ -3,29 +3,17 @@ import frappe from frappe import _ +from frappe.utils import nowdate from frappe.model.document import Document class CurrencyExchangeSettings(Document): def validate(self): - if len(self.req_params) > 3: - frappe.throw(_("Make sure no mandatory parameters are repeated.")) - transaction_date = '2021-08-01' + transaction_date = nowdate() from_currency = 'USD' to_currency = 'INR' - req_params = { - "transaction_date": transaction_date, - "from_currency": from_currency, - "to_currency": to_currency - } params = {} for row in self.req_params: - try: - params[row.key] = req_params[row.value] - req_params.pop(row.value) - except: - frappe.throw(_("Make sure no mandatory parameters are repeated.")) - for eparam in self.extra_params: - params[eparam.key] = eparam.value.format( + params[row.key] = row.value.format( transaction_date=transaction_date, to_currency=to_currency, from_currency=from_currency @@ -54,4 +42,4 @@ class CurrencyExchangeSettings(Document): if not isinstance(value, (int, float)): frappe.throw(_("Returned exchange rate is neither integer not float.")) self.url = response.url - frappe.msgprint("Exchange rate of USD to INR on 01-08-2021 is " + str(value)) + frappe.msgprint("Exchange rate of USD to INR is " + str(value)) diff --git a/erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json b/erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json index dbb886fe60..886d38529e 100644 --- a/erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json +++ b/erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json @@ -19,17 +19,16 @@ }, { "fieldname": "value", - "fieldtype": "Select", + "fieldtype": "Data", "in_list_view": 1, "label": "Value", - "options": "\ntransaction_date\nfrom_currency\nto_currency", "reqd": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-09-03 18:50:47.145457", + "modified": "2021-09-04 11:24:21.944002", "modified_by": "Administrator", "module": "Setup", "name": "Currency Exchange Settings Details", diff --git a/erpnext/setup/doctype/currency_exchange_settings_extra_details/__init__.py b/erpnext/setup/doctype/currency_exchange_settings_extra_details/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/setup/doctype/currency_exchange_settings_extra_details/currency_exchange_settings_extra_details.json b/erpnext/setup/doctype/currency_exchange_settings_extra_details/currency_exchange_settings_extra_details.json deleted file mode 100644 index f21f9cef0b..0000000000 --- a/erpnext/setup/doctype/currency_exchange_settings_extra_details/currency_exchange_settings_extra_details.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "actions": [], - "creation": "2021-09-02 15:18:17.888667", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "key", - "value" - ], - "fields": [ - { - "fieldname": "key", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Key", - "reqd": 1, - "unique": 1 - }, - { - "fieldname": "value", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Value", - "reqd": 1 - } - ], - "index_web_pages_for_search": 1, - "istable": 1, - "links": [], - "modified": "2021-09-03 18:50:28.482851", - "modified_by": "Administrator", - "module": "Setup", - "name": "Currency Exchange Settings Extra Details", - "owner": "Administrator", - "permissions": [], - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/setup/doctype/currency_exchange_settings_extra_details/currency_exchange_settings_extra_details.py b/erpnext/setup/doctype/currency_exchange_settings_extra_details/currency_exchange_settings_extra_details.py deleted file mode 100644 index ee51533091..0000000000 --- a/erpnext/setup/doctype/currency_exchange_settings_extra_details/currency_exchange_settings_extra_details.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -# import frappe -from frappe.model.document import Document - -class CurrencyExchangeSettingsExtraDetails(Document): - pass diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index ad4d90003d..55be9ee32a 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -59,15 +59,18 @@ def set_single_defaults(): pass except frappe.ValidationError: pass - ces = frappe.get_single('Currency Exchange Settings') - ces.api_endpoint = "https://api.exchangerate.host/convert" - ces.append('result_key', {'key': 'result'}) - ces.append('req_params', {'key': 'date', 'value': 'transaction_date'}) - ces.append('req_params', {'key': 'from', 'value': 'from_currency'}) - ces.append('req_params', {'key': 'to', 'value': 'to_currency'}) - ces.save() - frappe.db.set_default("date_format", "dd-mm-yyyy") + frappe.db.set_default("date_format", "dd-mm-yyyy") + ces = frappe.get_single('Currency Exchange Settings') + try: + ces.api_endpoint = "https://api.exchangerate.host/convert" + ces.append('result_key', {'key': 'result'}) + ces.append('req_params', {'key': 'date', 'value': '{transaction_date}'}) + ces.append('req_params', {'key': 'from', 'value': '{from_currency}'}) + ces.append('req_params', {'key': 'to', 'value': '{to_currency}'}) + ces.save() + except frappe.ValidationError: + pass def create_compact_item_print_custom_field(): create_custom_field('Print Settings', { diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index 9146566f65..4fcd7d5936 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -108,9 +108,7 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No } params = {} for row in settings.req_params: - params[row.key] = req_params[row.value] - for eparam in settings.extra_params: - params[eparam.key] = format_ces_api(eparam.value, req_params) + params[row.key] = format_ces_api(row.value, req_params) response = requests.get(format_ces_api(settings.api_endpoint, req_params), params=params) # expire in 6 hours response.raise_for_status() From 06340ad08ae286cf7c9b63c637d7c47be7eca76b Mon Sep 17 00:00:00 2001 From: rtdany10 Date: Sat, 4 Sep 2021 17:53:51 +0530 Subject: [PATCH 006/193] feat: button to restore default settings --- .../currency_exchange_settings.js | 25 ++++++++++++++++--- .../currency_exchange_settings.py | 2 +- .../currency_exchange_settings_details.json | 5 ++-- .../currency_exchange_settings_result.json | 5 ++-- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.js b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.js index 6b0ccb7713..c48bca8b8f 100644 --- a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.js +++ b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.js @@ -2,7 +2,26 @@ // For license information, please see license.txt frappe.ui.form.on('Currency Exchange Settings', { - // refresh: function(frm) { - - // } + refresh: function(frm) { + frm.add_custom_button(__('Restore Defaults'), function(){ + frm.doc.api_endpoint = "https://api.exchangerate.host/convert"; + frm.clear_table("req_params") + frm.clear_table("result_key") + let params = { + date: '{transaction_date}', + from: '{from_currency}', + to: '{to_currency}' + } + var row; + $.each(params, function(key, value){ + row = frm.add_child("req_params"); + row.key = key; + row.value = value; + }) + row = frm.add_child("result_key"); + row.key = 'result'; + frm.refresh_fields(); + frm.save(); + }); + } }); diff --git a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py index badd14f159..fa5286a4bb 100644 --- a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py +++ b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py @@ -37,7 +37,7 @@ class CurrencyExchangeSettings(Document): to_currency=to_currency, from_currency=from_currency )] - except KeyError: + except Exception: frappe.throw("Invalid result key. Response: " + response.text) if not isinstance(value, (int, float)): frappe.throw(_("Returned exchange rate is neither integer not float.")) diff --git a/erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json b/erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json index 886d38529e..c9b27e1961 100644 --- a/erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json +++ b/erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json @@ -14,8 +14,7 @@ "fieldtype": "Data", "in_list_view": 1, "label": "Key", - "reqd": 1, - "unique": 1 + "reqd": 1 }, { "fieldname": "value", @@ -28,7 +27,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-09-04 11:24:21.944002", + "modified": "2021-09-04 17:49:17.383982", "modified_by": "Administrator", "module": "Setup", "name": "Currency Exchange Settings Details", diff --git a/erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.json b/erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.json index 1b2c623867..387e245a2f 100644 --- a/erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.json +++ b/erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.json @@ -13,14 +13,13 @@ "fieldtype": "Data", "in_list_view": 1, "label": "Key", - "reqd": 1, - "unique": 1 + "reqd": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-09-03 15:41:09.997576", + "modified": "2021-09-04 17:49:33.858070", "modified_by": "Administrator", "module": "Setup", "name": "Currency Exchange Settings Result", From 8116b9b62f16461e615a08d0da98458a403b85e8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 20 Oct 2021 19:55:00 +0530 Subject: [PATCH 007/193] fix: Updates in term loan processing --- .../loan_management/doctype/loan/loan.json | 7 +- erpnext/loan_management/doctype/loan/loan.py | 4 +- .../loan_management/doctype/loan/test_loan.py | 27 +++- .../loan_application/loan_application.py | 2 +- .../loan_interest_accrual.py | 33 +++++ .../doctype/loan_repayment/loan_repayment.py | 135 +++++++++++++++--- 6 files changed, 181 insertions(+), 27 deletions(-) diff --git a/erpnext/loan_management/doctype/loan/loan.json b/erpnext/loan_management/doctype/loan/loan.json index c9f23ca4df..10676809de 100644 --- a/erpnext/loan_management/doctype/loan/loan.json +++ b/erpnext/loan_management/doctype/loan/loan.json @@ -240,12 +240,14 @@ "label": "Repayment Schedule" }, { + "allow_on_submit": 1, "depends_on": "eval:doc.is_term_loan == 1", "fieldname": "repayment_schedule", "fieldtype": "Table", "label": "Repayment Schedule", "no_copy": 1, - "options": "Repayment Schedule" + "options": "Repayment Schedule", + "read_only": 1 }, { "fieldname": "section_break_17", @@ -360,10 +362,11 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-04-19 18:10:32.360818", + "modified": "2021-10-20 08:28:16.796105", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan", + "naming_rule": "Expression (old style)", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py index 7dbd42297e..73134eedd2 100644 --- a/erpnext/loan_management/doctype/loan/loan.py +++ b/erpnext/loan_management/doctype/loan/loan.py @@ -65,7 +65,7 @@ class Loan(AccountsController): self.rate_of_interest = frappe.db.get_value("Loan Type", self.loan_type, "rate_of_interest") if self.repayment_method == "Repay Over Number of Periods": - self.monthly_repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods) + self.monthly_repayment_amount = get_monthly_repayment_amount(self.loan_amount, self.rate_of_interest, self.repayment_periods) def check_sanctioned_amount_limit(self): sanctioned_amount_limit = get_sanctioned_amount_limit(self.applicant_type, self.applicant, self.company) @@ -207,7 +207,7 @@ def validate_repayment_method(repayment_method, loan_amount, monthly_repayment_a if monthly_repayment_amount > loan_amount: frappe.throw(_("Monthly Repayment Amount cannot be greater than Loan Amount")) -def get_monthly_repayment_amount(repayment_method, loan_amount, rate_of_interest, repayment_periods): +def get_monthly_repayment_amount(loan_amount, rate_of_interest, repayment_periods): if rate_of_interest: monthly_interest_rate = flt(rate_of_interest) / (12 *100) monthly_repayment_amount = math.ceil((loan_amount * monthly_interest_rate * diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py index ec0aebbb8a..c10cf36d9d 100644 --- a/erpnext/loan_management/doctype/loan/test_loan.py +++ b/erpnext/loan_management/doctype/loan/test_loan.py @@ -297,6 +297,27 @@ class TestLoan(unittest.TestCase): self.assertEqual(amounts[0], 11250.00) self.assertEqual(amounts[1], 78303.00) + def test_repayment_schedule_update(self): + loan = create_loan(self.applicant2, "Personal Loan", 200000, "Repay Over Number of Periods", 4, + applicant_type='Customer', repayment_start_date='2021-04-30', posting_date='2021-04-01') + + loan.submit() + + make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date='2021-04-01') + + process_loan_interest_accrual_for_term_loans(posting_date='2021-05-01') + process_loan_interest_accrual_for_term_loans(posting_date='2021-06-01') + + repayment_entry = create_repayment_entry(loan.name, self.applicant2, '2021-06-05', 120000) + repayment_entry.submit() + + loan.load_from_db() + + self.assertEqual(flt(loan.get('repayment_schedule')[3].principal_amount, 2), 32151.83) + self.assertEqual(flt(loan.get('repayment_schedule')[3].interest_amount, 2), 225.06) + self.assertEqual(flt(loan.get('repayment_schedule')[3].total_payment, 2), 32376.89) + self.assertEqual(flt(loan.get('repayment_schedule')[3].balance_loan_amount, 2), 0) + def test_security_shortfall(self): pledges = [{ "loan_security": "Test Security 2", @@ -940,18 +961,18 @@ def create_loan_application(company, applicant, loan_type, proposed_pledges, rep def create_loan(applicant, loan_type, loan_amount, repayment_method, repayment_periods, - repayment_start_date=None, posting_date=None): + applicant_type=None, repayment_start_date=None, posting_date=None): loan = frappe.get_doc({ "doctype": "Loan", - "applicant_type": "Employee", + "applicant_type": applicant_type or "Employee", "company": "_Test Company", "applicant": applicant, "loan_type": loan_type, "loan_amount": loan_amount, "repayment_method": repayment_method, "repayment_periods": repayment_periods, - "repayment_start_date": nowdate(), + "repayment_start_date": repayment_start_date or nowdate(), "is_term_loan": 1, "posting_date": posting_date or nowdate() }) diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.py b/erpnext/loan_management/doctype/loan_application/loan_application.py index e492920abb..e24692e34a 100644 --- a/erpnext/loan_management/doctype/loan_application/loan_application.py +++ b/erpnext/loan_management/doctype/loan_application/loan_application.py @@ -83,7 +83,7 @@ class LoanApplication(Document): if self.is_term_loan: if self.repayment_method == "Repay Over Number of Periods": - self.repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods) + self.repayment_amount = get_monthly_repayment_amount(self.loan_amount, self.rate_of_interest, self.repayment_periods) if self.repayment_method == "Repay Fixed Amount per Period": monthly_interest_rate = flt(self.rate_of_interest) / (12 *100) diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index 93513a83e7..4cd4c75a4b 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -76,6 +76,39 @@ class LoanInterestAccrual(AccountsController): }) ) + if self.payable_principal_amount: + gle_map.append( + self.get_gl_dict({ + "account": self.loan_account, + "party_type": self.applicant_type, + "party": self.applicant, + "against": self.interest_income_account, + "debit": self.payable_principal_amount, + "debit_in_account_currency": self.interest_amount, + "against_voucher_type": "Loan", + "against_voucher": self.loan, + "remarks": _("Interest accrued from {0} to {1} against loan: {2}").format( + self.last_accrual_date, self.posting_date, self.loan), + "cost_center": erpnext.get_default_cost_center(self.company), + "posting_date": self.posting_date + }) + ) + + gle_map.append( + self.get_gl_dict({ + "account": self.interest_income_account, + "against": self.loan_account, + "credit": self.payable_principal_amount, + "credit_in_account_currency": self.interest_amount, + "against_voucher_type": "Loan", + "against_voucher": self.loan, + "remarks": ("Interest accrued from {0} to {1} against loan: {2}").format( + self.last_accrual_date, self.posting_date, self.loan), + "cost_center": erpnext.get_default_cost_center(self.company), + "posting_date": self.posting_date + }) + ) + if gle_map: make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj) diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index 13b7357327..9f3fe76198 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import add_days, cint, date_diff, flt, get_datetime, getdate +from frappe.utils import add_days, add_months, cint, date_diff, flt, get_datetime, getdate from six import iteritems import erpnext @@ -38,10 +38,12 @@ class LoanRepayment(AccountsController): def on_submit(self): self.update_paid_amount() + self.update_repayment_schedule() self.make_gl_entries() def on_cancel(self): self.mark_as_unpaid() + self.update_repayment_schedule() self.ignore_linked_doctypes = ['GL Entry'] self.make_gl_entries(cancel=1) @@ -164,6 +166,10 @@ class LoanRepayment(AccountsController): if loan.status == "Loan Closure Requested": frappe.db.set_value("Loan", self.against_loan, "status", "Disbursed") + def update_repayment_schedule(self): + if self.is_term_loan and self.principal_amount_paid > self.payable_principal_amount: + regenerate_repayment_schedule(self.against_loan) + def allocate_amounts(self, repayment_details): self.set('repayment_details', []) self.principal_amount_paid = 0 @@ -185,50 +191,93 @@ class LoanRepayment(AccountsController): interest_paid -= self.total_penalty_paid - total_interest_paid = 0 - # interest_paid = self.amount_paid - self.principal_amount_paid - self.penalty_amount + if self.is_term_loan: + interest_paid, updated_entries = self.allocate_interest_amount(interest_paid, repayment_details) + self.allocate_principal_amount_for_term_loans(interest_paid, repayment_details, updated_entries) + else: + interest_paid, updated_entries = self.allocate_interest_amount(interest_paid, repayment_details) + self.allocate_excess_payment_for_demand_loans(interest_paid, repayment_details) + + def allocate_interest_amount(self, interest_paid, repayment_details): + updated_entries = {} + self.total_interest_paid = 0 + idx = 1 if interest_paid > 0: for lia, amounts in iteritems(repayment_details.get('pending_accrual_entries', [])): - if amounts['interest_amount'] + amounts['payable_principal_amount'] <= interest_paid: + interest_amount = 0 + if amounts['interest_amount'] <= interest_paid: interest_amount = amounts['interest_amount'] - paid_principal = amounts['payable_principal_amount'] - self.principal_amount_paid += paid_principal - interest_paid -= (interest_amount + paid_principal) + self.total_interest_paid += interest_amount + interest_paid -= interest_amount elif interest_paid: if interest_paid >= amounts['interest_amount']: interest_amount = amounts['interest_amount'] - paid_principal = interest_paid - interest_amount - self.principal_amount_paid += paid_principal + self.total_interest_paid += interest_amount interest_paid = 0 else: interest_amount = interest_paid + self.total_interest_paid += interest_amount interest_paid = 0 - paid_principal=0 - total_interest_paid += interest_amount - self.append('repayment_details', { - 'loan_interest_accrual': lia, - 'paid_interest_amount': interest_amount, - 'paid_principal_amount': paid_principal - }) + if interest_amount: + self.append('repayment_details', { + 'loan_interest_accrual': lia, + 'paid_interest_amount': interest_amount, + 'paid_principal_amount': 0 + }) + updated_entries[lia] = idx + idx += 1 + return interest_paid, updated_entries + + def allocate_principal_amount_for_term_loans(self, interest_paid, repayment_details, updated_entries): + if interest_paid > 0: + for lia, amounts in iteritems(repayment_details.get('pending_accrual_entries', [])): + paid_principal = 0 + if amounts['payable_principal_amount'] <= interest_paid: + paid_principal = amounts['payable_principal_amount'] + self.principal_amount_paid += paid_principal + interest_paid -= paid_principal + elif interest_paid: + if interest_paid >= amounts['payable_principal_amount']: + paid_principal = amounts['payable_principal_amount'] + self.principal_amount_paid += paid_principal + interest_paid = 0 + else: + paid_principal = interest_paid + self.principal_amount_paid += paid_principal + interest_paid = 0 + + if updated_entries.get(lia): + idx = updated_entries.get(lia) + self.get('repayment_details')[idx-1].paid_principal_amount += paid_principal + else: + self.append('repayment_details', { + 'loan_interest_accrual': lia, + 'paid_interest_amount': 0, + 'paid_principal_amount': paid_principal + }) + + if interest_paid > 0: + self.principal_amount_paid += interest_paid + + def allocate_excess_payment_for_demand_loans(self, interest_paid, repayment_details): if repayment_details['unaccrued_interest'] and interest_paid > 0: # no of days for which to accrue interest # Interest can only be accrued for an entire day and not partial if interest_paid > repayment_details['unaccrued_interest']: interest_paid -= repayment_details['unaccrued_interest'] - total_interest_paid += repayment_details['unaccrued_interest'] + self.total_interest_paid += repayment_details['unaccrued_interest'] else: # get no of days for which interest can be paid per_day_interest = get_per_day_interest(self.pending_principal_amount, self.rate_of_interest, self.posting_date) no_of_days = cint(interest_paid/per_day_interest) - total_interest_paid += no_of_days * per_day_interest + self.total_interest_paid += no_of_days * per_day_interest interest_paid -= no_of_days * per_day_interest - self.total_interest_paid = total_interest_paid if interest_paid > 0: self.principal_amount_paid += interest_paid @@ -364,6 +413,54 @@ def get_penalty_details(against_loan): else: return None, 0 +def regenerate_repayment_schedule(loan): + from erpnext.loan_management.doctype.loan.loan import get_monthly_repayment_amount + + loan_doc = frappe.get_doc('Loan', loan) + next_accrual_date = None + + for term in reversed(loan_doc.get('repayment_schedule')): + if not term.is_accrued: + next_accrual_date = term.payment_date + + if not term.is_accrued: + loan_doc.remove(term) + + loan_doc.save() + + if loan_doc.status in ('Disbursed', 'Loan Closure Requested', 'Closed'): + balance_amount = loan_doc.total_payment - loan_doc.total_principal_paid \ + - loan_doc.total_interest_payable - loan_doc.written_off_amount + else: + balance_amount = loan_doc.disbursed_amount - loan_doc.total_principal_paid \ + - loan_doc.total_interest_payable - loan_doc.written_off_amount + + monthly_repayment_amount = get_monthly_repayment_amount(loan_doc.loan_amount, + loan_doc.rate_of_interest, loan_doc.repayment_periods) + + payment_date = next_accrual_date + + while(balance_amount > 0): + interest_amount = flt(balance_amount * flt(loan_doc.rate_of_interest) / (12*100)) + principal_amount = monthly_repayment_amount - interest_amount + balance_amount = flt(balance_amount + interest_amount - monthly_repayment_amount) + if balance_amount < 0: + principal_amount += balance_amount + balance_amount = 0.0 + + total_payment = principal_amount + interest_amount + loan_doc.append("repayment_schedule", { + "payment_date": payment_date, + "principal_amount": principal_amount, + "interest_amount": interest_amount, + "total_payment": total_payment, + "balance_loan_amount": balance_amount + }) + next_payment_date = add_months(payment_date, 1) + payment_date = next_payment_date + + loan_doc.save() + # This function returns the amounts that are payable at the time of loan repayment based on posting date # So it pulls all the unpaid Loan Interest Accrual Entries and calculates the penalty if applicable From 1a5f0da6cafa7001373a5463f04c09fcbd0dd29e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 22 Oct 2021 10:46:56 +0530 Subject: [PATCH 008/193] fix: Loan repayment schedule date --- erpnext/loan_management/doctype/loan/loan.py | 10 ++++++++-- .../doctype/loan_repayment/loan_repayment.py | 7 +++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py index 73134eedd2..878fd183d1 100644 --- a/erpnext/loan_management/doctype/loan/loan.py +++ b/erpnext/loan_management/doctype/loan/loan.py @@ -9,7 +9,7 @@ import math import frappe from frappe import _ -from frappe.utils import add_months, flt, getdate, now_datetime, nowdate +from frappe.utils import add_months, flt, get_last_day, getdate, now_datetime, nowdate from six import string_types import erpnext @@ -102,7 +102,7 @@ class Loan(AccountsController): "total_payment": total_payment, "balance_loan_amount": balance_amount }) - next_payment_date = add_months(payment_date, 1) + next_payment_date = add_single_month(payment_date) payment_date = next_payment_date def set_repayment_period(self): @@ -391,3 +391,9 @@ def get_shortfall_applicants(): "value": len(applicants), "fieldtype": "Int" } + +def add_single_month(date): + if getdate(date) == get_last_day(date): + return get_last_day(add_months(date, 1)) + else: + return add_months(date, 1) \ No newline at end of file diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index 9f3fe76198..53ff43a507 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -414,7 +414,10 @@ def get_penalty_details(against_loan): return None, 0 def regenerate_repayment_schedule(loan): - from erpnext.loan_management.doctype.loan.loan import get_monthly_repayment_amount + from erpnext.loan_management.doctype.loan.loan import ( + add_single_month, + get_monthly_repayment_amount, + ) loan_doc = frappe.get_doc('Loan', loan) next_accrual_date = None @@ -456,7 +459,7 @@ def regenerate_repayment_schedule(loan): "total_payment": total_payment, "balance_loan_amount": balance_amount }) - next_payment_date = add_months(payment_date, 1) + next_payment_date = add_single_month(payment_date) payment_date = next_payment_date loan_doc.save() From dcae9ba86e20daa8a5eac2990607c434b812af79 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 23 Oct 2021 18:06:34 +0530 Subject: [PATCH 009/193] fix: Unsecured loan status update --- .../loan_disbursement/loan_disbursement.py | 13 ++-- .../loan_interest_accrual.py | 17 +++-- .../doctype/loan_repayment/loan_repayment.py | 68 ++++++++++++------- .../loan_security_unpledge.py | 12 ++-- 4 files changed, 64 insertions(+), 46 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py index 6d9d4f490d..d072422010 100644 --- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py +++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py @@ -178,20 +178,19 @@ def get_total_pledged_security_value(loan): @frappe.whitelist() def get_disbursal_amount(loan, on_current_security_price=0): + from erpnext.loan_management.doctype.loan_repayment.loan_repayment import ( + get_pending_principal_amount, + ) + loan_details = frappe.get_value("Loan", loan, ["loan_amount", "disbursed_amount", "total_payment", "total_principal_paid", "total_interest_payable", "status", "is_term_loan", "is_secured_loan", - "maximum_loan_amount"], as_dict=1) + "maximum_loan_amount", "written_off_amount"], as_dict=1) if loan_details.is_secured_loan and frappe.get_all('Loan Security Shortfall', filters={'loan': loan, 'status': 'Pending'}): return 0 - if loan_details.status == 'Disbursed': - pending_principal_amount = flt(loan_details.total_payment) - flt(loan_details.total_interest_payable) \ - - flt(loan_details.total_principal_paid) - else: - pending_principal_amount = flt(loan_details.disbursed_amount) - flt(loan_details.total_interest_payable) \ - - flt(loan_details.total_principal_paid) + pending_principal_amount = get_pending_principal_amount(loan_details) security_value = 0.0 if loan_details.is_secured_loan and on_current_security_price: diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index 4cd4c75a4b..3dd1328124 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -117,7 +117,10 @@ class LoanInterestAccrual(AccountsController): # rate of interest is 13.5 then first loan interest accural will be on '01-10-2019' # which means interest will be accrued for 30 days which should be equal to 11095.89 def calculate_accrual_amount_for_demand_loans(loan, posting_date, process_loan_interest, accrual_type): - from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts + from erpnext.loan_management.doctype.loan_repayment.loan_repayment import ( + calculate_amounts, + get_pending_principal_amount, + ) no_of_days = get_no_of_days_for_interest_accural(loan, posting_date) precision = cint(frappe.db.get_default("currency_precision")) or 2 @@ -125,12 +128,7 @@ def calculate_accrual_amount_for_demand_loans(loan, posting_date, process_loan_i if no_of_days <= 0: return - if loan.status == 'Disbursed': - pending_principal_amount = flt(loan.total_payment) - flt(loan.total_interest_payable) \ - - flt(loan.total_principal_paid) - flt(loan.written_off_amount) - else: - pending_principal_amount = flt(loan.disbursed_amount) - flt(loan.total_interest_payable) \ - - flt(loan.total_principal_paid) - flt(loan.written_off_amount) + pending_principal_amount = get_pending_principal_amount(loan) interest_per_day = get_per_day_interest(pending_principal_amount, loan.rate_of_interest, posting_date) payable_interest = interest_per_day * no_of_days @@ -168,7 +166,7 @@ def make_accrual_interest_entry_for_demand_loans(posting_date, process_loan_inte if not open_loans: open_loans = frappe.get_all("Loan", - fields=["name", "total_payment", "total_amount_paid", "loan_account", "interest_income_account", + fields=["name", "total_payment", "total_amount_paid", "loan_account", "interest_income_account", "loan_amount", "is_term_loan", "status", "disbursement_date", "disbursed_amount", "applicant_type", "applicant", "rate_of_interest", "total_interest_payable", "written_off_amount", "total_principal_paid", "repayment_start_date"], filters=query_filters) @@ -225,7 +223,8 @@ def get_term_loans(date, term_loan=None, loan_type=None): AND l.is_term_loan =1 AND rs.payment_date <= %s AND rs.is_accrued=0 {0} - AND l.status = 'Disbursed'""".format(condition), (getdate(date)), as_dict=1) + AND l.status = 'Disbursed' + ORDER BY rs.payment_date""".format(condition), (getdate(date)), as_dict=1) return term_loans diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index 53ff43a507..9f13ee68ba 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -126,7 +126,18 @@ class LoanRepayment(AccountsController): }) def update_paid_amount(self): - loan = frappe.get_doc("Loan", self.against_loan) + loan = frappe.get_value("Loan", self.against_loan, ['total_amount_paid', 'total_principal_paid', + 'status', 'is_secured_loan', 'total_payment', 'loan_amount', 'total_interest_payable', + 'written_off_amount'], as_dict=1) + + loan.update({ + 'total_amount_paid': loan.total_amount_paid + self.amount_paid, + 'total_principal_paid': loan.total_principal_paid + self.principal_amount_paid + }) + + pending_principal_amount = get_pending_principal_amount(loan) + if not loan.is_secured_loan and pending_principal_amount < 0: + loan.update({'status': 'Loan Closure Requested'}) for payment in self.repayment_details: frappe.db.sql(""" UPDATE `tabLoan Interest Accrual` @@ -135,17 +146,31 @@ class LoanRepayment(AccountsController): WHERE name = %s""", (flt(payment.paid_principal_amount), flt(payment.paid_interest_amount), payment.loan_interest_accrual)) - frappe.db.sql(""" UPDATE `tabLoan` SET total_amount_paid = %s, total_principal_paid = %s - WHERE name = %s """, (loan.total_amount_paid + self.amount_paid, - loan.total_principal_paid + self.principal_amount_paid, self.against_loan)) + frappe.db.sql(""" UPDATE `tabLoan` + SET total_amount_paid = %s, total_principal_paid = %s, status = %s + WHERE name = %s """, (loan.total_amount_paid, loan.total_principal_paid, loan.status, + self.against_loan)) update_shortfall_status(self.against_loan, self.principal_amount_paid) def mark_as_unpaid(self): - loan = frappe.get_doc("Loan", self.against_loan) + loan = frappe.get_value("Loan", self.against_loan, ['total_amount_paid', 'total_principal_paid', + 'status', 'is_secured_loan', 'total_payment', 'loan_amount', 'total_interest_payable', + 'written_off_amount'], as_dict=1) no_of_repayments = len(self.repayment_details) + loan.update({ + 'total_amount_paid': loan.total_amount_paid - self.amount_paid, + 'total_principal_paid': loan.total_principal_paid - self.principal_amount_paid + }) + + if loan.status == 'Loan Closure Requested': + if loan.disbursed_amount >= loan.loan_amount: + loan['status'] = 'Disbursed' + else: + loan['status'] = 'Partially Disbursed' + for payment in self.repayment_details: frappe.db.sql(""" UPDATE `tabLoan Interest Accrual` SET paid_principal_amount = `paid_principal_amount` - %s, @@ -159,12 +184,9 @@ class LoanRepayment(AccountsController): lia_doc = frappe.get_doc('Loan Interest Accrual', payment.loan_interest_accrual) lia_doc.cancel() - frappe.db.sql(""" UPDATE `tabLoan` SET total_amount_paid = %s, total_principal_paid = %s - WHERE name = %s """, (loan.total_amount_paid - self.amount_paid, - loan.total_principal_paid - self.principal_amount_paid, self.against_loan)) - - if loan.status == "Loan Closure Requested": - frappe.db.set_value("Loan", self.against_loan, "status", "Disbursed") + frappe.db.sql(""" UPDATE `tabLoan` + SET total_amount_paid = %s, total_principal_paid = %s, status = %s + WHERE name = %s """, (loan.total_amount_paid, loan.total_principal_paid, loan.status, self.against_loan)) def update_repayment_schedule(self): if self.is_term_loan and self.principal_amount_paid > self.payable_principal_amount: @@ -431,12 +453,7 @@ def regenerate_repayment_schedule(loan): loan_doc.save() - if loan_doc.status in ('Disbursed', 'Loan Closure Requested', 'Closed'): - balance_amount = loan_doc.total_payment - loan_doc.total_principal_paid \ - - loan_doc.total_interest_payable - loan_doc.written_off_amount - else: - balance_amount = loan_doc.disbursed_amount - loan_doc.total_principal_paid \ - - loan_doc.total_interest_payable - loan_doc.written_off_amount + balance_amount = get_pending_principal_amount(loan_doc) monthly_repayment_amount = get_monthly_repayment_amount(loan_doc.loan_amount, loan_doc.rate_of_interest, loan_doc.repayment_periods) @@ -464,6 +481,16 @@ def regenerate_repayment_schedule(loan): loan_doc.save() +def get_pending_principal_amount(loan): + if loan.status in ('Disbursed', 'Closed') or loan.disbursed_amount >= loan.loan_amount: + pending_principal_amount = flt(loan.total_payment) - flt(loan.total_principal_paid) \ + - flt(loan.total_interest_payable) - flt(loan.written_off_amount) + else: + pending_principal_amount = flt(loan.disbursed_amount) - flt(loan.total_principal_paid) \ + - flt(loan.total_interest_payable) - flt(loan.written_off_amount) + + return pending_principal_amount + # This function returns the amounts that are payable at the time of loan repayment based on posting date # So it pulls all the unpaid Loan Interest Accrual Entries and calculates the penalty if applicable @@ -511,12 +538,7 @@ def get_amounts(amounts, against_loan, posting_date): if due_date and not final_due_date: final_due_date = add_days(due_date, loan_type_details.grace_period_in_days) - if against_loan_doc.status in ('Disbursed', 'Loan Closure Requested', 'Closed'): - pending_principal_amount = against_loan_doc.total_payment - against_loan_doc.total_principal_paid \ - - against_loan_doc.total_interest_payable - against_loan_doc.written_off_amount - else: - pending_principal_amount = against_loan_doc.disbursed_amount - against_loan_doc.total_principal_paid \ - - against_loan_doc.total_interest_payable - against_loan_doc.written_off_amount + pending_principal_amount = get_pending_principal_amount(against_loan_doc) unaccrued_interest = 0 if due_date: diff --git a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py index 0af0de1a53..cb3ded7488 100644 --- a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py +++ b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py @@ -30,6 +30,9 @@ class LoanSecurityUnpledge(Document): d.idx, frappe.bold(d.loan_security))) def validate_unpledge_qty(self): + from erpnext.loan_management.doctype.loan_repayment.loan_repayment import ( + get_pending_principal_amount, + ) from erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall import ( get_ltv_ratio, ) @@ -46,15 +49,10 @@ class LoanSecurityUnpledge(Document): "valid_upto": (">=", get_datetime()) }, as_list=1)) - loan_details = frappe.get_value("Loan", self.loan, ['total_payment', 'total_principal_paid', + loan_details = frappe.get_value("Loan", self.loan, ['total_payment', 'total_principal_paid', 'loan_amount', 'total_interest_payable', 'written_off_amount', 'disbursed_amount', 'status'], as_dict=1) - if loan_details.status == 'Disbursed': - pending_principal_amount = flt(loan_details.total_payment) - flt(loan_details.total_interest_payable) \ - - flt(loan_details.total_principal_paid) - flt(loan_details.written_off_amount) - else: - pending_principal_amount = flt(loan_details.disbursed_amount) - flt(loan_details.total_interest_payable) \ - - flt(loan_details.total_principal_paid) - flt(loan_details.written_off_amount) + pending_principal_amount = get_pending_principal_amount(loan_details) security_value = 0 unpledge_qty_map = {} From 8f6600b27a697d36300d5bb4b3e3303d511839bb Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 25 Oct 2021 16:21:26 +0530 Subject: [PATCH 010/193] fix: Repayment schedule revert on cancel --- .../doctype/loan_repayment/loan_repayment.py | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index 962ae5ea09..8cc53ca854 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -42,8 +42,9 @@ class LoanRepayment(AccountsController): self.make_gl_entries() def on_cancel(self): + self.check_future_accruals() + self.update_repayment_schedule(cancel=1) self.mark_as_unpaid() - self.update_repayment_schedule() self.ignore_linked_doctypes = ['GL Entry'] self.make_gl_entries(cancel=1) @@ -188,9 +189,16 @@ class LoanRepayment(AccountsController): SET total_amount_paid = %s, total_principal_paid = %s, status = %s WHERE name = %s """, (loan.total_amount_paid, loan.total_principal_paid, loan.status, self.against_loan)) - def update_repayment_schedule(self): + def check_future_accruals(self): + future_accrual_date = frappe.db.get_value("Loan Interest Accrual", {"posting_date": (">", self.posting_date), + "docstatus": 1, "loan": self.against_loan}, 'posting_date') + + if future_accrual_date: + frappe.throw("Cannot cancel. Interest accruals already processed till {0}".format(get_datetime(future_accrual_date))) + + def update_repayment_schedule(self, cancel=0): if self.is_term_loan and self.principal_amount_paid > self.payable_principal_amount: - regenerate_repayment_schedule(self.against_loan) + regenerate_repayment_schedule(self.against_loan, cancel) def allocate_amounts(self, repayment_details): self.set('repayment_details', []) @@ -435,7 +443,7 @@ def get_penalty_details(against_loan): else: return None, 0 -def regenerate_repayment_schedule(loan): +def regenerate_repayment_schedule(loan, cancel=0): from erpnext.loan_management.doctype.loan.loan import ( add_single_month, get_monthly_repayment_amount, @@ -443,20 +451,34 @@ def regenerate_repayment_schedule(loan): loan_doc = frappe.get_doc('Loan', loan) next_accrual_date = None + accrued_entries = 0 + last_repayment_amount = 0 + last_balance_amount = 0 for term in reversed(loan_doc.get('repayment_schedule')): if not term.is_accrued: next_accrual_date = term.payment_date - - if not term.is_accrued: loan_doc.remove(term) + else: + accrued_entries += 1 + if not last_repayment_amount: + last_repayment_amount = term.total_payment + if not last_balance_amount: + last_balance_amount = term.balance_loan_amount loan_doc.save() balance_amount = get_pending_principal_amount(loan_doc) - monthly_repayment_amount = get_monthly_repayment_amount(loan_doc.loan_amount, - loan_doc.rate_of_interest, loan_doc.repayment_periods) + if loan_doc.repayment_method == 'Repay Fixed Amount per Period': + monthly_repayment_amount = flt(balance_amount/len(loan_doc.get('repayment_schedule')) - accrued_entries) + else: + if not cancel: + monthly_repayment_amount = get_monthly_repayment_amount(balance_amount, + loan_doc.rate_of_interest, loan_doc.repayment_periods - accrued_entries) + else: + monthly_repayment_amount = last_repayment_amount + balance_amount = last_balance_amount payment_date = next_accrual_date From 7291d9f236a809595add2d90b7f2bfc03bfa2b82 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Wed, 3 Nov 2021 14:13:22 +0000 Subject: [PATCH 011/193] feat: option for preconfigured selectable service providers --- .../currency_exchange_settings/__init__.py | 0 .../currency_exchange_settings.js | 49 +++++++++++++++++ .../currency_exchange_settings.json | 52 +++++++++++-------- .../currency_exchange_settings.py | 0 .../test_currency_exchange_settings.py | 8 +++ .../__init__.py | 0 .../currency_exchange_settings_details.json | 4 +- .../currency_exchange_settings_details.py | 0 .../__init__.py | 0 .../currency_exchange_settings_result.json | 4 +- .../currency_exchange_settings_result.py | 2 +- .../currency_exchange_settings.js | 27 ---------- .../test_currency_exchange_settings.py | 8 --- 13 files changed, 92 insertions(+), 62 deletions(-) rename erpnext/{setup => accounts}/doctype/currency_exchange_settings/__init__.py (100%) create mode 100644 erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js rename erpnext/{setup => accounts}/doctype/currency_exchange_settings/currency_exchange_settings.json (88%) rename erpnext/{setup => accounts}/doctype/currency_exchange_settings/currency_exchange_settings.py (100%) create mode 100644 erpnext/accounts/doctype/currency_exchange_settings/test_currency_exchange_settings.py rename erpnext/{setup => accounts}/doctype/currency_exchange_settings_details/__init__.py (100%) rename erpnext/{setup => accounts}/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json (91%) rename erpnext/{setup => accounts}/doctype/currency_exchange_settings_details/currency_exchange_settings_details.py (100%) rename erpnext/{setup => accounts}/doctype/currency_exchange_settings_result/__init__.py (100%) rename erpnext/{setup => accounts}/doctype/currency_exchange_settings_result/currency_exchange_settings_result.json (89%) rename erpnext/{setup => accounts}/doctype/currency_exchange_settings_result/currency_exchange_settings_result.py (69%) delete mode 100644 erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.js delete mode 100644 erpnext/setup/doctype/currency_exchange_settings/test_currency_exchange_settings.py diff --git a/erpnext/setup/doctype/currency_exchange_settings/__init__.py b/erpnext/accounts/doctype/currency_exchange_settings/__init__.py similarity index 100% rename from erpnext/setup/doctype/currency_exchange_settings/__init__.py rename to erpnext/accounts/doctype/currency_exchange_settings/__init__.py diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js new file mode 100644 index 0000000000..f29183a316 --- /dev/null +++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js @@ -0,0 +1,49 @@ +// Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Currency Exchange Settings', { + service_provider: function(frm) { + if (frm.doc.service_provider == "Exchangerate.host"){ + frm.doc.api_endpoint = "https://api.exchangerate.host/convert"; + frm.clear_table("req_params") + frm.clear_table("result_key") + let params = { + date: '{transaction_date}', + from: '{from_currency}', + to: '{to_currency}' + } + var row; + $.each(params, function(key, value){ + row = frm.add_child("req_params"); + row.key = key; + row.value = value; + }) + row = frm.add_child("result_key"); + row.key = 'result'; + frm.refresh_fields(); + frm.save(); + } + else if (frm.doc.service_provider == "Frankfurter.app"){ + frm.doc.api_endpoint = "https://frankfurter.app/{transaction_date}"; + frm.clear_table("req_params") + frm.clear_table("result_key") + var row; + let result = ['rates', '{to_currency}'] + let params = { + base: '{from_currency}', + symbols: '{to_currency}' + } + $.each(params, function(key, value){ + row = frm.add_child("req_params"); + row.key = key; + row.value = value; + }) + $.each(result, function(key, value){ + row = frm.add_child("result_key"); + row.key = value; + }) + frm.refresh_fields(); + frm.save(); + } + } +}); diff --git a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.json b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json similarity index 88% rename from erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.json rename to erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json index d3d23043c4..a0530c19a3 100644 --- a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.json +++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json @@ -6,6 +6,7 @@ "engine": "InnoDB", "field_order": [ "api_details_section", + "service_provider", "api_endpoint", "url", "column_break_3", @@ -16,6 +17,11 @@ "result_key" ], "fields": [ + { + "fieldname": "api_details_section", + "fieldtype": "Section Break", + "label": "API Details" + }, { "fieldname": "api_endpoint", "fieldtype": "Data", @@ -23,15 +29,27 @@ "label": "API Endpoint", "reqd": 1 }, + { + "fieldname": "url", + "fieldtype": "Data", + "label": "Example URL", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "help", + "fieldtype": "HTML", + "label": "Help", + "options": "

Currency Exchange Settings Help

\n

There are 3 variables that could be used within the endpoint, result key and in values of the parameter.

\n

Exchange rate between {from_currency} and {to_currency} on {transaction_date} is fetched by the API.

\n

Example: If your endpoint is exchange.com/2021-08-01, then, you will have to input exchange.com/{transaction_date}

" + }, { "fieldname": "section_break_2", "fieldtype": "Section Break", "label": "Request Parameters" }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, { "fieldname": "req_params", "fieldtype": "Table", @@ -40,12 +58,7 @@ "reqd": 1 }, { - "fieldname": "api_details_section", - "fieldtype": "Section Break", - "label": "API Details" - }, - { - "fieldname": "column_break_3", + "fieldname": "column_break_4", "fieldtype": "Column Break" }, { @@ -56,24 +69,19 @@ "reqd": 1 }, { - "fieldname": "url", - "fieldtype": "Data", - "label": "Example URL", - "read_only": 1 - }, - { - "fieldname": "help", - "fieldtype": "HTML", - "label": "Help", - "options": "

Currency Exchange Settings Help

\n

There are 3 variables that could be used within the endpoint, result key and in values of the parameter.

\n

Exchange rate between {from_currency} and {to_currency} on {transaction_date} is fetched by the API.

\n

Example: If your endpoint is exchange.com/2021-08-01, then, you will have to input exchange.com/{transaction_date}

" + "fieldname": "service_provider", + "fieldtype": "Select", + "label": "Service Provider", + "options": "Exchangerate.host\nFrankfurter.app\nCustom", + "reqd": 1 } ], "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-09-04 11:41:34.375637", + "modified": "2021-11-03 19:27:27.922353", "modified_by": "Administrator", - "module": "Setup", + "module": "Accounts", "name": "Currency Exchange Settings", "owner": "Administrator", "permissions": [ diff --git a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py similarity index 100% rename from erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.py rename to erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py diff --git a/erpnext/accounts/doctype/currency_exchange_settings/test_currency_exchange_settings.py b/erpnext/accounts/doctype/currency_exchange_settings/test_currency_exchange_settings.py new file mode 100644 index 0000000000..59c579978f --- /dev/null +++ b/erpnext/accounts/doctype/currency_exchange_settings/test_currency_exchange_settings.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +import unittest + +class TestCurrencyExchangeSettings(unittest.TestCase): + pass diff --git a/erpnext/setup/doctype/currency_exchange_settings_details/__init__.py b/erpnext/accounts/doctype/currency_exchange_settings_details/__init__.py similarity index 100% rename from erpnext/setup/doctype/currency_exchange_settings_details/__init__.py rename to erpnext/accounts/doctype/currency_exchange_settings_details/__init__.py diff --git a/erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json b/erpnext/accounts/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json similarity index 91% rename from erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json rename to erpnext/accounts/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json index c9b27e1961..30935871c6 100644 --- a/erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json +++ b/erpnext/accounts/doctype/currency_exchange_settings_details/currency_exchange_settings_details.json @@ -27,9 +27,9 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-09-04 17:49:17.383982", + "modified": "2021-11-03 19:14:55.889037", "modified_by": "Administrator", - "module": "Setup", + "module": "Accounts", "name": "Currency Exchange Settings Details", "owner": "Administrator", "permissions": [], diff --git a/erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.py b/erpnext/accounts/doctype/currency_exchange_settings_details/currency_exchange_settings_details.py similarity index 100% rename from erpnext/setup/doctype/currency_exchange_settings_details/currency_exchange_settings_details.py rename to erpnext/accounts/doctype/currency_exchange_settings_details/currency_exchange_settings_details.py diff --git a/erpnext/setup/doctype/currency_exchange_settings_result/__init__.py b/erpnext/accounts/doctype/currency_exchange_settings_result/__init__.py similarity index 100% rename from erpnext/setup/doctype/currency_exchange_settings_result/__init__.py rename to erpnext/accounts/doctype/currency_exchange_settings_result/__init__.py diff --git a/erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.json b/erpnext/accounts/doctype/currency_exchange_settings_result/currency_exchange_settings_result.json similarity index 89% rename from erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.json rename to erpnext/accounts/doctype/currency_exchange_settings_result/currency_exchange_settings_result.json index 387e245a2f..fff5337616 100644 --- a/erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.json +++ b/erpnext/accounts/doctype/currency_exchange_settings_result/currency_exchange_settings_result.json @@ -19,9 +19,9 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-09-04 17:49:33.858070", + "modified": "2021-11-03 19:14:40.054245", "modified_by": "Administrator", - "module": "Setup", + "module": "Accounts", "name": "Currency Exchange Settings Result", "owner": "Administrator", "permissions": [], diff --git a/erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.py b/erpnext/accounts/doctype/currency_exchange_settings_result/currency_exchange_settings_result.py similarity index 69% rename from erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.py rename to erpnext/accounts/doctype/currency_exchange_settings_result/currency_exchange_settings_result.py index 2c4fb61ab9..49da97bd5e 100644 --- a/erpnext/setup/doctype/currency_exchange_settings_result/currency_exchange_settings_result.py +++ b/erpnext/accounts/doctype/currency_exchange_settings_result/currency_exchange_settings_result.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and contributors # For license information, please see license.txt # import frappe diff --git a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.js b/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.js deleted file mode 100644 index c48bca8b8f..0000000000 --- a/erpnext/setup/doctype/currency_exchange_settings/currency_exchange_settings.js +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Currency Exchange Settings', { - refresh: function(frm) { - frm.add_custom_button(__('Restore Defaults'), function(){ - frm.doc.api_endpoint = "https://api.exchangerate.host/convert"; - frm.clear_table("req_params") - frm.clear_table("result_key") - let params = { - date: '{transaction_date}', - from: '{from_currency}', - to: '{to_currency}' - } - var row; - $.each(params, function(key, value){ - row = frm.add_child("req_params"); - row.key = key; - row.value = value; - }) - row = frm.add_child("result_key"); - row.key = 'result'; - frm.refresh_fields(); - frm.save(); - }); - } -}); diff --git a/erpnext/setup/doctype/currency_exchange_settings/test_currency_exchange_settings.py b/erpnext/setup/doctype/currency_exchange_settings/test_currency_exchange_settings.py deleted file mode 100644 index 80a9a1768d..0000000000 --- a/erpnext/setup/doctype/currency_exchange_settings/test_currency_exchange_settings.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -# import frappe -import unittest - -class TestCurrencyExchangeSettings(unittest.TestCase): - pass From 08b2735a7928610650a8f7e880cb63f726cdb218 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Thu, 4 Nov 2021 05:01:37 +0000 Subject: [PATCH 012/193] fix: make fields editable only when service provider is custom --- .../currency_exchange_settings.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json index a0530c19a3..091102ce47 100644 --- a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json +++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json @@ -27,6 +27,7 @@ "fieldtype": "Data", "in_list_view": 1, "label": "API Endpoint", + "read_only_depends_on": "eval: doc.service_provider != \"Custom\"", "reqd": 1 }, { @@ -55,6 +56,7 @@ "fieldtype": "Table", "label": "Parameters", "options": "Currency Exchange Settings Details", + "read_only_depends_on": "eval: doc.service_provider != \"Custom\"", "reqd": 1 }, { @@ -66,6 +68,7 @@ "fieldtype": "Table", "label": "Result Key", "options": "Currency Exchange Settings Result", + "read_only_depends_on": "eval: doc.service_provider != \"Custom\"", "reqd": 1 }, { @@ -79,7 +82,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-11-03 19:27:27.922353", + "modified": "2021-11-04 10:27:09.332768", "modified_by": "Administrator", "module": "Accounts", "name": "Currency Exchange Settings", From e65a76b21425918e6b0b0bdcadef6cf431faa503 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Thu, 4 Nov 2021 05:04:10 +0000 Subject: [PATCH 013/193] chore: clean up --- .../currency_exchange_settings.js | 54 +++++++++---------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js index f29183a316..58a2b8c7a4 100644 --- a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js +++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js @@ -4,46 +4,44 @@ frappe.ui.form.on('Currency Exchange Settings', { service_provider: function(frm) { if (frm.doc.service_provider == "Exchangerate.host"){ - frm.doc.api_endpoint = "https://api.exchangerate.host/convert"; - frm.clear_table("req_params") - frm.clear_table("result_key") + let result = ['result'] let params = { date: '{transaction_date}', from: '{from_currency}', to: '{to_currency}' } - var row; - $.each(params, function(key, value){ - row = frm.add_child("req_params"); - row.key = key; - row.value = value; - }) - row = frm.add_child("result_key"); - row.key = 'result'; - frm.refresh_fields(); - frm.save(); + add_param(frm, "https://api.exchangerate.host/convert", params, result) } else if (frm.doc.service_provider == "Frankfurter.app"){ - frm.doc.api_endpoint = "https://frankfurter.app/{transaction_date}"; - frm.clear_table("req_params") - frm.clear_table("result_key") - var row; let result = ['rates', '{to_currency}'] let params = { base: '{from_currency}', symbols: '{to_currency}' } - $.each(params, function(key, value){ - row = frm.add_child("req_params"); - row.key = key; - row.value = value; - }) - $.each(result, function(key, value){ - row = frm.add_child("result_key"); - row.key = value; - }) - frm.refresh_fields(); - frm.save(); + add_param(frm, "https://frankfurter.app/{transaction_date}", params, result) } } }); + + +function add_param(frm, api, params, result){ + var row; + frm.clear_table("req_params") + frm.clear_table("result_key") + + frm.doc.api_endpoint = api; + + $.each(params, function(key, value){ + row = frm.add_child("req_params"); + row.key = key; + row.value = value; + }); + + $.each(result, function(key, value){ + row = frm.add_child("result_key"); + row.key = value; + }); + + frm.refresh_fields(); + frm.save(); +} From 1f8be8498570d8de0299f0cea5da6a566c39283b Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Thu, 4 Nov 2021 05:12:09 +0000 Subject: [PATCH 014/193] fix : remove mutable data structures from argument defaults --- erpnext/setup/utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index 4fcd7d5936..c8b4374bac 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -122,11 +122,11 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No frappe.msgprint(_("Unable to find exchange rate for {0} to {1} for key date {2}. Please create a Currency Exchange record manually").format(from_currency, to_currency, transaction_date)) return 0.0 -def format_ces_api(data="", param={}): +def format_ces_api(data, param): return data.format( - transaction_date=param["transaction_date"], - to_currency=param["to_currency"], - from_currency=param["from_currency"] + transaction_date=param.get("transaction_date"), + to_currency=param.get("to_currency"), + from_currency=param.get("from_currency") ) def enable_all_roles_and_domains(): From 4a0f2175ad22c2e16f5b5e81e151aca69bfb4c3d Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Thu, 4 Nov 2021 05:25:48 +0000 Subject: [PATCH 015/193] fix: pre-commit issues --- .../currency_exchange_settings/currency_exchange_settings.py | 3 ++- .../test_currency_exchange_settings.py | 1 + .../currency_exchange_settings_details.py | 1 + .../currency_exchange_settings_result.py | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py index fa5286a4bb..e515542f1d 100644 --- a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py +++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py @@ -3,8 +3,9 @@ import frappe from frappe import _ -from frappe.utils import nowdate from frappe.model.document import Document +from frappe.utils import nowdate + class CurrencyExchangeSettings(Document): def validate(self): diff --git a/erpnext/accounts/doctype/currency_exchange_settings/test_currency_exchange_settings.py b/erpnext/accounts/doctype/currency_exchange_settings/test_currency_exchange_settings.py index 59c579978f..2778729f58 100644 --- a/erpnext/accounts/doctype/currency_exchange_settings/test_currency_exchange_settings.py +++ b/erpnext/accounts/doctype/currency_exchange_settings/test_currency_exchange_settings.py @@ -4,5 +4,6 @@ # import frappe import unittest + class TestCurrencyExchangeSettings(unittest.TestCase): pass diff --git a/erpnext/accounts/doctype/currency_exchange_settings_details/currency_exchange_settings_details.py b/erpnext/accounts/doctype/currency_exchange_settings_details/currency_exchange_settings_details.py index f870b11c5a..a6ad7634a5 100644 --- a/erpnext/accounts/doctype/currency_exchange_settings_details/currency_exchange_settings_details.py +++ b/erpnext/accounts/doctype/currency_exchange_settings_details/currency_exchange_settings_details.py @@ -4,5 +4,6 @@ # import frappe from frappe.model.document import Document + class CurrencyExchangeSettingsDetails(Document): pass diff --git a/erpnext/accounts/doctype/currency_exchange_settings_result/currency_exchange_settings_result.py b/erpnext/accounts/doctype/currency_exchange_settings_result/currency_exchange_settings_result.py index 49da97bd5e..177412860a 100644 --- a/erpnext/accounts/doctype/currency_exchange_settings_result/currency_exchange_settings_result.py +++ b/erpnext/accounts/doctype/currency_exchange_settings_result/currency_exchange_settings_result.py @@ -4,5 +4,6 @@ # import frappe from frappe.model.document import Document + class CurrencyExchangeSettingsResult(Document): pass From 4716523c7768cb62672c06a39ab041f89256f609 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Thu, 4 Nov 2021 05:38:00 +0000 Subject: [PATCH 016/193] fix: sider issues --- .../currency_exchange_settings.js | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js index 58a2b8c7a4..22cfc906fb 100644 --- a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js +++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js @@ -3,41 +3,41 @@ frappe.ui.form.on('Currency Exchange Settings', { service_provider: function(frm) { - if (frm.doc.service_provider == "Exchangerate.host"){ - let result = ['result'] - let params = { - date: '{transaction_date}', - from: '{from_currency}', - to: '{to_currency}' - } - add_param(frm, "https://api.exchangerate.host/convert", params, result) - } - else if (frm.doc.service_provider == "Frankfurter.app"){ - let result = ['rates', '{to_currency}'] - let params = { - base: '{from_currency}', - symbols: '{to_currency}' - } - add_param(frm, "https://frankfurter.app/{transaction_date}", params, result) - } + if (frm.doc.service_provider == "Exchangerate.host") { + let result = ['result']; + let params = { + date: '{transaction_date}', + from: '{from_currency}', + to: '{to_currency}' + }; + add_param(frm, "https://api.exchangerate.host/convert", params, result); + } + else if (frm.doc.service_provider == "Frankfurter.app") { + let result = ['rates', '{to_currency}']; + let params = { + base: '{from_currency}', + symbols: '{to_currency}' + }; + add_param(frm, "https://frankfurter.app/{transaction_date}", params, result); + } } }); -function add_param(frm, api, params, result){ +function add_param(frm, api, params, result) { var row; - frm.clear_table("req_params") - frm.clear_table("result_key") + frm.clear_table("req_params"); + frm.clear_table("result_key"); frm.doc.api_endpoint = api; - $.each(params, function(key, value){ + $.each(params, function(key, value) { row = frm.add_child("req_params"); row.key = key; row.value = value; }); - $.each(result, function(key, value){ + $.each(result, function(key, value) { row = frm.add_child("result_key"); row.key = value; }); From e50893e99116b7bf5796ea91ca9459962e5dedb5 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Sat, 13 Nov 2021 13:12:38 +0530 Subject: [PATCH 017/193] fix: eslint brace style issue --- .../currency_exchange_settings/currency_exchange_settings.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js index 22cfc906fb..a3a6561d35 100644 --- a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js +++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js @@ -11,8 +11,7 @@ frappe.ui.form.on('Currency Exchange Settings', { to: '{to_currency}' }; add_param(frm, "https://api.exchangerate.host/convert", params, result); - } - else if (frm.doc.service_provider == "Frankfurter.app") { + } else if (frm.doc.service_provider == "Frankfurter.app") { let result = ['rates', '{to_currency}']; let params = { base: '{from_currency}', From c572a4cb8828a4d045b3dbdb05db6b8f093489d7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 15 Nov 2021 09:28:56 +0530 Subject: [PATCH 018/193] fix: Book unaccrued interest check --- .../loan_management/doctype/loan_repayment/loan_repayment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index a6fa639870..f7d9e6602e 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -93,7 +93,7 @@ class LoanRepayment(AccountsController): def book_unaccrued_interest(self): precision = cint(frappe.db.get_default("currency_precision")) or 2 - if self.total_interest_paid > self.interest_payable: + if flt(self.total_interest_paid, precision) > flt(self.interest_payable, precision): if not self.is_term_loan: # get last loan interest accrual date last_accrual_date = get_last_accrual_date(self.against_loan) From 6eb779392edaca95e5d90e610246164524e17b36 Mon Sep 17 00:00:00 2001 From: Anupam Date: Fri, 19 Nov 2021 10:13:05 +0530 Subject: [PATCH 019/193] feat: carry forward communication and comments throughout the sales cycle --- .../crm/doctype/opportunity/opportunity.py | 5 +++++ erpnext/crm/doctype/prospect/prospect.py | 7 ++++++ erpnext/crm/utils.py | 22 +++++++++++++++++++ .../selling/doctype/quotation/quotation.py | 10 +++++++++ 4 files changed, 44 insertions(+) diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 0bef80a749..d565ad2f2c 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -12,6 +12,7 @@ from frappe.utils import cint, cstr, flt, get_fullname from erpnext.setup.utils import get_exchange_rate from erpnext.utilities.transaction_base import TransactionBase +from erpnext.crm.utils import copy_comments, add_link_in_communication class Opportunity(TransactionBase): @@ -19,6 +20,10 @@ class Opportunity(TransactionBase): if self.opportunity_from == "Lead": frappe.get_doc("Lead", self.party_name).set_status(update=True) + if self.opportunity_from in ["Lead", "Prospect"]: + copy_comments(self.opportunity_from, self.party_name, self) + add_link_in_communication(self.opportunity_from, self.party_name, self) + def validate(self): self._prev = frappe._dict({ "contact_date": frappe.db.get_value("Opportunity", self.name, "contact_date") if \ diff --git a/erpnext/crm/doctype/prospect/prospect.py b/erpnext/crm/doctype/prospect/prospect.py index 367aa3d312..736b3dfdd5 100644 --- a/erpnext/crm/doctype/prospect/prospect.py +++ b/erpnext/crm/doctype/prospect/prospect.py @@ -6,6 +6,8 @@ from frappe.contacts.address_and_contact import load_address_and_contact from frappe.model.document import Document from frappe.model.mapper import get_mapped_doc +from erpnext.crm.utils import copy_comments, add_link_in_communication + class Prospect(Document): def onload(self): @@ -20,6 +22,11 @@ class Prospect(Document): def on_trash(self): self.unlink_dynamic_links() + def after_insert(self): + for row in self.get('prospect_lead'): + copy_comments("Lead", row.lead, self) + add_link_in_communication("Lead", row.lead, self) + def update_lead_details(self): for row in self.get('prospect_lead'): lead = frappe.get_value('Lead', row.lead, ['lead_name', 'status', 'email_id', 'mobile_no'], as_dict=True) diff --git a/erpnext/crm/utils.py b/erpnext/crm/utils.py index 95b19ec21e..531d6c1594 100644 --- a/erpnext/crm/utils.py +++ b/erpnext/crm/utils.py @@ -21,3 +21,25 @@ def update_lead_phone_numbers(contact, method): lead = frappe.get_doc("Lead", contact_lead) lead.db_set("phone", phone) lead.db_set("mobile_no", mobile_no) + +def copy_comments(doctype, docname, doc): + comments = frappe.db.get_values("Comment", filters={"reference_doctype": doctype, "reference_name": docname}, fieldname="*") + for comment in comments: + comment = frappe.get_doc(comment.update({"doctype":"Comment"})) + comment.name = None + comment.reference_doctype = doc.doctype + comment.reference_name = doc.name + comment.insert() + +def add_link_in_communication(doctype, docname, doc): + communications = frappe.get_all("Communication", filters={"reference_doctype": doctype, "reference_name": docname}, pluck='name') + communication_links = frappe.get_all('Communication Link', + { + "link_doctype": doctype, + "link_name": docname, + "parent": ("not in", communications) + }, pluck="parent") + + for communication in communications + communication_links: + communication_doc = frappe.get_doc("Communication", communication) + communication_doc.add_link(doc.doctype, doc.name, autosave=True) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index c4752aebb5..adea9ec406 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -8,6 +8,7 @@ from frappe.model.mapper import get_mapped_doc from frappe.utils import flt, getdate, nowdate from erpnext.controllers.selling_controller import SellingController +from erpnext.crm.utils import copy_comments, add_link_in_communication form_grid_templates = { "items": "templates/form_grid/item_grid.html" @@ -34,6 +35,15 @@ class Quotation(SellingController): from erpnext.stock.doctype.packed_item.packed_item import make_packing_list make_packing_list(self) + def after_insert(self): + if self.opportunity: + copy_comments("Opportunity", self.opportunity, self) + add_link_in_communication("Opportunity", self.opportunity, self) + + elif self.quotation_to == "Lead" and self.party_name: + copy_comments("Lead", self.party_name, self) + add_link_in_communication("Lead", self.party_name, self) + def validate_valid_till(self): if self.valid_till and getdate(self.valid_till) < getdate(self.transaction_date): frappe.throw(_("Valid till date cannot be before transaction date")) From 6dda1548bc021f15b54db2efc43bd8905eac59d8 Mon Sep 17 00:00:00 2001 From: Anupam Date: Fri, 19 Nov 2021 10:22:50 +0530 Subject: [PATCH 020/193] fix: linter issues --- erpnext/crm/doctype/opportunity/opportunity.py | 2 +- erpnext/crm/doctype/prospect/prospect.py | 2 +- erpnext/selling/doctype/quotation/quotation.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index d565ad2f2c..2c990c4133 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -10,9 +10,9 @@ from frappe.email.inbox import link_communication_to_document from frappe.model.mapper import get_mapped_doc from frappe.utils import cint, cstr, flt, get_fullname +rom erpnext.crm.utils import add_link_in_communication, copy_comments from erpnext.setup.utils import get_exchange_rate from erpnext.utilities.transaction_base import TransactionBase -from erpnext.crm.utils import copy_comments, add_link_in_communication class Opportunity(TransactionBase): diff --git a/erpnext/crm/doctype/prospect/prospect.py b/erpnext/crm/doctype/prospect/prospect.py index 736b3dfdd5..c2553106db 100644 --- a/erpnext/crm/doctype/prospect/prospect.py +++ b/erpnext/crm/doctype/prospect/prospect.py @@ -6,7 +6,7 @@ from frappe.contacts.address_and_contact import load_address_and_contact from frappe.model.document import Document from frappe.model.mapper import get_mapped_doc -from erpnext.crm.utils import copy_comments, add_link_in_communication +from erpnext.crm.utils import add_link_in_communication, copy_comments class Prospect(Document): diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index adea9ec406..979d5c6c96 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -8,7 +8,7 @@ from frappe.model.mapper import get_mapped_doc from frappe.utils import flt, getdate, nowdate from erpnext.controllers.selling_controller import SellingController -from erpnext.crm.utils import copy_comments, add_link_in_communication +from erpnext.crm.utils import add_link_in_communication, copy_comments form_grid_templates = { "items": "templates/form_grid/item_grid.html" From 6210f10207e86a192af943196b44821aa31de0f5 Mon Sep 17 00:00:00 2001 From: Anupam Date: Fri, 19 Nov 2021 10:32:04 +0530 Subject: [PATCH 021/193] fix: linter issues --- erpnext/crm/doctype/opportunity/opportunity.py | 2 +- erpnext/crm/utils.py | 2 +- erpnext/selling/doctype/quotation/quotation.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 2c990c4133..3a04df0f50 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -10,7 +10,7 @@ from frappe.email.inbox import link_communication_to_document from frappe.model.mapper import get_mapped_doc from frappe.utils import cint, cstr, flt, get_fullname -rom erpnext.crm.utils import add_link_in_communication, copy_comments +from erpnext.crm.utils import add_link_in_communication, copy_comments from erpnext.setup.utils import get_exchange_rate from erpnext.utilities.transaction_base import TransactionBase diff --git a/erpnext/crm/utils.py b/erpnext/crm/utils.py index 531d6c1594..8ae991ff17 100644 --- a/erpnext/crm/utils.py +++ b/erpnext/crm/utils.py @@ -39,7 +39,7 @@ def add_link_in_communication(doctype, docname, doc): "link_name": docname, "parent": ("not in", communications) }, pluck="parent") - + for communication in communications + communication_links: communication_doc = frappe.get_doc("Communication", communication) communication_doc.add_link(doc.doctype, doc.name, autosave=True) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 979d5c6c96..2bb9b5582a 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -39,7 +39,7 @@ class Quotation(SellingController): if self.opportunity: copy_comments("Opportunity", self.opportunity, self) add_link_in_communication("Opportunity", self.opportunity, self) - + elif self.quotation_to == "Lead" and self.party_name: copy_comments("Lead", self.party_name, self) add_link_in_communication("Lead", self.party_name, self) From d6bc121999352d300dc3aa188d14092a226d8e60 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 25 Nov 2021 13:15:25 +0530 Subject: [PATCH 022/193] fix: Validate Finished Goods for Independent Manufacture entries as well - Check if FG exists even if WO is absent - Check if multiple FGs are there, block. --- .../stock/doctype/stock_entry/stock_entry.py | 53 +++++++++++++------ 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index d31e65a4cc..bc722b31b2 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -719,21 +719,34 @@ class StockEntry(StockController): return finished_item def validate_finished_goods(self): - """validation: finished good quantity should be same as manufacturing quantity""" - if not self.work_order: return + """ + 1. Check if FG exists + 2. Check if Multiple FG Items are present + 3. Check FG Item and Qty against WO if present + """ + production_item, wo_qty, finished_items = None, 0, [] - production_item, wo_qty = frappe.db.get_value("Work Order", - self.work_order, ["production_item", "qty"]) + wo_details = frappe.db.get_value( + "Work Order", self.work_order, ["production_item", "qty"] + ) + if wo_details: + production_item, wo_qty = wo_details - finished_items = [] for d in self.get('items'): if d.is_finished_item: + if not self.work_order: + finished_items.append(d.item_code) + continue # Independent Manufacture Entry, no WO to match against + if d.item_code != production_item: frappe.throw(_("Finished Item {0} does not match with Work Order {1}") - .format(d.item_code, self.work_order)) + .format(d.item_code, self.work_order) + ) elif flt(d.transfer_qty) > flt(self.fg_completed_qty): - frappe.throw(_("Quantity in row {0} ({1}) must be same as manufactured quantity {2}"). \ - format(d.idx, d.transfer_qty, self.fg_completed_qty)) + frappe.throw(_("Quantity in row {0} ({1}) must be same as manufactured quantity {2}") + .format(d.idx, d.transfer_qty, self.fg_completed_qty) + ) + finished_items.append(d.item_code) if len(set(finished_items)) > 1: @@ -741,16 +754,24 @@ class StockEntry(StockController): if self.purpose == "Manufacture": if not finished_items: - frappe.throw(_('Finished Good has not set in the stock entry {0}') - .format(self.name)) + frappe.throw( + msg=_("There must be atleast 1 Finished Good in this Stock Entry").format(self.name), + title=_("Missing Finished Good") + ) - allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings", - "overproduction_percentage_for_work_order")) + allowance_percentage = flt( + frappe.db.get_single_value( + "Manufacturing Settings","overproduction_percentage_for_work_order" + ) + ) + allowed_qty = wo_qty + ((allowance_percentage/100) * wo_qty) - allowed_qty = wo_qty + (allowance_percentage/100 * wo_qty) - if self.fg_completed_qty > allowed_qty: - frappe.throw(_("For quantity {0} should not be greater than work order quantity {1}") - .format(flt(self.fg_completed_qty), wo_qty)) + # No work order could mean independent Manufacture entry, if so skip validation + if self.work_order and self.fg_completed_qty > allowed_qty: + frappe.throw( + _("For quantity {0} should not be greater than work order quantity {1}") + .format(flt(self.fg_completed_qty), wo_qty) + ) def update_stock_ledger(self): sl_entries = [] From 8c9779d69dca3744fc057aafeecdf2c31f622d60 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 25 Nov 2021 13:23:10 +0530 Subject: [PATCH 023/193] fix: Dont auto set is finished item or is scrap itm checkbox for independent entry - System cant differentiate between scrap and FG when WO is absent, so dont auto set - User must set it manually - `validate_finished_goods` checks this --- erpnext/stock/doctype/stock_entry/stock_entry.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index bc722b31b2..d3295d6159 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -699,6 +699,11 @@ class StockEntry(StockController): finished_item = self.get_finished_item() + if not finished_item: + # In case of independent Manufacture entry, don't auto set + # user must decide and set + return + for d in self.items: if d.t_warehouse and not d.s_warehouse: if self.purpose=="Repack" or d.item_code == finished_item: From affb29194bc68a2faf1f16bb40cefc4bc307cec1 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 26 Nov 2021 13:19:04 +0530 Subject: [PATCH 024/193] test: Independent Manufacture Entry - Check validations - Check if FG basic rate is calculated correctly --- .../stock/doctype/stock_entry/stock_entry.py | 10 ++++-- .../doctype/stock_entry/test_stock_entry.py | 34 ++++++++++++++++++- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index d3295d6159..30e0bb8455 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -35,6 +35,7 @@ from erpnext.stock.stock_ledger import NegativeStockError, get_previous_sle, get from erpnext.stock.utils import get_bin, get_incoming_rate +class FinishedGoodError(frappe.ValidationError): pass class IncorrectValuationRateError(frappe.ValidationError): pass class DuplicateEntryForWorkOrderError(frappe.ValidationError): pass class OperationsNotCompleteError(frappe.ValidationError): pass @@ -755,13 +756,18 @@ class StockEntry(StockController): finished_items.append(d.item_code) if len(set(finished_items)) > 1: - frappe.throw(_("Multiple items cannot be marked as finished item")) + frappe.throw( + msg=_("Multiple items cannot be marked as finished item"), + title=_("Note"), + exc=FinishedGoodError + ) if self.purpose == "Manufacture": if not finished_items: frappe.throw( msg=_("There must be atleast 1 Finished Good in this Stock Entry").format(self.name), - title=_("Missing Finished Good") + title=_("Missing Finished Good"), + exc=FinishedGoodError ) allowance_percentage = flt( diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 067946785a..59cadc4d70 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -15,7 +15,7 @@ from erpnext.stock.doctype.item.test_item import ( set_item_variant_settings, ) from erpnext.stock.doctype.serial_no.serial_no import * # noqa -from erpnext.stock.doctype.stock_entry.stock_entry import move_sample_to_retention_warehouse +from erpnext.stock.doctype.stock_entry.stock_entry import (move_sample_to_retention_warehouse, FinishedGoodError) from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import StockFreezeError from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import ( @@ -868,6 +868,38 @@ class TestStockEntry(unittest.TestCase): distributed_costs = [d.additional_cost for d in se.items] self.assertEqual([40.0, 60.0], distributed_costs) + def test_independent_manufacture_entry(self): + "Test FG items and incoming rate calculation in Maniufacture Entry without WO or BOM linked." + se = frappe.get_doc( + doctype="Stock Entry", + purpose="Manufacture", + stock_entry_type="Manufacture", + company="_Test Company", + items=[ + frappe._dict(item_code="_Test Item", qty=1, basic_rate=200, s_warehouse="_Test Warehouse - _TC"), + frappe._dict(item_code="_Test FG Item", qty=4, t_warehouse="_Test Warehouse 1 - _TC") + ] + ) + # SE must have atleast one FG + self.assertRaises(FinishedGoodError, se.save) + + se.items[0].is_finished_item = 1 + se.items[1].is_finished_item = 1 + # SE cannot have multiple FGs + self.assertRaises(FinishedGoodError, se.save) + + se.items[0].is_finished_item = 0 + se.save() + + # Check if FG cost is calculated based on RM total cost + # RM total cost = 200, FG rate = 200/4(FG qty) = 50 + self.assertEqual(se.items[1].basic_rate, 50) + self.assertEqual(se.value_difference, 0.0) + self.assertEqual(se.total_incoming_value, se.total_outgoing_value) + + # teardown + se.delete() + def make_serialized_item(**args): args = frappe._dict(args) se = frappe.copy_doc(test_records[0]) From dfff972a78e802e3364548bf6378361e51a4c46a Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 26 Nov 2021 13:25:23 +0530 Subject: [PATCH 025/193] fix: Linter (imports) --- erpnext/stock/doctype/stock_entry/test_stock_entry.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 59cadc4d70..1d59e5d6e0 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -15,7 +15,10 @@ from erpnext.stock.doctype.item.test_item import ( set_item_variant_settings, ) from erpnext.stock.doctype.serial_no.serial_no import * # noqa -from erpnext.stock.doctype.stock_entry.stock_entry import (move_sample_to_retention_warehouse, FinishedGoodError) +from erpnext.stock.doctype.stock_entry.stock_entry import ( + FinishedGoodError, + move_sample_to_retention_warehouse, +) from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import StockFreezeError from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import ( From ad5d7897a8b032c3152e344450b785d60cf8ab17 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Thu, 9 Dec 2021 13:31:54 +0000 Subject: [PATCH 026/193] feat: ledger merger doctype --- .../accounts/doctype/ledger_merge/__init__.py | 0 .../doctype/ledger_merge/ledger_merge.js | 88 ++++++++++++++ .../doctype/ledger_merge/ledger_merge.json | 109 ++++++++++++++++++ .../doctype/ledger_merge/ledger_merge.py | 8 ++ .../doctype/ledger_merge/test_ledger_merge.py | 8 ++ .../doctype/ledger_merge_accounts/__init__.py | 0 .../ledger_merge_accounts.json | 43 +++++++ .../ledger_merge_accounts.py | 8 ++ 8 files changed, 264 insertions(+) create mode 100644 erpnext/accounts/doctype/ledger_merge/__init__.py create mode 100644 erpnext/accounts/doctype/ledger_merge/ledger_merge.js create mode 100644 erpnext/accounts/doctype/ledger_merge/ledger_merge.json create mode 100644 erpnext/accounts/doctype/ledger_merge/ledger_merge.py create mode 100644 erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py create mode 100644 erpnext/accounts/doctype/ledger_merge_accounts/__init__.py create mode 100644 erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.json create mode 100644 erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.py diff --git a/erpnext/accounts/doctype/ledger_merge/__init__.py b/erpnext/accounts/doctype/ledger_merge/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/doctype/ledger_merge/ledger_merge.js b/erpnext/accounts/doctype/ledger_merge/ledger_merge.js new file mode 100644 index 0000000000..ff69ddce97 --- /dev/null +++ b/erpnext/accounts/doctype/ledger_merge/ledger_merge.js @@ -0,0 +1,88 @@ +// Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Ledger Merge', { + setup: function(frm) { + frm.set_query("account", function(doc) { + if (!doc.company) frappe.throw(__('Please set Company')); + if (!doc.root_type) frappe.throw(__('Please set Root Type')); + return { + filters: { + is_group: 0, + root_type: doc.root_type, + company: doc.company + } + } + }); + + frm.set_query('account', 'merge_accounts', function(doc, cdt, cdn) { + if (!doc.company) frappe.throw(__('Please set Company')); + if (!doc.root_type) frappe.throw(__('Please set Root Type')); + if (!doc.account) frappe.throw(__('Please set Account')); + return { + filters: { + is_group: 0, + root_type: doc.root_type, + name: ["!=", doc.account], + company: doc.company + } + } + }); + }, + + refresh: function(frm) { + frm.page.hide_icon_group(); + }, + + onload_post_render: function(frm) { + frm.trigger('update_primary_action'); + }, + + after_save: function(frm) { + frm.trigger('update_primary_action'); + }, + + update_primary_action: function(frm) { + if (frm.is_dirty()) { + frm.enable_save(); + return; + } + frm.disable_save(); + if (frm.doc.status !== 'Success') { + if (!frm.is_new()) { + let label = + frm.doc.status === 'Pending' ? __('Start Merge') : __('Retry'); + frm.page.set_primary_action(label, () => frm.events.start_merge(frm)); + } else { + frm.page.set_primary_action(__('Save'), () => frm.save()); + } + } + }, + + start_merge: function(frm) { + console.log('Hi'); + frm.trigger('set_merge_status'); + }, + + set_merge_status: function(frm) { + if (frm.doc.status == "Pending") return; + let successful_records = 0; + frm.doc.merge_accounts.forEach((row) => { + if(row.merged) successful_records += 1; + }); + let message_args = [successful_records, frm.doc.merge_accounts.length]; + frm.dashboard.set_headline(__('Successfully merged {0} out of {1}.', message_args)); + } +}); + +frappe.ui.form.on('Ledger Merge Accounts', { + merge_accounts_add: function(frm, cdt, cdn) { + frm.trigger('update_primary_action'); + }, + merge_accounts_remove: function(frm, cdt, cdn) { + frm.trigger('update_primary_action'); + }, + account: function(frm, cdt, cdn) { + frm.trigger('update_primary_action'); + } +}) diff --git a/erpnext/accounts/doctype/ledger_merge/ledger_merge.json b/erpnext/accounts/doctype/ledger_merge/ledger_merge.json new file mode 100644 index 0000000000..b3652bbabd --- /dev/null +++ b/erpnext/accounts/doctype/ledger_merge/ledger_merge.json @@ -0,0 +1,109 @@ +{ + "actions": [], + "autoname": "format:{account} merger on {creation}", + "creation": "2021-12-09 15:38:04.556584", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "section_break_1", + "root_type", + "account", + "column_break_3", + "company", + "status", + "section_break_5", + "merge_accounts" + ], + "fields": [ + { + "depends_on": "root_type", + "fieldname": "account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Account", + "options": "Account", + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "section_break_1", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "merge_accounts", + "fieldtype": "Table", + "label": "Accounts to Merge", + "options": "Ledger Merge Accounts", + "reqd": 1 + }, + { + "depends_on": "account", + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "options": "Pending\nSuccess\nPartial Success\nError", + "read_only": 1 + }, + { + "fieldname": "root_type", + "fieldtype": "Select", + "label": "Root Type", + "options": "\nAsset\nLiability\nIncome\nExpense\nEquity", + "reqd": 1, + "set_only_once": 1 + } + ], + "hide_toolbar": 1, + "links": [], + "modified": "2021-12-09 18:35:30.720538", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Ledger Merge", + "naming_rule": "Expression", + "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, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/ledger_merge/ledger_merge.py b/erpnext/accounts/doctype/ledger_merge/ledger_merge.py new file mode 100644 index 0000000000..bf12ff5b2a --- /dev/null +++ b/erpnext/accounts/doctype/ledger_merge/ledger_merge.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe.model.document import Document + +class LedgerMerge(Document): + pass diff --git a/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py b/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py new file mode 100644 index 0000000000..c24caedda8 --- /dev/null +++ b/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + +class TestLedgerMerge(unittest.TestCase): + pass diff --git a/erpnext/accounts/doctype/ledger_merge_accounts/__init__.py b/erpnext/accounts/doctype/ledger_merge_accounts/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.json b/erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.json new file mode 100644 index 0000000000..f5dab369a7 --- /dev/null +++ b/erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.json @@ -0,0 +1,43 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2021-12-09 15:44:58.033398", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "account", + "merged" + ], + "fields": [ + { + "columns": 8, + "fieldname": "account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Account", + "options": "Account", + "reqd": 1 + }, + { + "columns": 2, + "default": "0", + "fieldname": "merged", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Merged", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-12-09 15:50:09.047183", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Ledger Merge Accounts", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.py b/erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.py new file mode 100644 index 0000000000..1b37095735 --- /dev/null +++ b/erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class LedgerMergeAccounts(Document): + pass From 57a7633f9b0d7c5409d92ea2e33e7cd9c5529808 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Thu, 9 Dec 2021 15:22:46 +0000 Subject: [PATCH 027/193] feat: form updates and progressbar --- .../doctype/ledger_merge/ledger_merge.js | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/ledger_merge/ledger_merge.js b/erpnext/accounts/doctype/ledger_merge/ledger_merge.js index ff69ddce97..6a768ca972 100644 --- a/erpnext/accounts/doctype/ledger_merge/ledger_merge.js +++ b/erpnext/accounts/doctype/ledger_merge/ledger_merge.js @@ -3,6 +3,27 @@ frappe.ui.form.on('Ledger Merge', { setup: function(frm) { + frappe.realtime.on('data_import_refresh', ({ ledger_merge }) => { + if (ledger_merge !== frm.doc.name) return; + frm.refresh(); + }); + + frappe.realtime.on('data_import_progress', data => { + if (data.data_import !== frm.doc.name) return; + let message = __('Merging {0} of {1}', [data.current, data.total]); + let percent = Math.floor((data.current * 100) / data.total); + frm.dashboard.show_progress(__('Merge Progress'), percent, message); + frm.page.set_indicator(__('In Progress'), 'orange'); + + // hide progress when complete + if (data.current === data.total) { + setTimeout(() => { + frm.dashboard.hide(); + frm.refresh(); + }, 2000); + } + }); + frm.set_query("account", function(doc) { if (!doc.company) frappe.throw(__('Please set Company')); if (!doc.root_type) frappe.throw(__('Please set Root Type')); @@ -32,6 +53,7 @@ frappe.ui.form.on('Ledger Merge', { refresh: function(frm) { frm.page.hide_icon_group(); + frm.trigger('set_merge_status'); }, onload_post_render: function(frm) { @@ -60,8 +82,15 @@ frappe.ui.form.on('Ledger Merge', { }, start_merge: function(frm) { - console.log('Hi'); - frm.trigger('set_merge_status'); + frm.call({ + method: 'form_start_merge', + args: { docname: frm.doc.name }, + btn: frm.page.btn_primary + }).then(r => { + if (r.message === true) { + frm.disable_save(); + } + }); }, set_merge_status: function(frm) { @@ -72,6 +101,16 @@ frappe.ui.form.on('Ledger Merge', { }); let message_args = [successful_records, frm.doc.merge_accounts.length]; frm.dashboard.set_headline(__('Successfully merged {0} out of {1}.', message_args)); + }, + + root_type: function(frm) { + frm.set_value('account', ''); + frm.set_value('merge_accounts', []); + }, + + company: function(frm) { + frm.set_value('account', ''); + frm.set_value('merge_accounts', []); } }); From 75f8117daf4257d6ae275a195cc285e9f276ad4f Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Thu, 9 Dec 2021 15:23:10 +0000 Subject: [PATCH 028/193] feat: merge functionality --- .../doctype/ledger_merge/ledger_merge.py | 63 ++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/ledger_merge/ledger_merge.py b/erpnext/accounts/doctype/ledger_merge/ledger_merge.py index bf12ff5b2a..48ac1e7622 100644 --- a/erpnext/accounts/doctype/ledger_merge/ledger_merge.py +++ b/erpnext/accounts/doctype/ledger_merge/ledger_merge.py @@ -4,5 +4,66 @@ import frappe from frappe.model.document import Document +from erpnext.accounts.doctype.account.account import merge_account + + class LedgerMerge(Document): - pass + def start_merge(self): + from frappe.core.page.background_jobs.background_jobs import get_info + from frappe.utils.background_jobs import enqueue + from frappe.utils.scheduler import is_scheduler_inactive + + if is_scheduler_inactive() and not frappe.flags.in_test: + frappe.throw( + _("Scheduler is inactive. Cannot merge accounts."), title=_("Scheduler Inactive") + ) + + enqueued_jobs = [d.get("job_name") for d in get_info()] + + if self.name not in enqueued_jobs: + enqueue( + start_merge, + queue="default", + timeout=6000, + event="ledger_merge", + job_name=self.name, + docname=self.name, + now=frappe.conf.developer_mode or frappe.flags.in_test, + ) + return True + + return False + +@frappe.whitelist() +def form_start_merge(docname): + return frappe.get_doc("Ledger Merge", docname).start_merge() + +def start_merge(docname): + ledger_merge = frappe.get_doc("Ledger Merge", docname) + successful_merges = 0 + total = len(ledger_merge.merge_accounts) + for row in ledger_merge.merge_accounts: + try: + merge_account(row.account, ledger_merge.account, 0, ledger_merge.root_type, ledger_merge.company) + row.db_set('merged', 1) + frappe.db.commit() + successful_merges += 1 + frappe.publish_realtime("ledger_merge_progress", { + "ledger_merge": ledger_merge.name, + "current": successful_merges, + "total": total + } + ) + except Exception: + frappe.db.rollback() + ledger_merge.db_set("status", "Error") + frappe.log_error(title=ledger_merge.name) + finally: + if successful_merges == total: + ledger_merge.db_set('status', 'Success') + elif successful_merges > 0: + ledger_merge.db_set('status', 'Partial Success') + else: + ledger_merge.db_set('status', 'Error') + + frappe.publish_realtime("ledger_merge_refresh", {"ledger_merge": ledger_merge.name}) From 7554f112600c1e35aff4523d038827f306be4881 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Fri, 10 Dec 2021 08:45:47 +0000 Subject: [PATCH 029/193] feat: progress bar, account name field --- .../doctype/ledger_merge/ledger_merge.js | 21 ++++----- .../doctype/ledger_merge/ledger_merge.json | 15 ++++++- .../doctype/ledger_merge/ledger_merge.py | 45 ++++++++++--------- .../ledger_merge_accounts.json | 14 +++++- 4 files changed, 57 insertions(+), 38 deletions(-) diff --git a/erpnext/accounts/doctype/ledger_merge/ledger_merge.js b/erpnext/accounts/doctype/ledger_merge/ledger_merge.js index 6a768ca972..ffc4ac43d3 100644 --- a/erpnext/accounts/doctype/ledger_merge/ledger_merge.js +++ b/erpnext/accounts/doctype/ledger_merge/ledger_merge.js @@ -3,25 +3,17 @@ frappe.ui.form.on('Ledger Merge', { setup: function(frm) { - frappe.realtime.on('data_import_refresh', ({ ledger_merge }) => { + frappe.realtime.on('ledger_merge_refresh', ({ ledger_merge }) => { if (ledger_merge !== frm.doc.name) return; frm.refresh(); }); - frappe.realtime.on('data_import_progress', data => { - if (data.data_import !== frm.doc.name) return; + frappe.realtime.on('ledger_merge_progress', data => { + if (data.ledger_merge !== frm.doc.name) return; let message = __('Merging {0} of {1}', [data.current, data.total]); let percent = Math.floor((data.current * 100) / data.total); frm.dashboard.show_progress(__('Merge Progress'), percent, message); frm.page.set_indicator(__('In Progress'), 'orange'); - - // hide progress when complete - if (data.current === data.total) { - setTimeout(() => { - frm.dashboard.hide(); - frm.refresh(); - }, 2000); - } }); frm.set_query("account", function(doc) { @@ -61,10 +53,15 @@ frappe.ui.form.on('Ledger Merge', { }, after_save: function(frm) { - frm.trigger('update_primary_action'); + setTimeout(() => { + frm.trigger('update_primary_action'); + }, 750); }, update_primary_action: function(frm) { + console.log(!frm.is_new()); + console.log(frm.is_dirty()); + console.log("-"); if (frm.is_dirty()) { frm.enable_save(); return; diff --git a/erpnext/accounts/doctype/ledger_merge/ledger_merge.json b/erpnext/accounts/doctype/ledger_merge/ledger_merge.json index b3652bbabd..641b462a29 100644 --- a/erpnext/accounts/doctype/ledger_merge/ledger_merge.json +++ b/erpnext/accounts/doctype/ledger_merge/ledger_merge.json @@ -1,6 +1,6 @@ { "actions": [], - "autoname": "format:{account} merger on {creation}", + "autoname": "format:{account_name} merger on {creation}", "creation": "2021-12-09 15:38:04.556584", "doctype": "DocType", "editable_grid": 1, @@ -9,6 +9,7 @@ "section_break_1", "root_type", "account", + "account_name", "column_break_3", "company", "status", @@ -68,11 +69,21 @@ "options": "\nAsset\nLiability\nIncome\nExpense\nEquity", "reqd": 1, "set_only_once": 1 + }, + { + "depends_on": "account", + "fetch_from": "account.account_name", + "fetch_if_empty": 1, + "fieldname": "account_name", + "fieldtype": "Data", + "label": "Account Name", + "read_only": 1, + "reqd": 1 } ], "hide_toolbar": 1, "links": [], - "modified": "2021-12-09 18:35:30.720538", + "modified": "2021-12-09 23:41:11.097097", "modified_by": "Administrator", "module": "Accounts", "name": "Ledger Merge", diff --git a/erpnext/accounts/doctype/ledger_merge/ledger_merge.py b/erpnext/accounts/doctype/ledger_merge/ledger_merge.py index 48ac1e7622..a23f565841 100644 --- a/erpnext/accounts/doctype/ledger_merge/ledger_merge.py +++ b/erpnext/accounts/doctype/ledger_merge/ledger_merge.py @@ -43,27 +43,28 @@ def start_merge(docname): successful_merges = 0 total = len(ledger_merge.merge_accounts) for row in ledger_merge.merge_accounts: - try: - merge_account(row.account, ledger_merge.account, 0, ledger_merge.root_type, ledger_merge.company) - row.db_set('merged', 1) - frappe.db.commit() - successful_merges += 1 - frappe.publish_realtime("ledger_merge_progress", { - "ledger_merge": ledger_merge.name, - "current": successful_merges, - "total": total - } - ) - except Exception: - frappe.db.rollback() - ledger_merge.db_set("status", "Error") - frappe.log_error(title=ledger_merge.name) - finally: - if successful_merges == total: - ledger_merge.db_set('status', 'Success') - elif successful_merges > 0: - ledger_merge.db_set('status', 'Partial Success') - else: - ledger_merge.db_set('status', 'Error') + if not row.merged: + try: + merge_account(row.account, ledger_merge.account, 0, ledger_merge.root_type, ledger_merge.company) + row.db_set('merged', 1) + frappe.db.commit() + successful_merges += 1 + frappe.publish_realtime("ledger_merge_progress", { + "ledger_merge": ledger_merge.name, + "current": successful_merges, + "total": total + } + ) + except Exception: + frappe.db.rollback() + ledger_merge.db_set("status", "Error") + frappe.log_error(title=ledger_merge.name) + finally: + if successful_merges == total: + ledger_merge.db_set('status', 'Success') + elif successful_merges > 0: + ledger_merge.db_set('status', 'Partial Success') + else: + ledger_merge.db_set('status', 'Error') frappe.publish_realtime("ledger_merge_refresh", {"ledger_merge": ledger_merge.name}) diff --git a/erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.json b/erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.json index f5dab369a7..524e480892 100644 --- a/erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.json +++ b/erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.json @@ -7,11 +7,12 @@ "engine": "InnoDB", "field_order": [ "account", + "account_name", "merged" ], "fields": [ { - "columns": 8, + "columns": 4, "fieldname": "account", "fieldtype": "Link", "in_list_view": 1, @@ -27,12 +28,21 @@ "in_list_view": 1, "label": "Merged", "read_only": 1 + }, + { + "columns": 4, + "fetch_from": "account.account_name", + "fieldname": "account_name", + "fieldtype": "Data", + "label": "Account Name", + "read_only": 1, + "reqd": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-12-09 15:50:09.047183", + "modified": "2021-12-09 23:19:15.193921", "modified_by": "Administrator", "module": "Accounts", "name": "Ledger Merge Accounts", From 614b9270e7562e31293b9898e79b6c0269ed56c8 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Fri, 10 Dec 2021 09:45:00 +0000 Subject: [PATCH 030/193] fix: convert whitespace to tabs --- .../doctype/ledger_merge/ledger_merge.js | 134 +++++++++--------- .../ledger_merge_accounts.py | 1 + 2 files changed, 67 insertions(+), 68 deletions(-) diff --git a/erpnext/accounts/doctype/ledger_merge/ledger_merge.js b/erpnext/accounts/doctype/ledger_merge/ledger_merge.js index ffc4ac43d3..871edc71fd 100644 --- a/erpnext/accounts/doctype/ledger_merge/ledger_merge.js +++ b/erpnext/accounts/doctype/ledger_merge/ledger_merge.js @@ -2,8 +2,8 @@ // For license information, please see license.txt frappe.ui.form.on('Ledger Merge', { - setup: function(frm) { - frappe.realtime.on('ledger_merge_refresh', ({ ledger_merge }) => { + setup: function(frm) { + frappe.realtime.on('ledger_merge_refresh', ({ ledger_merge }) => { if (ledger_merge !== frm.doc.name) return; frm.refresh(); }); @@ -11,57 +11,54 @@ frappe.ui.form.on('Ledger Merge', { frappe.realtime.on('ledger_merge_progress', data => { if (data.ledger_merge !== frm.doc.name) return; let message = __('Merging {0} of {1}', [data.current, data.total]); - let percent = Math.floor((data.current * 100) / data.total); + let percent = Math.floor((data.current * 100) / data.total); frm.dashboard.show_progress(__('Merge Progress'), percent, message); frm.page.set_indicator(__('In Progress'), 'orange'); }); - frm.set_query("account", function(doc) { - if (!doc.company) frappe.throw(__('Please set Company')); - if (!doc.root_type) frappe.throw(__('Please set Root Type')); - return { - filters: { - is_group: 0, - root_type: doc.root_type, - company: doc.company - } - } - }); - - frm.set_query('account', 'merge_accounts', function(doc, cdt, cdn) { - if (!doc.company) frappe.throw(__('Please set Company')); - if (!doc.root_type) frappe.throw(__('Please set Root Type')); - if (!doc.account) frappe.throw(__('Please set Account')); + frm.set_query("account", function(doc) { + if (!doc.company) frappe.throw(__('Please set Company')); + if (!doc.root_type) frappe.throw(__('Please set Root Type')); return { - filters: { - is_group: 0, - root_type: doc.root_type, - name: ["!=", doc.account], - company: doc.company - } - } + filters: { + is_group: 0, + root_type: doc.root_type, + company: doc.company + } + } }); - }, - refresh: function(frm) { - frm.page.hide_icon_group(); - frm.trigger('set_merge_status'); + frm.set_query('account', 'merge_accounts', function(doc, cdt, cdn) { + if (!doc.company) frappe.throw(__('Please set Company')); + if (!doc.root_type) frappe.throw(__('Please set Root Type')); + if (!doc.account) frappe.throw(__('Please set Account')); + return { + filters: { + is_group: 0, + root_type: doc.root_type, + name: ["!=", doc.account], + company: doc.company + } + } + }); }, - onload_post_render: function(frm) { + refresh: function(frm) { + frm.page.hide_icon_group(); + frm.trigger('set_merge_status'); + }, + + onload_post_render: function(frm) { frm.trigger('update_primary_action'); }, - after_save: function(frm) { - setTimeout(() => { - frm.trigger('update_primary_action'); - }, 750); + after_save: function(frm) { + setTimeout(() => { + frm.trigger('update_primary_action'); + }, 500); }, update_primary_action: function(frm) { - console.log(!frm.is_new()); - console.log(frm.is_dirty()); - console.log("-"); if (frm.is_dirty()) { frm.enable_save(); return; @@ -69,8 +66,7 @@ frappe.ui.form.on('Ledger Merge', { frm.disable_save(); if (frm.doc.status !== 'Success') { if (!frm.is_new()) { - let label = - frm.doc.status === 'Pending' ? __('Start Merge') : __('Retry'); + let label = frm.doc.status === 'Pending' ? __('Start Merge') : __('Retry'); frm.page.set_primary_action(label, () => frm.events.start_merge(frm)); } else { frm.page.set_primary_action(__('Save'), () => frm.save()); @@ -78,8 +74,8 @@ frappe.ui.form.on('Ledger Merge', { } }, - start_merge: function(frm) { - frm.call({ + start_merge: function(frm) { + frm.call({ method: 'form_start_merge', args: { docname: frm.doc.name }, btn: frm.page.btn_primary @@ -88,37 +84,39 @@ frappe.ui.form.on('Ledger Merge', { frm.disable_save(); } }); - }, + }, - set_merge_status: function(frm) { - if (frm.doc.status == "Pending") return; - let successful_records = 0; - frm.doc.merge_accounts.forEach((row) => { + set_merge_status: function(frm) { + if (frm.doc.status == "Pending") return; + let successful_records = 0; + frm.doc.merge_accounts.forEach((row) => { if(row.merged) successful_records += 1; }); - let message_args = [successful_records, frm.doc.merge_accounts.length]; - frm.dashboard.set_headline(__('Successfully merged {0} out of {1}.', message_args)); - }, + let message_args = [successful_records, frm.doc.merge_accounts.length]; + frm.dashboard.set_headline(__('Successfully merged {0} out of {1}.', message_args)); + }, - root_type: function(frm) { - frm.set_value('account', ''); - frm.set_value('merge_accounts', []); - }, + root_type: function(frm) { + frm.set_value('account', ''); + frm.set_value('merge_accounts', []); + }, - company: function(frm) { - frm.set_value('account', ''); - frm.set_value('merge_accounts', []); - } + company: function(frm) { + frm.set_value('account', ''); + frm.set_value('merge_accounts', []); + } }); frappe.ui.form.on('Ledger Merge Accounts', { - merge_accounts_add: function(frm, cdt, cdn) { - frm.trigger('update_primary_action'); - }, - merge_accounts_remove: function(frm, cdt, cdn) { - frm.trigger('update_primary_action'); - }, - account: function(frm, cdt, cdn) { - frm.trigger('update_primary_action'); - } -}) + merge_accounts_add: function(frm, cdt, cdn) { + frm.trigger('update_primary_action'); + }, + + merge_accounts_remove: function(frm, cdt, cdn) { + frm.trigger('update_primary_action'); + }, + + account: function(frm, cdt, cdn) { + frm.trigger('update_primary_action'); + } +}); diff --git a/erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.py b/erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.py index 1b37095735..30dfd65782 100644 --- a/erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.py +++ b/erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.py @@ -4,5 +4,6 @@ # import frappe from frappe.model.document import Document + class LedgerMergeAccounts(Document): pass From 75de5be53e7b218a3ff89d98cdfbefb41e2eebc8 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Fri, 10 Dec 2021 10:27:15 +0000 Subject: [PATCH 031/193] fix: import issue and minor fixes --- .../doctype/ledger_merge/ledger_merge.js | 29 ++++++++++++------- .../doctype/ledger_merge/ledger_merge.json | 4 +-- .../doctype/ledger_merge/ledger_merge.py | 1 + .../doctype/ledger_merge/test_ledger_merge.py | 1 + .../ledger_merge_accounts.json | 3 +- 5 files changed, 23 insertions(+), 15 deletions(-) diff --git a/erpnext/accounts/doctype/ledger_merge/ledger_merge.js b/erpnext/accounts/doctype/ledger_merge/ledger_merge.js index 871edc71fd..849c5fb9b5 100644 --- a/erpnext/accounts/doctype/ledger_merge/ledger_merge.js +++ b/erpnext/accounts/doctype/ledger_merge/ledger_merge.js @@ -5,7 +5,10 @@ frappe.ui.form.on('Ledger Merge', { setup: function(frm) { frappe.realtime.on('ledger_merge_refresh', ({ ledger_merge }) => { if (ledger_merge !== frm.doc.name) return; - frm.refresh(); + frappe.model.clear_doc(frm.doc.doctype, frm.doc.name); + frappe.model.with_doc(frm.doc.doctype, frm.doc.name).then(() => { + frm.refresh(); + }); }); frappe.realtime.on('ledger_merge_progress', data => { @@ -25,30 +28,31 @@ frappe.ui.form.on('Ledger Merge', { root_type: doc.root_type, company: doc.company } - } + }; }); - frm.set_query('account', 'merge_accounts', function(doc, cdt, cdn) { + frm.set_query('account', 'merge_accounts', function(doc) { if (!doc.company) frappe.throw(__('Please set Company')); if (!doc.root_type) frappe.throw(__('Please set Root Type')); if (!doc.account) frappe.throw(__('Please set Account')); + let acc = [doc.account]; + frm.doc.merge_accounts.forEach((row) => { + acc.push(row.account); + }); return { filters: { is_group: 0, root_type: doc.root_type, - name: ["!=", doc.account], + name: ["not in", acc], company: doc.company } - } + }; }); }, refresh: function(frm) { frm.page.hide_icon_group(); frm.trigger('set_merge_status'); - }, - - onload_post_render: function(frm) { frm.trigger('update_primary_action'); }, @@ -90,7 +94,7 @@ frappe.ui.form.on('Ledger Merge', { if (frm.doc.status == "Pending") return; let successful_records = 0; frm.doc.merge_accounts.forEach((row) => { - if(row.merged) successful_records += 1; + if (row.merged) successful_records += 1; }); let message_args = [successful_records, frm.doc.merge_accounts.length]; frm.dashboard.set_headline(__('Successfully merged {0} out of {1}.', message_args)); @@ -108,15 +112,18 @@ frappe.ui.form.on('Ledger Merge', { }); frappe.ui.form.on('Ledger Merge Accounts', { - merge_accounts_add: function(frm, cdt, cdn) { + merge_accounts_add: function(frm) { frm.trigger('update_primary_action'); }, - merge_accounts_remove: function(frm, cdt, cdn) { + merge_accounts_remove: function(frm) { frm.trigger('update_primary_action'); }, account: function(frm, cdt, cdn) { + let row = frappe.get_doc(cdt, cdn); + row.account_name = row.account; + frm.refresh_field('merge_accounts'); frm.trigger('update_primary_action'); } }); diff --git a/erpnext/accounts/doctype/ledger_merge/ledger_merge.json b/erpnext/accounts/doctype/ledger_merge/ledger_merge.json index 641b462a29..ee39e08c29 100644 --- a/erpnext/accounts/doctype/ledger_merge/ledger_merge.json +++ b/erpnext/accounts/doctype/ledger_merge/ledger_merge.json @@ -21,7 +21,6 @@ "depends_on": "root_type", "fieldname": "account", "fieldtype": "Link", - "in_list_view": 1, "label": "Account", "options": "Account", "reqd": 1, @@ -58,6 +57,7 @@ { "fieldname": "status", "fieldtype": "Select", + "in_list_view": 1, "label": "Status", "options": "Pending\nSuccess\nPartial Success\nError", "read_only": 1 @@ -83,7 +83,7 @@ ], "hide_toolbar": 1, "links": [], - "modified": "2021-12-09 23:41:11.097097", + "modified": "2021-12-10 15:28:34.520588", "modified_by": "Administrator", "module": "Accounts", "name": "Ledger Merge", diff --git a/erpnext/accounts/doctype/ledger_merge/ledger_merge.py b/erpnext/accounts/doctype/ledger_merge/ledger_merge.py index a23f565841..63130ca659 100644 --- a/erpnext/accounts/doctype/ledger_merge/ledger_merge.py +++ b/erpnext/accounts/doctype/ledger_merge/ledger_merge.py @@ -2,6 +2,7 @@ # For license information, please see license.txt import frappe +from frappe import _ from frappe.model.document import Document from erpnext.accounts.doctype.account.account import merge_account diff --git a/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py b/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py index c24caedda8..8c7276e3e6 100644 --- a/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py +++ b/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py @@ -4,5 +4,6 @@ # import frappe import unittest + class TestLedgerMerge(unittest.TestCase): pass diff --git a/erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.json b/erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.json index 524e480892..4ce55ada7f 100644 --- a/erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.json +++ b/erpnext/accounts/doctype/ledger_merge_accounts/ledger_merge_accounts.json @@ -31,7 +31,6 @@ }, { "columns": 4, - "fetch_from": "account.account_name", "fieldname": "account_name", "fieldtype": "Data", "label": "Account Name", @@ -42,7 +41,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-12-09 23:19:15.193921", + "modified": "2021-12-10 15:27:24.477139", "modified_by": "Administrator", "module": "Accounts", "name": "Ledger Merge Accounts", From 2813e5ee2956f4667ecb9e02cb3a824c0fb7a0bd Mon Sep 17 00:00:00 2001 From: ruthra Date: Mon, 13 Dec 2021 12:26:23 +0530 Subject: [PATCH 032/193] feat: new column 'Time taken to Deliver' in sales order analysis --- .../sales_order_analysis/sales_order_analysis.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py index 82e5d0ce57..f1edca4cef 100644 --- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py +++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py @@ -61,6 +61,7 @@ def get_data(conditions, filters): IF(so.status in ('Completed','To Bill'), 0, (SELECT delay_days)) as delay, soi.qty, soi.delivered_qty, (soi.qty - soi.delivered_qty) AS pending_qty, + IF((SELECT pending_qty) = 0, DATEDIFF(Max(dn.posting_date), so.transaction_date), 0) as time_taken_to_deliver, IFNULL(SUM(sii.qty), 0) as billed_qty, soi.base_amount as amount, (soi.delivered_qty * soi.base_rate) as delivered_qty_amount, @@ -70,9 +71,13 @@ def get_data(conditions, filters): so.company, soi.name FROM `tabSales Order` so, - `tabSales Order Item` soi + (`tabSales Order Item` soi LEFT JOIN `tabSales Invoice Item` sii - ON sii.so_detail = soi.name and sii.docstatus = 1 + ON sii.so_detail = soi.name and sii.docstatus = 1) + LEFT JOIN `tabDelivery Note Item` dni + on dni.so_detail = soi.name + RIGHT JOIN `tabDelivery Note` dn + on dni.parent = dn.name and dn.docstatus = 1 WHERE soi.parent = so.name and so.status not in ('Stopped', 'Closed', 'On Hold') @@ -259,6 +264,12 @@ def get_columns(filters): "fieldname": "delay", "fieldtype": "Data", "width": 100 + }, + { + "label": _("Time Taken to Deliver"), + "fieldname": "time_taken_to_deliver", + "fieldtype": "Data", + "width": 100 } ]) if not filters.get("group_by_so"): From 9232f7599870b755c23a7f179306c6eed93f60dc Mon Sep 17 00:00:00 2001 From: ruthra Date: Tue, 14 Dec 2021 19:45:09 +0530 Subject: [PATCH 033/193] refactor: change field to duration and fetch elapsed seconds --- .../report/sales_order_analysis/sales_order_analysis.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py index f1edca4cef..0c0acc76e3 100644 --- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py +++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py @@ -61,7 +61,7 @@ def get_data(conditions, filters): IF(so.status in ('Completed','To Bill'), 0, (SELECT delay_days)) as delay, soi.qty, soi.delivered_qty, (soi.qty - soi.delivered_qty) AS pending_qty, - IF((SELECT pending_qty) = 0, DATEDIFF(Max(dn.posting_date), so.transaction_date), 0) as time_taken_to_deliver, + IF((SELECT pending_qty) = 0, (TO_SECONDS(Max(dn.posting_date))-TO_SECONDS(so.transaction_date)), 0) as time_taken_to_deliver, IFNULL(SUM(sii.qty), 0) as billed_qty, soi.base_amount as amount, (soi.delivered_qty * soi.base_rate) as delivered_qty_amount, @@ -268,7 +268,7 @@ def get_columns(filters): { "label": _("Time Taken to Deliver"), "fieldname": "time_taken_to_deliver", - "fieldtype": "Data", + "fieldtype": "Duration", "width": 100 } ]) From 9f235526d43658e333bc20bf8847bb8d21764332 Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Wed, 15 Dec 2021 22:34:44 +0530 Subject: [PATCH 034/193] fix: Validation in POS for item batch no stock quantity --- .../doctype/pos_invoice/pos_invoice.py | 22 +++++++++- .../doctype/pos_invoice/test_pos_invoice.py | 43 ++++++++++++++++++- erpnext/stock/doctype/batch/batch.py | 26 +++++++++++ 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 0d6404c324..f99b87d047 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -15,6 +15,7 @@ from erpnext.accounts.doctype.sales_invoice.sales_invoice import ( update_multi_mode_option, ) from erpnext.accounts.party import get_due_date, get_party_account +from erpnext.stock.doctype.batch.batch import get_pos_reserved_batch_qty from erpnext.stock.doctype.serial_no.serial_no import get_pos_reserved_serial_nos, get_serial_nos @@ -124,9 +125,26 @@ class POSInvoice(SalesInvoice): frappe.throw(_("Row #{}: Serial No. {} has already been transacted into another POS Invoice. Please select valid serial no.") .format(item.idx, bold_invalid_serial_nos), title=_("Item Unavailable")) elif invalid_serial_nos: - frappe.throw(_("Row #{}: Serial Nos. {} has already been transacted into another POS Invoice. Please select valid serial no.") + frappe.throw(_("Row #{}: Serial Nos. {} have already been transacted into another POS Invoice. Please select valid serial no.") .format(item.idx, bold_invalid_serial_nos), title=_("Item Unavailable")) + def validate_pos_reserved_batch_qty(self, item): + filters = {"item_code": item.item_code, "warehouse": item.warehouse, "batch_no":item.batch_no} + + available_batch_qty = frappe.db.get_value('Batch', item.batch_no, 'batch_qty') + reserved_batch_qty = get_pos_reserved_batch_qty(filters) + + bold_item_name = frappe.bold(item.item_name) + bold_extra_batch_qty_needed = frappe.bold(abs(available_batch_qty - reserved_batch_qty - item.qty)) + bold_invalid_batch_no = frappe.bold(item.batch_no) + + if (available_batch_qty - reserved_batch_qty) == 0: + frappe.throw(_("Row #{}: Batch No. {} of item {} has no stock available. Please select valid batch no.") + .format(item.idx, bold_invalid_batch_no, bold_item_name), title=_("Item Unavailable")) + elif (available_batch_qty - reserved_batch_qty - item.qty) < 0: + frappe.throw(_("Row #{}: Batch No. {} of item {} has less than required stock available, {} more required") + .format(item.idx, bold_invalid_batch_no, bold_item_name, bold_extra_batch_qty_needed), title=_("Item Unavailable")) + def validate_delivered_serial_nos(self, item): serial_nos = get_serial_nos(item.serial_no) delivered_serial_nos = frappe.db.get_list('Serial No', { @@ -149,6 +167,8 @@ class POSInvoice(SalesInvoice): if d.serial_no: self.validate_pos_reserved_serial_nos(d) self.validate_delivered_serial_nos(d) + elif d.batch_no: + self.validate_pos_reserved_batch_qty(d) else: if allow_negative_stock: return diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py index 6696333537..666e3f3e40 100644 --- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py @@ -521,6 +521,41 @@ class TestPOSInvoice(unittest.TestCase): rounded_total = frappe.db.get_value("Sales Invoice", pos_inv2.consolidated_invoice, "rounded_total") self.assertEqual(rounded_total, 400) + def test_pos_batch_ietm_qty_validation(self): + from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import ( + create_batch_item_with_batch, + ) + create_batch_item_with_batch('_BATCH ITEM', 'TestBatch 01') + item = frappe.get_doc('Item', '_BATCH ITEM') + batch = frappe.get_doc('Batch', 'TestBatch 01') + batch.submit() + item.batch_no = 'TestBatch 01' + item.save() + + se = make_stock_entry(target="_Test Warehouse - _TC", item_code="_BATCH ITEM", qty=2, basic_rate=100, batch_no='TestBatch 01') + + pos_inv1 = create_pos_invoice(item=item.name, rate=300, qty=1, do_not_submit=1) + pos_inv1.items[0].batch_no = 'TestBatch 01' + pos_inv1.save() + pos_inv1.submit() + + pos_inv2 = create_pos_invoice(item=item.name, rate=300, qty=2, do_not_submit=1) + pos_inv2.items[0].batch_no = 'TestBatch 01' + pos_inv2.save() + + self.assertRaises(frappe.ValidationError, pos_inv2.submit) + + #teardown + pos_inv1.reload() + pos_inv1.cancel() + pos_inv1.delete() + pos_inv2.reload() + pos_inv2.delete() + se.cancel() + batch.reload() + batch.cancel() + batch.delete() + def create_pos_invoice(**args): args = frappe._dict(args) pos_profile = None @@ -557,7 +592,8 @@ def create_pos_invoice(**args): "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", - "serial_no": args.serial_no + "serial_no": args.serial_no, + "batch_no": args.batch_no }) if not args.do_not_save: @@ -570,3 +606,8 @@ def create_pos_invoice(**args): pos_inv.payment_schedule = [] return pos_inv + +def make_batch_item(item_name): + from erpnext.stock.doctype.item.test_item import make_item + if not frappe.db.exists(item_name): + return make_item(item_name, dict(has_batch_no = 1, create_new_batch = 1, is_stock_item=1)) \ No newline at end of file diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index fdefd24878..cfe7f0a0b7 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -312,3 +312,29 @@ def make_batch(args): if frappe.db.get_value("Item", args.item, "has_batch_no"): args.doctype = "Batch" frappe.get_doc(args).insert().name + +@frappe.whitelist() +def get_pos_reserved_batch_qty(filters): + import json + + if isinstance(filters, str): + filters = json.loads(filters) + + pos_transacted_batch_nos = frappe.db.sql("""select item.qty + from `tabPOS Invoice` p, `tabPOS Invoice Item` item + where p.name = item.parent + and p.consolidated_invoice is NULL + and p.status != "Consolidated" + and p.docstatus = 1 + and item.docstatus = 1 + and item.item_code = %(item_code)s + and item.warehouse = %(warehouse)s + and item.batch_no = %(batch_no)s + + """, filters, as_dict=1) + + reserved_batch_qty = 0.0 + for d in pos_transacted_batch_nos: + reserved_batch_qty += d.qty + + return reserved_batch_qty \ No newline at end of file From 564d23fa8d4fbd91976c1adbc17029eafa50c04d Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 16 Dec 2021 11:19:03 +0530 Subject: [PATCH 035/193] fix(ksa): enable ksa pos invoice print format --- erpnext/patches.txt | 2 +- erpnext/patches/v13_0/disable_ksa_print_format_for_others.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 65ad79ef23..d9cedab52a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -316,5 +316,5 @@ erpnext.patches.v13_0.create_ksa_vat_custom_fields erpnext.patches.v14_0.rename_ongoing_status_in_sla_documents erpnext.patches.v14_0.migrate_crm_settings erpnext.patches.v13_0.rename_ksa_qr_field -erpnext.patches.v13_0.disable_ksa_print_format_for_others +erpnext.patches.v13_0.disable_ksa_print_format_for_others # 16-12-2021 erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template \ No newline at end of file diff --git a/erpnext/patches/v13_0/disable_ksa_print_format_for_others.py b/erpnext/patches/v13_0/disable_ksa_print_format_for_others.py index c815b3bb3c..aa2a2d3b78 100644 --- a/erpnext/patches/v13_0/disable_ksa_print_format_for_others.py +++ b/erpnext/patches/v13_0/disable_ksa_print_format_for_others.py @@ -3,10 +3,13 @@ import frappe +from erpnext.regional.saudi_arabia.setup import add_print_formats + def execute(): company = frappe.get_all('Company', filters = {'country': 'Saudi Arabia'}) if company: + add_print_formats() return if frappe.db.exists('DocType', 'Print Format'): From 68beee2a00dba2b08ca3265f509f60d636b2dcb8 Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Thu, 16 Dec 2021 13:26:21 +0530 Subject: [PATCH 036/193] fix: replaced sql query with frappe.qb --- erpnext/stock/doctype/batch/batch.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index cfe7f0a0b7..ebca87e71d 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -320,18 +320,19 @@ def get_pos_reserved_batch_qty(filters): if isinstance(filters, str): filters = json.loads(filters) - pos_transacted_batch_nos = frappe.db.sql("""select item.qty - from `tabPOS Invoice` p, `tabPOS Invoice Item` item - where p.name = item.parent - and p.consolidated_invoice is NULL - and p.status != "Consolidated" - and p.docstatus = 1 - and item.docstatus = 1 - and item.item_code = %(item_code)s - and item.warehouse = %(warehouse)s - and item.batch_no = %(batch_no)s + p = frappe.qb.DocType("POS Invoice").as_("p") + item = frappe.qb.DocType("POS Invoice Item").as_("item") - """, filters, as_dict=1) + pos_transacted_batch_nos = frappe.qb.from_(p).from_(item).select(item.qty).where( + (p.name == item.parent) & + (p.consolidated_invoice.isnull()) & + (p.status != "Consolidated") & + (p.docstatus == 1) & + (item.docstatus == 1) & + (item.item_code == filters.get('item_code')) & + (item.warehouse == filters.get('warehouse')) & + (item.batch_no == filters.get('batch_no')) + ).run(as_dict=True) reserved_batch_qty = 0.0 for d in pos_transacted_batch_nos: From 0e4842b986b5379174349b4153d64a50278fd438 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 16 Dec 2021 16:51:41 +0530 Subject: [PATCH 037/193] fix: Multicurrency invoices using subscription --- erpnext/accounts/doctype/subscription/subscription.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index 1dae87f2a4..467d4a1334 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -23,6 +23,7 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_accounting_dimensions, ) from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate +from erpnext.accounts.party import get_party_account_currency class Subscription(Document): @@ -355,6 +356,9 @@ class Subscription(Document): if frappe.db.get_value('Supplier', self.party, 'tax_withholding_category'): invoice.apply_tds = 1 + ### Add party currency to invoice + invoice.currency = get_party_account_currency(self.party_type, self.party, self.company) + ## Add dimensions in invoice for subscription: accounting_dimensions = get_accounting_dimensions() From 6ab753015efdc6a3017942ad319f965aec4aec91 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 16 Dec 2021 17:38:29 +0530 Subject: [PATCH 038/193] fix: Add tests for multicurrency subscription --- .../doctype/subscription/test_subscription.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py index 9dd370bd47..6f67bc5128 100644 --- a/erpnext/accounts/doctype/subscription/test_subscription.py +++ b/erpnext/accounts/doctype/subscription/test_subscription.py @@ -60,15 +60,38 @@ def create_plan(): plan.billing_interval_count = 3 plan.insert() + if not frappe.db.exists('Subscription Plan', '_Test Plan Multicurrency'): + plan = frappe.new_doc('Subscription Plan') + plan.plan_name = '_Test Plan Multicurrency' + plan.item = '_Test Non Stock Item' + plan.price_determination = "Fixed Rate" + plan.cost = 50 + plan.currency = 'USD' + plan.billing_interval = 'Month' + plan.billing_interval_count = 1 + plan.insert() + +def create_parties(): if not frappe.db.exists('Supplier', '_Test Supplier'): supplier = frappe.new_doc('Supplier') supplier.supplier_name = '_Test Supplier' supplier.supplier_group = 'All Supplier Groups' supplier.insert() + if not frappe.db.exists('Customer', '_Test Subscription Customer'): + customer = frappe.new_doc('Customer') + customer.customer_name = '_Test Subscription Customer' + customer.billing_currency = 'USD' + customer.append('accounts', { + 'company': '_Test Company', + 'account': '_Test Receivable USD - _TC' + }) + customer.insert() + class TestSubscription(unittest.TestCase): def setUp(self): create_plan() + create_parties() def test_create_subscription_with_trial_with_correct_period(self): subscription = frappe.new_doc('Subscription') @@ -637,3 +660,22 @@ class TestSubscription(unittest.TestCase): subscription.process() self.assertEqual(len(subscription.invoices), 1) + + def test_multicurrency_subscription(self): + subscription = frappe.new_doc('Subscription') + subscription.party_type = 'Customer' + subscription.party = '_Test Subscription Customer' + subscription.generate_invoice_at_period_start = 1 + subscription.company = '_Test Company' + # select subscription start date as '2018-01-15' + subscription.start_date = '2018-01-01' + subscription.append('plans', {'plan': '_Test Plan Multicurrency', 'qty': 1}) + subscription.save() + + subscription.process() + self.assertEqual(len(subscription.invoices), 1) + self.assertEqual(subscription.status, 'Unpaid') + + # Check the currency of the created invoice + currency = frappe.db.get_value('Sales Invoice', subscription.invoices[0].invoice, 'currency') + self.assertEqual(currency, 'USD') \ No newline at end of file From b3a3367d1d2b4ec8a92ef3cb70ee4e9eaf6918ae Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Fri, 17 Dec 2021 13:06:30 +0000 Subject: [PATCH 039/193] feat: allow group accounts --- .../accounts/doctype/ledger_merge/ledger_merge.js | 3 +-- .../accounts/doctype/ledger_merge/ledger_merge.json | 12 +++++++++++- .../accounts/doctype/ledger_merge/ledger_merge.py | 9 +++++++-- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/ledger_merge/ledger_merge.js b/erpnext/accounts/doctype/ledger_merge/ledger_merge.js index 849c5fb9b5..b2db98dbd0 100644 --- a/erpnext/accounts/doctype/ledger_merge/ledger_merge.js +++ b/erpnext/accounts/doctype/ledger_merge/ledger_merge.js @@ -24,7 +24,6 @@ frappe.ui.form.on('Ledger Merge', { if (!doc.root_type) frappe.throw(__('Please set Root Type')); return { filters: { - is_group: 0, root_type: doc.root_type, company: doc.company } @@ -41,7 +40,7 @@ frappe.ui.form.on('Ledger Merge', { }); return { filters: { - is_group: 0, + is_group: doc.is_group, root_type: doc.root_type, name: ["not in", acc], company: doc.company diff --git a/erpnext/accounts/doctype/ledger_merge/ledger_merge.json b/erpnext/accounts/doctype/ledger_merge/ledger_merge.json index ee39e08c29..dd816df627 100644 --- a/erpnext/accounts/doctype/ledger_merge/ledger_merge.json +++ b/erpnext/accounts/doctype/ledger_merge/ledger_merge.json @@ -13,6 +13,7 @@ "column_break_3", "company", "status", + "is_group", "section_break_5", "merge_accounts" ], @@ -79,11 +80,20 @@ "label": "Account Name", "read_only": 1, "reqd": 1 + }, + { + "default": "0", + "depends_on": "account", + "fetch_from": "account.is_group", + "fieldname": "is_group", + "fieldtype": "Check", + "label": "Is Group", + "read_only": 1 } ], "hide_toolbar": 1, "links": [], - "modified": "2021-12-10 15:28:34.520588", + "modified": "2021-12-12 21:34:55.155146", "modified_by": "Administrator", "module": "Accounts", "name": "Ledger Merge", diff --git a/erpnext/accounts/doctype/ledger_merge/ledger_merge.py b/erpnext/accounts/doctype/ledger_merge/ledger_merge.py index 63130ca659..830ad370d7 100644 --- a/erpnext/accounts/doctype/ledger_merge/ledger_merge.py +++ b/erpnext/accounts/doctype/ledger_merge/ledger_merge.py @@ -46,7 +46,13 @@ def start_merge(docname): for row in ledger_merge.merge_accounts: if not row.merged: try: - merge_account(row.account, ledger_merge.account, 0, ledger_merge.root_type, ledger_merge.company) + merge_account( + row.account, + ledger_merge.account, + ledger_merge.is_group, + ledger_merge.root_type, + ledger_merge.company + ) row.db_set('merged', 1) frappe.db.commit() successful_merges += 1 @@ -58,7 +64,6 @@ def start_merge(docname): ) except Exception: frappe.db.rollback() - ledger_merge.db_set("status", "Error") frappe.log_error(title=ledger_merge.name) finally: if successful_merges == total: From c1a0f649cb08484f6eb558791531bd932ca86999 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 17 Dec 2021 19:06:19 +0530 Subject: [PATCH 040/193] fix: convert asynchronous field update to synchronous (#28930) (cherry picked from commit 5adf85f5b7ba94591381b03709b1fee6aa9331a5) Co-authored-by: Dan Navarro on Ubuntu Work --- erpnext/public/js/utils.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 93a273187a..9339c5d998 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -430,12 +430,9 @@ erpnext.utils.select_alternate_items = function(opts) { qty = row.qty; } row[item_field] = d.alternate_item; - frm.script_manager.trigger(item_field, row.doctype, row.name) - .then(() => { - frappe.model.set_value(row.doctype, row.name, 'qty', qty); - frappe.model.set_value(row.doctype, row.name, - opts.original_item_field, d.item_code); - }); + frappe.model.set_value(row.doctype, row.name, 'qty', qty); + frappe.model.set_value(row.doctype, row.name, opts.original_item_field, d.item_code); + frm.trigger(item_field, row.doctype, row.name); }); refresh_field(opts.child_docname); From 31ac1011b85411f26f0c22fd7e2aef13f9e232e1 Mon Sep 17 00:00:00 2001 From: Anupam Date: Fri, 17 Dec 2021 19:21:08 +0530 Subject: [PATCH 041/193] feat: made carry-forward comments/communication configurable and added test cases --- .../doctype/crm_settings/crm_settings.json | 36 ++++++++- .../crm/doctype/opportunity/opportunity.py | 5 +- .../doctype/opportunity/test_opportunity.py | 79 ++++++++++++++++--- erpnext/crm/doctype/prospect/prospect.py | 7 +- erpnext/crm/utils.py | 15 ++-- .../selling/doctype/quotation/quotation.py | 13 +-- 6 files changed, 126 insertions(+), 29 deletions(-) diff --git a/erpnext/crm/doctype/crm_settings/crm_settings.json b/erpnext/crm/doctype/crm_settings/crm_settings.json index 8f0fa315c1..a2d608b032 100644 --- a/erpnext/crm/doctype/crm_settings/crm_settings.json +++ b/erpnext/crm/doctype/crm_settings/crm_settings.json @@ -17,7 +17,9 @@ "column_break_9", "create_event_on_next_contact_date_opportunity", "quotation_section", - "default_valid_till" + "default_valid_till", + "section_break_13", + "carry_forward_communication_and_comments" ], "fields": [ { @@ -85,13 +87,23 @@ "fieldname": "quotation_section", "fieldtype": "Section Break", "label": "Quotation" + }, + { + "fieldname": "section_break_13", + "fieldtype": "Section Break" + }, + { + "default": "0", + "fieldname": "carry_forward_communication_and_comments", + "fieldtype": "Check", + "label": "Carry forward Communication and Comments" } ], "icon": "fa fa-cog", "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-11-03 10:00:36.883496", + "modified": "2021-12-14 16:51:57.839939", "modified_by": "Administrator", "module": "CRM", "name": "CRM Settings", @@ -105,6 +117,26 @@ "role": "System Manager", "share": 1, "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "Sales Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "Sales Master Manager", + "share": 1, + "write": 1 } ], "sort_field": "modified", diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 6b4c3d5a3e..a4fd7658ee 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -22,8 +22,9 @@ class Opportunity(TransactionBase): frappe.get_doc("Lead", self.party_name).set_status(update=True) if self.opportunity_from in ["Lead", "Prospect"]: - copy_comments(self.opportunity_from, self.party_name, self) - add_link_in_communication(self.opportunity_from, self.party_name, self) + if frappe.db.get_single_value("CRM Settings", "carry_forward_communication_and_comments"): + copy_comments(self.opportunity_from, self.party_name, self) + add_link_in_communication(self.opportunity_from, self.party_name, self) def validate(self): self._prev = frappe._dict({ diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.py b/erpnext/crm/doctype/opportunity/test_opportunity.py index 6e6fed58cb..1bc58cd049 100644 --- a/erpnext/crm/doctype/opportunity/test_opportunity.py +++ b/erpnext/crm/doctype/opportunity/test_opportunity.py @@ -4,10 +4,12 @@ import unittest import frappe -from frappe.utils import random_string, today +from frappe.utils import now_datetime, random_string, today from erpnext.crm.doctype.lead.lead import make_customer from erpnext.crm.doctype.opportunity.opportunity import make_quotation +from erpnext.crm.doctype.lead.test_lead import make_lead +from erpnext.crm.utils import get_linked_communication_list test_records = frappe.get_test_records('Opportunity') @@ -28,16 +30,7 @@ class TestOpportunity(unittest.TestCase): self.assertEqual(doc.status, "Quotation") def test_make_new_lead_if_required(self): - new_lead_email_id = "new{}@example.com".format(random_string(5)) - args = { - "doctype": "Opportunity", - "contact_email": new_lead_email_id, - "opportunity_type": "Sales", - "with_items": 0, - "transaction_date": today() - } - # new lead should be created against the new.opportunity@example.com - opp_doc = frappe.get_doc(args).insert(ignore_permissions=True) + opp_doc = make_opportunity_from_lead() self.assertTrue(opp_doc.party_name) self.assertEqual(opp_doc.opportunity_from, "Lead") @@ -66,6 +59,53 @@ class TestOpportunity(unittest.TestCase): opportunity_doc = make_opportunity(with_items=1, rate=1100, qty=2) self.assertEqual(opportunity_doc.total, 2200) + def test_carry_forward_of_email_and_comments(self): + frappe.db.set_value("CRM Settings", "CRM Settings", "carry_forward_communication_and_comments", 1) + lead_doc = make_lead() + lead_doc.add_comment('Comment', text='Test Comment 1') + lead_doc.add_comment('Comment', text='Test Comment 2') + create_communication(lead_doc.doctype, lead_doc.name, lead_doc.email_id) + create_communication(lead_doc.doctype, lead_doc.name, lead_doc.email_id) + + opp_doc = make_opportunity(opportunity_from="Lead", lead=lead_doc.name) + opportunity_comment_count = frappe.db.count("Comment", {"reference_doctype": opp_doc.doctype, "reference_name": opp_doc.name}) + opportunity_communication_count = len(get_linked_communication_list(opp_doc.doctype, opp_doc.name)) + self.assertEqual(opportunity_comment_count, 2) + self.assertEqual(opportunity_communication_count, 2) + + opp_doc.add_comment('Comment', text='Test Comment 3') + opp_doc.add_comment('Comment', text='Test Comment 4') + create_communication(opp_doc.doctype, opp_doc.name, opp_doc.contact_email) + create_communication(opp_doc.doctype, opp_doc.name, opp_doc.contact_email) + + quotation_doc = make_quotation(opp_doc.name) + quotation_doc.append('items', { + "item_code": "_Test Item", + "qty": 1 + }) + quotation_doc.run_method("set_missing_values") + quotation_doc.run_method("calculate_taxes_and_totals") + quotation_doc.save() + + quotation_comment_count = frappe.db.count("Comment", {"reference_doctype": quotation_doc.doctype, "reference_name": quotation_doc.name, "comment_type": "Comment"}) + quotation_communication_count = len(get_linked_communication_list(quotation_doc.doctype, quotation_doc.name)) + self.assertEqual(quotation_comment_count, 4) + self.assertEqual(quotation_communication_count, 4) + +def make_opportunity_from_lead(): + new_lead_email_id = "new{}@example.com".format(random_string(5)) + args = { + "doctype": "Opportunity", + "contact_email": new_lead_email_id, + "opportunity_type": "Sales", + "with_items": 0, + "transaction_date": today() + } + # new lead should be created against the new.opportunity@example.com + opp_doc = frappe.get_doc(args).insert(ignore_permissions=True) + + return opp_doc + def make_opportunity(**args): args = frappe._dict(args) @@ -95,3 +135,20 @@ def make_opportunity(**args): opp_doc.insert() return opp_doc + +def create_communication(reference_doctype, reference_name, sender, sent_or_received=None, creation=None): + communication = frappe.get_doc({ + "doctype": "Communication", + "communication_type": "Communication", + "communication_medium": "Email", + "sent_or_received": sent_or_received or "Sent", + "email_status": "Open", + "subject": "Test Subject", + "sender": sender, + "content": "Test", + "status": "Linked", + "reference_doctype": reference_doctype, + "creation": creation or now_datetime(), + "reference_name": reference_name + }) + communication.save() \ No newline at end of file diff --git a/erpnext/crm/doctype/prospect/prospect.py b/erpnext/crm/doctype/prospect/prospect.py index c2553106db..cc4c1d37f8 100644 --- a/erpnext/crm/doctype/prospect/prospect.py +++ b/erpnext/crm/doctype/prospect/prospect.py @@ -23,9 +23,10 @@ class Prospect(Document): self.unlink_dynamic_links() def after_insert(self): - for row in self.get('prospect_lead'): - copy_comments("Lead", row.lead, self) - add_link_in_communication("Lead", row.lead, self) + if frappe.db.get_single_value("CRM Settings", "carry_forward_communication_and_comments"): + for row in self.get('prospect_lead'): + copy_comments("Lead", row.lead, self) + add_link_in_communication("Lead", row.lead, self) def update_lead_details(self): for row in self.get('prospect_lead'): diff --git a/erpnext/crm/utils.py b/erpnext/crm/utils.py index 8ae991ff17..741df9def6 100644 --- a/erpnext/crm/utils.py +++ b/erpnext/crm/utils.py @@ -23,7 +23,7 @@ def update_lead_phone_numbers(contact, method): lead.db_set("mobile_no", mobile_no) def copy_comments(doctype, docname, doc): - comments = frappe.db.get_values("Comment", filters={"reference_doctype": doctype, "reference_name": docname}, fieldname="*") + comments = frappe.db.get_values("Comment", filters={"reference_doctype": doctype, "reference_name": docname, "comment_type": "Comment"}, fieldname="*") for comment in comments: comment = frappe.get_doc(comment.update({"doctype":"Comment"})) comment.name = None @@ -32,6 +32,13 @@ def copy_comments(doctype, docname, doc): comment.insert() def add_link_in_communication(doctype, docname, doc): + communication_list = get_linked_communication_list(doctype, docname) + + for communication in communication_list: + communication_doc = frappe.get_doc("Communication", communication) + communication_doc.add_link(doc.doctype, doc.name, autosave=True) + +def get_linked_communication_list(doctype, docname): communications = frappe.get_all("Communication", filters={"reference_doctype": doctype, "reference_name": docname}, pluck='name') communication_links = frappe.get_all('Communication Link', { @@ -39,7 +46,5 @@ def add_link_in_communication(doctype, docname, doc): "link_name": docname, "parent": ("not in", communications) }, pluck="parent") - - for communication in communications + communication_links: - communication_doc = frappe.get_doc("Communication", communication) - communication_doc.add_link(doc.doctype, doc.name, autosave=True) + + return communications + communication_links diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 2bb9b5582a..daab6fbb8f 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -36,13 +36,14 @@ class Quotation(SellingController): make_packing_list(self) def after_insert(self): - if self.opportunity: - copy_comments("Opportunity", self.opportunity, self) - add_link_in_communication("Opportunity", self.opportunity, self) + if frappe.db.get_single_value("CRM Settings", "carry_forward_communication_and_comments"): + if self.opportunity: + copy_comments("Opportunity", self.opportunity, self) + add_link_in_communication("Opportunity", self.opportunity, self) - elif self.quotation_to == "Lead" and self.party_name: - copy_comments("Lead", self.party_name, self) - add_link_in_communication("Lead", self.party_name, self) + elif self.quotation_to == "Lead" and self.party_name: + copy_comments("Lead", self.party_name, self) + add_link_in_communication("Lead", self.party_name, self) def validate_valid_till(self): if self.valid_till and getdate(self.valid_till) < getdate(self.transaction_date): From e85da7a03a45787ec785f1d0b5d0efc56f145149 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Fri, 17 Dec 2021 21:48:10 +0530 Subject: [PATCH 042/193] fix: Add Invoice Number field to list view in Opening Inv Creation Tool (#28938) --- .../opening_invoice_creation_tool_item.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json b/erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json index 040624f926..5c19091c3f 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool_item/opening_invoice_creation_tool_item.json @@ -110,12 +110,13 @@ "description": "Reference number of the invoice from the previous system", "fieldname": "invoice_number", "fieldtype": "Data", + "in_list_view": 1, "label": "Invoice Number" } ], "istable": 1, "links": [], - "modified": "2021-12-13 18:15:41.295007", + "modified": "2021-12-17 19:25:06.053187", "modified_by": "Administrator", "module": "Accounts", "name": "Opening Invoice Creation Tool Item", From 399446460ae6580ff689db67f3b196f65475b1b4 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Fri, 17 Dec 2021 16:18:42 +0000 Subject: [PATCH 043/193] feat: tests --- .../doctype/ledger_merge/ledger_merge.py | 1 + .../doctype/ledger_merge/test_ledger_merge.py | 111 +++++++++++++++++- 2 files changed, 110 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/ledger_merge/ledger_merge.py b/erpnext/accounts/doctype/ledger_merge/ledger_merge.py index 830ad370d7..d45ae22c2f 100644 --- a/erpnext/accounts/doctype/ledger_merge/ledger_merge.py +++ b/erpnext/accounts/doctype/ledger_merge/ledger_merge.py @@ -72,5 +72,6 @@ def start_merge(docname): ledger_merge.db_set('status', 'Partial Success') else: ledger_merge.db_set('status', 'Error') + frappe.db.commit() frappe.publish_realtime("ledger_merge_refresh", {"ledger_merge": ledger_merge.name}) diff --git a/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py b/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py index 8c7276e3e6..67606dd03c 100644 --- a/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py +++ b/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py @@ -1,9 +1,116 @@ # Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and Contributors # See license.txt -# import frappe +import frappe import unittest +from erpnext.accounts.doctype.ledger_merge.ledger_merge import start_merge class TestLedgerMerge(unittest.TestCase): - pass + def test_merge_success(self): + if not frappe.db.exists("Account", "Indirect Expenses - _TC"): + acc = frappe.new_doc("Account") + acc.account_name = "Indirect Expenses" + acc.is_group = 1 + acc.parent_account = "Expenses - _TC" + acc.company = "_Test Company" + acc.insert() + if not frappe.db.exists("Account", "Indirect Test Expenses - _TC"): + acc = frappe.new_doc("Account") + acc.account_name = "Indirect Test Expenses" + acc.is_group = 1 + acc.parent_account = "Expenses - _TC" + acc.company = "_Test Company" + acc.insert() + if not frappe.db.exists("Account", "Administrative Test Expenses - _TC"): + acc = frappe.new_doc("Account") + acc.account_name = "Administrative Test Expenses" + acc.parent_account = "Indirect Test Expenses - _TC" + acc.company = "_Test Company" + acc.insert() + + doc = frappe.get_doc({ + "doctype": "Ledger Merge", + "company": "_Test Company", + "root_type": frappe.db.get_value("Account", "Indirect Test Expenses - _TC", "root_type"), + "account": "Indirect Expenses - _TC", + "merge_accounts": [ + { + "account": "Indirect Test Expenses - _TC", + "account_name": "Indirect Expenses" + } + ] + }).insert(ignore_permissions=True) + + parent = frappe.db.get_value("Account", "Administrative Test Expenses - _TC", "parent_account") + self.assertEqual(parent, "Indirect Test Expenses - _TC") + + start_merge(doc.name) + + parent = frappe.db.get_value("Account", "Administrative Test Expenses - _TC", "parent_account") + self.assertEqual(parent, "Indirect Expenses - _TC") + + self.assertFalse(frappe.db.exists("Account", "Indirect Test Expenses - _TC")) + + def test_partial_merge_success(self): + if not frappe.db.exists("Account", "Indirect Income - _TC"): + acc = frappe.new_doc("Account") + acc.account_name = "Indirect Income" + acc.is_group = 1 + acc.parent_account = "Income - _TC" + acc.company = "_Test Company" + acc.insert() + if not frappe.db.exists("Account", "Indirect Test Income - _TC"): + acc = frappe.new_doc("Account") + acc.account_name = "Indirect Test Income" + acc.is_group = 1 + acc.parent_account = "Income - _TC" + acc.company = "_Test Company" + acc.insert() + if not frappe.db.exists("Account", "Administrative Test Income - _TC"): + acc = frappe.new_doc("Account") + acc.account_name = "Administrative Test Income" + acc.parent_account = "Indirect Test Income - _TC" + acc.company = "_Test Company" + acc.insert() + + doc = frappe.get_doc({ + "doctype": "Ledger Merge", + "company": "_Test Company", + "root_type": frappe.db.get_value("Account", "Indirect Income - _TC", "root_type"), + "account": "Indirect Income - _TC", + "merge_accounts": [ + { + "account": "Indirect Test Income - _TC", + "account_name": "Indirect Test Income" + }, + { + "account": "Administrative Test Income - _TC", + "account_name": "Administrative Test Income" + } + ] + }).insert(ignore_permissions=True) + + parent = frappe.db.get_value("Account", "Administrative Test Income - _TC", "parent_account") + self.assertEqual(parent, "Indirect Test Income - _TC") + + start_merge(doc.name) + + parent = frappe.db.get_value("Account", "Administrative Test Income - _TC", "parent_account") + self.assertEqual(parent, "Indirect Income - _TC") + + self.assertFalse(frappe.db.exists("Account", "Indirect Test Income - _TC")) + self.assertTrue(frappe.db.exists("Account", "Administrative Test Income - _TC")) + + def tearDown(self): + for entry in frappe.db.get_all("Ledger Merge"): + frappe.delete_doc("Ledger Merge", entry.name) + + test_accounts = [ + "Indirect Test Expenses - _TC", + "Administrative Test Expenses - _TC", + "Indirect Test Income - _TC", + "Administrative Test Income - _TC" + ] + for account in test_accounts: + frappe.delete_doc_if_exists("Account", account) From 32e5db7cb8e23e653896cb1f8efe0c21a58367de Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Fri, 17 Dec 2021 16:38:16 +0000 Subject: [PATCH 044/193] chore: styling according to linter --- erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py b/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py index 67606dd03c..f7315362b7 100644 --- a/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py +++ b/erpnext/accounts/doctype/ledger_merge/test_ledger_merge.py @@ -1,8 +1,10 @@ # Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and Contributors # See license.txt -import frappe import unittest + +import frappe + from erpnext.accounts.doctype.ledger_merge.ledger_merge import start_merge From ab7f38224f9eba579fa9cf2444463451e173bbe6 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Fri, 17 Dec 2021 16:42:24 +0000 Subject: [PATCH 045/193] fix: remove db commit --- erpnext/accounts/doctype/ledger_merge/ledger_merge.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/accounts/doctype/ledger_merge/ledger_merge.py b/erpnext/accounts/doctype/ledger_merge/ledger_merge.py index d45ae22c2f..830ad370d7 100644 --- a/erpnext/accounts/doctype/ledger_merge/ledger_merge.py +++ b/erpnext/accounts/doctype/ledger_merge/ledger_merge.py @@ -72,6 +72,5 @@ def start_merge(docname): ledger_merge.db_set('status', 'Partial Success') else: ledger_merge.db_set('status', 'Error') - frappe.db.commit() frappe.publish_realtime("ledger_merge_refresh", {"ledger_merge": ledger_merge.name}) From 0ca467a28f6c4234f21861be8314e85ea35764ed Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Sat, 18 Dec 2021 16:16:47 +0530 Subject: [PATCH 046/193] fix: remove rounding of avg rating (#28945) --- .../hr/doctype/interview_feedback/test_interview_feedback.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/interview_feedback/test_interview_feedback.py b/erpnext/hr/doctype/interview_feedback/test_interview_feedback.py index 4185f2827a..d2ec5b9438 100644 --- a/erpnext/hr/doctype/interview_feedback/test_interview_feedback.py +++ b/erpnext/hr/doctype/interview_feedback/test_interview_feedback.py @@ -59,7 +59,7 @@ class TestInterviewFeedback(unittest.TestCase): }, 'average_rating') # 1. average should be reflected in Interview Detail. - self.assertEqual(avg_on_interview_detail, round(feedback_1.average_rating)) + self.assertEqual(avg_on_interview_detail, feedback_1.average_rating) '''For Second Interviewer Feedback''' interviewer = interview.interview_details[1].interviewer From 5b40d9e7cd2cb63e52e6a3c8ff4f36c553fd8db9 Mon Sep 17 00:00:00 2001 From: Anupam Date: Sat, 18 Dec 2021 20:12:57 +0530 Subject: [PATCH 047/193] fix: linter issues --- erpnext/crm/doctype/opportunity/test_opportunity.py | 4 ++-- erpnext/crm/utils.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.py b/erpnext/crm/doctype/opportunity/test_opportunity.py index 1bc58cd049..e768da2313 100644 --- a/erpnext/crm/doctype/opportunity/test_opportunity.py +++ b/erpnext/crm/doctype/opportunity/test_opportunity.py @@ -7,8 +7,8 @@ import frappe from frappe.utils import now_datetime, random_string, today from erpnext.crm.doctype.lead.lead import make_customer -from erpnext.crm.doctype.opportunity.opportunity import make_quotation from erpnext.crm.doctype.lead.test_lead import make_lead +from erpnext.crm.doctype.opportunity.opportunity import make_quotation from erpnext.crm.utils import get_linked_communication_list test_records = frappe.get_test_records('Opportunity') @@ -66,7 +66,7 @@ class TestOpportunity(unittest.TestCase): lead_doc.add_comment('Comment', text='Test Comment 2') create_communication(lead_doc.doctype, lead_doc.name, lead_doc.email_id) create_communication(lead_doc.doctype, lead_doc.name, lead_doc.email_id) - + opp_doc = make_opportunity(opportunity_from="Lead", lead=lead_doc.name) opportunity_comment_count = frappe.db.count("Comment", {"reference_doctype": opp_doc.doctype, "reference_name": opp_doc.name}) opportunity_communication_count = len(get_linked_communication_list(opp_doc.doctype, opp_doc.name)) diff --git a/erpnext/crm/utils.py b/erpnext/crm/utils.py index 741df9def6..a4576a287e 100644 --- a/erpnext/crm/utils.py +++ b/erpnext/crm/utils.py @@ -46,5 +46,5 @@ def get_linked_communication_list(doctype, docname): "link_name": docname, "parent": ("not in", communications) }, pluck="parent") - + return communications + communication_links From 0e0e4f723f9324fd66e42757f2bf01a354bb6f69 Mon Sep 17 00:00:00 2001 From: Anupam Date: Mon, 20 Dec 2021 09:48:40 +0530 Subject: [PATCH 048/193] fix: test case --- erpnext/crm/doctype/opportunity/test_opportunity.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.py b/erpnext/crm/doctype/opportunity/test_opportunity.py index e768da2313..0b3d4e0a0a 100644 --- a/erpnext/crm/doctype/opportunity/test_opportunity.py +++ b/erpnext/crm/doctype/opportunity/test_opportunity.py @@ -34,8 +34,7 @@ class TestOpportunity(unittest.TestCase): self.assertTrue(opp_doc.party_name) self.assertEqual(opp_doc.opportunity_from, "Lead") - self.assertEqual(frappe.db.get_value("Lead", opp_doc.party_name, "email_id"), - new_lead_email_id) + self.assertEqual(frappe.db.get_value("Lead", opp_doc.party_name, "email_id"), opp_doc.contact_email) # create new customer and create new contact against 'new.opportunity@example.com' customer = make_customer(opp_doc.party_name).insert(ignore_permissions=True) @@ -47,7 +46,7 @@ class TestOpportunity(unittest.TestCase): "link_name": customer.name }] }) - contact.add_email(new_lead_email_id, is_primary=True) + contact.add_email(opp_doc.contact_email, is_primary=True) contact.insert(ignore_permissions=True) opp_doc = frappe.get_doc(args).insert(ignore_permissions=True) From 6c96ed4e11b17acf46d5ea9bba4f1df60cd3238a Mon Sep 17 00:00:00 2001 From: Pruthvi Patel Date: Sat, 30 Oct 2021 16:44:15 +0530 Subject: [PATCH 049/193] refactor: update_invoice_status with query builder --- erpnext/controllers/accounts_controller.py | 104 +++++++++++---------- 1 file changed, 55 insertions(+), 49 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 2c92820a74..a2cfdec5d9 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -7,6 +7,7 @@ import json import frappe from frappe import _, throw from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied +from frappe.query_builder.functions import Sum from frappe.utils import ( add_days, add_months, @@ -1684,58 +1685,63 @@ def get_advance_payment_entries(party_type, party, party_account, order_doctype, def update_invoice_status(): """Updates status as Overdue for applicable invoices. Runs daily.""" today = getdate() - + payment_schedule = frappe.qb.DocType("Payment Schedule") for doctype in ("Sales Invoice", "Purchase Invoice"): - frappe.db.sql(""" - UPDATE `tab{doctype}` invoice SET invoice.status = 'Overdue' - WHERE invoice.docstatus = 1 - AND invoice.status REGEXP '^Unpaid|^Partly Paid' - AND invoice.outstanding_amount > 0 - AND ( - {or_condition} - ( - ( - CASE - WHEN invoice.party_account_currency = invoice.currency - THEN ( - CASE - WHEN invoice.disable_rounded_total - THEN invoice.grand_total - ELSE invoice.rounded_total - END - ) - ELSE ( - CASE - WHEN invoice.disable_rounded_total - THEN invoice.base_grand_total - ELSE invoice.base_rounded_total - END - ) - END - ) - invoice.outstanding_amount - ) < ( - SELECT SUM( - CASE - WHEN invoice.party_account_currency = invoice.currency - THEN ps.payment_amount - ELSE ps.base_payment_amount - END - ) - FROM `tabPayment Schedule` ps - WHERE ps.parent = invoice.name - AND ps.due_date < %(today)s - ) - ) - """.format( - doctype=doctype, - or_condition=( - "invoice.is_pos AND invoice.due_date < %(today)s OR" - if doctype == "Sales Invoice" - else "" - ) - ), {"today": today} + invoice = frappe.qb.DocType(doctype) + + consider_base_amount = invoice.party_account_currency != invoice.currency + payment_amount = ( + frappe.qb.terms.Case() + .when(consider_base_amount, payment_schedule.base_payment_amount) + .else_(payment_schedule.payment_amount) ) + payable_amount = ( + frappe.qb.from_(payment_schedule) + .select(Sum(payment_amount)) + .where( + (payment_schedule.parent == invoice.name) + & (payment_schedule.due_date < today) + ) + ) + + total = ( + frappe.qb.terms.Case() + .when(invoice.disable_rounded_total, invoice.grand_total) + .else_(invoice.rounded_total) + ) + + base_total = ( + frappe.qb.terms.Case() + .when(invoice.disable_rounded_total, invoice.base_grand_total) + .else_(invoice.base_rounded_total) + ) + + total_amount = ( + frappe.qb.terms.Case() + .when(consider_base_amount, base_total) + .else_(total) + ) + + is_overdue = total_amount - invoice.outstanding_amount < payable_amount + + conditions = ( + (invoice.docstatus == 1) + & (invoice.outstanding_amount > 0) + & ( + invoice.status.like('Unpaid%') + | invoice.status.like('Partly Paid%') + ) + & ( + (invoice.is_pos & invoice.due_date < today) | is_overdue + if doctype == "Sales Invoice" + else is_overdue + ) + ) + + frappe.qb.update(invoice).set("status", "Overdue").where(conditions).run() + + @frappe.whitelist() def get_payment_terms(terms_template, posting_date=None, grand_total=None, base_grand_total=None, bill_date=None): if not terms_template: From 0799f378b4e29d376b1e3cdbe3edc132cf15009e Mon Sep 17 00:00:00 2001 From: Pruthvi Patel Date: Fri, 12 Nov 2021 12:56:29 +0530 Subject: [PATCH 050/193] fix: consider `Discounted` status --- erpnext/controllers/accounts_controller.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index a2cfdec5d9..6f1d853f2c 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1729,8 +1729,8 @@ def update_invoice_status(): (invoice.docstatus == 1) & (invoice.outstanding_amount > 0) & ( - invoice.status.like('Unpaid%') - | invoice.status.like('Partly Paid%') + invoice.status.like("Unpaid%") + | invoice.status.like("Partly Paid%") ) & ( (invoice.is_pos & invoice.due_date < today) | is_overdue @@ -1739,7 +1739,13 @@ def update_invoice_status(): ) ) - frappe.qb.update(invoice).set("status", "Overdue").where(conditions).run() + status = ( + frappe.qb.terms.Case() + .when(invoice.status.like("%Discounted"), "Overdue and Discounted") + .else_("Overdue") + ) + + frappe.qb.update(invoice).set("status", status).where(conditions).run() @frappe.whitelist() From 4b29fb6d08a28f0b399d9e56c25a867b4035d5db Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sat, 18 Dec 2021 18:40:22 +0530 Subject: [PATCH 051/193] refactor: move Fifo Valuation to a new class --- erpnext/stock/stock_ledger.py | 98 ++++++-------------------- erpnext/stock/valuation.py | 125 ++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 78 deletions(-) create mode 100644 erpnext/stock/valuation.py diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index e95c0fcd23..056e4a768d 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -16,6 +16,7 @@ from erpnext.stock.utils import ( get_or_make_bin, get_valuation_method, ) +from erpnext.stock.valuation import FifoValuation class NegativeStockError(frappe.ValidationError): pass @@ -456,9 +457,8 @@ class update_entries_after(object): self.wh_data.qty_after_transaction += flt(sle.actual_qty) self.wh_data.stock_value = flt(self.wh_data.qty_after_transaction) * flt(self.wh_data.valuation_rate) else: - self.get_fifo_values(sle) + self.update_fifo_values(sle) self.wh_data.qty_after_transaction += flt(sle.actual_qty) - self.wh_data.stock_value = sum(flt(batch[0]) * flt(batch[1]) for batch in self.wh_data.stock_queue) # rounding as per precision self.wh_data.stock_value = flt(self.wh_data.stock_value, self.precision) @@ -696,87 +696,39 @@ class update_entries_after(object): sle.voucher_type, sle.voucher_no, self.allow_zero_rate, currency=erpnext.get_company_currency(sle.company), company=sle.company) - def get_fifo_values(self, sle): + def update_fifo_values(self, sle): incoming_rate = flt(sle.incoming_rate) actual_qty = flt(sle.actual_qty) outgoing_rate = flt(sle.outgoing_rate) + fifo_queue = FifoValuation(self.wh_data.stock_queue) if actual_qty > 0: - if not self.wh_data.stock_queue: - self.wh_data.stock_queue.append([0, 0]) - - # last row has the same rate, just updated the qty - if self.wh_data.stock_queue[-1][1]==incoming_rate: - self.wh_data.stock_queue[-1][0] += actual_qty - else: - # Item has a positive balance qty, add new entry - if self.wh_data.stock_queue[-1][0] > 0: - self.wh_data.stock_queue.append([actual_qty, incoming_rate]) - else: # negative balance qty - qty = self.wh_data.stock_queue[-1][0] + actual_qty - if qty > 0: # new balance qty is positive - self.wh_data.stock_queue[-1] = [qty, incoming_rate] - else: # new balance qty is still negative, maintain same rate - self.wh_data.stock_queue[-1][0] = qty + fifo_queue.add_stock(qty=actual_qty, rate=incoming_rate) else: - qty_to_pop = abs(actual_qty) - while qty_to_pop: - if not self.wh_data.stock_queue: - # Get valuation rate from last sle if exists or from valuation rate field in item master - allow_zero_valuation_rate = self.check_if_allow_zero_valuation_rate(sle.voucher_type, sle.voucher_detail_no) - if not allow_zero_valuation_rate: - _rate = get_valuation_rate(sle.item_code, sle.warehouse, - sle.voucher_type, sle.voucher_no, self.allow_zero_rate, - currency=erpnext.get_company_currency(sle.company), company=sle.company) - else: - _rate = 0 - - self.wh_data.stock_queue.append([0, _rate]) - - index = None - if outgoing_rate > 0: - # Find the entry where rate matched with outgoing rate - for i, v in enumerate(self.wh_data.stock_queue): - if v[1] == outgoing_rate: - index = i - break - - # If no entry found with outgoing rate, collapse stack - if index is None: # nosemgrep - new_stock_value = sum(d[0]*d[1] for d in self.wh_data.stock_queue) - qty_to_pop*outgoing_rate - new_stock_qty = sum(d[0] for d in self.wh_data.stock_queue) - qty_to_pop - self.wh_data.stock_queue = [[new_stock_qty, new_stock_value/new_stock_qty if new_stock_qty > 0 else outgoing_rate]] - break + def rate_generator() -> float: + allow_zero_valuation_rate = self.check_if_allow_zero_valuation_rate(sle.voucher_type, sle.voucher_detail_no) + if not allow_zero_valuation_rate: + return get_valuation_rate(sle.item_code, sle.warehouse, + sle.voucher_type, sle.voucher_no, self.allow_zero_rate, + currency=erpnext.get_company_currency(sle.company), company=sle.company) else: - index = 0 + return 0.0 - # select first batch or the batch with same rate - batch = self.wh_data.stock_queue[index] - if qty_to_pop >= batch[0]: - # consume current batch - qty_to_pop = _round_off_if_near_zero(qty_to_pop - batch[0]) - self.wh_data.stock_queue.pop(index) - if not self.wh_data.stock_queue and qty_to_pop: - # stock finished, qty still remains to be withdrawn - # negative stock, keep in as a negative batch - self.wh_data.stock_queue.append([-qty_to_pop, outgoing_rate or batch[1]]) - break + fifo_queue.remove_stock(qty=abs(actual_qty), rate=outgoing_rate, rate_generator=rate_generator) - else: - # qty found in current batch - # consume it and exit - batch[0] = batch[0] - qty_to_pop - qty_to_pop = 0 - - stock_value = _round_off_if_near_zero(sum(flt(batch[0]) * flt(batch[1]) for batch in self.wh_data.stock_queue)) - stock_qty = _round_off_if_near_zero(sum(flt(batch[0]) for batch in self.wh_data.stock_queue)) + stock_qty, stock_value = fifo_queue.get_total_stock_and_value() + self.wh_data.stock_queue = fifo_queue.get_state() + self.wh_data.stock_value = stock_value if stock_qty: - self.wh_data.valuation_rate = stock_value / flt(stock_qty) + self.wh_data.valuation_rate = stock_value / stock_qty + if not self.wh_data.stock_queue: self.wh_data.stock_queue.append([0, sle.incoming_rate or sle.outgoing_rate or self.wh_data.valuation_rate]) + + def check_if_allow_zero_valuation_rate(self, voucher_type, voucher_detail_no): ref_item_dt = "" @@ -1158,13 +1110,3 @@ def get_future_sle_with_negative_batch_qty(args): and timestamp(posting_date, posting_time) >= timestamp(%(posting_date)s, %(posting_time)s) limit 1 """, args, as_dict=1) - - -def _round_off_if_near_zero(number: float, precision: int = 6) -> float: - """ Rounds off the number to zero only if number is close to zero for decimal - specified in precision. Precision defaults to 6. - """ - if flt(number) < (1.0 / (10**precision)): - return 0 - - return flt(number) diff --git a/erpnext/stock/valuation.py b/erpnext/stock/valuation.py new file mode 100644 index 0000000000..33191d89ee --- /dev/null +++ b/erpnext/stock/valuation.py @@ -0,0 +1,125 @@ +from typing import Callable, List, NewType, Optional, Tuple + +from frappe.utils import flt + +FifoBin = NewType("FifoBin", List[float]) + +# Indexes of values inside FIFO bin 2-tuple +QTY = 0 +RATE = 1 + + +class FifoValuation: + """Valuation method where a queue of all the incoming stock is maintained. + + New stock is added at end of the queue. + Qty consumption happens on First In First Out basis. + + Queue is implemented using "bins" of [qty, rate]. + + ref: https://en.wikipedia.org/wiki/FIFO_and_LIFO_accounting + """ + + def __init__(self, state: Optional[List[FifoBin]]): + self.queue: List[FifoBin] = state if state is not None else [] + + def get_state(self) -> List[FifoBin]: + """Get current state of queue.""" + return self.queue + + def get_total_stock_and_value(self) -> Tuple[float, float]: + total_qty = 0.0 + total_value = 0.0 + + for qty, rate in self.queue: + total_qty += flt(qty) + total_value += flt(qty) * flt(rate) + + return _round_off_if_near_zero(total_qty), _round_off_if_near_zero(total_value) + + def add_stock(self, qty: float, rate: float) -> List[FifoBin]: + """Update fifo queue with new stock and return queue. + + args: + qty: new quantity to add + rate: incoming rate of new quantity""" + + if not len(self.queue): + self.queue.append([0, 0]) + + # last row has the same rate, merge new bin. + if self.queue[-1][RATE] == rate: + self.queue[-1][QTY] += qty + else: + # Item has a positive balance qty, add new entry + if self.queue[-1][QTY] > 0: + self.queue.append([qty, rate]) + else: # negative balance qty + qty = self.queue[-1][QTY] + qty + if qty > 0: # new balance qty is positive + self.queue[-1] = [qty, rate] + else: # new balance qty is still negative, maintain same rate + self.queue[-1][QTY] = qty + return self.get_state() + + def remove_stock( + self, qty: float, rate: float, rate_generator: Callable[[], float] + ) -> List[FifoBin]: + """Remove stock from the queue and return queue. + + args: + qty: quantity to remove + rate: outgoing rate + rate_generator: function to be called if queue is not found and rate is required. + """ + + while qty: + if not len(self.queue): + # rely on rate generator. + self.queue.append([0, rate_generator()]) + + index = None + if rate > 0: + # Find the entry where rate matched with outgoing rate + for idx, fifo_bin in enumerate(self.queue): + if fifo_bin[RATE] == rate: + index = idx + break + + # If no entry found with outgoing rate, collapse stack + if index is None: # nosemgrep + new_stock_value = sum(d[QTY] * d[RATE] for d in self.queue) - qty * rate + new_stock_qty = sum(d[QTY] for d in self.queue) - qty + self.queue = [[new_stock_qty, new_stock_value / new_stock_qty if new_stock_qty > 0 else rate]] + break + else: + index = 0 + + # select first bin or the bin with same rate + fifo_bin = self.queue[index] + if qty >= fifo_bin[QTY]: + # consume current bin + qty = _round_off_if_near_zero(qty - fifo_bin[QTY]) + self.queue.pop(index) + if not self.queue and qty: + # stock finished, qty still remains to be withdrawn + # negative stock, keep in as a negative bin + self.queue.append([-qty, rate or fifo_bin[RATE]]) + break + + else: + # qty found in current bin consume it and exit + fifo_bin[QTY] = fifo_bin[QTY] - qty + qty = 0 + + return self.get_state() + + +def _round_off_if_near_zero(number: float, precision: int = 6) -> float: + """Rounds off the number to zero only if number is close to zero for decimal + specified in precision. Precision defaults to 6. + """ + if flt(number) < (1.0 / (10 ** precision)): + return 0 + + return flt(number) From a71b47665264f6115e961918b454be962573e5df Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sat, 18 Dec 2021 19:33:58 +0530 Subject: [PATCH 052/193] feat: repr, eq and iter for fifo queue --- erpnext/stock/valuation.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/erpnext/stock/valuation.py b/erpnext/stock/valuation.py index 33191d89ee..617e1ca15a 100644 --- a/erpnext/stock/valuation.py +++ b/erpnext/stock/valuation.py @@ -23,6 +23,17 @@ class FifoValuation: def __init__(self, state: Optional[List[FifoBin]]): self.queue: List[FifoBin] = state if state is not None else [] + def __repr__(self): + return str(self.queue) + + def __iter__(self): + return iter(self.queue) + + def __eq__(self, other): + if isinstance(other, list): + return self.queue == other + return self.queue == other.queue + def get_state(self) -> List[FifoBin]: """Get current state of queue.""" return self.queue From e6e679cf7add6e7beaa7db783b078ad6f6b22792 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sat, 18 Dec 2021 20:36:47 +0530 Subject: [PATCH 053/193] fix: round off if near zero while popping from queue --- erpnext/stock/valuation.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/valuation.py b/erpnext/stock/valuation.py index 617e1ca15a..80e5d376e1 100644 --- a/erpnext/stock/valuation.py +++ b/erpnext/stock/valuation.py @@ -120,17 +120,17 @@ class FifoValuation: else: # qty found in current bin consume it and exit - fifo_bin[QTY] = fifo_bin[QTY] - qty + fifo_bin[QTY] = _round_off_if_near_zero(fifo_bin[QTY] - qty) qty = 0 return self.get_state() -def _round_off_if_near_zero(number: float, precision: int = 6) -> float: +def _round_off_if_near_zero(number: float, precision: int = 7) -> float: """Rounds off the number to zero only if number is close to zero for decimal specified in precision. Precision defaults to 6. """ - if flt(number) < (1.0 / (10 ** precision)): - return 0 + if abs(0.0 - flt(number)) < (1.0 / (10 ** precision)): + return 0.0 return flt(number) From 1833f7ab819861460b638cbb72c6785c0978c394 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sat, 18 Dec 2021 19:37:41 +0530 Subject: [PATCH 054/193] test: FifoValuation tests --- erpnext/stock/tests/test_valuation.py | 121 ++++++++++++++++++++++++++ erpnext/stock/valuation.py | 2 +- 2 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 erpnext/stock/tests/test_valuation.py diff --git a/erpnext/stock/tests/test_valuation.py b/erpnext/stock/tests/test_valuation.py new file mode 100644 index 0000000000..d415af300a --- /dev/null +++ b/erpnext/stock/tests/test_valuation.py @@ -0,0 +1,121 @@ +import unittest + +from erpnext.stock.valuation import FifoValuation, _round_off_if_near_zero + + +def rate_generator(): + return 0.0 + +class TestFifoValuation(unittest.TestCase): + + def setUp(self): + self.queue = FifoValuation([]) + + def tearDown(self): + qty, value = self.queue.get_total_stock_and_value() + self.assertTotalQty(qty) + self.assertTotalValue(value) + + def assertTotalQty(self, qty): + self.assertEqual(sum(q for q, _ in self.queue), qty, msg=f"queue: {self.queue}") + + def assertTotalValue(self, value): + self.assertEqual(sum(q * r for q, r in self.queue), value, msg=f"queue: {self.queue}") + + def test_simple_addition(self): + self.queue.add_stock(1, 10) + self.assertTotalQty(1) + + def test_simple_removal(self): + self.queue.add_stock(1, 10) + self.queue.remove_stock(1, 0, rate_generator) + self.assertTotalQty(0) + + def test_merge_new_stock(self): + self.queue.add_stock(1, 10) + self.queue.add_stock(1, 10) + self.assertEqual(self.queue, [[2, 10]]) + + def test_adding_negative_stock_keeps_rate(self): + self.queue = FifoValuation([[-5.0, 100]]) + self.queue.add_stock(1, 10) + self.assertEqual(self.queue, [[-4, 100]]) + + def test_adding_negative_stock_updates_rate(self): + self.queue = FifoValuation([[-5.0, 100]]) + self.queue.add_stock(6, 10) + self.assertEqual(self.queue, [[1, 10]]) + + + def test_negative_stock(self): + self.queue.remove_stock(1, 5, rate_generator) + self.assertEqual(self.queue, [[-1, 5]]) + + # XXX + self.queue.remove_stock(1, 10, rate_generator) + self.assertTotalQty(-2) + + self.queue.add_stock(2, 10) + self.assertTotalQty(0) + self.assertTotalValue(0) + + def test_removing_specified_rate(self): + self.queue.add_stock(1, 10) + self.queue.add_stock(1, 20) + + self.queue.remove_stock(1, 20, rate_generator) + self.assertEqual(self.queue, [[1, 10]]) + + + def test_remove_multiple_bins(self): + self.queue.add_stock(1, 10) + self.queue.add_stock(2, 20) + self.queue.add_stock(1, 20) + self.queue.add_stock(5, 20) + + self.queue.remove_stock(4, 0, rate_generator) + self.assertEqual(self.queue, [[5, 20]]) + + + def test_remove_multiple_bins_with_rate(self): + self.queue.add_stock(1, 10) + self.queue.add_stock(2, 20) + self.queue.add_stock(1, 20) + self.queue.add_stock(5, 20) + + self.queue.remove_stock(3, 20, rate_generator) + self.assertEqual(self.queue, [[1, 10], [5, 20]]) + + def test_collapsing_of_queue(self): + self.queue.add_stock(1, 1) + self.queue.add_stock(1, 2) + self.queue.add_stock(1, 3) + self.queue.add_stock(1, 4) + + self.assertTotalValue(10) + + self.queue.remove_stock(3, 1, rate_generator) + # XXX + self.assertEqual(self.queue, [[1, 7]]) + + def test_rounding_off(self): + self.queue.add_stock(1.0, 1.0) + self.queue.remove_stock(1.0 - 1e-9, 0.0, rate_generator) + self.assertTotalQty(0) + + def test_rounding_off_near_zero(self): + self.assertEqual(_round_off_if_near_zero(0), 0) + self.assertEqual(_round_off_if_near_zero(1), 1) + self.assertEqual(_round_off_if_near_zero(-1), -1) + self.assertEqual(_round_off_if_near_zero(-1e-8), 0) + self.assertEqual(_round_off_if_near_zero(1e-8), 0) + + def test_totals(self): + self.queue.add_stock(1, 10) + self.queue.add_stock(2, 13) + self.queue.add_stock(1, 17) + self.queue.remove_stock(1, 0, rate_generator) + self.queue.remove_stock(1, 0, rate_generator) + self.queue.remove_stock(1, 0, rate_generator) + self.queue.add_stock(5, 17) + self.queue.add_stock(8, 11) diff --git a/erpnext/stock/valuation.py b/erpnext/stock/valuation.py index 80e5d376e1..42a122fbd4 100644 --- a/erpnext/stock/valuation.py +++ b/erpnext/stock/valuation.py @@ -128,7 +128,7 @@ class FifoValuation: def _round_off_if_near_zero(number: float, precision: int = 7) -> float: """Rounds off the number to zero only if number is close to zero for decimal - specified in precision. Precision defaults to 6. + specified in precision. Precision defaults to 7. """ if abs(0.0 - flt(number)) < (1.0 / (10 ** precision)): return 0.0 From db1c0889d36b4ec55c7cbc704a82b8b51b8192e0 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 19 Dec 2021 18:37:12 +0530 Subject: [PATCH 055/193] feat: get consumed bins from Fifo queue on pop --- erpnext/stock/valuation.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/erpnext/stock/valuation.py b/erpnext/stock/valuation.py index 42a122fbd4..6e885ab518 100644 --- a/erpnext/stock/valuation.py +++ b/erpnext/stock/valuation.py @@ -48,8 +48,8 @@ class FifoValuation: return _round_off_if_near_zero(total_qty), _round_off_if_near_zero(total_value) - def add_stock(self, qty: float, rate: float) -> List[FifoBin]: - """Update fifo queue with new stock and return queue. + def add_stock(self, qty: float, rate: float) -> None: + """Update fifo queue with new stock. args: qty: new quantity to add @@ -71,12 +71,11 @@ class FifoValuation: self.queue[-1] = [qty, rate] else: # new balance qty is still negative, maintain same rate self.queue[-1][QTY] = qty - return self.get_state() def remove_stock( self, qty: float, rate: float, rate_generator: Callable[[], float] ) -> List[FifoBin]: - """Remove stock from the queue and return queue. + """Remove stock from the queue and return popped bins. args: qty: quantity to remove @@ -84,6 +83,7 @@ class FifoValuation: rate_generator: function to be called if queue is not found and rate is required. """ + consumed_bins = [] while qty: if not len(self.queue): # rely on rate generator. @@ -111,19 +111,22 @@ class FifoValuation: if qty >= fifo_bin[QTY]: # consume current bin qty = _round_off_if_near_zero(qty - fifo_bin[QTY]) - self.queue.pop(index) + to_consume = self.queue.pop(index) + consumed_bins.append(list(to_consume)) + if not self.queue and qty: # stock finished, qty still remains to be withdrawn # negative stock, keep in as a negative bin self.queue.append([-qty, rate or fifo_bin[RATE]]) + consumed_bins.append([qty, rate or fifo_bin[RATE]]) break - else: # qty found in current bin consume it and exit fifo_bin[QTY] = _round_off_if_near_zero(fifo_bin[QTY] - qty) + consumed_bins.append([qty, fifo_bin[RATE]]) qty = 0 - return self.get_state() + return consumed_bins def _round_off_if_near_zero(number: float, precision: int = 7) -> float: From a00d8d02b22e71e5955fb118ad97d163e97efa41 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 19 Dec 2021 18:45:04 +0530 Subject: [PATCH 056/193] refactor: outgoing rate and defaults for remove_stock --- erpnext/stock/stock_ledger.py | 2 +- erpnext/stock/valuation.py | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 056e4a768d..498b9f51f6 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -714,7 +714,7 @@ class update_entries_after(object): else: return 0.0 - fifo_queue.remove_stock(qty=abs(actual_qty), rate=outgoing_rate, rate_generator=rate_generator) + fifo_queue.remove_stock(qty=abs(actual_qty), outgoing_rate=outgoing_rate, rate_generator=rate_generator) stock_qty, stock_value = fifo_queue.get_total_stock_and_value() diff --git a/erpnext/stock/valuation.py b/erpnext/stock/valuation.py index 6e885ab518..86a4eb705e 100644 --- a/erpnext/stock/valuation.py +++ b/erpnext/stock/valuation.py @@ -73,7 +73,7 @@ class FifoValuation: self.queue[-1][QTY] = qty def remove_stock( - self, qty: float, rate: float, rate_generator: Callable[[], float] + self, qty: float, outgoing_rate: float = 0.0, rate_generator: Callable[[], float] = None ) -> List[FifoBin]: """Remove stock from the queue and return popped bins. @@ -82,6 +82,8 @@ class FifoValuation: rate: outgoing rate rate_generator: function to be called if queue is not found and rate is required. """ + if not rate_generator: + rate_generator = lambda : 0.0 # noqa consumed_bins = [] while qty: @@ -90,18 +92,18 @@ class FifoValuation: self.queue.append([0, rate_generator()]) index = None - if rate > 0: + if outgoing_rate > 0: # Find the entry where rate matched with outgoing rate for idx, fifo_bin in enumerate(self.queue): - if fifo_bin[RATE] == rate: + if fifo_bin[RATE] == outgoing_rate: index = idx break # If no entry found with outgoing rate, collapse stack if index is None: # nosemgrep - new_stock_value = sum(d[QTY] * d[RATE] for d in self.queue) - qty * rate + new_stock_value = sum(d[QTY] * d[RATE] for d in self.queue) - qty * outgoing_rate new_stock_qty = sum(d[QTY] for d in self.queue) - qty - self.queue = [[new_stock_qty, new_stock_value / new_stock_qty if new_stock_qty > 0 else rate]] + self.queue = [[new_stock_qty, new_stock_value / new_stock_qty if new_stock_qty > 0 else outgoing_rate]] break else: index = 0 @@ -117,8 +119,8 @@ class FifoValuation: if not self.queue and qty: # stock finished, qty still remains to be withdrawn # negative stock, keep in as a negative bin - self.queue.append([-qty, rate or fifo_bin[RATE]]) - consumed_bins.append([qty, rate or fifo_bin[RATE]]) + self.queue.append([-qty, outgoing_rate or fifo_bin[RATE]]) + consumed_bins.append([qty, outgoing_rate or fifo_bin[RATE]]) break else: # qty found in current bin consume it and exit From 9d177433cbbab279fb244b10e8491ab5d4cd1e43 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 19 Dec 2021 18:51:55 +0530 Subject: [PATCH 057/193] fix: return consumed bin on collapsing queue --- erpnext/stock/valuation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/valuation.py b/erpnext/stock/valuation.py index 86a4eb705e..7f54facda8 100644 --- a/erpnext/stock/valuation.py +++ b/erpnext/stock/valuation.py @@ -99,11 +99,12 @@ class FifoValuation: index = idx break - # If no entry found with outgoing rate, collapse stack + # If no entry found with outgoing rate, collapse queue if index is None: # nosemgrep new_stock_value = sum(d[QTY] * d[RATE] for d in self.queue) - qty * outgoing_rate new_stock_qty = sum(d[QTY] for d in self.queue) - qty self.queue = [[new_stock_qty, new_stock_value / new_stock_qty if new_stock_qty > 0 else outgoing_rate]] + consumed_bins.append([qty, outgoing_rate]) break else: index = 0 From aa0e163b1fdea3e253a8eeb99847f4fe852c3fc6 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 19 Dec 2021 17:02:04 +0530 Subject: [PATCH 058/193] test: hypothesis tests for FIFO queue --- dev-requirements.txt | 1 + erpnext/stock/tests/test_valuation.py | 75 +++++++++++++++++++++------ 2 files changed, 61 insertions(+), 15 deletions(-) create mode 100644 dev-requirements.txt diff --git a/dev-requirements.txt b/dev-requirements.txt new file mode 100644 index 0000000000..15545c0efa --- /dev/null +++ b/dev-requirements.txt @@ -0,0 +1 @@ +hypothesis~=6.31.0 diff --git a/erpnext/stock/tests/test_valuation.py b/erpnext/stock/tests/test_valuation.py index d415af300a..065c490289 100644 --- a/erpnext/stock/tests/test_valuation.py +++ b/erpnext/stock/tests/test_valuation.py @@ -1,10 +1,14 @@ import unittest +from hypothesis import given +from hypothesis import strategies as st + from erpnext.stock.valuation import FifoValuation, _round_off_if_near_zero +qty_gen = st.floats(min_value=-1e6, max_value=1e6) +value_gen = st.floats(min_value=1, max_value=1e6) +stock_queue_generator = st.lists(st.tuples(qty_gen, value_gen), min_size=10) -def rate_generator(): - return 0.0 class TestFifoValuation(unittest.TestCase): @@ -17,10 +21,10 @@ class TestFifoValuation(unittest.TestCase): self.assertTotalValue(value) def assertTotalQty(self, qty): - self.assertEqual(sum(q for q, _ in self.queue), qty, msg=f"queue: {self.queue}") + self.assertAlmostEqual(sum(q for q, _ in self.queue), qty, msg=f"queue: {self.queue}", places=4) def assertTotalValue(self, value): - self.assertEqual(sum(q * r for q, r in self.queue), value, msg=f"queue: {self.queue}") + self.assertAlmostEqual(sum(q * r for q, r in self.queue), value, msg=f"queue: {self.queue}", places=2) def test_simple_addition(self): self.queue.add_stock(1, 10) @@ -28,7 +32,7 @@ class TestFifoValuation(unittest.TestCase): def test_simple_removal(self): self.queue.add_stock(1, 10) - self.queue.remove_stock(1, 0, rate_generator) + self.queue.remove_stock(1) self.assertTotalQty(0) def test_merge_new_stock(self): @@ -48,11 +52,11 @@ class TestFifoValuation(unittest.TestCase): def test_negative_stock(self): - self.queue.remove_stock(1, 5, rate_generator) + self.queue.remove_stock(1, 5) self.assertEqual(self.queue, [[-1, 5]]) # XXX - self.queue.remove_stock(1, 10, rate_generator) + self.queue.remove_stock(1, 10) self.assertTotalQty(-2) self.queue.add_stock(2, 10) @@ -63,7 +67,7 @@ class TestFifoValuation(unittest.TestCase): self.queue.add_stock(1, 10) self.queue.add_stock(1, 20) - self.queue.remove_stock(1, 20, rate_generator) + self.queue.remove_stock(1, 20) self.assertEqual(self.queue, [[1, 10]]) @@ -73,7 +77,7 @@ class TestFifoValuation(unittest.TestCase): self.queue.add_stock(1, 20) self.queue.add_stock(5, 20) - self.queue.remove_stock(4, 0, rate_generator) + self.queue.remove_stock(4) self.assertEqual(self.queue, [[5, 20]]) @@ -83,7 +87,7 @@ class TestFifoValuation(unittest.TestCase): self.queue.add_stock(1, 20) self.queue.add_stock(5, 20) - self.queue.remove_stock(3, 20, rate_generator) + self.queue.remove_stock(3, 20) self.assertEqual(self.queue, [[1, 10], [5, 20]]) def test_collapsing_of_queue(self): @@ -94,13 +98,13 @@ class TestFifoValuation(unittest.TestCase): self.assertTotalValue(10) - self.queue.remove_stock(3, 1, rate_generator) + self.queue.remove_stock(3, 1) # XXX self.assertEqual(self.queue, [[1, 7]]) def test_rounding_off(self): self.queue.add_stock(1.0, 1.0) - self.queue.remove_stock(1.0 - 1e-9, 0.0, rate_generator) + self.queue.remove_stock(1.0 - 1e-9) self.assertTotalQty(0) def test_rounding_off_near_zero(self): @@ -114,8 +118,49 @@ class TestFifoValuation(unittest.TestCase): self.queue.add_stock(1, 10) self.queue.add_stock(2, 13) self.queue.add_stock(1, 17) - self.queue.remove_stock(1, 0, rate_generator) - self.queue.remove_stock(1, 0, rate_generator) - self.queue.remove_stock(1, 0, rate_generator) + self.queue.remove_stock(1) + self.queue.remove_stock(1) + self.queue.remove_stock(1) self.queue.add_stock(5, 17) self.queue.add_stock(8, 11) + + @given(stock_queue_generator) + def test_fifo_qty_hypothesis(self, stock_queue): + self.queue = FifoValuation([]) + total_qty = 0 + + for qty, rate in stock_queue: + if qty == 0: + continue + if qty > 0: + self.queue.add_stock(qty, rate) + total_qty += qty + else: + qty = abs(qty) + consumed = self.queue.remove_stock(qty) + self.assertAlmostEqual(qty, sum(q for q, _ in consumed), msg=f"incorrect consumption {consumed}") + total_qty -= qty + self.assertTotalQty(total_qty) + + @given(stock_queue_generator) + def test_fifo_qty_value_nonneg_hypothesis(self, stock_queue): + self.queue = FifoValuation([]) + total_qty = 0.0 + total_value = 0.0 + + for qty, rate in stock_queue: + # don't allow negative stock + if qty == 0 or total_qty + qty < 0 or abs(qty) < 0.1: + continue + if qty > 0: + self.queue.add_stock(qty, rate) + total_qty += qty + total_value += qty * rate + else: + qty = abs(qty) + consumed = self.queue.remove_stock(qty) + self.assertAlmostEqual(qty, sum(q for q, _ in consumed), msg=f"incorrect consumption {consumed}") + total_qty -= qty + total_value -= sum(q * r for q, r in consumed) + self.assertTotalQty(total_qty) + self.assertTotalValue(total_value) From 745caf911a0e6b8019efae0a63ea7f489228848c Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 19 Dec 2021 19:08:09 +0530 Subject: [PATCH 059/193] perf: specify slots on FIFO queue --- erpnext/stock/valuation.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/stock/valuation.py b/erpnext/stock/valuation.py index 7f54facda8..48efde64d2 100644 --- a/erpnext/stock/valuation.py +++ b/erpnext/stock/valuation.py @@ -20,6 +20,10 @@ class FifoValuation: ref: https://en.wikipedia.org/wiki/FIFO_and_LIFO_accounting """ + # specifying the attributes to save resources + # ref: https://docs.python.org/3/reference/datamodel.html#slots + __slots__ = ["queue",] + def __init__(self, state: Optional[List[FifoBin]]): self.queue: List[FifoBin] = state if state is not None else [] From 107b40410536b2a8c0ed683971f79efbce23c682 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 19 Dec 2021 20:47:08 +0530 Subject: [PATCH 060/193] chore: rename FifoValuation to FIFOValuation --- erpnext/stock/stock_ledger.py | 4 ++-- erpnext/stock/tests/test_valuation.py | 12 ++++++------ erpnext/stock/valuation.py | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 498b9f51f6..f45bee1650 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -16,7 +16,7 @@ from erpnext.stock.utils import ( get_or_make_bin, get_valuation_method, ) -from erpnext.stock.valuation import FifoValuation +from erpnext.stock.valuation import FIFOValuation class NegativeStockError(frappe.ValidationError): pass @@ -701,7 +701,7 @@ class update_entries_after(object): actual_qty = flt(sle.actual_qty) outgoing_rate = flt(sle.outgoing_rate) - fifo_queue = FifoValuation(self.wh_data.stock_queue) + fifo_queue = FIFOValuation(self.wh_data.stock_queue) if actual_qty > 0: fifo_queue.add_stock(qty=actual_qty, rate=incoming_rate) else: diff --git a/erpnext/stock/tests/test_valuation.py b/erpnext/stock/tests/test_valuation.py index 065c490289..85788bac7f 100644 --- a/erpnext/stock/tests/test_valuation.py +++ b/erpnext/stock/tests/test_valuation.py @@ -3,7 +3,7 @@ import unittest from hypothesis import given from hypothesis import strategies as st -from erpnext.stock.valuation import FifoValuation, _round_off_if_near_zero +from erpnext.stock.valuation import FIFOValuation, _round_off_if_near_zero qty_gen = st.floats(min_value=-1e6, max_value=1e6) value_gen = st.floats(min_value=1, max_value=1e6) @@ -13,7 +13,7 @@ stock_queue_generator = st.lists(st.tuples(qty_gen, value_gen), min_size=10) class TestFifoValuation(unittest.TestCase): def setUp(self): - self.queue = FifoValuation([]) + self.queue = FIFOValuation([]) def tearDown(self): qty, value = self.queue.get_total_stock_and_value() @@ -41,12 +41,12 @@ class TestFifoValuation(unittest.TestCase): self.assertEqual(self.queue, [[2, 10]]) def test_adding_negative_stock_keeps_rate(self): - self.queue = FifoValuation([[-5.0, 100]]) + self.queue = FIFOValuation([[-5.0, 100]]) self.queue.add_stock(1, 10) self.assertEqual(self.queue, [[-4, 100]]) def test_adding_negative_stock_updates_rate(self): - self.queue = FifoValuation([[-5.0, 100]]) + self.queue = FIFOValuation([[-5.0, 100]]) self.queue.add_stock(6, 10) self.assertEqual(self.queue, [[1, 10]]) @@ -126,7 +126,7 @@ class TestFifoValuation(unittest.TestCase): @given(stock_queue_generator) def test_fifo_qty_hypothesis(self, stock_queue): - self.queue = FifoValuation([]) + self.queue = FIFOValuation([]) total_qty = 0 for qty, rate in stock_queue: @@ -144,7 +144,7 @@ class TestFifoValuation(unittest.TestCase): @given(stock_queue_generator) def test_fifo_qty_value_nonneg_hypothesis(self, stock_queue): - self.queue = FifoValuation([]) + self.queue = FIFOValuation([]) total_qty = 0.0 total_value = 0.0 diff --git a/erpnext/stock/valuation.py b/erpnext/stock/valuation.py index 48efde64d2..45c5083099 100644 --- a/erpnext/stock/valuation.py +++ b/erpnext/stock/valuation.py @@ -9,7 +9,7 @@ QTY = 0 RATE = 1 -class FifoValuation: +class FIFOValuation: """Valuation method where a queue of all the incoming stock is maintained. New stock is added at end of the queue. From 4d48ca42282c28ab682dbcec0391b9770630df3a Mon Sep 17 00:00:00 2001 From: Pruthvi Patel Date: Mon, 20 Dec 2021 13:23:14 +0530 Subject: [PATCH 061/193] test: update_invoice_status --- .../sales_invoice/test_sales_invoice.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 6a488ea96e..c02c80a0fd 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -20,6 +20,7 @@ from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_comp from erpnext.accounts.utils import PaymentEntryUnlinkError from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries from erpnext.assets.doctype.asset.test_asset import create_asset, create_asset_data +from erpnext.controllers.accounts_controller import update_invoice_status from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_data from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency from erpnext.regional.india.utils import get_ewb_data @@ -2385,6 +2386,41 @@ class TestSalesInvoice(unittest.TestCase): si.reload() self.assertEqual(si.status, "Paid") + def test_update_invoice_status(self): + today = nowdate() + + # Sales Invoice without Payment Schedule + si = create_sales_invoice(posting_date=add_days(today, -5)) + + # Sales Invoice with Payment Schedule + si_with_payment_schedule = create_sales_invoice(do_not_submit=True) + si_with_payment_schedule.extend("payment_schedule", [ + { + "due_date": add_days(today, -5), + "invoice_portion": 50, + "payment_amount": si_with_payment_schedule.grand_total / 2 + }, + { + "due_date": add_days(today, 5), + "invoice_portion": 50, + "payment_amount": si_with_payment_schedule.grand_total / 2 + } + ]) + si_with_payment_schedule.submit() + + + for invoice in (si, si_with_payment_schedule): + invoice.db_set("status", "Unpaid") + update_invoice_status() + invoice.reload() + self.assertEqual(invoice.status, "Overdue") + + invoice.db_set("status", "Unpaid and Discounted") + update_invoice_status() + invoice.reload() + self.assertEqual(invoice.status, "Overdue and Discounted") + + def test_sales_commission(self): si = frappe.copy_doc(test_records[0]) item = copy.deepcopy(si.get('items')[0]) From 16a90d3e606cfed1e0ce499ece6b223162451dc6 Mon Sep 17 00:00:00 2001 From: Pruthvi Patel Date: Mon, 20 Dec 2021 13:24:19 +0530 Subject: [PATCH 062/193] Update erpnext/controllers/accounts_controller.py Co-authored-by: Saqib --- erpnext/controllers/accounts_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 6f1d853f2c..12059f7747 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1733,7 +1733,7 @@ def update_invoice_status(): | invoice.status.like("Partly Paid%") ) & ( - (invoice.is_pos & invoice.due_date < today) | is_overdue + ((invoice.is_pos & invoice.due_date < today) | is_overdue) if doctype == "Sales Invoice" else is_overdue ) From c6a87125e99946bad01910f4c396df6761e4aeb8 Mon Sep 17 00:00:00 2001 From: Anupam Date: Mon, 20 Dec 2021 14:14:44 +0530 Subject: [PATCH 063/193] feat: added forward communication comments check in CRM Settings --- erpnext/crm/doctype/crm_settings/crm_settings.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/crm/doctype/crm_settings/crm_settings.json b/erpnext/crm/doctype/crm_settings/crm_settings.json index a2d608b032..c15f559ab3 100644 --- a/erpnext/crm/doctype/crm_settings/crm_settings.json +++ b/erpnext/crm/doctype/crm_settings/crm_settings.json @@ -90,20 +90,22 @@ }, { "fieldname": "section_break_13", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Other Settings" }, { "default": "0", + "description": "All the comments and Emails will be copied from one document to another newly created document(Lead -> Opportunity -> Quotation) throughout the sales cycle.", "fieldname": "carry_forward_communication_and_comments", "fieldtype": "Check", - "label": "Carry forward Communication and Comments" + "label": "Carry Forward Communication and Comments" } ], "icon": "fa fa-cog", "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-12-14 16:51:57.839939", + "modified": "2021-12-20 12:51:38.894252", "modified_by": "Administrator", "module": "CRM", "name": "CRM Settings", From 5d6199cf35226d0c858c1cfc3a5fa264d7ccd10d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 20 Dec 2021 16:54:52 +0530 Subject: [PATCH 064/193] fix: Error on viewing consolidated financial statement --- .../consolidated_financial_statement.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 01799d5804..758e3e9337 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -370,7 +370,7 @@ def get_account_heads(root_type, companies, filters): accounts = get_accounts(root_type, filters) if not accounts: - return None, None + return None, None, None accounts = update_parent_account_names(accounts) From f5d5281119a4d7430e384ced9eaafbe446609a18 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 20 Dec 2021 16:57:53 +0530 Subject: [PATCH 065/193] chore: drop deprecated qunit tests --- .../doctype/account/tests/test_account.js | 29 ---- .../account/tests/test_account_with_number.js | 69 ---------- .../account/tests/test_make_tax_account.js | 46 ------- .../test_accounts_settings.js | 35 ----- .../journal_entry/test_journal_entry.js | 39 ------ .../tests/test_payment_against_invoice.js | 55 -------- .../test_payment_against_purchase_invoice.js | 60 -------- .../payment_entry/tests/test_payment_entry.js | 28 ---- .../tests/test_payment_entry_write_off.js | 67 --------- .../pricing_rule/tests/test_pricing_rule.js | 28 ---- ...st_pricing_rule_with_different_currency.js | 58 -------- .../test_pricing_rule_with_same_currency.js | 56 -------- .../purchase_invoice/test_purchase_invoice.js | 74 ---------- ...est_purchase_taxes_and_charges_template.js | 28 ---- .../sales_invoice/test_sales_invoice.js | 73 ---------- .../sales_invoice/tests/test_sales_invoice.js | 42 ------ .../tests/test_sales_invoice_with_margin.js | 35 ----- .../tests/test_sales_invoice_with_payment.js | 56 -------- ...test_sales_invoice_with_payment_request.js | 51 ------- .../test_sales_invoice_with_serialize_item.js | 44 ------ .../test_sales_taxes_and_charges_template.js | 28 ---- .../shipping_rule/test_shipping_rule.js | 36 ----- .../tests/test_shipping_rule_for_buying.js | 36 ----- .../doctype/subscription/test_subscription.js | 32 ----- erpnext/agriculture/doctype/crop/test_crop.js | 116 ---------------- .../doctype/crop_cycle/test_crop_cycle.js | 34 ----- .../doctype/disease/test_disease.js | 38 ----- .../doctype/fertilizer/test_fertilizer.js | 31 ----- .../doctype/soil_texture/test_soil_texture.js | 26 ---- .../water_analysis/test_water_analysis.js | 25 ---- .../tests/test_purchase_order.js | 80 ----------- .../tests/test_purchase_order_get_items.js | 61 -------- .../tests/test_purchase_order_receipt.js | 74 ---------- ...hase_order_with_discount_on_grand_total.js | 47 ------- ..._purchase_order_with_item_wise_discount.js | 44 ------ .../test_purchase_order_with_multi_uom.js | 39 ------ .../test_purchase_order_with_shipping_rule.js | 43 ------ ...t_purchase_order_with_taxes_and_charges.js | 44 ------ .../tests/test_request_for_quotation.js | 76 ---------- .../test_request_for_quotation_for_status.js | 128 ----------------- .../buying/doctype/supplier/test_supplier.js | 77 ----------- .../tests/test_supplier_quotation.js | 74 ---------- ...pplier_quotation_for_item_wise_discount.js | 34 ----- ...upplier_quotation_for_taxes_and_charges.js | 37 ----- .../lead/tests/test_lead_individual.js | 43 ------ .../lead/tests/test_lead_organization.js | 55 -------- .../doctype/opportunity/test_opportunity.js | 56 -------- .../academic_term/test_academic_term.js | 24 ---- .../test_assessment_criteria.js | 16 --- .../test_assessment_criteria_group.js | 15 -- .../assessment_group/test_assessment_group.js | 65 --------- .../assessment_plan/test_assessment_plan.js | 54 -------- .../test_assessment_result.js | 73 ---------- .../test_assessment_result_tool.js | 29 ---- .../education/doctype/course/test_course.js | 36 ----- .../test_education_settings.js | 31 ----- erpnext/education/doctype/fees/test_fees.js | 31 ----- .../grading_scale/test_grading_scale.js | 102 -------------- .../doctype/guardian/test_guardian.js | 34 ----- .../doctype/instructor/test_instructor.js | 20 --- .../education/doctype/program/test_program.js | 34 ----- erpnext/education/doctype/room/test_room.js | 22 --- .../test_student_admission.js | 40 ------ .../tests/test_student_applicant.js | 95 ------------- .../test_student_applicant_dummy_data.js | 87 ------------ .../tests/test_student_applicant_options.js | 110 --------------- .../test_student_attendance.js | 31 ----- .../test_student_attendance_tool.js | 85 ------------ .../test_student_batch_name.js | 19 --- .../student_category/test_student_category.js | 19 --- .../student_group/test_student_group.js | 56 -------- .../test_student_group_creation_tool.js | 84 ----------- .../test_student_leave_application.js | 69 ---------- .../doctype/student_log/test_student_log.js | 35 ----- .../hr/doctype/appraisal/test_appraisal.js | 57 -------- .../test_appraisal_template.js | 29 ---- .../hr/doctype/attendance/test_attendance.js | 39 ------ erpnext/hr/doctype/employee/test_employee.js | 40 ------ .../test_employee_attendance_tool.js | 61 -------- .../employment_type/test_employment_type.js | 22 --- .../expense_claim/test_expense_claim.js | 44 ------ .../test_expense_claim_type.js | 29 ---- .../doctype/holiday_list/test_holiday_list.js | 42 ------ .../job_applicant/test_job_applicant.js | 28 ---- .../hr/doctype/job_offer/test_job_offer.js | 51 ------- .../doctype/job_opening/test_job_opening.js | 26 ---- .../leave_allocation/test_leave_allocation.js | 41 ------ .../test_leave_application.js | 42 ------ .../leave_block_list/test_leave_block_list.js | 27 ---- .../test_leave_control_panel.js | 50 ------- .../hr/doctype/leave_type/test_leave_type.js | 22 --- .../tests/test_training_event.js | 59 -------- .../test_training_feedback.js | 51 ------- .../test_training_result.js | 52 ------- erpnext/manufacturing/doctype/bom/test_bom.js | 63 --------- .../doctype/operation/test_operation.js | 49 ------- .../doctype/work_order/test_work_order.js | 130 ------------------ .../doctype/workstation/test_workstation.js | 89 ------------ .../non_profit/doctype/donor/test_donor.js | 27 ---- .../test_grant_application.js | 30 ---- .../non_profit/doctype/member/test_member.js | 26 ---- .../membership_type/test_membership_type.js | 25 ---- .../doctype/volunteer/test_volunteer.js | 34 ----- .../volunteer_type/test_volunteer_type.js | 27 ---- .../payroll_entry/test_payroll_entry.js | 62 --------- .../test_set_salary_components.js | 61 -------- .../doctype/salary_slip/test_salary_slip.js | 55 -------- .../salary_structure/test_salary_structure.js | 78 ----------- .../activity_type/test_activity_type.js | 21 --- .../projects/doctype/task/tests/test_task.js | 24 ---- .../doctype/task/tests/test_task_tree.js | 88 ------------ .../doctype/restaurant/test_restaurant.js | 50 ------- .../restaurant_menu/test_restaurant_menu.js | 77 ----------- .../test_restaurant_order_entry.js | 53 ------- .../test_restaurant_reservation.js | 27 ---- .../restaurant_table/test_restaurant_table.js | 41 ------ .../product_bundle/test_product_bundle.js | 35 ----- .../doctype/quotation/tests/test_quotation.js | 58 -------- .../test_quotation_submit_cancel_amend.js | 41 ------ ..._quotation_with_discount_on_grand_total.js | 43 ------ .../test_quotation_with_item_wise_discount.js | 37 ----- .../tests/test_quotation_with_margin.js | 35 ----- .../tests/test_quotation_with_multi_uom.js | 38 ----- .../test_quotation_with_shipping_rule.js | 35 ----- .../test_quotation_with_taxes_and_charges.js | 40 ------ .../sales_order/tests/test_sales_order.js | 68 --------- ...es_order_with_bypass_credit_limit_check.js | 58 -------- ...ales_order_with_discount_on_grand_total.js | 43 ------ ...est_sales_order_with_item_wise_discount.js | 38 ----- .../tests/test_sales_order_with_margin.js | 37 ----- .../tests/test_sales_order_with_multi_uom.js | 38 ----- ...sales_order_with_multiple_delivery_date.js | 59 -------- .../test_sales_order_with_pricing_rule.js | 34 ----- .../test_sales_order_with_shipping_rule.js | 35 ----- ...test_sales_order_with_taxes_and_charges.js | 40 ------ ...order_without_bypass_credit_limit_check.js | 62 --------- .../doctype/company/tests/test_company.js | 25 ---- .../company/tests/test_company_production.js | 19 --- erpnext/stock/doctype/batch/test_batch.js | 22 --- .../delivery_note/test_delivery_note.js | 35 ----- .../test_delivery_note_with_margin.js | 36 ----- erpnext/stock/doctype/item/tests/test_item.js | 121 ---------------- .../doctype/item_price/test_item_price.js | 22 --- .../tests/test_material_request.js | 39 ------ .../tests/test_material_request_from_bom.js | 27 ---- .../test_material_request_type_manufacture.js | 29 ---- ...st_material_request_type_material_issue.js | 29 ---- ...material_request_type_material_transfer.js | 29 ---- .../doctype/price_list/test_price_list_uom.js | 58 -------- .../purchase_receipt/test_purchase_receipt.js | 42 ------ .../tests/test_stock_entry_for_manufacture.js | 26 ---- .../test_stock_entry_for_material_issue.js | 30 ---- ..._for_material_issue_with_serialize_item.js | 34 ----- .../test_stock_entry_for_material_receipt.js | 31 ----- ...for_material_receipt_for_serialize_item.js | 34 ----- .../test_stock_entry_for_material_transfer.js | 33 ----- ...y_for_material_transfer_for_manufacture.js | 33 ----- .../tests/test_stock_entry_for_repack.js | 41 ------ .../tests/test_stock_entry_for_subcontract.js | 33 ----- .../test_stock_reconciliation.js | 31 ----- .../stock/doctype/warehouse/test_warehouse.js | 19 --- 161 files changed, 7404 deletions(-) delete mode 100644 erpnext/accounts/doctype/account/tests/test_account.js delete mode 100644 erpnext/accounts/doctype/account/tests/test_account_with_number.js delete mode 100644 erpnext/accounts/doctype/account/tests/test_make_tax_account.js delete mode 100644 erpnext/accounts/doctype/accounts_settings/test_accounts_settings.js delete mode 100644 erpnext/accounts/doctype/journal_entry/test_journal_entry.js delete mode 100644 erpnext/accounts/doctype/payment_entry/tests/test_payment_against_invoice.js delete mode 100644 erpnext/accounts/doctype/payment_entry/tests/test_payment_against_purchase_invoice.js delete mode 100644 erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js delete mode 100644 erpnext/accounts/doctype/payment_entry/tests/test_payment_entry_write_off.js delete mode 100644 erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule.js delete mode 100644 erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule_with_different_currency.js delete mode 100644 erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule_with_same_currency.js delete mode 100644 erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.js delete mode 100644 erpnext/accounts/doctype/purchase_taxes_and_charges_template/test_purchase_taxes_and_charges_template.js delete mode 100644 erpnext/accounts/doctype/sales_invoice/test_sales_invoice.js delete mode 100644 erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice.js delete mode 100644 erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_margin.js delete mode 100644 erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment.js delete mode 100644 erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment_request.js delete mode 100644 erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_serialize_item.js delete mode 100644 erpnext/accounts/doctype/sales_taxes_and_charges_template/test_sales_taxes_and_charges_template.js delete mode 100644 erpnext/accounts/doctype/shipping_rule/test_shipping_rule.js delete mode 100644 erpnext/accounts/doctype/shipping_rule/tests/test_shipping_rule_for_buying.js delete mode 100644 erpnext/accounts/doctype/subscription/test_subscription.js delete mode 100644 erpnext/agriculture/doctype/crop/test_crop.js delete mode 100644 erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.js delete mode 100644 erpnext/agriculture/doctype/disease/test_disease.js delete mode 100644 erpnext/agriculture/doctype/fertilizer/test_fertilizer.js delete mode 100644 erpnext/agriculture/doctype/soil_texture/test_soil_texture.js delete mode 100644 erpnext/agriculture/doctype/water_analysis/test_water_analysis.js delete mode 100644 erpnext/buying/doctype/purchase_order/tests/test_purchase_order.js delete mode 100644 erpnext/buying/doctype/purchase_order/tests/test_purchase_order_get_items.js delete mode 100644 erpnext/buying/doctype/purchase_order/tests/test_purchase_order_receipt.js delete mode 100644 erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_discount_on_grand_total.js delete mode 100644 erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_item_wise_discount.js delete mode 100644 erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_multi_uom.js delete mode 100644 erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_shipping_rule.js delete mode 100644 erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_taxes_and_charges.js delete mode 100644 erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation.js delete mode 100644 erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation_for_status.js delete mode 100644 erpnext/buying/doctype/supplier/test_supplier.js delete mode 100644 erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation.js delete mode 100644 erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_item_wise_discount.js delete mode 100644 erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_taxes_and_charges.js delete mode 100644 erpnext/crm/doctype/lead/tests/test_lead_individual.js delete mode 100644 erpnext/crm/doctype/lead/tests/test_lead_organization.js delete mode 100644 erpnext/crm/doctype/opportunity/test_opportunity.js delete mode 100644 erpnext/education/doctype/academic_term/test_academic_term.js delete mode 100644 erpnext/education/doctype/assessment_criteria/test_assessment_criteria.js delete mode 100644 erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.js delete mode 100644 erpnext/education/doctype/assessment_group/test_assessment_group.js delete mode 100644 erpnext/education/doctype/assessment_plan/test_assessment_plan.js delete mode 100644 erpnext/education/doctype/assessment_result/test_assessment_result.js delete mode 100644 erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.js delete mode 100644 erpnext/education/doctype/course/test_course.js delete mode 100644 erpnext/education/doctype/education_settings/test_education_settings.js delete mode 100644 erpnext/education/doctype/fees/test_fees.js delete mode 100644 erpnext/education/doctype/grading_scale/test_grading_scale.js delete mode 100644 erpnext/education/doctype/guardian/test_guardian.js delete mode 100644 erpnext/education/doctype/instructor/test_instructor.js delete mode 100644 erpnext/education/doctype/program/test_program.js delete mode 100644 erpnext/education/doctype/room/test_room.js delete mode 100644 erpnext/education/doctype/student_admission/test_student_admission.js delete mode 100644 erpnext/education/doctype/student_applicant/tests/test_student_applicant.js delete mode 100644 erpnext/education/doctype/student_applicant/tests/test_student_applicant_dummy_data.js delete mode 100644 erpnext/education/doctype/student_applicant/tests/test_student_applicant_options.js delete mode 100644 erpnext/education/doctype/student_attendance/test_student_attendance.js delete mode 100644 erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.js delete mode 100644 erpnext/education/doctype/student_batch_name/test_student_batch_name.js delete mode 100644 erpnext/education/doctype/student_category/test_student_category.js delete mode 100644 erpnext/education/doctype/student_group/test_student_group.js delete mode 100644 erpnext/education/doctype/student_group_creation_tool/test_student_group_creation_tool.js delete mode 100644 erpnext/education/doctype/student_leave_application/test_student_leave_application.js delete mode 100644 erpnext/education/doctype/student_log/test_student_log.js delete mode 100644 erpnext/hr/doctype/appraisal/test_appraisal.js delete mode 100644 erpnext/hr/doctype/appraisal_template/test_appraisal_template.js delete mode 100644 erpnext/hr/doctype/attendance/test_attendance.js delete mode 100644 erpnext/hr/doctype/employee/test_employee.js delete mode 100644 erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js delete mode 100644 erpnext/hr/doctype/employment_type/test_employment_type.js delete mode 100644 erpnext/hr/doctype/expense_claim/test_expense_claim.js delete mode 100644 erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.js delete mode 100644 erpnext/hr/doctype/holiday_list/test_holiday_list.js delete mode 100644 erpnext/hr/doctype/job_applicant/test_job_applicant.js delete mode 100644 erpnext/hr/doctype/job_offer/test_job_offer.js delete mode 100644 erpnext/hr/doctype/job_opening/test_job_opening.js delete mode 100644 erpnext/hr/doctype/leave_allocation/test_leave_allocation.js delete mode 100644 erpnext/hr/doctype/leave_application/test_leave_application.js delete mode 100644 erpnext/hr/doctype/leave_block_list/test_leave_block_list.js delete mode 100644 erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js delete mode 100644 erpnext/hr/doctype/leave_type/test_leave_type.js delete mode 100644 erpnext/hr/doctype/training_event/tests/test_training_event.js delete mode 100644 erpnext/hr/doctype/training_feedback/test_training_feedback.js delete mode 100644 erpnext/hr/doctype/training_result_employee/test_training_result.js delete mode 100644 erpnext/manufacturing/doctype/bom/test_bom.js delete mode 100644 erpnext/manufacturing/doctype/operation/test_operation.js delete mode 100644 erpnext/manufacturing/doctype/work_order/test_work_order.js delete mode 100644 erpnext/manufacturing/doctype/workstation/test_workstation.js delete mode 100644 erpnext/non_profit/doctype/donor/test_donor.js delete mode 100644 erpnext/non_profit/doctype/grant_application/test_grant_application.js delete mode 100644 erpnext/non_profit/doctype/member/test_member.js delete mode 100644 erpnext/non_profit/doctype/membership_type/test_membership_type.js delete mode 100644 erpnext/non_profit/doctype/volunteer/test_volunteer.js delete mode 100644 erpnext/non_profit/doctype/volunteer_type/test_volunteer_type.js delete mode 100644 erpnext/payroll/doctype/payroll_entry/test_payroll_entry.js delete mode 100644 erpnext/payroll/doctype/payroll_entry/test_set_salary_components.js delete mode 100644 erpnext/payroll/doctype/salary_slip/test_salary_slip.js delete mode 100644 erpnext/payroll/doctype/salary_structure/test_salary_structure.js delete mode 100644 erpnext/projects/doctype/activity_type/test_activity_type.js delete mode 100644 erpnext/projects/doctype/task/tests/test_task.js delete mode 100644 erpnext/projects/doctype/task/tests/test_task_tree.js delete mode 100644 erpnext/restaurant/doctype/restaurant/test_restaurant.js delete mode 100644 erpnext/restaurant/doctype/restaurant_menu/test_restaurant_menu.js delete mode 100644 erpnext/restaurant/doctype/restaurant_order_entry/test_restaurant_order_entry.js delete mode 100644 erpnext/restaurant/doctype/restaurant_reservation/test_restaurant_reservation.js delete mode 100644 erpnext/restaurant/doctype/restaurant_table/test_restaurant_table.js delete mode 100644 erpnext/selling/doctype/product_bundle/test_product_bundle.js delete mode 100644 erpnext/selling/doctype/quotation/tests/test_quotation.js delete mode 100644 erpnext/selling/doctype/quotation/tests/test_quotation_submit_cancel_amend.js delete mode 100644 erpnext/selling/doctype/quotation/tests/test_quotation_with_discount_on_grand_total.js delete mode 100644 erpnext/selling/doctype/quotation/tests/test_quotation_with_item_wise_discount.js delete mode 100644 erpnext/selling/doctype/quotation/tests/test_quotation_with_margin.js delete mode 100644 erpnext/selling/doctype/quotation/tests/test_quotation_with_multi_uom.js delete mode 100644 erpnext/selling/doctype/quotation/tests/test_quotation_with_shipping_rule.js delete mode 100644 erpnext/selling/doctype/quotation/tests/test_quotation_with_taxes_and_charges.js delete mode 100644 erpnext/selling/doctype/sales_order/tests/test_sales_order.js delete mode 100644 erpnext/selling/doctype/sales_order/tests/test_sales_order_with_bypass_credit_limit_check.js delete mode 100644 erpnext/selling/doctype/sales_order/tests/test_sales_order_with_discount_on_grand_total.js delete mode 100644 erpnext/selling/doctype/sales_order/tests/test_sales_order_with_item_wise_discount.js delete mode 100644 erpnext/selling/doctype/sales_order/tests/test_sales_order_with_margin.js delete mode 100644 erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multi_uom.js delete mode 100644 erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multiple_delivery_date.js delete mode 100644 erpnext/selling/doctype/sales_order/tests/test_sales_order_with_pricing_rule.js delete mode 100644 erpnext/selling/doctype/sales_order/tests/test_sales_order_with_shipping_rule.js delete mode 100644 erpnext/selling/doctype/sales_order/tests/test_sales_order_with_taxes_and_charges.js delete mode 100644 erpnext/selling/doctype/sales_order/tests/test_sales_order_without_bypass_credit_limit_check.js delete mode 100644 erpnext/setup/doctype/company/tests/test_company.js delete mode 100644 erpnext/setup/doctype/company/tests/test_company_production.js delete mode 100644 erpnext/stock/doctype/batch/test_batch.js delete mode 100644 erpnext/stock/doctype/delivery_note/test_delivery_note.js delete mode 100644 erpnext/stock/doctype/delivery_note/test_delivery_note_with_margin.js delete mode 100644 erpnext/stock/doctype/item/tests/test_item.js delete mode 100644 erpnext/stock/doctype/item_price/test_item_price.js delete mode 100644 erpnext/stock/doctype/material_request/tests/test_material_request.js delete mode 100644 erpnext/stock/doctype/material_request/tests/test_material_request_from_bom.js delete mode 100644 erpnext/stock/doctype/material_request/tests/test_material_request_type_manufacture.js delete mode 100644 erpnext/stock/doctype/material_request/tests/test_material_request_type_material_issue.js delete mode 100644 erpnext/stock/doctype/material_request/tests/test_material_request_type_material_transfer.js delete mode 100644 erpnext/stock/doctype/price_list/test_price_list_uom.js delete mode 100644 erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.js delete mode 100644 erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_manufacture.js delete mode 100644 erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue.js delete mode 100644 erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue_with_serialize_item.js delete mode 100644 erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt.js delete mode 100644 erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt_for_serialize_item.js delete mode 100644 erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer.js delete mode 100644 erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer_for_manufacture.js delete mode 100644 erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_repack.js delete mode 100644 erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_subcontract.js delete mode 100644 erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js delete mode 100644 erpnext/stock/doctype/warehouse/test_warehouse.js diff --git a/erpnext/accounts/doctype/account/tests/test_account.js b/erpnext/accounts/doctype/account/tests/test_account.js deleted file mode 100644 index 039e33e011..0000000000 --- a/erpnext/accounts/doctype/account/tests/test_account.js +++ /dev/null @@ -1,29 +0,0 @@ -QUnit.module('accounts'); - -QUnit.test("test account", function(assert) { - assert.expect(4); - let done = assert.async(); - frappe.run_serially([ - () => frappe.set_route('Tree', 'Account'), - () => frappe.timeout(3), - () => frappe.click_button('Expand All'), - () => frappe.timeout(1), - () => frappe.click_link('Debtors'), - () => frappe.click_button('Edit'), - () => frappe.timeout(1), - () => { - assert.ok(cur_frm.doc.root_type=='Asset'); - assert.ok(cur_frm.doc.report_type=='Balance Sheet'); - assert.ok(cur_frm.doc.account_type=='Receivable'); - }, - () => frappe.click_button('Ledger'), - () => frappe.timeout(1), - () => { - // check if general ledger report shown - assert.deepEqual(frappe.get_route(), ['query-report', 'General Ledger']); - window.history.back(); - return frappe.timeout(1); - }, - () => done() - ]); -}); diff --git a/erpnext/accounts/doctype/account/tests/test_account_with_number.js b/erpnext/accounts/doctype/account/tests/test_account_with_number.js deleted file mode 100644 index c03e27865f..0000000000 --- a/erpnext/accounts/doctype/account/tests/test_account_with_number.js +++ /dev/null @@ -1,69 +0,0 @@ -QUnit.module('accounts'); - -QUnit.test("test account with number", function(assert) { - assert.expect(7); - let done = assert.async(); - frappe.run_serially([ - () => frappe.set_route('Tree', 'Account'), - () => frappe.click_link('Income'), - () => frappe.click_button('Add Child'), - () => frappe.timeout(.5), - () => { - cur_dialog.fields_dict.account_name.$input.val("Test Income"); - cur_dialog.fields_dict.account_number.$input.val("4010"); - }, - () => frappe.click_button('Create New'), - () => frappe.timeout(1), - () => { - assert.ok($('a:contains("4010 - Test Income"):visible').length!=0, "Account created with number"); - }, - () => frappe.click_link('4010 - Test Income'), - () => frappe.click_button('Edit'), - () => frappe.timeout(.5), - () => frappe.click_button('Update Account Number'), - () => frappe.timeout(.5), - () => { - cur_dialog.fields_dict.account_number.$input.val("4020"); - }, - () => frappe.timeout(1), - () => cur_dialog.primary_action(), - () => frappe.timeout(1), - () => cur_frm.refresh_fields(), - () => frappe.timeout(.5), - () => { - var abbr = frappe.get_abbr(frappe.defaults.get_default("Company")); - var new_account = "4020 - Test Income - " + abbr; - assert.ok(cur_frm.doc.name==new_account, "Account renamed"); - assert.ok(cur_frm.doc.account_name=="Test Income", "account name remained same"); - assert.ok(cur_frm.doc.account_number=="4020", "Account number updated to 4020"); - }, - () => frappe.timeout(1), - () => frappe.click_button('Menu'), - () => frappe.click_link('Rename'), - () => frappe.timeout(.5), - () => { - cur_dialog.fields_dict.new_name.$input.val("4030 - Test Income"); - }, - () => frappe.timeout(.5), - () => frappe.click_button("Rename"), - () => frappe.timeout(2), - () => { - assert.ok(cur_frm.doc.account_name=="Test Income", "account name remained same"); - assert.ok(cur_frm.doc.account_number=="4030", "Account number updated to 4030"); - }, - () => frappe.timeout(.5), - () => frappe.click_button('Chart of Accounts'), - () => frappe.timeout(.5), - () => frappe.click_button('Menu'), - () => frappe.click_link('Refresh'), - () => frappe.click_button('Expand All'), - () => frappe.click_link('4030 - Test Income'), - () => frappe.click_button('Delete'), - () => frappe.click_button('Yes'), - () => frappe.timeout(.5), - () => { - assert.ok($('a:contains("4030 - Test Account"):visible').length==0, "Account deleted"); - }, - () => done() - ]); -}); diff --git a/erpnext/accounts/doctype/account/tests/test_make_tax_account.js b/erpnext/accounts/doctype/account/tests/test_make_tax_account.js deleted file mode 100644 index a0e09a13ce..0000000000 --- a/erpnext/accounts/doctype/account/tests/test_make_tax_account.js +++ /dev/null @@ -1,46 +0,0 @@ -QUnit.module('accounts'); -QUnit.test("test account", assert => { - assert.expect(3); - let done = assert.async(); - frappe.run_serially([ - () => frappe.set_route('Tree', 'Account'), - () => frappe.click_button('Expand All'), - () => frappe.click_link('Duties and Taxes - '+ frappe.get_abbr(frappe.defaults.get_default("Company"))), - () => { - if($('a:contains("CGST"):visible').length == 0){ - return frappe.map_tax.make('CGST', 9); - } - }, - () => { - if($('a:contains("SGST"):visible').length == 0){ - return frappe.map_tax.make('SGST', 9); - } - }, - () => { - if($('a:contains("IGST"):visible').length == 0){ - return frappe.map_tax.make('IGST', 18); - } - }, - () => { - assert.ok($('a:contains("CGST"):visible').length!=0, "CGST Checked"); - assert.ok($('a:contains("SGST"):visible').length!=0, "SGST Checked"); - assert.ok($('a:contains("IGST"):visible').length!=0, "IGST Checked"); - }, - () => done() - ]); -}); - - -frappe.map_tax = { - make:function(text,rate){ - return frappe.run_serially([ - () => frappe.click_button('Add Child'), - () => frappe.timeout(0.2), - () => cur_dialog.set_value('account_name',text), - () => cur_dialog.set_value('account_type','Tax'), - () => cur_dialog.set_value('tax_rate',rate), - () => cur_dialog.set_value('account_currency','INR'), - () => frappe.click_button('Create New'), - ]); - } -}; diff --git a/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.js b/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.js deleted file mode 100644 index f9aa166964..0000000000 --- a/erpnext/accounts/doctype/accounts_settings/test_accounts_settings.js +++ /dev/null @@ -1,35 +0,0 @@ -QUnit.module('accounts'); - -QUnit.test("test: Accounts Settings doesn't allow negatives", function (assert) { - let done = assert.async(); - - assert.expect(2); - - frappe.run_serially([ - () => frappe.set_route('Form', 'Accounts Settings', 'Accounts Settings'), - () => frappe.timeout(2), - () => unchecked_if_checked(cur_frm, 'Allow Stale Exchange Rates', frappe.click_check), - () => cur_frm.set_value('stale_days', 0), - () => frappe.click_button('Save'), - () => frappe.timeout(2), - () => { - assert.ok(cur_dialog); - }, - () => frappe.click_button('Close'), - () => cur_frm.set_value('stale_days', -1), - () => frappe.click_button('Save'), - () => frappe.timeout(2), - () => { - assert.ok(cur_dialog); - }, - () => frappe.click_button('Close'), - () => done() - ]); - -}); - -const unchecked_if_checked = function(frm, field_name, fn){ - if (frm.doc.allow_stale) { - return fn(field_name); - } -}; diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.js b/erpnext/accounts/doctype/journal_entry/test_journal_entry.js deleted file mode 100644 index 28ccd95592..0000000000 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.js +++ /dev/null @@ -1,39 +0,0 @@ -QUnit.module('Journal Entry'); - -QUnit.test("test journal entry", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Journal Entry', [ - {posting_date:frappe.datetime.add_days(frappe.datetime.nowdate(), 0)}, - {accounts: [ - [ - {'account':'Debtors - '+frappe.get_abbr(frappe.defaults.get_default('Company'))}, - {'party_type':'Customer'}, - {'party':'Test Customer 1'}, - {'credit_in_account_currency':1000}, - {'is_advance':'Yes'}, - ], - [ - {'account':'HDFC - '+frappe.get_abbr(frappe.defaults.get_default('Company'))}, - {'debit_in_account_currency':1000}, - ] - ]}, - {cheque_no:1234}, - {cheque_date: frappe.datetime.add_days(frappe.datetime.nowdate(), -1)}, - {user_remark: 'Test'}, - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.total_debit==1000, "total debit correct"); - assert.ok(cur_frm.doc.total_credit==1000, "total credit correct"); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_invoice.js b/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_invoice.js deleted file mode 100644 index 4f27b74d4b..0000000000 --- a/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_invoice.js +++ /dev/null @@ -1,55 +0,0 @@ -QUnit.module('Payment Entry'); - -QUnit.test("test payment entry", function(assert) { - assert.expect(6); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Sales Invoice', [ - {customer: 'Test Customer 1'}, - {items: [ - [ - {'item_code': 'Test Product 1'}, - {'qty': 1}, - {'rate': 101}, - ] - ]} - ]); - }, - () => cur_frm.save(), - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(1), - () => frappe.tests.click_button('Close'), - () => frappe.timeout(1), - () => frappe.click_button('Make'), - () => frappe.timeout(1), - () => frappe.click_link('Payment'), - () => frappe.timeout(2), - () => { - assert.equal(frappe.get_route()[1], 'Payment Entry', - 'made payment entry'); - assert.equal(cur_frm.doc.party, 'Test Customer 1', - 'customer set in payment entry'); - assert.equal(cur_frm.doc.paid_amount, 101, - 'paid amount set in payment entry'); - assert.equal(cur_frm.doc.references[0].allocated_amount, 101, - 'amount allocated against sales invoice'); - }, - () => frappe.timeout(1), - () => cur_frm.set_value('paid_amount', 100), - () => frappe.timeout(1), - () => { - frappe.model.set_value("Payment Entry Reference", cur_frm.doc.references[0].name, - "allocated_amount", 101); - }, - () => frappe.timeout(1), - () => frappe.click_button('Write Off Difference Amount'), - () => frappe.timeout(1), - () => { - assert.equal(cur_frm.doc.difference_amount, 0, 'difference amount is zero'); - assert.equal(cur_frm.doc.deductions[0].amount, 1, 'Write off amount = 1'); - }, - () => done() - ]); -}); diff --git a/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_purchase_invoice.js b/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_purchase_invoice.js deleted file mode 100644 index e8db2c3159..0000000000 --- a/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_purchase_invoice.js +++ /dev/null @@ -1,60 +0,0 @@ -QUnit.module('Payment Entry'); - -QUnit.test("test payment entry", function(assert) { - assert.expect(7 ); - let done = assert.async(); - - frappe.run_serially([ - () => { - return frappe.tests.make('Purchase Invoice', [ - {supplier: 'Test Supplier'}, - {bill_no: 'in1234'}, - {items: [ - [ - {'qty': 2}, - {'item_code': 'Test Product 1'}, - {'rate':1000}, - ] - ]}, - {update_stock:1}, - {supplier_address: 'Test1-Billing'}, - {contact_person: 'Contact 3-Test Supplier'}, - {tc_name: 'Test Term 1'}, - {terms: 'This is just a Test'} - ]); - }, - - () => cur_frm.save(), - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(1), - () => frappe.click_button('Make'), - () => frappe.timeout(2), - () => frappe.click_link('Payment'), - () => frappe.timeout(3), - () => cur_frm.set_value('mode_of_payment','Cash'), - () => frappe.timeout(3), - () => { - assert.equal(frappe.get_route()[1], 'Payment Entry', - 'made payment entry'); - assert.equal(cur_frm.doc.party, 'Test Supplier', - 'supplier set in payment entry'); - assert.equal(cur_frm.doc.paid_amount, 2000, - 'paid amount set in payment entry'); - assert.equal(cur_frm.doc.references[0].allocated_amount, 2000, - 'amount allocated against purchase invoice'); - assert.equal(cur_frm.doc.references[0].bill_no, 'in1234', - 'invoice number allocated against purchase invoice'); - assert.equal(cur_frm.get_field('total_allocated_amount').value, 2000, - 'correct amount allocated in Write Off'); - assert.equal(cur_frm.get_field('unallocated_amount').value, 0, - 'correct amount unallocated in Write Off'); - }, - - () => cur_frm.save(), - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(3), - () => done() - ]); -}); diff --git a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js b/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js deleted file mode 100644 index 34af79fcd1..0000000000 --- a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js +++ /dev/null @@ -1,28 +0,0 @@ -QUnit.module('Accounts'); - -QUnit.test("test payment entry", function(assert) { - assert.expect(1); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Payment Entry', [ - {payment_type:'Receive'}, - {mode_of_payment:'Cash'}, - {party_type:'Customer'}, - {party:'Test Customer 3'}, - {paid_amount:675}, - {reference_no:123}, - {reference_date: frappe.datetime.add_days(frappe.datetime.nowdate(), 0)}, - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.total_allocated_amount==675, "Allocated AmountCorrect"); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry_write_off.js b/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry_write_off.js deleted file mode 100644 index 8c7f6f47dd..0000000000 --- a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry_write_off.js +++ /dev/null @@ -1,67 +0,0 @@ -QUnit.module('Payment Entry'); - -QUnit.test("test payment entry", function(assert) { - assert.expect(8); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Sales Invoice', [ - {customer: 'Test Customer 1'}, - {company: 'For Testing'}, - {currency: 'INR'}, - {selling_price_list: '_Test Price List'}, - {items: [ - [ - {'qty': 1}, - {'item_code': 'Test Product 1'}, - ] - ]} - ]); - }, - () => frappe.timeout(1), - () => cur_frm.save(), - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(1.5), - () => frappe.click_button('Close'), - () => frappe.timeout(0.5), - () => frappe.click_button('Make'), - () => frappe.timeout(1), - () => frappe.click_link('Payment'), - () => frappe.timeout(2), - () => cur_frm.set_value("paid_to", "_Test Cash - FT"), - () => frappe.timeout(0.5), - () => { - assert.equal(frappe.get_route()[1], 'Payment Entry', 'made payment entry'); - assert.equal(cur_frm.doc.party, 'Test Customer 1', 'customer set in payment entry'); - assert.equal(cur_frm.doc.paid_from, 'Debtors - FT', 'customer account set in payment entry'); - assert.equal(cur_frm.doc.paid_amount, 100, 'paid amount set in payment entry'); - assert.equal(cur_frm.doc.references[0].allocated_amount, 100, - 'amount allocated against sales invoice'); - }, - () => cur_frm.set_value('paid_amount', 95), - () => frappe.timeout(1), - () => { - frappe.model.set_value("Payment Entry Reference", - cur_frm.doc.references[0].name, "allocated_amount", 100); - }, - () => frappe.timeout(.5), - () => { - assert.equal(cur_frm.doc.difference_amount, 5, 'difference amount is 5'); - }, - () => { - frappe.db.set_value("Company", "For Testing", "write_off_account", "_Test Write Off - FT"); - frappe.timeout(1); - frappe.db.set_value("Company", "For Testing", - "exchange_gain_loss_account", "_Test Exchange Gain/Loss - FT"); - }, - () => frappe.timeout(1), - () => frappe.click_button('Write Off Difference Amount'), - () => frappe.timeout(2), - () => { - assert.equal(cur_frm.doc.difference_amount, 0, 'difference amount is zero'); - assert.equal(cur_frm.doc.deductions[0].amount, 5, 'Write off amount = 5'); - }, - () => done() - ]); -}); diff --git a/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule.js b/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule.js deleted file mode 100644 index 8279b59cb4..0000000000 --- a/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule.js +++ /dev/null @@ -1,28 +0,0 @@ -QUnit.module('Pricing Rule'); - -QUnit.test("test pricing rule", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make("Pricing Rule", [ - {title: 'Test Pricing Rule'}, - {item_code:'Test Product 2'}, - {selling:1}, - {applicable_for:'Customer'}, - {customer:'Test Customer 3'}, - {currency: frappe.defaults.get_default("currency")} - {min_qty:1}, - {max_qty:20}, - {valid_upto: frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {discount_percentage:10}, - {for_price_list:'Standard Selling'} - ]); - }, - () => { - assert.ok(cur_frm.doc.item_code=='Test Product 2'); - assert.ok(cur_frm.doc.customer=='Test Customer 3'); - }, - () => done() - ]); -}); diff --git a/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule_with_different_currency.js b/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule_with_different_currency.js deleted file mode 100644 index 4a29956502..0000000000 --- a/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule_with_different_currency.js +++ /dev/null @@ -1,58 +0,0 @@ -QUnit.module('Pricing Rule'); - -QUnit.test("test pricing rule with different currency", function(assert) { - assert.expect(3); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make("Pricing Rule", [ - {title: 'Test Pricing Rule 2'}, - {apply_on: 'Item Code'}, - {item_code:'Test Product 4'}, - {selling:1}, - {priority: 1}, - {min_qty:1}, - {max_qty:20}, - {valid_upto: frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {margin_type: 'Amount'}, - {margin_rate_or_amount: 20}, - {rate_or_discount: 'Rate'}, - {rate:200}, - {currency:'USD'} - - ]); - }, - () => cur_frm.save(), - () => frappe.timeout(0.3), - () => { - assert.ok(cur_frm.doc.item_code=='Test Product 4'); - }, - - () => { - return frappe.tests.make('Sales Order', [ - {customer: 'Test Customer 1'}, - {currency: 'INR'}, - {items: [ - [ - {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {'qty': 5}, - {'item_code': "Test Product 4"} - ] - ]} - ]); - }, - () => cur_frm.save(), - () => frappe.timeout(0.3), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].pricing_rule=='Test Pricing Rule 2', "Pricing rule correct"); - // margin not applied because different currency in pricing rule - assert.ok(cur_frm.doc.items[0].margin_type==null, "Margin correct"); - }, - () => frappe.timeout(0.3), - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule_with_same_currency.js b/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule_with_same_currency.js deleted file mode 100644 index 601ff6bd3d..0000000000 --- a/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule_with_same_currency.js +++ /dev/null @@ -1,56 +0,0 @@ -QUnit.module('Pricing Rule'); - -QUnit.test("test pricing rule with same currency", function(assert) { - assert.expect(4); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make("Pricing Rule", [ - {title: 'Test Pricing Rule 1'}, - {apply_on: 'Item Code'}, - {item_code:'Test Product 4'}, - {selling:1}, - {min_qty:1}, - {max_qty:20}, - {valid_upto: frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {rate_or_discount: 'Rate'}, - {rate:200}, - {currency:'USD'} - - ]); - }, - () => cur_frm.save(), - () => frappe.timeout(0.3), - () => { - assert.ok(cur_frm.doc.item_code=='Test Product 4'); - }, - - () => { - return frappe.tests.make('Sales Order', [ - {customer: 'Test Customer 1'}, - {currency: 'USD'}, - {items: [ - [ - {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {'qty': 5}, - {'item_code': "Test Product 4"} - ] - ]} - ]); - }, - () => cur_frm.save(), - () => frappe.timeout(0.3), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].pricing_rule=='Test Pricing Rule 1', "Pricing rule correct"); - assert.ok(cur_frm.doc.items[0].price_list_rate==200, "Item rate correct"); - // get_total - assert.ok(cur_frm.doc.total== 1000, "Total correct"); - }, - () => frappe.timeout(0.3), - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.js deleted file mode 100644 index 94b3b9ed33..0000000000 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.js +++ /dev/null @@ -1,74 +0,0 @@ -QUnit.module('Purchase Invoice'); - -QUnit.test("test purchase invoice", function(assert) { - assert.expect(9); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Purchase Invoice', [ - {supplier: 'Test Supplier'}, - {bill_no: 'in123'}, - {items: [ - [ - {'qty': 5}, - {'item_code': 'Test Product 1'}, - {'rate':100}, - ] - ]}, - {update_stock:1}, - {supplier_address: 'Test1-Billing'}, - {contact_person: 'Contact 3-Test Supplier'}, - {taxes_and_charges: 'TEST In State GST - FT'}, - {tc_name: 'Test Term 1'}, - {terms: 'This is Test'}, - {payment_terms_template: '_Test Payment Term Template UI'} - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); - // get tax details - assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct"); - // get tax account head details - assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct"); - // grand_total Calculated - assert.ok(cur_frm.doc.grand_total==590, "Grad Total correct"); - - assert.ok(cur_frm.doc.payment_terms_template, "Payment Terms Template is correct"); - assert.ok(cur_frm.doc.payment_schedule.length > 0, "Payment Term Schedule is not empty"); - - }, - () => { - let date = cur_frm.doc.due_date; - frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1)); - frappe.timeout(0.5); - assert.ok(cur_dialog && cur_dialog.is_visible, 'Message is displayed to user'); - }, - () => frappe.timeout(1), - () => frappe.tests.click_button('Close'), - () => frappe.timeout(0.5), - () => frappe.tests.set_form_values(cur_frm, [{'payment_terms_schedule': ''}]), - () => { - let date = cur_frm.doc.due_date; - frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1)); - frappe.timeout(0.5); - assert.ok(cur_dialog && cur_dialog.is_visible, 'Message is displayed to user'); - }, - () => frappe.timeout(1), - () => frappe.tests.click_button('Close'), - () => frappe.timeout(0.5), - () => frappe.tests.set_form_values(cur_frm, [{'payment_schedule': []}]), - () => { - let date = cur_frm.doc.due_date; - frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1)); - frappe.timeout(0.5); - assert.ok(!cur_dialog, 'Message is not shown'); - }, - () => cur_frm.save(), - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(1), - () => done() - ]); -}); diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/test_purchase_taxes_and_charges_template.js b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/test_purchase_taxes_and_charges_template.js deleted file mode 100644 index 10b05d0594..0000000000 --- a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/test_purchase_taxes_and_charges_template.js +++ /dev/null @@ -1,28 +0,0 @@ -QUnit.module('Sales Taxes and Charges Template'); - -QUnit.test("test sales taxes and charges template", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Purchase Taxes and Charges Template', [ - {title: "TEST In State GST"}, - {taxes:[ - [ - {charge_type:"On Net Total"}, - {account_head:"CGST - "+frappe.get_abbr(frappe.defaults.get_default("Company")) } - ], - [ - {charge_type:"On Net Total"}, - {account_head:"SGST - "+frappe.get_abbr(frappe.defaults.get_default("Company")) } - ] - ]} - ]); - }, - () => { - assert.ok(cur_frm.doc.title=='TEST In State GST'); - assert.ok(cur_frm.doc.name=='TEST In State GST - FT'); - }, - () => done() - ]); -}); diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.js deleted file mode 100644 index 1c052bd3fc..0000000000 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.js +++ /dev/null @@ -1,73 +0,0 @@ -QUnit.module('Sales Invoice'); - -QUnit.test("test sales Invoice", function(assert) { - assert.expect(9); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Sales Invoice', [ - {customer: 'Test Customer 1'}, - {items: [ - [ - {'qty': 5}, - {'item_code': 'Test Product 1'}, - ] - ]}, - {update_stock:1}, - {customer_address: 'Test1-Billing'}, - {shipping_address_name: 'Test1-Shipping'}, - {contact_person: 'Contact 1-Test Customer 1'}, - {taxes_and_charges: 'TEST In State GST - FT'}, - {tc_name: 'Test Term 1'}, - {terms: 'This is Test'}, - {payment_terms_template: '_Test Payment Term Template UI'} - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); - // get tax details - assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct"); - // get tax account head details - assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct"); - // grand_total Calculated - assert.ok(cur_frm.doc.grand_total==590, "Grand Total correct"); - - assert.ok(cur_frm.doc.payment_terms_template, "Payment Terms Template is correct"); - assert.ok(cur_frm.doc.payment_schedule.length > 0, "Payment Term Schedule is not empty"); - - }, - () => { - let date = cur_frm.doc.due_date; - frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1)); - frappe.timeout(0.5); - assert.ok(cur_dialog && cur_dialog.is_visible, 'Message is displayed to user'); - }, - () => frappe.timeout(1), - () => frappe.tests.click_button('Close'), - () => frappe.timeout(0.5), - () => frappe.tests.set_form_values(cur_frm, [{'payment_terms_schedule': ''}]), - () => { - let date = cur_frm.doc.due_date; - frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1)); - frappe.timeout(0.5); - assert.ok(cur_dialog && cur_dialog.is_visible, 'Message is displayed to user'); - }, - () => frappe.timeout(1), - () => frappe.tests.click_button('Close'), - () => frappe.timeout(0.5), - () => frappe.tests.set_form_values(cur_frm, [{'payment_schedule': []}]), - () => { - let date = cur_frm.doc.due_date; - frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1)); - frappe.timeout(0.5); - assert.ok(!cur_dialog, 'Message is not shown'); - }, - () => cur_frm.save(), - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice.js deleted file mode 100644 index 61d78e1fe4..0000000000 --- a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice.js +++ /dev/null @@ -1,42 +0,0 @@ -QUnit.module('Sales Invoice'); - -QUnit.test("test sales Invoice", function(assert) { - assert.expect(4); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Sales Invoice', [ - {customer: 'Test Customer 1'}, - {items: [ - [ - {'qty': 5}, - {'item_code': 'Test Product 1'}, - ] - ]}, - {update_stock:1}, - {customer_address: 'Test1-Billing'}, - {shipping_address_name: 'Test1-Shipping'}, - {contact_person: 'Contact 1-Test Customer 1'}, - {taxes_and_charges: 'TEST In State GST - FT'}, - {tc_name: 'Test Term 1'}, - {terms: 'This is Test'} - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); - // get tax details - assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct"); - // get tax account head details - assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct"); - // grand_total Calculated - assert.ok(cur_frm.doc.grand_total==590, "Grad Total correct"); - - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_margin.js b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_margin.js deleted file mode 100644 index cf2d0fbedb..0000000000 --- a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_margin.js +++ /dev/null @@ -1,35 +0,0 @@ -QUnit.module('Accounts'); - -QUnit.test("test sales invoice with margin", function(assert) { - assert.expect(3); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Sales Invoice', [ - {customer: 'Test Customer 1'}, - {selling_price_list: 'Test-Selling-USD'}, - {currency: 'USD'}, - {items: [ - [ - {'item_code': 'Test Product 4'}, - {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {'qty': 1}, - {'margin_type': 'Percentage'}, - {'margin_rate_or_amount': 20} - ] - ]} - ]); - }, - () => cur_frm.save(), - () => { - assert.ok(cur_frm.doc.items[0].rate_with_margin == 240, "Margin rate correct"); - assert.ok(cur_frm.doc.items[0].base_rate_with_margin == cur_frm.doc.conversion_rate * 240, "Base margin rate correct"); - assert.ok(cur_frm.doc.total == 240, "Amount correct"); - - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment.js b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment.js deleted file mode 100644 index 45d9a14bff..0000000000 --- a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment.js +++ /dev/null @@ -1,56 +0,0 @@ -QUnit.module('Sales Invoice'); - -QUnit.test("test sales Invoice with payment", function(assert) { - assert.expect(4); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Sales Invoice', [ - {customer: 'Test Customer 1'}, - {items: [ - [ - {'qty': 5}, - {'item_code': 'Test Product 1'}, - ] - ]}, - {update_stock:1}, - {customer_address: 'Test1-Billing'}, - {shipping_address_name: 'Test1-Shipping'}, - {contact_person: 'Contact 1-Test Customer 1'}, - {taxes_and_charges: 'TEST In State GST - FT'}, - {tc_name: 'Test Term 1'}, - {terms: 'This is Test'}, - {payment_terms_template: '_Test Payment Term Template UI'} - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); - // get tax details - assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct"); - // grand_total Calculated - assert.ok(cur_frm.doc.grand_total==590, "Grad Total correct"); - - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(2), - () => frappe.tests.click_button('Close'), - () => frappe.tests.click_button('Make'), - () => frappe.tests.click_link('Payment'), - () => frappe.timeout(0.2), - () => { cur_frm.set_value('mode_of_payment','Cash');}, - () => { cur_frm.set_value('paid_to','Cash - '+frappe.get_abbr(frappe.defaults.get_default('Company')));}, - () => {cur_frm.set_value('reference_no','TEST1234');}, - () => {cur_frm.set_value('reference_date',frappe.datetime.add_days(frappe.datetime.nowdate(), 0));}, - () => cur_frm.save(), - () => { - // get payment details - assert.ok(cur_frm.doc.paid_amount==590, "Paid Amount Correct"); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => done() - ]); -}); diff --git a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment_request.js b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment_request.js deleted file mode 100644 index 0464e4509f..0000000000 --- a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment_request.js +++ /dev/null @@ -1,51 +0,0 @@ -QUnit.module('Sales Invoice'); - -QUnit.test("test sales Invoice with payment request", function(assert) { - assert.expect(4); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Sales Invoice', [ - {customer: 'Test Customer 1'}, - {items: [ - [ - {'qty': 5}, - {'item_code': 'Test Product 1'}, - ] - ]}, - {update_stock:1}, - {customer_address: 'Test1-Billing'}, - {shipping_address_name: 'Test1-Shipping'}, - {contact_person: 'Contact 1-Test Customer 1'}, - {taxes_and_charges: 'TEST In State GST - FT'}, - {tc_name: 'Test Term 1'}, - {terms: 'This is Test'} - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); - // get tax details - assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct"); - // grand_total Calculated - assert.ok(cur_frm.doc.grand_total==590, "Grad Total correct"); - - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(2), - () => frappe.tests.click_button('Close'), - () => frappe.tests.click_button('Make'), - () => frappe.tests.click_link('Payment Request'), - () => frappe.timeout(0.2), - () => { cur_frm.set_value('print_format','GST Tax Invoice');}, - () => { cur_frm.set_value('email_to','test@gmail.com');}, - () => cur_frm.save(), - () => { - // get payment details - assert.ok(cur_frm.doc.grand_total==590, "grand total Correct"); - }, - () => done() - ]); -}); diff --git a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_serialize_item.js b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_serialize_item.js deleted file mode 100644 index af484d7899..0000000000 --- a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_serialize_item.js +++ /dev/null @@ -1,44 +0,0 @@ -QUnit.module('Sales Invoice'); - -QUnit.test("test sales Invoice with serialize item", function(assert) { - assert.expect(5); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Sales Invoice', [ - {customer: 'Test Customer 1'}, - {items: [ - [ - {'qty': 2}, - {'item_code': 'Test Product 4'}, - ] - ]}, - {update_stock:1}, - {customer_address: 'Test1-Billing'}, - {shipping_address_name: 'Test1-Shipping'}, - {contact_person: 'Contact 1-Test Customer 1'}, - {taxes_and_charges: 'TEST In State GST - FT'}, - {tc_name: 'Test Term 1'}, - {terms: 'This is Test'} - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct"); - // get tax details - assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct"); - // get tax account head details - assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct"); - // get batch number - assert.ok(cur_frm.doc.items[0].batch_no=='TEST-BATCH-001', " Batch Details correct"); - // grand_total Calculated - assert.ok(cur_frm.doc.grand_total==218, "Grad Total correct"); - - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_sales_taxes_and_charges_template.js b/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_sales_taxes_and_charges_template.js deleted file mode 100644 index 8cd42f63a4..0000000000 --- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_sales_taxes_and_charges_template.js +++ /dev/null @@ -1,28 +0,0 @@ -QUnit.module('Sales Taxes and Charges Template'); - -QUnit.test("test sales taxes and charges template", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Sales Taxes and Charges Template', [ - {title: "TEST In State GST"}, - {taxes:[ - [ - {charge_type:"On Net Total"}, - {account_head:"CGST - "+frappe.get_abbr(frappe.defaults.get_default("Company")) } - ], - [ - {charge_type:"On Net Total"}, - {account_head:"SGST - "+frappe.get_abbr(frappe.defaults.get_default("Company")) } - ] - ]} - ]); - }, - () => { - assert.ok(cur_frm.doc.title=='TEST In State GST'); - assert.ok(cur_frm.doc.name=='TEST In State GST - FT'); - }, - () => done() - ]); -}); diff --git a/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.js b/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.js deleted file mode 100644 index 63ea1bf35f..0000000000 --- a/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.js +++ /dev/null @@ -1,36 +0,0 @@ -QUnit.module('Shipping Rule'); - -QUnit.test("test Shipping Rule", function(assert) { - assert.expect(1); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make("Shipping Rule", [ - {label: "Next Day Shipping"}, - {shipping_rule_type: "Selling"}, - {calculate_based_on: 'Net Total'}, - {conditions:[ - [ - {from_value:1}, - {to_value:200}, - {shipping_amount:100} - ], - [ - {from_value:201}, - {to_value:2000}, - {shipping_amount:50} - ], - ]}, - {countries:[ - [ - {country:'India'} - ] - ]}, - {account:'Accounts Payable - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}, - {cost_center:'Main - '+frappe.get_abbr(frappe.defaults.get_default("Company"))} - ]); - }, - () => {assert.ok(cur_frm.doc.name=='Next Day Shipping');}, - () => done() - ]); -}); diff --git a/erpnext/accounts/doctype/shipping_rule/tests/test_shipping_rule_for_buying.js b/erpnext/accounts/doctype/shipping_rule/tests/test_shipping_rule_for_buying.js deleted file mode 100644 index f3668b8b40..0000000000 --- a/erpnext/accounts/doctype/shipping_rule/tests/test_shipping_rule_for_buying.js +++ /dev/null @@ -1,36 +0,0 @@ -QUnit.module('Shipping Rule'); - -QUnit.test("test Shipping Rule", function(assert) { - assert.expect(1); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make("Shipping Rule", [ - {label: "Two Day Shipping"}, - {shipping_rule_type: "Buying"}, - {fixed_shipping_amount: 0}, - {conditions:[ - [ - {from_value:1}, - {to_value:200}, - {shipping_amount:100} - ], - [ - {from_value:201}, - {to_value:3000}, - {shipping_amount:200} - ], - ]}, - {countries:[ - [ - {country:'India'} - ] - ]}, - {account:'Accounts Payable - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}, - {cost_center:'Main - '+frappe.get_abbr(frappe.defaults.get_default("Company"))} - ]); - }, - () => {assert.ok(cur_frm.doc.name=='Two Day Shipping');}, - () => done() - ]); -}); diff --git a/erpnext/accounts/doctype/subscription/test_subscription.js b/erpnext/accounts/doctype/subscription/test_subscription.js deleted file mode 100644 index 2872a2147f..0000000000 --- a/erpnext/accounts/doctype/subscription/test_subscription.js +++ /dev/null @@ -1,32 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Subscription", function (assert) { - assert.expect(4); - let done = assert.async(); - frappe.run_serially([ - // insert a new Subscription - () => { - return frappe.tests.make("Subscription", [ - {reference_doctype: 'Sales Invoice'}, - {reference_document: 'SINV-00004'}, - {start_date: frappe.datetime.month_start()}, - {end_date: frappe.datetime.month_end()}, - {frequency: 'Weekly'} - ]); - }, - () => cur_frm.savesubmit(), - () => frappe.timeout(1), - () => frappe.click_button('Yes'), - () => frappe.timeout(2), - () => { - assert.ok(cur_frm.doc.frequency.includes("Weekly"), "Set frequency Weekly"); - assert.ok(cur_frm.doc.reference_doctype.includes("Sales Invoice"), "Set base doctype Sales Invoice"); - assert.equal(cur_frm.doc.docstatus, 1, "Submitted subscription"); - assert.equal(cur_frm.doc.next_schedule_date, - frappe.datetime.add_days(frappe.datetime.get_today(), 7), "Set schedule date"); - }, - () => done() - ]); -}); diff --git a/erpnext/agriculture/doctype/crop/test_crop.js b/erpnext/agriculture/doctype/crop/test_crop.js deleted file mode 100644 index 40555634a2..0000000000 --- a/erpnext/agriculture/doctype/crop/test_crop.js +++ /dev/null @@ -1,116 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Crop", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(2); - - frappe.run_serially([ - // insert a new Item - () => frappe.tests.make('Item', [ - // values to be set - {item_code: 'Basil Seeds'}, - {item_name: 'Basil Seeds'}, - {item_group: 'Seed'} - ]), - // insert a new Item - () => frappe.tests.make('Item', [ - // values to be set - {item_code: 'Twigs'}, - {item_name: 'Twigs'}, - {item_group: 'By-product'} - ]), - // insert a new Item - () => frappe.tests.make('Item', [ - // values to be set - {item_code: 'Basil Leaves'}, - {item_name: 'Basil Leaves'}, - {item_group: 'Produce'} - ]), - // insert a new Crop - () => frappe.tests.make('Crop', [ - // values to be set - {title: 'Basil from seed'}, - {crop_name: 'Basil'}, - {scientific_name: 'Ocimum basilicum'}, - {materials_required: [ - [ - {item_code: 'Basil Seeds'}, - {qty: '25'}, - {uom: 'Nos'}, - {rate: '1'} - ], - [ - {item_code: 'Urea'}, - {qty: '5'}, - {uom: 'Kg'}, - {rate: '10'} - ] - ]}, - {byproducts: [ - [ - {item_code: 'Twigs'}, - {qty: '25'}, - {uom: 'Nos'}, - {rate: '1'} - ] - ]}, - {produce: [ - [ - {item_code: 'Basil Leaves'}, - {qty: '100'}, - {uom: 'Nos'}, - {rate: '1'} - ] - ]}, - {agriculture_task: [ - [ - {task_name: "Plough the field"}, - {start_day: 1}, - {end_day: 1}, - {holiday_management: "Ignore holidays"} - ], - [ - {task_name: "Plant the seeds"}, - {start_day: 2}, - {end_day: 3}, - {holiday_management: "Ignore holidays"} - ], - [ - {task_name: "Water the field"}, - {start_day: 4}, - {end_day: 4}, - {holiday_management: "Ignore holidays"} - ], - [ - {task_name: "First harvest"}, - {start_day: 8}, - {end_day: 8}, - {holiday_management: "Ignore holidays"} - ], - [ - {task_name: "Add the fertilizer"}, - {start_day: 10}, - {end_day: 12}, - {holiday_management: "Ignore holidays"} - ], - [ - {task_name: "Final cut"}, - {start_day: 15}, - {end_day: 15}, - {holiday_management: "Ignore holidays"} - ] - ]} - ]), - // agriculture task list - () => { - assert.equal(cur_frm.doc.name, 'Basil from seed'); - assert.equal(cur_frm.doc.period, 15); - }, - () => done() - ]); - -}); diff --git a/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.js b/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.js deleted file mode 100644 index 87184daedc..0000000000 --- a/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.js +++ /dev/null @@ -1,34 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Crop Cycle", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Crop Cycle - () => frappe.tests.make('Crop Cycle', [ - // values to be set - {title: 'Basil from seed 2017'}, - {detected_disease: [ - [ - {start_date: '2017-11-21'}, - {disease: 'Aphids'} - ] - ]}, - {linked_land_unit: [ - [ - {land_unit: 'Basil Farm'} - ] - ]}, - {crop: 'Basil from seed'}, - {start_date: '2017-11-11'}, - {cycle_type: 'Less than a year'} - ]), - () => assert.equal(cur_frm.doc.name, 'Basil from seed 2017'), - () => done() - ]); -}); diff --git a/erpnext/agriculture/doctype/disease/test_disease.js b/erpnext/agriculture/doctype/disease/test_disease.js deleted file mode 100644 index 33f60c4e15..0000000000 --- a/erpnext/agriculture/doctype/disease/test_disease.js +++ /dev/null @@ -1,38 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Disease", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Disease - () => frappe.tests.make('Disease', [ - // values to be set - {common_name: 'Aphids'}, - {scientific_name: 'Aphidoidea'}, - {treatment_task: [ - [ - {task_name: "Survey and find the aphid locations"}, - {start_day: 1}, - {end_day: 2}, - {holiday_management: "Ignore holidays"} - ], - [ - {task_name: "Apply Pesticides"}, - {start_day: 3}, - {end_day: 3}, - {holiday_management: "Ignore holidays"} - ] - ]} - ]), - () => { - assert.equal(cur_frm.doc.treatment_period, 3); - }, - () => done() - ]); - -}); diff --git a/erpnext/agriculture/doctype/fertilizer/test_fertilizer.js b/erpnext/agriculture/doctype/fertilizer/test_fertilizer.js deleted file mode 100644 index 5dd7313787..0000000000 --- a/erpnext/agriculture/doctype/fertilizer/test_fertilizer.js +++ /dev/null @@ -1,31 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Fertilizer", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Item - () => frappe.tests.make('Item', [ - // values to be set - {item_code: 'Urea'}, - {item_name: 'Urea'}, - {item_group: 'Fertilizer'} - ]), - // insert a new Fertilizer - () => frappe.tests.make('Fertilizer', [ - // values to be set - {fertilizer_name: 'Urea'}, - {item: 'Urea'} - ]), - () => { - assert.equal(cur_frm.doc.name, 'Urea'); - }, - () => done() - ]); - -}); diff --git a/erpnext/agriculture/doctype/soil_texture/test_soil_texture.js b/erpnext/agriculture/doctype/soil_texture/test_soil_texture.js deleted file mode 100644 index d93f852750..0000000000 --- a/erpnext/agriculture/doctype/soil_texture/test_soil_texture.js +++ /dev/null @@ -1,26 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Soil Texture", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(2); - - frappe.run_serially([ - // insert a new Soil Texture - () => frappe.tests.make('Soil Texture', [ - // values to be set - {location: '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[72.882185,19.076395]}}]}'}, - {collection_datetime: '2017-11-08'}, - {clay_composition: 20}, - {sand_composition: 30} - ]), - () => { - assert.equal(cur_frm.doc.silt_composition, 50); - assert.equal(cur_frm.doc.soil_type, 'Silt Loam'); - }, - () => done() - ]); -}); diff --git a/erpnext/agriculture/doctype/water_analysis/test_water_analysis.js b/erpnext/agriculture/doctype/water_analysis/test_water_analysis.js deleted file mode 100644 index bb01cb3ce2..0000000000 --- a/erpnext/agriculture/doctype/water_analysis/test_water_analysis.js +++ /dev/null @@ -1,25 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Water Analysis", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Water Analysis - () => frappe.tests.make('Water Analysis', [ - // values to be set - {location: '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[72.882185,19.076395]}}]}'}, - {collection_datetime: '2017-11-08 18:43:57'}, - {laboratory_testing_datetime: '2017-11-10 18:43:57'} - ]), - () => { - assert.equal(cur_frm.doc.result_datetime, '2017-11-10 18:43:57'); - }, - () => done() - ]); - -}); diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order.js deleted file mode 100644 index 012b0619cc..0000000000 --- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order.js +++ /dev/null @@ -1,80 +0,0 @@ -QUnit.module('Buying'); - -QUnit.test("test: purchase order", function(assert) { - assert.expect(16); - let done = assert.async(); - - frappe.run_serially([ - () => { - return frappe.tests.make('Purchase Order', [ - {supplier: 'Test Supplier'}, - {is_subcontracted: 'No'}, - {currency: 'INR'}, - {items: [ - [ - {"item_code": 'Test Product 4'}, - {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 2)}, - {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)}, - {"qty": 5}, - {"uom": 'Unit'}, - {"rate": 100}, - {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))} - ], - [ - {"item_code": 'Test Product 1'}, - {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)}, - {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)}, - {"qty": 2}, - {"uom": 'Unit'}, - {"rate": 100}, - {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))} - ] - ]}, - - {tc_name: 'Test Term 1'}, - {terms: 'This is a term.'} - ]); - }, - - () => { - // Get supplier details - assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Supplier name correct"); - assert.ok(cur_frm.doc.schedule_date == frappe.datetime.add_days(frappe.datetime.now_date(), 1), "Schedule Date correct"); - assert.ok(cur_frm.doc.contact_email == 'test@supplier.com', "Contact email correct"); - // Get item details - assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 4', "Item name correct"); - assert.ok(cur_frm.doc.items[0].description == 'Test Product 4', "Description correct"); - assert.ok(cur_frm.doc.items[0].qty == 5, "Quantity correct"); - assert.ok(cur_frm.doc.items[0].schedule_date == frappe.datetime.add_days(frappe.datetime.now_date(), 2), "Schedule Date correct"); - - assert.ok(cur_frm.doc.items[1].item_name == 'Test Product 1', "Item name correct"); - assert.ok(cur_frm.doc.items[1].description == 'Test Product 1', "Description correct"); - assert.ok(cur_frm.doc.items[1].qty == 2, "Quantity correct"); - assert.ok(cur_frm.doc.items[1].schedule_date == cur_frm.doc.schedule_date, "Schedule Date correct"); - // Calculate total - assert.ok(cur_frm.doc.total == 700, "Total correct"); - // Get terms - assert.ok(cur_frm.doc.terms == 'This is a term.', "Terms correct"); - }, - - () => cur_frm.print_doc(), - () => frappe.timeout(2), - () => { - assert.ok($('.btn-print-print').is(':visible'), "Print Format Available"); - assert.ok($('div > div:nth-child(5) > div > div > table > tbody > tr > td:nth-child(4) > div').text().includes('Test Product 4'), "Print Preview Works"); - }, - - () => cur_frm.print_doc(), - () => frappe.timeout(1), - - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(1), - - () => { - assert.ok(cur_frm.doc.status == 'To Receive and Bill', "Submitted successfully"); - }, - - () => done() - ]); -}); diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_get_items.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_get_items.js deleted file mode 100644 index bc3d767f95..0000000000 --- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_get_items.js +++ /dev/null @@ -1,61 +0,0 @@ -QUnit.module('Buying'); - -QUnit.test("test: purchase order with get items", function(assert) { - assert.expect(4); - let done = assert.async(); - - frappe.run_serially([ - () => { - return frappe.tests.make('Purchase Order', [ - {supplier: 'Test Supplier'}, - {is_subcontracted: 'No'}, - {buying_price_list: 'Test-Buying-USD'}, - {currency: 'USD'}, - {items: [ - [ - {"item_code": 'Test Product 4'}, - {"qty": 5}, - {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)}, - {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)}, - {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))} - ] - ]} - ]); - }, - - () => { - assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Supplier name correct"); - }, - - () => frappe.timeout(0.3), - () => frappe.click_button('Get items from'), - () => frappe.timeout(0.3), - - () => frappe.click_link('Product Bundle'), - () => frappe.timeout(0.5), - - () => cur_dialog.set_value('product_bundle', 'Computer'), - () => frappe.click_button('Get Items'), - () => frappe.timeout(1), - - // Check if items are fetched from Product Bundle - () => { - assert.ok(cur_frm.doc.items[1].item_name == 'CPU', "Product bundle item 1 correct"); - assert.ok(cur_frm.doc.items[2].item_name == 'Screen', "Product bundle item 2 correct"); - assert.ok(cur_frm.doc.items[3].item_name == 'Keyboard', "Product bundle item 3 correct"); - }, - - () => cur_frm.doc.items[1].warehouse = 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company")), - () => cur_frm.doc.items[2].warehouse = 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company")), - () => cur_frm.doc.items[3].warehouse = 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company")), - - () => cur_frm.save(), - () => frappe.timeout(1), - - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - - () => done() - ]); -}); diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_receipt.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_receipt.js deleted file mode 100644 index daf8d6c259..0000000000 --- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_receipt.js +++ /dev/null @@ -1,74 +0,0 @@ -QUnit.module('Buying'); - -QUnit.test("test: purchase order receipt", function(assert) { - assert.expect(5); - let done = assert.async(); - - frappe.run_serially([ - () => { - return frappe.tests.make('Purchase Order', [ - {supplier: 'Test Supplier'}, - {is_subcontracted: 'No'}, - {buying_price_list: 'Test-Buying-USD'}, - {currency: 'USD'}, - {items: [ - [ - {"item_code": 'Test Product 1'}, - {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)}, - {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)}, - {"qty": 5}, - {"uom": 'Unit'}, - {"rate": 100}, - {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))} - ] - ]}, - ]); - }, - - () => { - - // Check supplier and item details - assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Supplier name correct"); - assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 1', "Item name correct"); - assert.ok(cur_frm.doc.items[0].description == 'Test Product 1', "Description correct"); - assert.ok(cur_frm.doc.items[0].qty == 5, "Quantity correct"); - - }, - - () => frappe.timeout(1), - - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - - () => frappe.timeout(1.5), - () => frappe.click_button('Close'), - () => frappe.timeout(0.3), - - // Make Purchase Receipt - () => frappe.click_button('Make'), - () => frappe.timeout(0.3), - - () => frappe.click_link('Receipt'), - () => frappe.timeout(2), - - () => cur_frm.save(), - - // Save and submit Purchase Receipt - () => frappe.timeout(1), - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(1), - - // View Purchase order in Stock Ledger - () => frappe.click_button('View'), - () => frappe.timeout(0.3), - - () => frappe.click_link('Stock Ledger'), - () => frappe.timeout(2), - () => { - assert.ok($('div.slick-cell.l2.r2 > a').text().includes('Test Product 1') - && $('div.slick-cell.l9.r9 > div').text().includes(5), "Stock ledger entry correct"); - }, - () => done() - ]); -}); diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_discount_on_grand_total.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_discount_on_grand_total.js deleted file mode 100644 index 83eb295010..0000000000 --- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_discount_on_grand_total.js +++ /dev/null @@ -1,47 +0,0 @@ -QUnit.module('Buying'); - -QUnit.test("test: purchase order with discount on grand total", function(assert) { - assert.expect(4); - let done = assert.async(); - - frappe.run_serially([ - () => { - return frappe.tests.make('Purchase Order', [ - {supplier: 'Test Supplier'}, - {is_subcontracted: 'No'}, - {buying_price_list: 'Test-Buying-EUR'}, - {currency: 'EUR'}, - {items: [ - [ - {"item_code": 'Test Product 4'}, - {"qty": 5}, - {"uom": 'Unit'}, - {"rate": 500 }, - {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)}, - {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)}, - {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))} - ] - ]}, - {apply_discount_on: 'Grand Total'}, - {additional_discount_percentage: 10} - ]); - }, - - () => frappe.timeout(1), - - () => { - assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Supplier name correct"); - assert.ok(cur_frm.doc.items[0].rate == 500, "Rate correct"); - // Calculate total - assert.ok(cur_frm.doc.total == 2500, "Total correct"); - // Calculate grand total after discount - assert.ok(cur_frm.doc.grand_total == 2250, "Grand total correct"); - }, - - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - - () => done() - ]); -}); diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_item_wise_discount.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_item_wise_discount.js deleted file mode 100644 index a729dd9839..0000000000 --- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_item_wise_discount.js +++ /dev/null @@ -1,44 +0,0 @@ -QUnit.module('Buying'); - -QUnit.test("test: purchase order with item wise discount", function(assert) { - assert.expect(4); - let done = assert.async(); - - frappe.run_serially([ - () => { - return frappe.tests.make('Purchase Order', [ - {supplier: 'Test Supplier'}, - {is_subcontracted: 'No'}, - {buying_price_list: 'Test-Buying-EUR'}, - {currency: 'EUR'}, - {items: [ - [ - {"item_code": 'Test Product 4'}, - {"qty": 5}, - {"uom": 'Unit'}, - {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)}, - {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)}, - {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}, - {"discount_percentage": 20} - ] - ]} - ]); - }, - - () => frappe.timeout(1), - - () => { - assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Supplier name correct"); - assert.ok(cur_frm.doc.items[0].discount_percentage == 20, "Discount correct"); - // Calculate totals after discount - assert.ok(cur_frm.doc.total == 2000, "Total correct"); - assert.ok(cur_frm.doc.grand_total == 2000, "Grand total correct"); - }, - - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - - () => done() - ]); -}); diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_multi_uom.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_multi_uom.js deleted file mode 100644 index b605e76ddf..0000000000 --- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_multi_uom.js +++ /dev/null @@ -1,39 +0,0 @@ -QUnit.module('Buying'); - -QUnit.test("test: purchase order with multi UOM", function(assert) { - assert.expect(3); - let done = assert.async(); - - frappe.run_serially([ - () => { - return frappe.tests.make('Purchase Order', [ - {supplier: 'Test Supplier'}, - {is_subcontracted: 'No'}, - {items: [ - [ - {"item_code": 'Test Product 4'}, - {"qty": 5}, - {"uom": 'Unit'}, - {"rate": 100}, - {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)}, - {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)}, - {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))} - ] - ]} - ]); - }, - - () => { - assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Supplier name correct"); - assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 4', "Item name correct"); - assert.ok(cur_frm.doc.items[0].uom == 'Unit', "Multi UOM correct"); - }, - - () => frappe.timeout(1), - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - - () => done() - ]); -}); diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_shipping_rule.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_shipping_rule.js deleted file mode 100644 index c258756b2a..0000000000 --- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_shipping_rule.js +++ /dev/null @@ -1,43 +0,0 @@ -QUnit.module('Buying'); - -QUnit.test("test: purchase order with shipping rule", function(assert) { - assert.expect(3); - let done = assert.async(); - - frappe.run_serially([ - () => { - return frappe.tests.make('Purchase Order', [ - {supplier: 'Test Supplier'}, - {is_subcontracted: 'No'}, - {buying_price_list: 'Test-Buying-USD'}, - {currency: 'USD'}, - {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)}, - {items: [ - [ - {"item_code": 'Test Product 4'}, - {"qty": 5}, - {"uom": 'Unit'}, - {"rate": 500 }, - {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)}, - {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)}, - {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))} - ] - ]}, - - {shipping_rule:'Two Day Shipping'} - ]); - }, - - () => { - // Check grand total - assert.ok(cur_frm.doc.total_taxes_and_charges == 200, "Taxes and charges correct"); - assert.ok(cur_frm.doc.grand_total == 2700, "Grand total correct"); - }, - - () => frappe.timeout(0.3), - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_taxes_and_charges.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_taxes_and_charges.js deleted file mode 100644 index ccc383fd74..0000000000 --- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_taxes_and_charges.js +++ /dev/null @@ -1,44 +0,0 @@ -QUnit.module('Buying'); - -QUnit.test("test: purchase order with taxes and charges", function(assert) { - assert.expect(3); - let done = assert.async(); - - frappe.run_serially([ - () => { - return frappe.tests.make('Purchase Order', [ - {supplier: 'Test Supplier'}, - {is_subcontracted: 'No'}, - {buying_price_list: 'Test-Buying-USD'}, - {currency: 'USD'}, - {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)}, - {items: [ - [ - {"item_code": 'Test Product 4'}, - {"qty": 5}, - {"uom": 'Unit'}, - {"rate": 500 }, - {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)}, - {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)}, - {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))} - ] - ]}, - - {taxes_and_charges: 'TEST In State GST - FT'} - ]); - }, - - () => { - // Check taxes and calculate grand total - assert.ok(cur_frm.doc.taxes[1].account_head=='SGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), "Account Head abbr correct"); - assert.ok(cur_frm.doc.total_taxes_and_charges == 225, "Taxes and charges correct"); - assert.ok(cur_frm.doc.grand_total == 2725, "Grand total correct"); - }, - - () => frappe.timeout(0.3), - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation.js deleted file mode 100644 index 75f85f86d1..0000000000 --- a/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation.js +++ /dev/null @@ -1,76 +0,0 @@ -QUnit.module('Buying'); - -QUnit.test("test: request_for_quotation", function(assert) { - assert.expect(14); - let done = assert.async(); - let date; - frappe.run_serially([ - () => { - date = frappe.datetime.add_days(frappe.datetime.now_date(), 10); - return frappe.tests.make('Request for Quotation', [ - {transaction_date: date}, - {suppliers: [ - [ - {"supplier": 'Test Supplier'}, - {"email_id": 'test@supplier.com'} - ] - ]}, - {items: [ - [ - {"item_code": 'Test Product 4'}, - {"qty": 5}, - {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(),20)}, - {"warehouse": 'All Warehouses - '+frappe.get_abbr(frappe.defaults.get_default("Company"))} - ] - ]}, - {message_for_supplier: 'Please supply the specified items at the best possible rates'}, - {tc_name: 'Test Term 1'} - ]); - }, - () => frappe.timeout(3), - () => { - assert.ok(cur_frm.doc.transaction_date == date, "Date correct"); - assert.ok(cur_frm.doc.company == cur_frm.doc.company, "Company correct"); - assert.ok(cur_frm.doc.suppliers[0].supplier_name == 'Test Supplier', "Supplier name correct"); - assert.ok(cur_frm.doc.suppliers[0].contact == 'Contact 3-Test Supplier', "Contact correct"); - assert.ok(cur_frm.doc.suppliers[0].email_id == 'test@supplier.com', "Email id correct"); - assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 4', "Item Name correct"); - assert.ok(cur_frm.doc.items[0].warehouse == 'All Warehouses - '+frappe.get_abbr(frappe.defaults.get_default("Company")), "Warehouse correct"); - assert.ok(cur_frm.doc.message_for_supplier == 'Please supply the specified items at the best possible rates', "Reply correct"); - assert.ok(cur_frm.doc.tc_name == 'Test Term 1', "Term name correct"); - }, - () => frappe.timeout(3), - () => cur_frm.print_doc(), - () => frappe.timeout(1), - () => { - assert.ok($('.btn-print-print').is(':visible'), "Print Format Available"); - assert.ok($('.section-break+ .section-break .column-break:nth-child(1) .value').text().includes("Test Product 4"), "Print Preview Works"); - }, - () => cur_frm.print_doc(), - () => frappe.timeout(1), - () => frappe.click_button('Get items from'), - () => frappe.timeout(0.3), - () => frappe.click_link('Material Request'), - () => frappe.timeout(1), - () => frappe.click_button('Get Items'), - () => frappe.timeout(1), - () => { - assert.ok(cur_frm.doc.items[1].item_name == 'Test Product 1', "Getting items from material requests work"); - }, - () => cur_frm.save(), - () => frappe.timeout(1), - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(1), - () => { - assert.ok(cur_frm.doc.docstatus == 1, "Quotation request submitted"); - }, - () => frappe.click_button('Send Supplier Emails'), - () => frappe.timeout(6), - () => { - assert.ok($('div.modal.fade.in > div.modal-dialog > div > div.modal-body.ui-front > div.msgprint').text().includes("Email sent to supplier Test Supplier"), "Send emails working"); - }, - () => frappe.click_button('Close'), - () => done() - ]); -}); diff --git a/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation_for_status.js b/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation_for_status.js deleted file mode 100644 index f06c3f34c4..0000000000 --- a/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation_for_status.js +++ /dev/null @@ -1,128 +0,0 @@ -QUnit.module('buying'); - -QUnit.test("Test: Request for Quotation", function (assert) { - assert.expect(5); - let done = assert.async(); - let rfq_name = ""; - - frappe.run_serially([ - // Go to RFQ list - () => frappe.set_route("List", "Request for Quotation"), - // Create a new RFQ - () => frappe.new_doc("Request for Quotation"), - () => frappe.timeout(1), - () => cur_frm.set_value("transaction_date", "04-04-2017"), - () => cur_frm.set_value("company", "For Testing"), - // Add Suppliers - () => { - cur_frm.fields_dict.suppliers.grid.grid_rows[0].toggle_view(); - }, - () => frappe.timeout(1), - () => { - cur_frm.fields_dict.suppliers.grid.grid_rows[0].doc.supplier = "_Test Supplier"; - frappe.click_check('Send Email'); - cur_frm.cur_grid.frm.script_manager.trigger('supplier'); - }, - () => frappe.timeout(1), - () => { - cur_frm.cur_grid.toggle_view(); - }, - () => frappe.timeout(1), - () => frappe.click_button('Add Row',0), - () => frappe.timeout(1), - () => { - cur_frm.fields_dict.suppliers.grid.grid_rows[1].toggle_view(); - }, - () => frappe.timeout(1), - () => { - cur_frm.fields_dict.suppliers.grid.grid_rows[1].doc.supplier = "_Test Supplier 1"; - frappe.click_check('Send Email'); - cur_frm.cur_grid.frm.script_manager.trigger('supplier'); - }, - () => frappe.timeout(1), - () => { - cur_frm.cur_grid.toggle_view(); - }, - () => frappe.timeout(1), - // Add Item - () => { - cur_frm.fields_dict.items.grid.grid_rows[0].toggle_view(); - }, - () => frappe.timeout(1), - () => { - cur_frm.fields_dict.items.grid.grid_rows[0].doc.item_code = "_Test Item"; - frappe.set_control('item_code',"_Test Item"); - frappe.set_control('qty',5); - frappe.set_control('schedule_date', "05-05-2017"); - cur_frm.cur_grid.frm.script_manager.trigger('supplier'); - }, - () => frappe.timeout(2), - () => { - cur_frm.cur_grid.toggle_view(); - }, - () => frappe.timeout(2), - () => { - cur_frm.fields_dict.items.grid.grid_rows[0].doc.warehouse = "_Test Warehouse - FT"; - }, - () => frappe.click_button('Save'), - () => frappe.timeout(1), - () => frappe.click_button('Submit'), - () => frappe.timeout(1), - () => frappe.click_button('Yes'), - () => frappe.timeout(1), - () => frappe.click_button('Menu'), - () => frappe.timeout(1), - () => frappe.click_link('Reload'), - () => frappe.timeout(1), - () => { - assert.equal(cur_frm.doc.docstatus, 1); - rfq_name = cur_frm.doc.name; - assert.ok(cur_frm.fields_dict.suppliers.grid.grid_rows[0].doc.quote_status == "Pending"); - assert.ok(cur_frm.fields_dict.suppliers.grid.grid_rows[1].doc.quote_status == "Pending"); - }, - () => { - cur_frm.fields_dict.suppliers.grid.grid_rows[0].toggle_view(); - }, - () => frappe.timeout(1), - () => frappe.timeout(1), - () => { - cur_frm.cur_grid.toggle_view(); - }, - () => frappe.click_button('Update'), - () => frappe.timeout(1), - - () => frappe.click_button('Supplier Quotation'), - () => frappe.timeout(1), - () => frappe.click_link('Make'), - () => frappe.timeout(1), - () => { - frappe.set_control('supplier',"_Test Supplier 1"); - }, - () => frappe.timeout(1), - () => frappe.click_button('Make Supplier Quotation'), - () => frappe.timeout(1), - () => cur_frm.set_value("company", "For Testing"), - () => cur_frm.fields_dict.items.grid.grid_rows[0].doc.rate = 4.99, - () => frappe.timeout(1), - () => frappe.click_button('Save'), - () => frappe.timeout(1), - () => frappe.click_button('Submit'), - () => frappe.timeout(1), - () => frappe.click_button('Yes'), - () => frappe.timeout(1), - () => frappe.set_route("List", "Request for Quotation"), - () => frappe.timeout(2), - () => frappe.set_route("List", "Request for Quotation"), - () => frappe.timeout(2), - () => frappe.click_link(rfq_name), - () => frappe.timeout(1), - () => frappe.click_button('Menu'), - () => frappe.timeout(1), - () => frappe.click_link('Reload'), - () => frappe.timeout(1), - () => { - assert.ok(cur_frm.fields_dict.suppliers.grid.grid_rows[1].doc.quote_status == "Received"); - }, - () => done() - ]); -}); diff --git a/erpnext/buying/doctype/supplier/test_supplier.js b/erpnext/buying/doctype/supplier/test_supplier.js deleted file mode 100644 index eaa4d0989d..0000000000 --- a/erpnext/buying/doctype/supplier/test_supplier.js +++ /dev/null @@ -1,77 +0,0 @@ -QUnit.module('Buying'); - -QUnit.test("test: supplier", function(assert) { - assert.expect(6); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Supplier', [ - {supplier_name: 'Test Supplier'}, - {supplier_group: 'Hardware'}, - {country: 'India'}, - {default_currency: 'INR'}, - {accounts: [ - [ - {'company': "For Testing"}, - {'account': "Creditors - FT"} - ]] - } - ]); - }, - () => frappe.timeout(1), - () => frappe.click_button('New Address'), - () => { - return frappe.tests.set_form_values(cur_frm, [ - {address_title:"Test3"}, - {address_type: "Billing"}, - {address_line1: "Billing Street 3"}, - {city: "Billing City 3"}, - ]); - }, - () => cur_frm.save(), - () => frappe.timeout(2), - () => frappe.click_button('New Address'), - () => { - return frappe.tests.set_form_values(cur_frm, [ - {address_title:"Test3"}, - {address_type: "Shipping"}, - {address_line1: "Shipping Street 3"}, - {city: "Shipping City 3"}, - ]); - }, - () => cur_frm.save(), - () => frappe.timeout(2), - () => frappe.click_button('New Address'), - () => { - return frappe.tests.set_form_values(cur_frm, [ - {address_title:"Test3"}, - {address_type: "Warehouse"}, - {address_line1: "Warehouse Street 3"}, - {city: "Warehouse City 3"}, - ]); - }, - () => cur_frm.save(), - () => frappe.timeout(2), - () => frappe.click_button('New Contact'), - () => { - return frappe.tests.set_form_values(cur_frm, [ - {first_name: "Contact 3"}, - {email_id: "test@supplier.com"} - ]); - }, - () => cur_frm.save(), - () => frappe.timeout(1), - () => frappe.set_route('Form', 'Supplier', 'Test Supplier'), - () => frappe.timeout(0.3), - - () => { - assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Name correct"); - assert.ok(cur_frm.doc.supplier_group == 'Hardware', "Type correct"); - assert.ok(cur_frm.doc.default_currency == 'INR', "Currency correct"); - assert.ok(cur_frm.doc.accounts[0].account == 'Creditors - '+frappe.get_abbr('For Testing'), " Account Head abbr correct"); - assert.ok($('.address-box:nth-child(3) p').text().includes('Shipping City 3'), "Address correct"); - assert.ok($('.col-sm-6+ .col-sm-6 .h6').text().includes('Contact 3'), "Contact correct"); - }, - () => done() - ]); -}); diff --git a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation.js deleted file mode 100644 index 20fb43026a..0000000000 --- a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation.js +++ /dev/null @@ -1,74 +0,0 @@ -QUnit.module('Buying'); - -QUnit.test("test: supplier quotation", function(assert) { - assert.expect(11); - let done = assert.async(); - let date; - - frappe.run_serially([ - () => { - date = frappe.datetime.add_days(frappe.datetime.now_date(), 10); - return frappe.tests.make('Supplier Quotation', [ - {supplier: 'Test Supplier'}, - {transaction_date: date}, - {currency: 'INR'}, - {items: [ - [ - {"item_code": 'Test Product 4'}, - {"qty": 5}, - {"uom": 'Unit'}, - {"rate": 200}, - {"warehouse": 'All Warehouses - '+frappe.get_abbr(frappe.defaults.get_default("Company"))} - ] - ]}, - {apply_discount_on: 'Grand Total'}, - {additional_discount_percentage: 10}, - {tc_name: 'Test Term 1'}, - {terms: 'This is a term'} - ]); - }, - () => frappe.timeout(3), - () => { - // Get Supplier details - assert.ok(cur_frm.doc.supplier == 'Test Supplier', "Supplier correct"); - assert.ok(cur_frm.doc.company == cur_frm.doc.company, "Company correct"); - // Get Contact details - assert.ok(cur_frm.doc.contact_person == 'Contact 3-Test Supplier', "Conatct correct"); - assert.ok(cur_frm.doc.contact_email == 'test@supplier.com', "Email correct"); - // Get uom - assert.ok(cur_frm.doc.items[0].uom == 'Unit', "Multi uom correct"); - assert.ok(cur_frm.doc.total == 1000, "Total correct"); - // Calculate total after discount - assert.ok(cur_frm.doc.grand_total == 900, "Grand total correct"); - // Get terms - assert.ok(cur_frm.doc.tc_name == 'Test Term 1', "Terms correct"); - }, - - () => cur_frm.print_doc(), - () => frappe.timeout(2), - () => { - assert.ok($('.btn-print-print').is(':visible'), "Print Format Available"); - assert.ok($("table > tbody > tr > td:nth-child(3) > div").text().includes("Test Product 4"), "Print Preview Works As Expected"); - }, - () => cur_frm.print_doc(), - () => frappe.timeout(1), - () => frappe.click_button('Get items from'), - () => frappe.timeout(0.3), - () => frappe.click_link('Material Request'), - () => frappe.timeout(0.3), - () => frappe.click_button('Get Items'), - () => frappe.timeout(1), - () => { - // Get item from Material Requests - assert.ok(cur_frm.doc.items[1].item_name == 'Test Product 1', "Getting items from material requests work"); - }, - - () => cur_frm.save(), - () => frappe.timeout(1), - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - - () => done() - ]); -}); diff --git a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_item_wise_discount.js b/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_item_wise_discount.js deleted file mode 100644 index 0a51565b08..0000000000 --- a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_item_wise_discount.js +++ /dev/null @@ -1,34 +0,0 @@ -QUnit.module('Buying'); - -QUnit.test("test: supplier quotation with item wise discount", function(assert){ - assert.expect(2); - let done = assert.async(); - - frappe.run_serially([ - () => { - return frappe.tests.make('Supplier Quotation', [ - {supplier: 'Test Supplier'}, - {company: 'For Testing'}, - {items: [ - [ - {"item_code": 'Test Product 4'}, - {"qty": 5}, - {"uom": 'Unit'}, - {"warehouse": 'All Warehouses - FT'}, - {'discount_percentage': 10}, - ] - ]} - ]); - }, - - () => { - assert.ok(cur_frm.doc.total == 900, "Total correct"); - assert.ok(cur_frm.doc.grand_total == 900, "Grand total correct"); - }, - - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_taxes_and_charges.js b/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_taxes_and_charges.js deleted file mode 100644 index 7ea3e6079c..0000000000 --- a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_taxes_and_charges.js +++ /dev/null @@ -1,37 +0,0 @@ -QUnit.module('Buying'); - -QUnit.test("test: supplier quotation with taxes and charges", function(assert) { - assert.expect(3); - let done = assert.async(); - let supplier_quotation_name; - - frappe.run_serially([ - () => { - return frappe.tests.make('Supplier Quotation', [ - {supplier: 'Test Supplier'}, - {items: [ - [ - {"item_code": 'Test Product 4'}, - {"qty": 5}, - {"rate": 100}, - {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))}, - ] - ]}, - {taxes_and_charges:'TEST In State GST - FT'}, - ]); - }, - () => {supplier_quotation_name = cur_frm.doc.name;}, - () => { - assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct"); - assert.ok(cur_frm.doc.total_taxes_and_charges == 45, "Taxes and charges correct"); - assert.ok(cur_frm.doc.grand_total == 545, "Grand total correct"); - }, - - () => cur_frm.save(), - () => frappe.timeout(0.3), - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/crm/doctype/lead/tests/test_lead_individual.js b/erpnext/crm/doctype/lead/tests/test_lead_individual.js deleted file mode 100644 index 66d33379ad..0000000000 --- a/erpnext/crm/doctype/lead/tests/test_lead_individual.js +++ /dev/null @@ -1,43 +0,0 @@ -QUnit.module("sales"); - -QUnit.test("test: lead", function (assert) { - assert.expect(4); - let done = assert.async(); - let lead_name = frappe.utils.get_random(10); - frappe.run_serially([ - // test lead creation - () => frappe.set_route("List", "Lead"), - () => frappe.new_doc("Lead"), - () => frappe.timeout(1), - () => cur_frm.set_value("lead_name", lead_name), - () => cur_frm.save(), - () => frappe.timeout(1), - () => { - assert.ok(cur_frm.doc.lead_name.includes(lead_name), - 'name correctly set'); - frappe.lead_name = cur_frm.doc.name; - }, - // create address and contact - () => frappe.click_link('Address & Contact'), - () => frappe.click_button('New Address'), - () => frappe.timeout(1), - () => frappe.set_control('address_line1', 'Gateway'), - () => frappe.set_control('city', 'Mumbai'), - () => cur_frm.save(), - () => frappe.timeout(3), - () => assert.equal(frappe.get_route()[1], 'Lead', - 'back to lead form'), - () => frappe.click_link('Address & Contact'), - () => assert.ok($('.address-box').text().includes('Mumbai'), - 'city is seen in address box'), - - // make opportunity - () => frappe.click_button('Make'), - () => frappe.click_link('Opportunity'), - () => frappe.timeout(2), - () => assert.equal(cur_frm.doc.lead, frappe.lead_name, - 'lead name correctly mapped'), - - () => done() - ]); -}); diff --git a/erpnext/crm/doctype/lead/tests/test_lead_organization.js b/erpnext/crm/doctype/lead/tests/test_lead_organization.js deleted file mode 100644 index 7fb957370b..0000000000 --- a/erpnext/crm/doctype/lead/tests/test_lead_organization.js +++ /dev/null @@ -1,55 +0,0 @@ -QUnit.module("sales"); - -QUnit.test("test: lead", function (assert) { - assert.expect(5); - let done = assert.async(); - let lead_name = frappe.utils.get_random(10); - frappe.run_serially([ - // test lead creation - () => frappe.set_route("List", "Lead"), - () => frappe.new_doc("Lead"), - () => frappe.timeout(1), - () => cur_frm.set_value("company_name", lead_name), - () => cur_frm.save(), - () => frappe.timeout(1), - () => { - assert.ok(cur_frm.doc.lead_name.includes(lead_name), - 'name correctly set'); - frappe.lead_name = cur_frm.doc.name; - }, - // create address and contact - () => frappe.click_link('Address & Contact'), - () => frappe.click_button('New Address'), - () => frappe.timeout(1), - () => frappe.set_control('address_line1', 'Gateway'), - () => frappe.set_control('city', 'Mumbai'), - () => cur_frm.save(), - () => frappe.timeout(3), - () => assert.equal(frappe.get_route()[1], 'Lead', - 'back to lead form'), - () => frappe.click_link('Address & Contact'), - () => assert.ok($('.address-box').text().includes('Mumbai'), - 'city is seen in address box'), - - () => frappe.click_button('New Contact'), - () => frappe.timeout(1), - () => frappe.set_control('first_name', 'John'), - () => frappe.set_control('last_name', 'Doe'), - () => cur_frm.save(), - () => frappe.timeout(3), - () => frappe.set_route('Form', 'Lead', cur_frm.doc.links[0].link_name), - () => frappe.timeout(1), - () => frappe.click_link('Address & Contact'), - () => assert.ok($('.address-box').text().includes('John'), - 'contact is seen in contact box'), - - // make customer - () => frappe.click_button('Make'), - () => frappe.click_link('Customer'), - () => frappe.timeout(2), - () => assert.equal(cur_frm.doc.lead_name, frappe.lead_name, - 'lead name correctly mapped'), - - () => done() - ]); -}); diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.js b/erpnext/crm/doctype/opportunity/test_opportunity.js deleted file mode 100644 index 45b97ddc4d..0000000000 --- a/erpnext/crm/doctype/opportunity/test_opportunity.js +++ /dev/null @@ -1,56 +0,0 @@ -QUnit.test("test: opportunity", function (assert) { - assert.expect(8); - let done = assert.async(); - frappe.run_serially([ - () => frappe.set_route('List', 'Opportunity'), - () => frappe.timeout(1), - () => frappe.click_button('New'), - () => frappe.timeout(1), - () => cur_frm.set_value('opportunity_from', 'Customer'), - () => cur_frm.set_value('customer', 'Test Customer 1'), - - // check items - () => cur_frm.set_value('with_items', 1), - () => frappe.tests.set_grid_values(cur_frm, 'items', [ - [ - {item_code:'Test Product 1'}, - {qty: 4} - ] - ]), - () => cur_frm.save(), - () => frappe.timeout(1), - () => { - assert.notOk(cur_frm.is_new(), 'saved'); - frappe.opportunity_name = cur_frm.doc.name; - }, - - // close and re-open - () => frappe.click_button('Close'), - () => frappe.timeout(1), - () => assert.equal(cur_frm.doc.status, 'Closed', - 'closed'), - - () => frappe.click_button('Reopen'), - () => assert.equal(cur_frm.doc.status, 'Open', - 'reopened'), - () => frappe.timeout(1), - - // make quotation - () => frappe.click_button('Make'), - () => frappe.click_link('Quotation', 1), - () => frappe.timeout(2), - () => { - assert.equal(frappe.get_route()[1], 'Quotation', - 'made quotation'); - assert.equal(cur_frm.doc.customer, 'Test Customer 1', - 'customer set in quotation'); - assert.equal(cur_frm.doc.items[0].item_code, 'Test Product 1', - 'item set in quotation'); - assert.equal(cur_frm.doc.items[0].qty, 4, - 'qty set in quotation'); - assert.equal(cur_frm.doc.items[0].prevdoc_docname, frappe.opportunity_name, - 'opportunity set in quotation'); - }, - () => done() - ]); -}); diff --git a/erpnext/education/doctype/academic_term/test_academic_term.js b/erpnext/education/doctype/academic_term/test_academic_term.js deleted file mode 100644 index 383b65a703..0000000000 --- a/erpnext/education/doctype/academic_term/test_academic_term.js +++ /dev/null @@ -1,24 +0,0 @@ -// Testing Setup Module in Education -QUnit.module('education'); - -QUnit.test('Test: Academic Term', function(assert){ - assert.expect(4); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Academic Term', [ - {academic_year: '2016-17'}, - {term_name: "Semester 1"}, - {term_start_date: '2016-07-20'}, - {term_end_date:'2017-06-20'}, - ]); - }, - () => { - assert.ok(cur_frm.doc.academic_year=='2016-17'); - assert.ok(cur_frm.doc.term_name=='Semester 1'); - assert.ok(cur_frm.doc.term_start_date=='2016-07-20'); - assert.ok(cur_frm.doc.term_end_date=='2017-06-20'); - }, - () => done() - ]); -}); diff --git a/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.js b/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.js deleted file mode 100644 index 724c4dac49..0000000000 --- a/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.js +++ /dev/null @@ -1,16 +0,0 @@ -// Education Assessment module -QUnit.module('education'); - -QUnit.test('Test: Assessment Criteria', function(assert){ - assert.expect(0); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Assessment Criteria', [ - {assessment_criteria: 'Pass'}, - {assessment_criteria_group: 'Reservation'} - ]); - }, - () => done() - ]); -}); diff --git a/erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.js b/erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.js deleted file mode 100644 index ab27e63723..0000000000 --- a/erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.js +++ /dev/null @@ -1,15 +0,0 @@ -// Education Assessment module -QUnit.module('education'); - -QUnit.test('Test: Assessment Criteria Group', function(assert){ - assert.expect(0); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Assessment Criteria Group', [ - {assessment_criteria_group: 'Reservation'} - ]); - }, - () => done() - ]); -}); diff --git a/erpnext/education/doctype/assessment_group/test_assessment_group.js b/erpnext/education/doctype/assessment_group/test_assessment_group.js deleted file mode 100644 index 00e6309837..0000000000 --- a/erpnext/education/doctype/assessment_group/test_assessment_group.js +++ /dev/null @@ -1,65 +0,0 @@ -// Education Assessment module -QUnit.module('education'); - -QUnit.test('Test: Assessment Group', function(assert){ - assert.expect(4); - let done = assert.async(); - - frappe.run_serially([ - () => frappe.set_route('Tree', 'Assessment Group'), - - // Checking adding child without selecting any Node - () => frappe.tests.click_button('New'), - () => frappe.timeout(0.2), - () => {assert.equal($(`.msgprint`).text(), "Select a group node first.", "Error message success");}, - () => frappe.tests.click_button('Close'), - () => frappe.timeout(0.2), - - // Creating child nodes - () => frappe.tests.click_link('All Assessment Groups'), - () => frappe.map_group.make('Assessment-group-1'), - () => frappe.map_group.make('Assessment-group-4', "All Assessment Groups", 1), - () => frappe.tests.click_link('Assessment-group-4'), - () => frappe.map_group.make('Assessment-group-5', "Assessment-group-3", 0), - - // Checking Edit button - () => frappe.timeout(0.5), - () => frappe.tests.click_link('Assessment-group-1'), - () => frappe.tests.click_button('Edit'), - () => frappe.timeout(0.5), - () => {assert.deepEqual(frappe.get_route(), ["Form", "Assessment Group", "Assessment-group-1"], "Edit route checks");}, - - // Deleting child Node - () => frappe.set_route('Tree', 'Assessment Group'), - () => frappe.timeout(0.5), - () => frappe.tests.click_link('Assessment-group-1'), - () => frappe.tests.click_button('Delete'), - () => frappe.timeout(0.5), - () => frappe.tests.click_button('Yes'), - - // Checking Collapse and Expand button - () => frappe.timeout(2), - () => frappe.tests.click_link('Assessment-group-4'), - () => frappe.click_button('Collapse'), - () => frappe.tests.click_link('All Assessment Groups'), - () => frappe.click_button('Collapse'), - () => {assert.ok($('.opened').size() == 0, 'Collapsed');}, - () => frappe.click_button('Expand'), - () => {assert.ok($('.opened').size() > 0, 'Expanded');}, - - () => done() - ]); -}); - -frappe.map_group = { - make:function(assessment_group_name, parent_assessment_group = 'All Assessment Groups', is_group = 0){ - return frappe.run_serially([ - () => frappe.click_button('Add Child'), - () => frappe.timeout(0.2), - () => cur_dialog.set_value('is_group', is_group), - () => cur_dialog.set_value('assessment_group_name', assessment_group_name), - () => cur_dialog.set_value('parent_assessment_group', parent_assessment_group), - () => frappe.click_button('Create New'), - ]); - } -}; diff --git a/erpnext/education/doctype/assessment_plan/test_assessment_plan.js b/erpnext/education/doctype/assessment_plan/test_assessment_plan.js deleted file mode 100644 index b0bff264e8..0000000000 --- a/erpnext/education/doctype/assessment_plan/test_assessment_plan.js +++ /dev/null @@ -1,54 +0,0 @@ -// Testing Assessment Module in education -QUnit.module('education'); - -QUnit.test('Test: Assessment Plan', function(assert){ - assert.expect(6); - let done = assert.async(); - let room_name, instructor_name, assessment_name; - - frappe.run_serially([ - () => frappe.db.get_value('Room', {'room_name': 'Room 1'}, 'name'), - (room) => {room_name = room.message.name;}, // Fetching Room name - () => frappe.db.get_value('Instructor', {'instructor_name': 'Instructor 1'}, 'name'), - (instructor) => {instructor_name = instructor.message.name;}, // Fetching Instructor name - - () => { - return frappe.tests.make('Assessment Plan', [ - {assessment_name: "Test-Mid-Term"}, - {assessment_group: 'Assessment-group-5'}, - {maximum_assessment_score: 100}, - {student_group: 'test-course-wise-group-2'}, - {course: 'Test_Sub'}, - {grading_scale: 'GTU'}, - {schedule_date: frappe.datetime.nowdate()}, - {room: room_name}, - {examiner: instructor_name}, - {supervisor: instructor_name}, - {from_time: "12:30:00"}, - {to_time: "2:30:00"} - ]); - }, - - () => { - assessment_name = cur_frm.doc.name; // Storing the name of current Assessment Plan - assert.equal(cur_frm.doc.assessment_criteria[0].assessment_criteria, 'Pass', 'Assessment Criteria auto-filled correctly'); - assert.equal(cur_frm.doc.assessment_criteria[0].maximum_score, 100, 'Maximum score correctly set'); - }, // Checking if the table was auto-filled upon selecting appropriate fields - - () => frappe.timeout(1), - () => frappe.tests.click_button('Submit'), - () => frappe.timeout(0.5), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.5), - () => {assert.equal(cur_frm.doc.docstatus, 1, "Assessment Plan submitted successfully");}, - - () => frappe.click_button('Assessment Result'), // Checking out Assessment Result button option - () => frappe.timeout(0.5), - () => { - assert.deepEqual(frappe.get_route(), ["Form", "Assessment Result Tool"], 'Assessment Result properly linked'); - assert.equal(cur_frm.doc.assessment_plan, assessment_name, 'Assessment correctly set'); - assert.equal(cur_frm.doc.student_group, 'test-course-wise-group-2', 'Course for Assessment correctly set'); - }, - () => done() - ]); -}); diff --git a/erpnext/education/doctype/assessment_result/test_assessment_result.js b/erpnext/education/doctype/assessment_result/test_assessment_result.js deleted file mode 100644 index d4eb4b8ba6..0000000000 --- a/erpnext/education/doctype/assessment_result/test_assessment_result.js +++ /dev/null @@ -1,73 +0,0 @@ -// Education Assessment module -QUnit.module('education'); - -QUnit.test('Test: Assessment Result', function(assert){ - assert.expect(25); - let done = assert.async(); - let student_list = []; - let assessment_name; - let tasks = [] - - frappe.run_serially([ - // Saving Assessment Plan name - () => frappe.db.get_value('Assessment Plan', {'assessment_name': 'Test-Mid-Term'}, 'name'), - (assessment_plan) => {assessment_name = assessment_plan.message.name;}, - // Fetching list of Student for which Result is supposed to be set - () => frappe.set_route('Form', 'Assessment Plan', assessment_name), - () => frappe.timeout(1), - () => frappe.tests.click_button('Assessment Result'), - () => frappe.timeout(1), - () => cur_frm.refresh(), - () => frappe.timeout(1), - () => { - $("tbody tr").each( function(i, input){ - student_list.push($(input).data().student); - }); - }, - - // Looping through each student in the list and setting up their score - () => { - student_list.forEach(index => { - tasks.push( - () => frappe.set_route('List', 'Assessment Result', 'List'), - () => frappe.timeout(0.5), - () => frappe.tests.click_button('New'), - () => frappe.timeout(0.5), - () => cur_frm.set_value('student', index), - () => cur_frm.set_value('assessment_plan', assessment_name), - () => frappe.timeout(0.2), - () => cur_frm.doc.details[0].score = (39 + (15 * student_list.indexOf(index))), - () => cur_frm.save(), - () => frappe.timeout(0.5), - - () => frappe.db.get_value('Assessment Plan', {'name': 'ASP00001'}, ['grading_scale', 'maximum_assessment_score']), - (assessment_plan) => { - assert.equal(cur_frm.doc.grading_scale, assessment_plan.message.grading_scale, 'Grading scale correctly fetched'); - assert.equal(cur_frm.doc.maximum_score, assessment_plan.message.maximum_assessment_score, 'Maximum score correctly fetched'); - - frappe.call({ - method: "erpnext.education.api.get_grade", - args: { - "grading_scale": assessment_plan.message.grading_scale, - "percentage": cur_frm.doc.total_score - }, - callback: function(r){ - assert.equal(cur_frm.doc.grade, r.message, "Grade correctly calculated"); - } - }); - }, - - () => frappe.tests.click_button('Submit'), - () => frappe.timeout(0.5), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.5), - () => {assert.equal();}, - () => {assert.equal(cur_frm.doc.docstatus, 1, "Submitted successfully");}, - ); - }); - return frappe.run_serially(tasks); - }, - - () => done() - ]); -}); diff --git a/erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.js b/erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.js deleted file mode 100644 index 7ef5c688fb..0000000000 --- a/erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.js +++ /dev/null @@ -1,29 +0,0 @@ -// Education Assessment module -QUnit.module('education'); - -QUnit.test('Test: Assessment Result Tool', function(assert){ - assert.expect(1); - let done = assert.async(); - let i, count = 0, assessment_name; - - frappe.run_serially([ - // Saving Assessment Plan name - () => frappe.db.get_value('Assessment Plan', {'assessment_name': 'Test-Mid-Term'}, 'name'), - (assessment_plan) => {assessment_name = assessment_plan.message.name;}, - - () => frappe.set_route('Form', 'Assessment Plan', assessment_name), - () => frappe.timeout(1), - () => frappe.tests.click_button('Assessment Result'), - () => frappe.timeout(1), - () => cur_frm.refresh(), - () => frappe.timeout(1), - () => { - for(i = 2; i < $('tbody tr').size() * 4; i = (i + 4)){ - if(($(`tbody td:eq("${i}")`) != "") && ($(`tbody td:eq("${i+1}")`) != "")) - count++; - } - assert.equal($('tbody tr').size(), count, 'All grades correctly displayed'); - }, - () => done() - ]); -}); diff --git a/erpnext/education/doctype/course/test_course.js b/erpnext/education/doctype/course/test_course.js deleted file mode 100644 index 2b6860cb7f..0000000000 --- a/erpnext/education/doctype/course/test_course.js +++ /dev/null @@ -1,36 +0,0 @@ -// Testing Setup Module in education -QUnit.module('education'); - -QUnit.test('test course', function(assert) { - assert.expect(8); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Course', [ - {course_name: 'Test_Subject'}, - {course_code: 'Test_Sub'}, - {department: 'Test Department'}, - {course_abbreviation: 'Test_Sub'}, - {course_intro: 'Test Subject Intro'}, - {default_grading_scale: 'GTU'}, - {assessment_criteria: [ - [ - {assessment_criteria: 'Pass'}, - {weightage: 100} - ] - ]} - ]); - }, - () => { - assert.ok(cur_frm.doc.course_name == 'Test_Subject', 'Course name correctly set'); - assert.ok(cur_frm.doc.course_code == 'Test_Sub', 'Course code correctly set'); - assert.ok(cur_frm.doc.department == 'Test Department', 'Department selected correctly'); - assert.ok(cur_frm.doc.course_abbreviation == 'Test_Sub'); - assert.ok(cur_frm.doc.course_intro == 'Test Subject Intro'); - assert.ok(cur_frm.doc.default_grading_scale == 'GTU', 'Grading scale selected correctly'); - assert.ok(cur_frm.doc.assessment_criteria[0].assessment_criteria == 'Pass', 'Assessment criteria selected correctly'); - assert.ok(cur_frm.doc.assessment_criteria[0].weightage == '100'); - }, - () => done() - ]); -}); diff --git a/erpnext/education/doctype/education_settings/test_education_settings.js b/erpnext/education/doctype/education_settings/test_education_settings.js deleted file mode 100644 index 990b0aa2a4..0000000000 --- a/erpnext/education/doctype/education_settings/test_education_settings.js +++ /dev/null @@ -1,31 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -// Testing Setup Module in Education -QUnit.module('education'); - -QUnit.test("test: Education Settings", function (assert) { - let done = assert.async(); - - assert.expect(3); - - frappe.run_serially([ - () => frappe.set_route("List", "Education Settings"), - () => frappe.timeout(0.4), - () => { - return frappe.tests.set_form_values(cur_frm, [ - {current_academic_year: '2016-17'}, - {current_academic_term: '2016-17 (Semester 1)'}, - {attendance_freeze_date: '2016-07-20'} - ]); - }, - () => { - cur_frm.save(); - assert.ok(cur_frm.doc.current_academic_year=="2016-17"); - assert.ok(cur_frm.doc.current_academic_term=="2016-17 (Semester 1)"); - assert.ok(cur_frm.doc.attendance_freeze_date=="2016-07-20"); - }, - () => done() - ]); -}); diff --git a/erpnext/education/doctype/fees/test_fees.js b/erpnext/education/doctype/fees/test_fees.js deleted file mode 100644 index 22e987e8c2..0000000000 --- a/erpnext/education/doctype/fees/test_fees.js +++ /dev/null @@ -1,31 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Fees", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially('Fees', [ - - // insert a new Fees - () => { - return frappe.tests.make('Fees', [ - {student: 'STUD00001'}, - {due_date: frappe.datetime.get_today()}, - {fee_structure: 'FS00001'} - ]); - }, - () => { - assert.equal(cur_frm.doc.grand_total===cur_frm.doc.outstanding_amount); - }, - () => frappe.timeout(0.3), - () => cur_frm.save(), - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => done() - ]); - -}); diff --git a/erpnext/education/doctype/grading_scale/test_grading_scale.js b/erpnext/education/doctype/grading_scale/test_grading_scale.js deleted file mode 100644 index fb56918fdb..0000000000 --- a/erpnext/education/doctype/grading_scale/test_grading_scale.js +++ /dev/null @@ -1,102 +0,0 @@ -// Education Assessment module -QUnit.module('education'); - -QUnit.test('Test: Grading Scale', function(assert){ - assert.expect(3); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Grading Scale', [ - {grading_scale_name: 'GTU'}, - {description: 'The score will be set according to 100 based system.'}, - {intervals: [ - [ - {grade_code: 'AA'}, - {threshold: '95'}, - {grade_description: 'First Class + Distinction'} - ], - [ - {grade_code: 'AB'}, - {threshold: '90'}, - {grade_description: 'First Class'} - ], - [ - {grade_code: 'BB'}, - {threshold: '80'}, - {grade_description: 'Distinction'} - ], - [ - {grade_code: 'BC'}, - {threshold: '70'}, - {grade_description: 'Second Class'} - ], - [ - {grade_code: 'CC'}, - {threshold: '60'}, - {grade_description: 'Third Class'} - ], - [ - {grade_code: 'CD'}, - {threshold: '50'}, - {grade_description: 'Average'} - ], - [ - {grade_code: 'DD'}, - {threshold: '40'}, - {grade_description: 'Pass'} - ], - [ - {grade_code: 'FF'}, - {threshold: '0'}, - {grade_description: 'Fail'} - ], - ]} - ]); - }, - () => { - return frappe.tests.make('Grading Scale', [ - {grading_scale_name: 'GTU-2'}, - {description: 'The score will be set according to 100 based system.'}, - {intervals: [ - [ - {grade_code: 'AA'}, - {threshold: '90'}, - {grade_description: 'Distinction'} - ], - [ - {grade_code: 'FF'}, - {threshold: '0'}, - {grade_description: 'Fail'} - ] - ]} - ]); - }, - - () => { - let grading_scale = ['GTU', 'GTU-2']; - let tasks = []; - grading_scale.forEach(index => { - tasks.push( - () => frappe.set_route('Form', 'Grading Scale', index), - () => frappe.timeout(0.5), - () => frappe.tests.click_button('Submit'), - () => frappe.timeout(0.5), - () => frappe.tests.click_button('Yes'), - () => {assert.equal(cur_frm.doc.docstatus, 1, 'Submitted successfully');} - ); - }); - return frappe.run_serially(tasks); - }, - - () => frappe.timeout(1), - () => frappe.set_route('Form', 'Grading Scale','GTU-2'), - () => frappe.timeout(0.5), - () => frappe.tests.click_button('Cancel'), - () => frappe.timeout(0.5), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.5), - () => {assert.equal(cur_frm.doc.docstatus, 2, 'Cancelled successfully');}, - - () => done() - ]); -}); diff --git a/erpnext/education/doctype/guardian/test_guardian.js b/erpnext/education/doctype/guardian/test_guardian.js deleted file mode 100644 index 1ea6dc290b..0000000000 --- a/erpnext/education/doctype/guardian/test_guardian.js +++ /dev/null @@ -1,34 +0,0 @@ -// Testing Student Module in education -QUnit.module('education'); - -QUnit.test('Test: Guardian', function(assert){ - assert.expect(9); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Guardian', [ - {guardian_name: 'Test Guardian'}, - {email_address: 'guardian@testmail.com'}, - {mobile_number: 9898980000}, - {alternate_number: 8989890000}, - {date_of_birth: '1982-07-22'}, - {education: 'Testing'}, - {occupation: 'Testing'}, - {designation: 'Testing'}, - {work_address: 'Testing address'} - ]); - }, - () => { - assert.ok(cur_frm.doc.guardian_name == 'Test Guardian'); - assert.ok(cur_frm.doc.email_address == 'guardian@testmail.com'); - assert.ok(cur_frm.doc.mobile_number == 9898980000); - assert.ok(cur_frm.doc.alternate_number == 8989890000); - assert.ok(cur_frm.doc.date_of_birth == '1982-07-22'); - assert.ok(cur_frm.doc.education == 'Testing'); - assert.ok(cur_frm.doc.occupation == 'Testing'); - assert.ok(cur_frm.doc.designation == 'Testing'); - assert.ok(cur_frm.doc.work_address == 'Testing address'); - }, - () => done() - ]); -}); diff --git a/erpnext/education/doctype/instructor/test_instructor.js b/erpnext/education/doctype/instructor/test_instructor.js deleted file mode 100644 index c584f45cca..0000000000 --- a/erpnext/education/doctype/instructor/test_instructor.js +++ /dev/null @@ -1,20 +0,0 @@ -// Testing Setup Module in education -QUnit.module('education'); - -QUnit.test('Test: Instructor', function(assert){ - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make("Instructor", [ - {instructor_name: 'Instructor 1'}, - {department: 'Test Department'} - ]); - }, - () => { - assert.ok(cur_frm.doc.instructor_name == 'Instructor 1'); - assert.ok(cur_frm.doc.department = 'Test Department'); - }, - () => done() - ]); -}); diff --git a/erpnext/education/doctype/program/test_program.js b/erpnext/education/doctype/program/test_program.js deleted file mode 100644 index b9ca41ae3f..0000000000 --- a/erpnext/education/doctype/program/test_program.js +++ /dev/null @@ -1,34 +0,0 @@ -// Testing Setup Module in education -QUnit.module('education'); - -QUnit.test('Test: Program', function(assert){ - assert.expect(6); - let done = assert.async(); - let fee_structure_code; - frappe.run_serially([ - () => { - return frappe.tests.make('Program', [ - {program_name: 'Standard Test'}, - {program_code: 'Standard Test'}, - {department: 'Test Department'}, - {program_abbreviation: 'Standard Test'}, - {courses: [ - [ - {course: 'Test_Sub'}, - {required: true} - ] - ]} - ]); - }, - - () => { - assert.ok(cur_frm.doc.program_name == 'Standard Test'); - assert.ok(cur_frm.doc.program_code == 'Standard Test'); - assert.ok(cur_frm.doc.department == 'Test Department'); - assert.ok(cur_frm.doc.program_abbreviation == 'Standard Test'); - assert.ok(cur_frm.doc.courses[0].course == 'Test_Sub'); - assert.ok(cur_frm.doc.courses[0].required == true); - }, - () => done() - ]); -}); diff --git a/erpnext/education/doctype/room/test_room.js b/erpnext/education/doctype/room/test_room.js deleted file mode 100644 index fdcbe92c17..0000000000 --- a/erpnext/education/doctype/room/test_room.js +++ /dev/null @@ -1,22 +0,0 @@ -// Testing Setup Module in Education -QUnit.module('education'); - -QUnit.test('Test: Room', function(assert){ - assert.expect(3); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Room', [ - {room_name: 'Room 1'}, - {room_number: '1'}, - {seating_capacity: '60'}, - ]); - }, - () => { - assert.ok(cur_frm.doc.room_name == 'Room 1'); - assert.ok(cur_frm.doc.room_number = '1'); - assert.ok(cur_frm.doc.seating_capacity = '60'); - }, - () => done() - ]); -}); diff --git a/erpnext/education/doctype/student_admission/test_student_admission.js b/erpnext/education/doctype/student_admission/test_student_admission.js deleted file mode 100644 index e01791a78a..0000000000 --- a/erpnext/education/doctype/student_admission/test_student_admission.js +++ /dev/null @@ -1,40 +0,0 @@ -// Testing Admission Module in Education -QUnit.module('education'); - -QUnit.test('Test: Student Admission', function(assert) { - assert.expect(10); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Student Admission', [ - {academic_year: '2016-17'}, - {admission_start_date: '2016-04-20'}, - {admission_end_date: '2016-05-31'}, - {title: '2016-17 Admissions'}, - {enable_admission_application: 1}, - {introduction: 'Test intro'}, - {program_details: [ - [ - {'program': 'Standard Test'}, - {'application_fee': 1000}, - {'applicant_naming_series': 'AP'}, - ] - ]} - ]); - }, - () => cur_frm.save(), - () => { - assert.ok(cur_frm.doc.academic_year == '2016-17'); - 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.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); - assert.ok(cur_frm.doc.program_details[0].applicant_naming_series == 'AP'); - assert.ok(cur_frm.doc.route == 'admissions/2016-17-Admissions', "Route successfully set"); - }, - () => done() - ]); -}); diff --git a/erpnext/education/doctype/student_applicant/tests/test_student_applicant.js b/erpnext/education/doctype/student_applicant/tests/test_student_applicant.js deleted file mode 100644 index fa67977985..0000000000 --- a/erpnext/education/doctype/student_applicant/tests/test_student_applicant.js +++ /dev/null @@ -1,95 +0,0 @@ -// Testing Admission module in Education -QUnit.module('education'); - -QUnit.test('Test: Student Applicant', function(assert){ - assert.expect(24); - let done = assert.async(); - let guradian_auto_code; - let guardian_name; - frappe.run_serially([ - () => frappe.set_route('List', 'Guardian'), - () => frappe.timeout(0.5), - () => {$(`a:contains("Test Guardian"):visible`)[0].click();}, - () => frappe.timeout(1), - () => { - guardian_name = cur_frm.doc.guardian_name; - guradian_auto_code = frappe.get_route()[2]; - }, - // Testing data entry for Student Applicant - () => { - return frappe.tests.make('Student Applicant',[ - {first_name: 'Fname'}, - {middle_name: 'Mname'}, - {last_name: 'Lname'}, - {program: 'Standard Test'}, - {student_admission: '2016-17 Admissions'}, - {academic_year: '2016-17'}, - {date_of_birth: '1995-07-20'}, - {student_email_id: 'test@testmail.com'}, - {gender: 'Male'}, - {student_mobile_number: '9898980000'}, - {blood_group: 'O+'}, - {address_line_1: 'Test appt, Test Society,'}, - {address_line_2: 'Test district, Test city.'}, - {city: 'Test'}, - {state: 'Test'}, - {pincode: '400086'} - ]); - }, - // Entry in Guardian child table - () => $('a:contains("Guardian Details"):visible').click(), - () => $('.btn:contains("Add Row"):visible').click(), - () => { - cur_frm.get_field("guardians").grid.grid_rows[0].doc.guardian = guradian_auto_code; - cur_frm.get_field("guardians").grid.grid_rows[0].doc.relation = "Father"; - cur_frm.get_field("guardians").grid.grid_rows[0].doc.guardian_name = guardian_name; - $('a:contains("Guardian Details"):visible').click(); - }, - // Entry in Sibling child table - () => $('a:contains("Sibling Details"):visible').click(), - () => $('.btn:contains("Add Row"):visible').click(), - () => { - cur_frm.get_field("siblings").grid.grid_rows[0].doc.full_name = "Test Name"; - cur_frm.get_field("siblings").grid.grid_rows[0].doc.gender = "Male"; - cur_frm.get_field("siblings").grid.grid_rows[0].doc.institution = "Test Institution"; - cur_frm.get_field("siblings").grid.grid_rows[0].doc.program = "Test Program"; - cur_frm.get_field("siblings").grid.grid_rows[0].doc.date_of_birth = "1995-07-20"; - $('span.hidden-xs.octicon.octicon-triangle-up').click(); - cur_frm.save(); - }, - () => { - assert.ok(cur_frm.doc.first_name == 'Fname'); - assert.ok(cur_frm.doc.middle_name == 'Mname'); - assert.ok(cur_frm.doc.last_name == 'Lname'); - assert.ok(cur_frm.doc.program == 'Standard Test', 'Program selected correctly'); - assert.ok(cur_frm.doc.student_admission == '2016-17 Admissions', 'Student Admission entry correctly selected'); - assert.ok(cur_frm.doc.academic_year == '2016-17'); - assert.ok(cur_frm.doc.date_of_birth == '1995-07-20'); - assert.ok(cur_frm.doc.student_email_id == 'test@testmail.com'); - assert.ok(cur_frm.doc.gender == 'Male'); - assert.ok(cur_frm.doc.student_mobile_number == '9898980000'); - assert.ok(cur_frm.doc.blood_group == 'O+'); - assert.ok(cur_frm.doc.address_line_1 == 'Test appt, Test Society,'); - assert.ok(cur_frm.doc.address_line_2 == 'Test district, Test city.'); - assert.ok(cur_frm.doc.city == 'Test'); - assert.ok(cur_frm.doc.state == 'Test'); - assert.ok(cur_frm.doc.pincode == '400086'); - }, - () => frappe.timeout(1), - () => $('a:contains("Guardian Details"):visible').click(), - () => { - assert.ok(cur_frm.get_field("guardians").grid.grid_rows[0].doc.guardian == guradian_auto_code, 'Guardian correctly selected from dropdown'); - assert.ok(cur_frm.get_field("guardians").grid.grid_rows[0].doc.relation == 'Father'); - assert.ok(cur_frm.get_field("guardians").grid.grid_rows[0].doc.guardian_name == guardian_name, 'Guardian name was correctly retrieved'); - }, - () => $('a:contains("Sibling Details"):visible').click(), - () => { - assert.ok(cur_frm.get_field("siblings").grid.grid_rows[0].doc.full_name == 'Test Name'); - assert.ok(cur_frm.get_field("siblings").grid.grid_rows[0].doc.gender == 'Male'); - assert.ok(cur_frm.get_field("siblings").grid.grid_rows[0].doc.institution == 'Test Institution'); - assert.ok(cur_frm.get_field("siblings").grid.grid_rows[0].doc.program == 'Test Program'); - assert.ok(cur_frm.get_field("siblings").grid.grid_rows[0].doc.date_of_birth == '1995-07-20'); - }, - () => done() - ]); -}); diff --git a/erpnext/education/doctype/student_applicant/tests/test_student_applicant_dummy_data.js b/erpnext/education/doctype/student_applicant/tests/test_student_applicant_dummy_data.js deleted file mode 100644 index 03101e41e0..0000000000 --- a/erpnext/education/doctype/student_applicant/tests/test_student_applicant_dummy_data.js +++ /dev/null @@ -1,87 +0,0 @@ -QUnit.module('Admission'); - -QUnit.test('Make Students', function(assert){ - assert.expect(0); - let done = assert.async(); - let tasks = []; - let loop = [1,2,3,4]; - let fname; - - frappe.run_serially([ - // Making School House to be used in this test and later - () => frappe.set_route('Form', 'School House/New School House'), - () => frappe.timeout(0.5), - () => cur_frm.doc.house_name = 'Test_house', - () => cur_frm.save(), - - // Making Student Applicant entries - () => { - loop.forEach(index => { - tasks.push(() => { - fname = "Fname" + index; - - return frappe.tests.make('Student Applicant', [ - {first_name: fname}, - {middle_name: "Mname"}, - {last_name: "Lname"}, - {program: "Standard Test"}, - {student_admission: "2016-17 Admissions"}, - {date_of_birth: '1995-08-20'}, - {student_email_id: ('test' + (index+3) + '@testmail.com')}, - {gender: 'Male'}, - {student_mobile_number: (9898980000 + index)}, - {blood_group: 'O+'}, - {address_line_1: 'Test appt, Test Society,'}, - {address_line_2: 'Test district, Test city.'}, - {city: 'Test'}, - {state: 'Test'}, - {pincode: '395007'} - ]); - }); - }); - return frappe.run_serially(tasks); - }, - - // Using Program Enrollment Tool to enroll all dummy student at once - () => frappe.set_route('Form', 'Program Enrollment Tool'), - () => { - cur_frm.set_value("get_students_from", "Student Applicants"); - cur_frm.set_value("academic_year", "2016-17"); - cur_frm.set_value("program", "Standard Test"); - }, - () => frappe.tests.click_button("Get Students"), - () => frappe.timeout(1), - () => frappe.tests.click_button("Enroll Students"), - () => frappe.timeout(1.5), - () => frappe.tests.click_button("Close"), - - // Submitting required data for each enrolled Student - () => { - tasks = []; - loop.forEach(index => { - tasks.push( - () => {fname = "Fname" + index + " Mname Lname";}, - () => frappe.set_route('List', 'Program Enrollment/List'), - () => frappe.timeout(0.6), - () => frappe.tests.click_link(fname), - () => frappe.timeout(0.4), - () => { - cur_frm.set_value('program', 'Standard Test'); - cur_frm.set_value('student_category', 'Reservation'); - cur_frm.set_value('student_batch_name', 'A'); - cur_frm.set_value('academic_year', '2016-17'); - cur_frm.set_value('academic_term', '2016-17 (Semester 1)'); - cur_frm.set_value('school_house', 'Test_house'); - }, - () => cur_frm.save(), - () => frappe.timeout(0.5), - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.5) - ); - }); - return frappe.run_serially(tasks); - }, - () => done() - ]); -}); diff --git a/erpnext/education/doctype/student_applicant/tests/test_student_applicant_options.js b/erpnext/education/doctype/student_applicant/tests/test_student_applicant_options.js deleted file mode 100644 index daa36e75ce..0000000000 --- a/erpnext/education/doctype/student_applicant/tests/test_student_applicant_options.js +++ /dev/null @@ -1,110 +0,0 @@ -// Testing Admission module in Education -QUnit.module('education'); - -QUnit.test('test student applicant', function(assert){ - assert.expect(11); - let done = assert.async(); - let testing_status; - frappe.run_serially([ - () => frappe.set_route('List', 'Student Applicant'), - () => frappe.timeout(0.5), - () => {$(`a:contains("Fname Mname Lname"):visible`)[0].click();}, - - // Checking different options - // 1. Moving forward with Submit - () => frappe.timeout(0.5), - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.5), - () => { - testing_status = $('span.indicator.orange').text(); - assert.ok(testing_status.indexOf('Submit this document to confirm') == -1); // checking if submit has been successfull - }, - - // 2. Cancelling the Submit request - () => frappe.timeout(0.5), - () => frappe.tests.click_button('Cancel'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.5), - () => { - testing_status = $('h1.editable-title').text(); - assert.ok(testing_status.indexOf('Cancelled') != -1); // checking if cancel request has been successfull - }, - - // 3. Checking Amend option - () => frappe.timeout(0.5), - () => frappe.tests.click_button('Amend'), - () => cur_frm.doc.student_email_id = "test2@testmail.com", // updating email id since same id again is not allowed - () => cur_frm.save(), - () => frappe.timeout(0.5), - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), // Submitting again after amend - () => { - testing_status = $('span.indicator.orange').text(); - assert.ok(testing_status.indexOf('Submit this document to confirm') == -1); // checking if submit has been successfull after amend - }, - - // Checking different Application status option - () => { - testing_status = $('h1.editable-title').text(); - assert.ok(testing_status.indexOf('Applied') != -1); // checking if Applied has been successfull - }, - () => cur_frm.set_value('application_status', "Rejected"), // Rejected Status - () => frappe.tests.click_button('Update'), - () => { - testing_status = $('h1.editable-title').text(); - assert.ok(testing_status.indexOf('Rejected') != -1); // checking if Rejected has been successfull - }, - () => cur_frm.set_value('application_status', "Admitted"), // Admitted Status - () => frappe.tests.click_button('Update'), - () => { - testing_status = $('h1.editable-title').text(); - assert.ok(testing_status.indexOf('Admitted') != -1); // checking if Admitted has been successfull - }, - () => cur_frm.set_value('application_status', "Approved"), // Approved Status - () => frappe.tests.click_button('Update'), - () => { - testing_status = $('h1.editable-title').text(); - assert.ok(testing_status.indexOf('Approved') != -1); // checking if Approved has been successfull - }, - - // Clicking on Enroll button should add the applicant's entry in Student doctype, and take you to Program Enrollment page - () => frappe.timeout(0.5), - () => frappe.tests.click_button('Enroll'), - () => frappe.timeout(0.5), - () => { - assert.ok(frappe.get_route()[0] == 'Form'); // Checking if the current page is Program Enrollment page or not - assert.ok(frappe.get_route()[1] == 'Program Enrollment'); - }, - - // Routing to Student List to check if the Applicant's entry has been made or not - () => frappe.timeout(0.5), - () => frappe.set_route('List', 'Student'), - () => frappe.timeout(0.5), - () => {$(`a:contains("Fname Mname Lname"):visible`)[0].click();}, - () => frappe.timeout(0.5), - () => {assert.ok(($(`h1.editable-title`).text()).indexOf('Enabled') != -1, 'Student entry successfully created');}, // Checking if the Student entry has been enabled - // Enrolling the Student into a Program - () => {$('.form-documents .row:nth-child(1) .col-xs-6:nth-child(1) .octicon-plus').click();}, - () => frappe.timeout(1), - () => cur_frm.set_value('program', 'Standard Test'), - () => frappe.timeout(1), - () => { - cur_frm.set_value('student_category', 'Reservation'); - cur_frm.set_value('student_batch_name', 'A'); - cur_frm.set_value('academic_year', '2016-17'); - cur_frm.set_value('academic_term', '2016-17 (Semester 1)'); - cur_frm.set_value('school_house', 'Test_house'); - }, - () => cur_frm.save(), - - // Submitting Program Enrollment form for our Test Student - () => frappe.timeout(1), - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => { - assert.ok(cur_frm.doc.docstatus == 1, "Program enrollment successfully submitted"); - }, - () => done() - ]); -}); diff --git a/erpnext/education/doctype/student_attendance/test_student_attendance.js b/erpnext/education/doctype/student_attendance/test_student_attendance.js deleted file mode 100644 index 3d30b090ba..0000000000 --- a/erpnext/education/doctype/student_attendance/test_student_attendance.js +++ /dev/null @@ -1,31 +0,0 @@ -// Testing Attendance Module in Education -QUnit.module('education'); - -QUnit.test('Test: Student Attendance', function(assert){ - assert.expect(2); - let done = assert.async(); - let student_code; - - frappe.run_serially([ - () => frappe.db.get_value('Student', {'student_email_id': 'test2@testmail.com'}, 'name'), - (student) => {student_code = student.message.name;}, // fetching student code from db - - () => { - return frappe.tests.make('Student Attendance', [ - {student: student_code}, - {date: frappe.datetime.nowdate()}, - {student_group: "test-batch-wise-group-2"}, - {status: "Absent"} - ]); - }, - - () => frappe.timeout(0.5), - () => {assert.equal(cur_frm.doc.status, "Absent", "Attendance correctly saved");}, - - () => frappe.timeout(0.5), - () => cur_frm.set_value("status", "Present"), - () => {assert.equal(cur_frm.doc.status, "Present", "Attendance correctly saved");}, - - () => done() - ]); -}); diff --git a/erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.js b/erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.js deleted file mode 100644 index b66d8397ba..0000000000 --- a/erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.js +++ /dev/null @@ -1,85 +0,0 @@ -// Testing Attendance Module in Education -QUnit.module('education'); - -QUnit.test('Test: Student Attendace Tool', function(assert){ - assert.expect(10); - let done = assert.async(); - let i, count = 0; - - frappe.run_serially([ - () => frappe.timeout(0.2), - () => frappe.set_route('Form', 'Student Attendance Tool'), - () => frappe.timeout(0.5), - - () => { - if(cur_frm.doc.based_on == 'Student Group' || cur_frm.doc.based_on == 'Course Schedule'){ - cur_frm.doc.based_on = 'Student Group'; - assert.equal(1, 1, 'Attendance basis correctly set'); - cur_frm.set_value("group_based_on", 'Batch'); - cur_frm.set_value("student_group", "test-batch-wise-group"); - cur_frm.set_value("date", frappe.datetime.nowdate()); - } - }, - () => frappe.timeout(0.5), - () => { - assert.equal($('input.students-check').size(), 5, "Student list based on batch correctly fetched"); - assert.equal(frappe.datetime.nowdate(), cur_frm.doc.date, 'Current date correctly set'); - - cur_frm.set_value("student_group", "test-batch-wise-group-2"); - assert.equal($('input.students-check').size(), 5, "Student list based on batch 2 correctly fetched"); - - cur_frm.set_value("group_based_on", 'Course'); - - cur_frm.set_value("student_group", "test-course-wise-group"); - assert.equal($('input.students-check').size(), 5, "Student list based on course correctly fetched"); - - cur_frm.set_value("student_group", "test-course-wise-group-2"); - assert.equal($('input.students-check').size(), 5, "Student list based on course 2 correctly fetched"); - }, - - () => frappe.timeout(1), - () => frappe.tests.click_button('Check all'), // Marking all Student as checked - () => { - for(i = 0; i < $('input.students-check').size(); i++){ - if($('input.students-check')[i].checked == true) - count++; - } - - if(count == $('input.students-check').size()) - assert.equal($('input.students-check').size(), count, "All students marked checked"); - }, - - () => frappe.timeout(1), - () => frappe.tests.click_button('Uncheck all'), // Marking all Student as unchecked - () => { - count = 0; - for(i = 0; i < $('input.students-check').size(); i++){ - if(!($('input.students-check')[i].checked)) - count++; - } - - if(count == $('input.students-check').size()) - assert.equal($('input.students-check').size(), count, "All students marked checked"); - }, - - () => frappe.timeout(1), - () => frappe.tests.click_button('Check all'), - () => frappe.tests.click_button('Mark Attendance'), - () => frappe.timeout(1), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(1), - () => { - assert.equal($('.msgprint').text(), "Attendance has been marked successfully.", "Attendance successfully marked"); - frappe.tests.click_button('Close'); - }, - - () => frappe.timeout(1), - () => frappe.set_route('List', 'Student Attendance/List'), - () => frappe.timeout(1), - () => { - assert.equal(cur_list.data.length, count, "Attendance list created"); - }, - - () => done() - ]); -}); diff --git a/erpnext/education/doctype/student_batch_name/test_student_batch_name.js b/erpnext/education/doctype/student_batch_name/test_student_batch_name.js deleted file mode 100644 index 6c761b8418..0000000000 --- a/erpnext/education/doctype/student_batch_name/test_student_batch_name.js +++ /dev/null @@ -1,19 +0,0 @@ -// Testing Setup Module in Education -QUnit.module('education'); - -QUnit.test('Test: Student Batch Name', function(assert){ - assert.expect(1); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Student Batch Name', [ - {batch_name: 'A'} - ]); - }, - () => cur_frm.save(), - () => { - assert.ok(cur_frm.doc.batch_name=='A'); - }, - () => done() - ]); -}); diff --git a/erpnext/education/doctype/student_category/test_student_category.js b/erpnext/education/doctype/student_category/test_student_category.js deleted file mode 100644 index 01f50e279d..0000000000 --- a/erpnext/education/doctype/student_category/test_student_category.js +++ /dev/null @@ -1,19 +0,0 @@ -// Testing Setup Module in Education -QUnit.module('education'); - -QUnit.test('Test: Student Category', function(assert){ - assert.expect(1); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Student Category', [ - {category: 'Reservation'} - ]); - }, - () => cur_frm.save(), - () => { - assert.ok(cur_frm.doc.name=='Reservation'); - }, - () => done() - ]); -}); diff --git a/erpnext/education/doctype/student_group/test_student_group.js b/erpnext/education/doctype/student_group/test_student_group.js deleted file mode 100644 index 4c7e47bc38..0000000000 --- a/erpnext/education/doctype/student_group/test_student_group.js +++ /dev/null @@ -1,56 +0,0 @@ -// Testing Student Module in Education -QUnit.module('education'); - -QUnit.test('Test: Student Group', function(assert){ - assert.expect(2); - let done = assert.async(); - let group_based_on = ["test-batch-wise-group", "test-course-wise-group"]; - let tasks = []; - - frappe.run_serially([ - // Creating a Batch and Course based group - () => { - return frappe.tests.make('Student Group', [ - {academic_year: '2016-17'}, - {academic_term: '2016-17 (Semester 1)'}, - {program: "Standard Test"}, - {group_based_on: 'Batch'}, - {student_group_name: group_based_on[0]}, - {max_strength: 10}, - {batch: 'A'} - ]); - }, - () => { - return frappe.tests.make('Student Group', [ - {academic_year: '2016-17'}, - {academic_term: '2016-17 (Semester 1)'}, - {program: "Standard Test"}, - {group_based_on: 'Course'}, - {student_group_name: group_based_on[1]}, - {max_strength: 10}, - {batch: 'A'}, - {course: 'Test_Sub'}, - ]); - }, - - // Populating the created group with Students - () => { - tasks = []; - group_based_on.forEach(index => { - tasks.push( - () => frappe.timeout(0.5), - () => frappe.set_route("Form", ('Student Group/' + index)), - () => frappe.timeout(0.5), - () => frappe.tests.click_button('Get Students'), - () => frappe.timeout(1), - () => { - assert.equal(cur_frm.doc.students.length, 5, 'Successfully fetched list of students'); - }, - ); - }); - return frappe.run_serially(tasks); - }, - - () => done() - ]); -}); diff --git a/erpnext/education/doctype/student_group_creation_tool/test_student_group_creation_tool.js b/erpnext/education/doctype/student_group_creation_tool/test_student_group_creation_tool.js deleted file mode 100644 index fa612ba272..0000000000 --- a/erpnext/education/doctype/student_group_creation_tool/test_student_group_creation_tool.js +++ /dev/null @@ -1,84 +0,0 @@ -QUnit.module('education'); - -QUnit.test('Test: Student Group Creation Tool', function(assert){ - assert.expect(5); - let done = assert.async(); - let instructor_code; - - frappe.run_serially([ - // Saving Instructor code beforehand - () => frappe.db.get_value('Instructor', {'instructor_name': 'Instructor 1'}, 'name'), - (instructor) => {instructor_code = instructor.message.name;}, - - // Setting up the creation tool to generate and save Student Group - () => frappe.set_route('Form', 'Student Group Creation Tool'), - () => frappe.timeout(0.5), - () => { - cur_frm.set_value("academic_year", "2016-17"); - cur_frm.set_value("academic_term", "2016-17 (Semester 1)"); - cur_frm.set_value("program", "Standard Test"); - frappe.tests.click_button('Get Courses'); - }, - () => frappe.timeout(1), - () => { - let no_of_courses = $('input.grid-row-check.pull-left').size() - 1; - assert.equal(cur_frm.doc.courses.length, no_of_courses, 'Successfully created groups using the tool'); - }, - - () => { - let d, grid, grid_row; - - for(d = 0; d < cur_frm.doc.courses.length; d++) - { - grid = cur_frm.get_field("courses").grid; - grid_row = grid.get_row(d).toggle_view(true); - if(grid_row.doc.student_group_name == 'Standard Test/A/2016-17 (Semester 1)'){ - grid_row.doc.max_strength = 10; - grid_row.doc.student_group_name = "test-batch-wise-group-2"; - $(`.octicon.octicon-triangle-up`).click(); - continue; - } - else if(grid_row.doc.student_group_name == 'Test_Sub/Standard Test/2016-17 (Semester 1)'){ - grid_row.doc.max_strength = 10; - grid_row.doc.student_group_name = "test-course-wise-group-2"; - $(`.octicon.octicon-triangle-up`).click(); - continue; - } - } - }, - - // Generating Student Group - () => frappe.timeout(0.5), - () => frappe.tests.click_button("Create Student Groups"), - () => frappe.timeout(0.5), - () => frappe.tests.click_button("Close"), - - // Goin to the generated group to set up student and instructor list - () => { - let group_name = ['Student Group/test-batch-wise-group-2', 'Student Group/test-course-wise-group-2']; - let tasks = []; - group_name.forEach(index => { - tasks.push( - () => frappe.timeout(1), - () => frappe.set_route("Form", index), - () => frappe.timeout(0.5), - () => { - assert.equal(cur_frm.doc.students.length, 5, 'Successfully fetched list of students'); - }, - () => frappe.timeout(0.5), - () => { - d = cur_frm.add_child('instructors'); - d.instructor = instructor_code; - cur_frm.save(); - }, - () => { - assert.equal(cur_frm.doc.instructors.length, 1, 'Instructor detail stored successfully'); - }, - ); - }); - return frappe.run_serially(tasks); - }, - - () => done() - ]); -}); diff --git a/erpnext/education/doctype/student_leave_application/test_student_leave_application.js b/erpnext/education/doctype/student_leave_application/test_student_leave_application.js deleted file mode 100644 index 6bbf17babf..0000000000 --- a/erpnext/education/doctype/student_leave_application/test_student_leave_application.js +++ /dev/null @@ -1,69 +0,0 @@ -// Testing Attendance Module in Education -QUnit.module('education'); - -QUnit.test('Test: Student Leave Application', function(assert){ - assert.expect(4); - let done = assert.async(); - let student_code; - let leave_code; - frappe.run_serially([ - () => frappe.db.get_value('Student', {'student_email_id': 'test2@testmail.com'}, 'name'), - (student) => {student_code = student.message.name;}, // fetching student code from db - - () => { - return frappe.tests.make('Student Leave Application', [ - {student: student_code}, - {from_date: '2017-08-02'}, - {to_date: '2017-08-04'}, - {mark_as_present: 0}, - {reason: "Sick Leave."} - ]); - }, - () => frappe.tests.click_button('Submit'), // Submitting the leave application - () => frappe.timeout(0.7), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.7), - () => { - assert.equal(cur_frm.doc.docstatus, 1, "Submitted leave application"); - leave_code = frappe.get_route()[2]; - }, - () => frappe.tests.click_button('Cancel'), // Cancelling the leave application - () => frappe.timeout(0.7), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(1), - () => {assert.equal(cur_frm.doc.docstatus, 2, "Cancelled leave application");}, - () => frappe.tests.click_button('Amend'), // Amending the leave application - () => frappe.timeout(1), - () => { - cur_frm.doc.mark_as_present = 1; - cur_frm.save(); - }, - () => frappe.timeout(0.7), - () => frappe.tests.click_button('Submit'), - () => frappe.timeout(0.7), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.7), - () => {assert.equal(cur_frm.doc.amended_from, leave_code, "Amended successfully");}, - - () => frappe.timeout(0.5), - () => { - return frappe.tests.make('Student Leave Application', [ - {student: student_code}, - {from_date: '2017-08-07'}, - {to_date: '2017-08-09'}, - {mark_as_present: 0}, - {reason: "Sick Leave."} - ]); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.timeout(0.7), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.7), - () => { - assert.equal(cur_frm.doc.docstatus, 1, "Submitted leave application"); - leave_code = frappe.get_route()[2]; - }, - - () => done() - ]); -}); diff --git a/erpnext/education/doctype/student_log/test_student_log.js b/erpnext/education/doctype/student_log/test_student_log.js deleted file mode 100644 index 4c90c5f6ef..0000000000 --- a/erpnext/education/doctype/student_log/test_student_log.js +++ /dev/null @@ -1,35 +0,0 @@ -// Testing Student Module in Education -QUnit.module('education'); - -QUnit.test('Test: Student Log', function(assert){ - assert.expect(9); - let done = assert.async(); - let student_code; - frappe.run_serially([ - () => frappe.db.get_value('Student', {'student_email_id': 'test2@testmail.com'}, 'name'), - (student) => {student_code = student.message.name;}, - () => { - return frappe.tests.make("Student Log", [ - {student: student_code}, - {academic_year: '2016-17'}, - {academic_term: '2016-17 (Semester 1)'}, - {program: "Standard Test"}, - {date: '2017-07-31'}, - {student_batch: 'A'}, - {log: 'This is Test log.'} - ]); - }, - () => { - assert.equal(cur_frm.doc.student, student_code, 'Student code was fetched properly'); - assert.equal(cur_frm.doc.student_name, 'Fname Mname Lname', 'Student name was correctly auto-fetched'); - assert.equal(cur_frm.doc.type, 'General', 'Default type selected'); - assert.equal(cur_frm.doc.academic_year, '2016-17'); - assert.equal(cur_frm.doc.academic_term, '2016-17 (Semester 1)'); - assert.equal(cur_frm.doc.program, 'Standard Test', 'Program correctly selected'); - assert.equal(cur_frm.doc.student_batch, 'A'); - assert.equal(cur_frm.doc.date, '2017-07-31'); - assert.equal(cur_frm.doc.log, 'This is Test log.'); - }, - () => done() - ]); -}); diff --git a/erpnext/hr/doctype/appraisal/test_appraisal.js b/erpnext/hr/doctype/appraisal/test_appraisal.js deleted file mode 100644 index fb1354c3f6..0000000000 --- a/erpnext/hr/doctype/appraisal/test_appraisal.js +++ /dev/null @@ -1,57 +0,0 @@ -QUnit.module('hr'); - -QUnit.test("Test: Expense Claim [HR]", function (assert) { - assert.expect(3); - let done = assert.async(); - let employee_name; - - frappe.run_serially([ - // Creating Appraisal - () => frappe.set_route('List','Appraisal','List'), - () => frappe.timeout(0.3), - () => frappe.click_button('Make a new Appraisal'), - () => { - cur_frm.set_value('kra_template','Test Appraisal 1'), - cur_frm.set_value('start_date','2017-08-21'), - cur_frm.set_value('end_date','2017-09-21'); - }, - () => frappe.timeout(1), - () => frappe.model.set_value('Appraisal Goal','New Appraisal Goal 1','score',4), - () => frappe.model.set_value('Appraisal Goal','New Appraisal Goal 1','score_earned',2), - () => frappe.model.set_value('Appraisal Goal','New Appraisal Goal 2','score',4), - () => frappe.model.set_value('Appraisal Goal','New Appraisal Goal 2','score_earned',2), - () => frappe.timeout(1), - () => frappe.db.get_value('Employee', {'employee_name': 'Test Employee 1'}, 'name'), - (r) => { - employee_name = r.message.name; - }, - - () => frappe.timeout(1), - () => cur_frm.set_value('employee',employee_name), - () => cur_frm.set_value('employee_name','Test Employee 1'), - () => cur_frm.set_value('company','For Testing'), - () => frappe.click_button('Calculate Total Score'), - () => frappe.timeout(1), - () => cur_frm.save(), - () => frappe.timeout(1), - () => cur_frm.save(), - - // Submitting the Appraisal - () => frappe.click_button('Submit'), - () => frappe.click_button('Yes'), - () => frappe.timeout(3), - - // Checking if the appraisal is correctly set for the employee - () => { - assert.equal('Submitted',cur_frm.get_field('status').value, - 'Appraisal is submitted'); - - assert.equal('Test Employee 1',cur_frm.get_field('employee_name').value, - 'Appraisal is created for correct employee'); - - assert.equal(4,cur_frm.get_field('total_score').value, - 'Total score is correctly calculated'); - }, - () => done() - ]); -}); diff --git a/erpnext/hr/doctype/appraisal_template/test_appraisal_template.js b/erpnext/hr/doctype/appraisal_template/test_appraisal_template.js deleted file mode 100644 index 3eb64e0850..0000000000 --- a/erpnext/hr/doctype/appraisal_template/test_appraisal_template.js +++ /dev/null @@ -1,29 +0,0 @@ -QUnit.module('hr'); -QUnit.test("Test: Appraisal Template [HR]", function (assert) { - assert.expect(1); - let done = assert.async(); - frappe.run_serially([ - // Job Opening creation - () => { - frappe.tests.make('Appraisal Template', [ - { kra_title: 'Test Appraisal 1'}, - { description: 'This is just a test'}, - { goals: [ - [ - { kra: 'Design'}, - { per_weightage: 50} - ], - [ - { kra: 'Code creation'}, - { per_weightage: 50} - ] - ]}, - ]); - }, - () => frappe.timeout(10), - () => { - assert.equal('Test Appraisal 1',cur_frm.doc.kra_title, 'Appraisal name correctly set'); - }, - () => done() - ]); -}); diff --git a/erpnext/hr/doctype/attendance/test_attendance.js b/erpnext/hr/doctype/attendance/test_attendance.js deleted file mode 100644 index b3e7fef02a..0000000000 --- a/erpnext/hr/doctype/attendance/test_attendance.js +++ /dev/null @@ -1,39 +0,0 @@ -QUnit.module('hr'); - -QUnit.test("Test: Attendance [HR]", function (assert) { - assert.expect(4); - let done = assert.async(); - - frappe.run_serially([ - // test attendance creation for one employee - () => frappe.set_route("List", "Attendance", "List"), - () => frappe.timeout(0.5), - () => frappe.new_doc("Attendance"), - () => frappe.timeout(1), - () => assert.equal("Attendance", cur_frm.doctype, - "Form for new Attendance opened successfully."), - // set values in form - () => cur_frm.set_value("company", "For Testing"), - () => { - frappe.db.get_value('Employee', {'employee_name':'Test Employee 1'}, 'name', function(r) { - cur_frm.set_value("employee", r.name) - }); - }, - () => frappe.timeout(1), - () => cur_frm.save(), - () => frappe.timeout(1), - // check docstatus of attendance before submit [Draft] - () => assert.equal("0", cur_frm.doc.docstatus, - "attendance is currently drafted"), - // check docstatus of attendance after submit [Present] - () => cur_frm.savesubmit(), - () => frappe.timeout(0.5), - () => frappe.click_button('Yes'), - () => assert.equal("1", cur_frm.doc.docstatus, - "attendance is saved after submit"), - // check if auto filled date is present day - () => assert.equal(frappe.datetime.nowdate(), cur_frm.doc.attendance_date, - "attendance for Present day is marked"), - () => done() - ]); -}); diff --git a/erpnext/hr/doctype/employee/test_employee.js b/erpnext/hr/doctype/employee/test_employee.js deleted file mode 100644 index 3a41458480..0000000000 --- a/erpnext/hr/doctype/employee/test_employee.js +++ /dev/null @@ -1,40 +0,0 @@ -QUnit.module('hr'); - -QUnit.test("Test: Employee [HR]", function (assert) { - assert.expect(4); - let done = assert.async(); - // let today_date = frappe.datetime.nowdate(); - let employee_creation = (name, joining_date, birth_date) => { - frappe.run_serially([ - // test employee creation - () => { - frappe.tests.make('Employee', [ - { employee_name: name}, - { salutation: 'Mr'}, - { company: 'For Testing'}, - { date_of_joining: joining_date}, - { date_of_birth: birth_date}, - { employment_type: 'Test Employment Type'}, - { holiday_list: 'Test Holiday List'}, - { branch: 'Test Branch'}, - { department: 'Test Department'}, - { designation: 'Test Designation'} - ]); - }, - () => frappe.timeout(2), - () => { - assert.ok(cur_frm.get_field('employee_name').value==name, - 'Name of an Employee is correctly set'); - assert.ok(cur_frm.get_field('gender').value=='Male', - 'Gender of an Employee is correctly set'); - }, - ]); - }; - frappe.run_serially([ - () => employee_creation('Test Employee 1','2017-04-01','1992-02-02'), - () => frappe.timeout(10), - () => employee_creation('Test Employee 3','2017-04-01','1992-02-02'), - () => frappe.timeout(10), - () => done() - ]); -}); diff --git a/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js b/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js deleted file mode 100644 index 48d4344df2..0000000000 --- a/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js +++ /dev/null @@ -1,61 +0,0 @@ -QUnit.module('hr'); - -QUnit.test("Test: Employee attendance tool [HR]", function (assert) { - assert.expect(2); - let done = assert.async(); - let today_date = frappe.datetime.nowdate(); - let date_of_attendance = frappe.datetime.add_days(today_date, -2); // previous day - - frappe.run_serially([ - // create employee - () => { - return frappe.tests.make('Employee', [ - {salutation: "Mr"}, - {employee_name: "Test Employee 2"}, - {company: "For Testing"}, - {date_of_joining: frappe.datetime.add_months(today_date, -2)}, // joined 2 month from now - {date_of_birth: frappe.datetime.add_months(today_date, -240)}, // age is 20 years - {employment_type: "Test Employment type"}, - {holiday_list: "Test Holiday list"}, - {branch: "Test Branch"}, - {department: "Test Department"}, - {designation: "Test Designation"} - ]); - }, - () => frappe.set_route("Form", "Employee Attendance Tool"), - () => frappe.timeout(0.5), - () => assert.equal("Employee Attendance Tool", cur_frm.doctype, - "Form for Employee Attendance Tool opened successfully."), - // set values in form - () => cur_frm.set_value("date", date_of_attendance), - () => cur_frm.set_value("branch", "Test Branch"), - () => cur_frm.set_value("department", "Test Department"), - () => cur_frm.set_value("company", "For Testing"), - () => frappe.timeout(1), - () => frappe.click_button('Check all'), - () => frappe.click_button('Mark Present'), - // check if attendance is marked - () => frappe.set_route("List", "Attendance", "List"), - () => frappe.timeout(1), - () => { - return frappe.call({ - method: "frappe.client.get_list", - args: { - doctype: "Employee", - filters: { - "branch": "Test Branch", - "department": "Test Department", - "company": "For Testing", - "status": "Active" - } - }, - callback: function(r) { - let marked_attendance = cur_list.data.filter(d => d.attendance_date == date_of_attendance); - assert.equal(marked_attendance.length, r.message.length, - 'all the attendance are marked for correct date'); - } - }); - }, - () => done() - ]); -}); diff --git a/erpnext/hr/doctype/employment_type/test_employment_type.js b/erpnext/hr/doctype/employment_type/test_employment_type.js deleted file mode 100644 index fd7c6a1ce3..0000000000 --- a/erpnext/hr/doctype/employment_type/test_employment_type.js +++ /dev/null @@ -1,22 +0,0 @@ -QUnit.module('hr'); - -QUnit.test("Test: Employment type [HR]", function (assert) { - assert.expect(1); - let done = assert.async(); - - frappe.run_serially([ - // test employment type creation - () => frappe.set_route("List", "Employment Type", "List"), - () => frappe.new_doc("Employment Type"), - () => frappe.timeout(1), - () => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(), - () => frappe.timeout(1), - () => cur_frm.set_value("employee_type_name", "Test Employment type"), - // save form - () => cur_frm.save(), - () => frappe.timeout(1), - () => assert.equal("Test Employment type", cur_frm.doc.employee_type_name, - 'name of employment type correctly saved'), - () => done() - ]); -}); diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.js b/erpnext/hr/doctype/expense_claim/test_expense_claim.js deleted file mode 100644 index 2529faec98..0000000000 --- a/erpnext/hr/doctype/expense_claim/test_expense_claim.js +++ /dev/null @@ -1,44 +0,0 @@ -QUnit.module('hr'); - -QUnit.test("Test: Expense Claim [HR]", function (assert) { - assert.expect(3); - let done = assert.async(); - let employee_name; - let d; - frappe.run_serially([ - // Creating Expense Claim - () => frappe.set_route('List','Expense Claim','List'), - () => frappe.timeout(0.3), - () => frappe.click_button('New'), - () => { - cur_frm.set_value('is_paid',1), - cur_frm.set_value('expenses',[]), - d = frappe.model.add_child(cur_frm.doc,'Expense Claim Detail','expenses'), - d.expense_date = '2017-08-01', - d.expense_type = 'Test Expense Type 1', - d.description = 'This is just to test Expense Claim', - d.amount = 2000, - d.sanctioned_amount=2000, - refresh_field('expenses'); - }, - () => frappe.timeout(1), - () => cur_frm.set_value('employee','Test Employee 1'), - () => cur_frm.set_value('company','For Testing'), - () => cur_frm.set_value('payable_account','Creditors - FT'), - () => cur_frm.set_value('cost_center','Main - FT'), - () => cur_frm.set_value('mode_of_payment','Cash'), - () => cur_frm.save(), - () => frappe.click_button('Submit'), - () => frappe.click_button('Yes'), - () => frappe.timeout(3), - - // Checking if the amount is correctly reimbursed for the employee - () => { - assert.equal("Test Employee 1",cur_frm.doc.employee, 'Employee name set correctly'); - assert.equal(1, cur_frm.doc.is_paid, 'Expense is paid as required'); - assert.equal(2000, cur_frm.doc.total_amount_reimbursed, 'Amount is reimbursed correctly'); - - }, - () => done() - ]); -}); diff --git a/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.js b/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.js deleted file mode 100644 index 3c9ed35313..0000000000 --- a/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.js +++ /dev/null @@ -1,29 +0,0 @@ -QUnit.module('hr'); - -QUnit.test("Test: Expense Claim Type [HR]", function (assert) { - assert.expect(1); - let done = assert.async(); - frappe.run_serially([ - // Creating a Expense Claim Type - () => { - frappe.tests.make('Expense Claim Type', [ - { expense_type: 'Test Expense Type 1'}, - { description:'This is just a test'}, - { accounts: [ - [ - { company: 'For Testing'}, - { default_account: 'Rounded Off - FT'} - ] - ]}, - ]); - }, - () => frappe.timeout(5), - - // Checking if the created type is present in the list - () => { - assert.equal('Test Expense Type 1', cur_frm.doc.expense_type, - 'Expense Claim Type created successfully'); - }, - () => done() - ]); -}); diff --git a/erpnext/hr/doctype/holiday_list/test_holiday_list.js b/erpnext/hr/doctype/holiday_list/test_holiday_list.js deleted file mode 100644 index ce766143a6..0000000000 --- a/erpnext/hr/doctype/holiday_list/test_holiday_list.js +++ /dev/null @@ -1,42 +0,0 @@ -QUnit.module('hr'); - -QUnit.test("Test: Holiday list [HR]", function (assert) { - assert.expect(3); - let done = assert.async(); - let date = frappe.datetime.add_months(frappe.datetime.nowdate(), -2); // date 2 months from now - - frappe.run_serially([ - // test holiday list creation - () => frappe.set_route("List", "Holiday List", "List"), - () => frappe.new_doc("Holiday List"), - () => frappe.timeout(1), - () => cur_frm.set_value("holiday_list_name", "Test Holiday list"), - () => cur_frm.set_value("from_date", date), - () => cur_frm.set_value("weekly_off", "Sunday"), // holiday list for sundays - () => frappe.click_button('Get Weekly Off Dates'), - - // save form - () => cur_frm.save(), - () => frappe.timeout(1), - () => assert.equal("Test Holiday list", cur_frm.doc.holiday_list_name, - 'name of holiday list correctly saved'), - - // check if holiday list contains correct days - () => { - var list = cur_frm.doc.holidays; - var list_length = list.length; - var i = 0; - for ( ; i < list_length; i++) - if (list[i].description != 'Sunday') break; - assert.equal(list_length, i, "all holidays are sundays in holiday list"); - }, - - // check if to_date is set one year from from_date - () => { - var date_year_later = frappe.datetime.add_days(frappe.datetime.add_months(date, 12), -1); // date after one year - assert.equal(date_year_later, cur_frm.doc.to_date, - "to date set correctly"); - }, - () => done() - ]); -}); diff --git a/erpnext/hr/doctype/job_applicant/test_job_applicant.js b/erpnext/hr/doctype/job_applicant/test_job_applicant.js deleted file mode 100644 index 741a182add..0000000000 --- a/erpnext/hr/doctype/job_applicant/test_job_applicant.js +++ /dev/null @@ -1,28 +0,0 @@ -QUnit.module('hr'); - -QUnit.test("Test: Job Opening [HR]", function (assert) { - assert.expect(2); - let done = assert.async(); - - frappe.run_serially([ - // Job Applicant creation - () => { - frappe.tests.make('Job Applicant', [ - { applicant_name: 'Utkarsh Goswami'}, - { email_id: 'goswamiutkarsh0@gmail.com'}, - { job_title: 'software-developer'}, - { cover_letter: 'Highly skilled in designing, testing, and developing software.'+ - ' This is just a test.'} - ]); - }, - () => frappe.timeout(4), - () => frappe.set_route('List','Job Applicant'), - () => frappe.timeout(3), - () => { - assert.ok(cur_list.data.length==1, 'Job Applicant created successfully'); - assert.ok(cur_list.data[0].name=='Utkarsh Goswami - goswamiutkarsh0@gmail.com - software-developer', - 'Correct job applicant with valid job title'); - }, - () => done() - ]); -}); diff --git a/erpnext/hr/doctype/job_offer/test_job_offer.js b/erpnext/hr/doctype/job_offer/test_job_offer.js deleted file mode 100644 index 5339b9c3d6..0000000000 --- a/erpnext/hr/doctype/job_offer/test_job_offer.js +++ /dev/null @@ -1,51 +0,0 @@ -QUnit.module('hr'); - -QUnit.test("Test: Job Offer [HR]", function (assert) { - assert.expect(3); - let done = assert.async(); - frappe.run_serially([ - // Job Offer Creation - () => { - frappe.tests.make('Job Offer', [ - { job_applicant: 'Utkarsh Goswami - goswamiutkarsh0@gmail.com - software-developer'}, - { applicant_name: 'Utkarsh Goswami'}, - { status: 'Accepted'}, - { designation: 'Software Developer'}, - { offer_terms: [ - [ - {offer_term: 'Responsibilities'}, - {value: 'Design, installation, testing and maintenance of software systems.'} - ], - [ - {offer_term: 'Department'}, - {value: 'Research & Development'} - ], - [ - {offer_term: 'Probationary Period'}, - {value: 'The Probation period is for 3 months.'} - ] - ]}, - ]); - }, - () => frappe.timeout(10), - () => frappe.click_button('Submit'), - () => frappe.timeout(2), - () => frappe.click_button('Yes'), - () => frappe.timeout(5), - // To check if the fields are correctly set - () => { - assert.ok(cur_frm.get_field('status').value=='Accepted', - 'Status of job offer is correct'); - assert.ok(cur_frm.get_field('designation').value=='Software Developer', - 'Designation of applicant is correct'); - }, - () => frappe.set_route('List','Job Offer','List'), - () => frappe.timeout(2), - // Checking the submission of and Job Offer - () => { - assert.ok(cur_list.data[0].docstatus==1,'Job Offer Submitted successfully'); - }, - () => frappe.timeout(2), - () => done() - ]); -}); diff --git a/erpnext/hr/doctype/job_opening/test_job_opening.js b/erpnext/hr/doctype/job_opening/test_job_opening.js deleted file mode 100644 index cc2f027e85..0000000000 --- a/erpnext/hr/doctype/job_opening/test_job_opening.js +++ /dev/null @@ -1,26 +0,0 @@ -QUnit.module('hr'); - -QUnit.test("Test: Job Opening [HR]", function (assert) { - assert.expect(2); - let done = assert.async(); - - frappe.run_serially([ - // Job Opening creation - () => { - frappe.tests.make('Job Opening', [ - { job_title: 'Software Developer'}, - { description: - 'You might be responsible for writing and coding individual'+ - ' programmes or providing an entirely new software resource.'} - ]); - }, - () => frappe.timeout(4), - () => frappe.set_route('List','Job Opening'), - () => frappe.timeout(3), - () => { - assert.ok(cur_list.data.length==1, 'Job Opening created successfully'); - assert.ok(cur_list.data[0].job_title=='Software Developer', 'Job title Correctly set'); - }, - () => done() - ]); -}); diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js deleted file mode 100644 index d5364fc8b2..0000000000 --- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js +++ /dev/null @@ -1,41 +0,0 @@ -QUnit.module('hr'); - -QUnit.test("Test: Leave allocation [HR]", function (assert) { - assert.expect(3); - let done = assert.async(); - let today_date = frappe.datetime.nowdate(); - - frappe.run_serially([ - // test creating leave alloction - () => frappe.set_route("List", "Leave Allocation", "List"), - () => frappe.new_doc("Leave Allocation"), - () => frappe.timeout(1), - () => { - frappe.db.get_value('Employee', {'employee_name':'Test Employee 1'}, 'name', function(r) { - cur_frm.set_value("employee", r.name) - }); - }, - () => frappe.timeout(1), - () => cur_frm.set_value("leave_type", "Test Leave type"), - () => cur_frm.set_value("to_date", frappe.datetime.add_months(today_date, 2)), // for two months - () => cur_frm.set_value("description", "This is just for testing"), - () => cur_frm.set_value("new_leaves_allocated", 2), - () => frappe.click_check('Add unused leaves from previous allocations'), - // save form - () => cur_frm.save(), - () => frappe.timeout(1), - () => cur_frm.savesubmit(), - () => frappe.timeout(1), - () => assert.equal("Confirm", cur_dialog.title, - 'confirmation for leave alloction shown'), - () => frappe.click_button('Yes'), - () => frappe.timeout(1), - // check auto filled from date - () => assert.equal(today_date, cur_frm.doc.from_date, - "from date correctly set"), - // check for total leaves - () => assert.equal(cur_frm.doc.unused_leaves + 2, cur_frm.doc.total_leaves_allocated, - "total leave calculation is correctly set"), - () => done() - ]); -}); diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.js b/erpnext/hr/doctype/leave_application/test_leave_application.js deleted file mode 100644 index 0866b0b6d2..0000000000 --- a/erpnext/hr/doctype/leave_application/test_leave_application.js +++ /dev/null @@ -1,42 +0,0 @@ -QUnit.module('hr'); - -QUnit.test("Test: Leave application [HR]", function (assert) { - assert.expect(4); - let done = assert.async(); - let today_date = frappe.datetime.nowdate(); - let leave_date = frappe.datetime.add_days(today_date, 1); // leave for tomorrow - - frappe.run_serially([ - // test creating leave application - () => frappe.db.get_value('Employee', {'employee_name':'Test Employee 1'}, 'name'), - (employee) => { - return frappe.tests.make('Leave Application', [ - {leave_type: "Test Leave type"}, - {from_date: leave_date}, // for today - {to_date: leave_date}, - {half_day: 1}, - {employee: employee.message.name}, - {follow_via_email: 0} - ]); - }, - - () => frappe.timeout(1), - () => frappe.click_button('Actions'), - () => frappe.click_link('Approve'), // approve the application [as administrator] - () => frappe.click_button('Yes'), - () => frappe.timeout(1), - () => assert.ok(cur_frm.doc.docstatus, - "leave application submitted after approval"), - - // check auto filled posting date [today] - - () => assert.equal(today_date, cur_frm.doc.posting_date, - "posting date correctly set"), - () => frappe.set_route("List", "Leave Application", "List"), - () => frappe.timeout(1), - // // check approved application in list - () => assert.deepEqual(["Test Employee 1", 1], [cur_list.data[0].employee_name, cur_list.data[0].docstatus]), - // "leave for correct employee is submitted"), - () => done() - ]); -}); diff --git a/erpnext/hr/doctype/leave_block_list/test_leave_block_list.js b/erpnext/hr/doctype/leave_block_list/test_leave_block_list.js deleted file mode 100644 index b39601b490..0000000000 --- a/erpnext/hr/doctype/leave_block_list/test_leave_block_list.js +++ /dev/null @@ -1,27 +0,0 @@ -QUnit.module('hr'); - -QUnit.test("Test: Leave block list [HR]", function (assert) { - assert.expect(1); - let done = assert.async(); - let today_date = frappe.datetime.nowdate(); - - frappe.run_serially([ - // test leave block list creation - () => frappe.set_route("List", "Leave Block List", "List"), - () => frappe.new_doc("Leave Block List"), - () => frappe.timeout(1), - () => cur_frm.set_value("leave_block_list_name", "Test Leave block list"), - () => cur_frm.set_value("company", "For Testing"), - () => frappe.click_button('Add Row'), - () => { - cur_frm.fields_dict.leave_block_list_dates.grid.grid_rows[0].doc.block_date = today_date; - cur_frm.fields_dict.leave_block_list_dates.grid.grid_rows[0].doc.reason = "Blocked leave test"; - }, - // save form - () => cur_frm.save(), - () => frappe.timeout(1), - () => assert.equal("Test Leave block list", cur_frm.doc.leave_block_list_name, - 'name of blocked leave list correctly saved'), - () => done() - ]); -}); diff --git a/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js b/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js deleted file mode 100644 index 9d37327717..0000000000 --- a/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js +++ /dev/null @@ -1,50 +0,0 @@ -QUnit.module('hr'); - -QUnit.test("Test: Leave control panel [HR]", function (assert) { - assert.expect(2); - let done = assert.async(); - let today_date = frappe.datetime.nowdate(); - - frappe.run_serially([ - // test leave allocation using leave control panel - () => frappe.set_route("Form", "Leave Control Panel"), - () => frappe.timeout(1), - () => cur_frm.set_value("leave_type", "Test Leave type"), - () => cur_frm.set_value("company", "For Testing"), - () => cur_frm.set_value("employment_type", "Test Employment Type"), - () => cur_frm.set_value("branch", "Test Branch"), - () => cur_frm.set_value("department", "Test Department"), - () => cur_frm.set_value("designation", "Test Designation"), - () => cur_frm.set_value("from_date", frappe.datetime.add_months(today_date, -2)), - () => cur_frm.set_value("to_date", frappe.datetime.add_days(today_date, -1)), // for two months [not today] - () => cur_frm.set_value("no_of_days", 3), - // allocate leaves - () => frappe.click_button('Allocate'), - () => frappe.timeout(1), - () => assert.equal("Message", cur_dialog.title, "leave alloction message shown"), - () => frappe.click_button('Close'), - () => frappe.set_route("List", "Leave Allocation", "List"), - () => frappe.timeout(1), - () => { - return frappe.call({ - method: "frappe.client.get_list", - args: { - doctype: "Employee", - filters: { - "branch": "Test Branch", - "department": "Test Department", - "company": "For Testing", - "designation": "Test Designation", - "status": "Active" - } - }, - callback: function(r) { - let leave_allocated = cur_list.data.filter(d => d.leave_type == "Test Leave type"); - assert.equal(r.message.length, leave_allocated.length, - 'leave allocation successfully done for all the employees'); - } - }); - }, - () => done() - ]); -}); diff --git a/erpnext/hr/doctype/leave_type/test_leave_type.js b/erpnext/hr/doctype/leave_type/test_leave_type.js deleted file mode 100644 index db910cde51..0000000000 --- a/erpnext/hr/doctype/leave_type/test_leave_type.js +++ /dev/null @@ -1,22 +0,0 @@ -QUnit.module('hr'); - -QUnit.test("Test: Leave type [HR]", function (assert) { - assert.expect(1); - let done = assert.async(); - - frappe.run_serially([ - // test leave type creation - () => frappe.set_route("List", "Leave Type", "List"), - () => frappe.new_doc("Leave Type"), - () => frappe.timeout(1), - () => cur_frm.set_value("leave_type_name", "Test Leave type"), - () => cur_frm.set_value("max_continuous_days_allowed", "5"), - () => frappe.click_check('Is Carry Forward'), - // save form - () => cur_frm.save(), - () => frappe.timeout(1), - () => assert.equal("Test Leave type", cur_frm.doc.leave_type_name, - 'leave type correctly saved'), - () => done() - ]); -}); diff --git a/erpnext/hr/doctype/training_event/tests/test_training_event.js b/erpnext/hr/doctype/training_event/tests/test_training_event.js deleted file mode 100644 index 08031a1963..0000000000 --- a/erpnext/hr/doctype/training_event/tests/test_training_event.js +++ /dev/null @@ -1,59 +0,0 @@ -QUnit.module('hr'); - -QUnit.test("Test: Training Event [HR]", function (assert) { - assert.expect(5); - let done = assert.async(); - let employee_name; - - frappe.run_serially([ - // Creation of Training Event - () => frappe.db.get_value('Employee', {'employee_name': 'Test Employee 1'}, 'name'), - (r) => { - employee_name = r.message.name; - }, - () => { - frappe.tests.make('Training Event', [ - { event_name: 'Test Training Event 1'}, - { location: 'Mumbai'}, - { start_time: '2017-09-01 11:00:0'}, - { end_time: '2017-09-01 17:00:0'}, - { introduction: 'This is just a test'}, - { employees: [ - [ - {employee: employee_name}, - {employee_name: 'Test Employee 1'}, - {attendance: 'Optional'} - ] - ]}, - ]); - }, - () => frappe.timeout(7), - () => frappe.click_button('Submit'), - () => frappe.timeout(1), - () => frappe.click_button('Yes'), - () => frappe.timeout(8), - () => { - // To check if the fields are correctly set - assert.ok(cur_frm.get_field('event_name').value == 'Test Training Event 1', - 'Event created successfully'); - - assert.ok(cur_frm.get_field('event_status').value=='Scheduled', - 'Status of event is correctly set'); - - assert.ok(cur_frm.doc.employees[0].employee_name=='Test Employee 1', - 'Attendee Employee is correctly set'); - - assert.ok(cur_frm.doc.employees[0].attendance=='Optional', - 'Attendance is correctly set'); - }, - - () => frappe.set_route('List','Training Event','List'), - () => frappe.timeout(2), - // Checking the submission of Training Event - () => { - assert.ok(cur_list.data[0].docstatus==1,'Training Event Submitted successfully'); - }, - () => frappe.timeout(2), - () => done() - ]); -}); diff --git a/erpnext/hr/doctype/training_feedback/test_training_feedback.js b/erpnext/hr/doctype/training_feedback/test_training_feedback.js deleted file mode 100644 index 5c825aea7f..0000000000 --- a/erpnext/hr/doctype/training_feedback/test_training_feedback.js +++ /dev/null @@ -1,51 +0,0 @@ -QUnit.module('hr'); - -QUnit.test("Test: Training Feedback [HR]", function (assert) { - assert.expect(3); - let done = assert.async(); - let employee_name; - - frappe.run_serially([ - // Creating Training Feedback - () => frappe.set_route('List','Training Feedback','List'), - () => frappe.timeout(0.3), - () => frappe.click_button('Make a new Training Feedback'), - () => frappe.timeout(1), - () => frappe.db.get_value('Employee', {'employee_name': 'Test Employee 1'}, 'name'), - (r) => { - employee_name = r.message.name; - }, - () => cur_frm.set_value('employee',employee_name), - () => cur_frm.set_value('employee_name','Test Employee 1'), - () => cur_frm.set_value('training_event','Test Training Event 1'), - () => cur_frm.set_value('event_name','Test Training Event 1'), - () => cur_frm.set_value('feedback','Great Experience. This is just a test.'), - () => frappe.timeout(1), - () => cur_frm.save(), - () => frappe.timeout(1), - () => cur_frm.save(), - - // Submitting the feedback - () => frappe.click_button('Submit'), - () => frappe.click_button('Yes'), - () => frappe.timeout(3), - - // Checking if the feedback is given by correct employee - () => { - assert.equal('Test Employee 1',cur_frm.get_field('employee_name').value, - 'Feedback is given by correct employee'); - - assert.equal('Test Training Event 1',cur_frm.get_field('training_event').value, - 'Feedback is given for correct event'); - }, - - () => frappe.set_route('List','Training Feedback','List'), - () => frappe.timeout(2), - - // Checking the submission of Training Result - () => { - assert.ok(cur_list.data[0].docstatus==1,'Training Feedback Submitted successfully'); - }, - () => done() - ]); -}); diff --git a/erpnext/hr/doctype/training_result_employee/test_training_result.js b/erpnext/hr/doctype/training_result_employee/test_training_result.js deleted file mode 100644 index 3f39750835..0000000000 --- a/erpnext/hr/doctype/training_result_employee/test_training_result.js +++ /dev/null @@ -1,52 +0,0 @@ -QUnit.module('hr'); - -QUnit.test("Test: Training Result [HR]", function (assert) { - assert.expect(5); - let done = assert.async(); - frappe.run_serially([ - // Creating Training Result - () => frappe.set_route('List','Training Result','List'), - () => frappe.timeout(0.3), - () => frappe.click_button('Make a new Training Result'), - () => { - cur_frm.set_value('training_event','Test Training Event 1'); - }, - () => frappe.timeout(1), - () => frappe.model.set_value('Training Result Employee','New Training Result Employee 1','hours',4), - () => frappe.model.set_value('Training Result Employee','New Training Result Employee 1','grade','A'), - () => frappe.model.set_value('Training Result Employee','New Training Result Employee 1','comments','Nice Seminar'), - () => frappe.timeout(1), - () => cur_frm.save(), - () => frappe.timeout(1), - () => cur_frm.save(), - - // Submitting the Training Result - () => frappe.click_button('Submit'), - () => frappe.click_button('Yes'), - () => frappe.timeout(4), - - // Checking if the fields are correctly set - () => { - assert.equal('Test Training Event 1',cur_frm.get_field('training_event').value, - 'Training Result is created'); - - assert.equal('Test Employee 1',cur_frm.doc.employees[0].employee_name, - 'Training Result is created for correct employee'); - - assert.equal(4,cur_frm.doc.employees[0].hours, - 'Hours field is correctly calculated'); - - assert.equal('A',cur_frm.doc.employees[0].grade, - 'Grade field is correctly set'); - }, - - () => frappe.set_route('List','Training Result','List'), - () => frappe.timeout(2), - - // Checking the submission of Training Result - () => { - assert.ok(cur_list.data[0].docstatus==1,'Training Result Submitted successfully'); - }, - () => done() - ]); -}); diff --git a/erpnext/manufacturing/doctype/bom/test_bom.js b/erpnext/manufacturing/doctype/bom/test_bom.js deleted file mode 100644 index 98a9198b79..0000000000 --- a/erpnext/manufacturing/doctype/bom/test_bom.js +++ /dev/null @@ -1,63 +0,0 @@ -QUnit.test("test: item", function (assert) { - assert.expect(1); - let done = assert.async(); - - frappe.run_serially([ - // test item creation - () => frappe.set_route("List", "Item"), - - // Create a BOM for a laptop - () => frappe.tests.make( - "BOM", [ - {item: "Laptop"}, - {quantity: 1}, - {with_operations: 1}, - {company: "For Testing"}, - {operations: [ - [ - {operation: "Assemble CPU"}, - {time_in_mins: 60}, - ], - [ - {operation: "Assemble Keyboard"}, - {time_in_mins: 30}, - ], - [ - {operation: "Assemble Screen"}, - {time_in_mins: 30}, - ] - ]}, - {scrap_items: [ - [ - {item_code: "Scrap item"} - ] - ]}, - {items: [ - [ - {item_code: "CPU"}, - {qty: 1} - ], - [ - {item_code: "Keyboard"}, - {qty: 1} - ], - [ - {item_code: "Screen"}, - {qty: 1} - ] - ]}, - ] - ), - () => cur_frm.savesubmit(), - () => frappe.timeout(1), - () => frappe.click_button('Yes'), - () => frappe.timeout(1), - - () => { - assert.ok(cur_frm.doc.operating_cost + cur_frm.doc.raw_material_cost - - cur_frm.doc.scrap_material_cost == cur_frm.doc.total_cost, 'Total_Cost calculated correctly'); - }, - - () => done() - ]); -}); diff --git a/erpnext/manufacturing/doctype/operation/test_operation.js b/erpnext/manufacturing/doctype/operation/test_operation.js deleted file mode 100644 index fd7783f0f4..0000000000 --- a/erpnext/manufacturing/doctype/operation/test_operation.js +++ /dev/null @@ -1,49 +0,0 @@ -QUnit.test("test: operation", function (assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - // test operation creation - () => frappe.set_route("List", "Operation"), - - // Create a Keyboard operation - () => { - return frappe.tests.make( - "Operation", [ - {__newname: "Assemble Keyboard"}, - {workstation: "Keyboard assembly workstation"} - ] - ); - }, - () => frappe.timeout(3), - () => { - assert.ok(cur_frm.docname.includes('Assemble Keyboard'), - 'Assemble Keyboard created successfully'); - assert.ok(cur_frm.doc.workstation.includes('Keyboard assembly workstation'), - 'Keyboard assembly workstation was linked successfully'); - }, - - // Create a Screen operation - () => { - return frappe.tests.make( - "Operation", [ - {__newname: 'Assemble Screen'}, - {workstation: "Screen assembly workstation"} - ] - ); - }, - () => frappe.timeout(3), - - // Create a CPU operation - () => { - return frappe.tests.make( - "Operation", [ - {__newname: 'Assemble CPU'}, - {workstation: "CPU assembly workstation"} - ] - ); - }, - () => frappe.timeout(3), - - () => done() - ]); -}); diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.js b/erpnext/manufacturing/doctype/work_order/test_work_order.js deleted file mode 100644 index 1e224eb468..0000000000 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.js +++ /dev/null @@ -1,130 +0,0 @@ -QUnit.test("test: work order", function (assert) { - assert.expect(25); - let done = assert.async(); - let laptop_quantity = 5; - let items = ["CPU", "Keyboard", "Screen"]; - let operation_items = ["CPU", "Keyboard", "Screen"]; - let click_make = () => { - let element = $(`.btn-primary:contains("Make"):visible`); - if(!element.length) { - throw `did not find any button containing 'Make'`; - } - element.click(); - return frappe.timeout(1); - }; - - frappe.run_serially([ - // test work order - () => frappe.set_route("List", "Work Order", "List"), - () => frappe.timeout(3), - - // Create a laptop work order - () => { - return frappe.tests.make('Work Order', [ - {production_item: 'Laptop'}, - {company: 'For Testing'}, - {qty: laptop_quantity}, - {scrap_warehouse: "Laptop Scrap Warehouse - FT"}, - {wip_warehouse: "Work In Progress - FT"}, - {fg_warehouse: "Finished Goods - FT"} - ]); - }, - () => frappe.timeout(3), - () => { - assert.equal(cur_frm.doc.planned_operating_cost, cur_frm.doc.total_operating_cost, - "Total and Planned Cost is equal"); - assert.equal(cur_frm.doc.planned_operating_cost, cur_frm.doc.total_operating_cost, - "Total and Planned Cost is equal"); - - items.forEach(function(item, index) { - assert.equal(item, cur_frm.doc.required_items[index].item_code, `Required item ${item} added`); - assert.equal("Stores - FT", cur_frm.doc.required_items[index].source_warehouse, `Item ${item} warhouse verified`); - assert.equal("5", cur_frm.doc.required_items[index].required_qty, `Item ${item} quantity verified`); - }); - - operation_items.forEach(function(operation_item, index) { - assert.equal(`Assemble ${operation_item}`, cur_frm.doc.operations[index].operation, - `Operation ${operation_item} added`); - assert.equal(`${operation_item} assembly workstation`, cur_frm.doc.operations[index].workstation, - `Workstation ${operation_item} linked`); - }); - }, - - // Submit the work order - () => cur_frm.savesubmit(), - () => frappe.timeout(1), - () => frappe.click_button('Yes'), - () => frappe.timeout(2.5), - - // Confirm the work order timesheet, save and submit it - () => frappe.click_link("TS-00"), - () => frappe.timeout(1), - () => frappe.click_button("Submit"), - () => frappe.timeout(1), - () => frappe.click_button("Yes"), - () => frappe.timeout(2.5), - - // Start the work order process - () => frappe.set_route("List", "Work Order", "List"), - () => frappe.timeout(2), - () => frappe.click_link("Laptop"), - () => frappe.timeout(1), - () => frappe.click_button("Start"), - () => frappe.timeout(0.5), - () => click_make(), - () => frappe.timeout(1), - () => frappe.click_button("Save"), - () => frappe.timeout(0.5), - - () => { - assert.equal(cur_frm.doc.total_outgoing_value, cur_frm.doc.total_incoming_value, - "Total incoming and outgoing cost is equal"); - assert.equal(cur_frm.doc.total_outgoing_value, "99000", - "Outgoing cost is correct"); // Price of each item x5 - }, - // Submit for work - () => frappe.click_button("Submit"), - () => frappe.timeout(0.5), - () => frappe.click_button("Yes"), - () => frappe.timeout(0.5), - - // Finish the work order by sending for manufacturing - () => frappe.set_route("List", "Work Order"), - () => frappe.timeout(1), - () => frappe.click_link("Laptop"), - () => frappe.timeout(1), - - () => { - assert.ok(frappe.tests.is_visible("5 items in progress", 'p'), "Work order initiated"); - assert.ok(frappe.tests.is_visible("Finish"), "Finish button visible"); - }, - - () => frappe.click_button("Finish"), - () => frappe.timeout(0.5), - () => click_make(), - () => { - assert.equal(cur_frm.doc.total_incoming_value, "105700", - "Incoming cost is correct "+cur_frm.doc.total_incoming_value); // Price of each item x5, values are in INR - assert.equal(cur_frm.doc.total_outgoing_value, "99000", - "Outgoing cost is correct"); // Price of each item x5, values are in INR - assert.equal(cur_frm.doc.total_incoming_value - cur_frm.doc.total_outgoing_value, cur_frm.doc.value_difference, - "Value difference is correct"); // Price of each item x5, values are in INR - }, - () => frappe.click_button("Save"), - () => frappe.timeout(1), - () => frappe.click_button("Submit"), - () => frappe.timeout(1), - () => frappe.click_button("Yes"), - () => frappe.timeout(1), - - // Manufacturing finished - () => frappe.set_route("List", "Work Order", "List"), - () => frappe.timeout(1), - () => frappe.click_link("Laptop"), - () => frappe.timeout(1), - - () => assert.ok(frappe.tests.is_visible("5 items produced", 'p'), "Work order completed"), - - () => done() - ]); -}); diff --git a/erpnext/manufacturing/doctype/workstation/test_workstation.js b/erpnext/manufacturing/doctype/workstation/test_workstation.js deleted file mode 100644 index 1df53d058f..0000000000 --- a/erpnext/manufacturing/doctype/workstation/test_workstation.js +++ /dev/null @@ -1,89 +0,0 @@ -QUnit.test("test: workstation", function (assert) { - assert.expect(9); - let done = assert.async(); - let elec_rate = 50; - let rent = 100; - let consumable_rate = 20; - let labour_rate = 500; - frappe.run_serially([ - // test workstation creation - () => frappe.set_route("List", "Workstation"), - - // Create a keyboard workstation - () => frappe.tests.make( - "Workstation", [ - {workstation_name: "Keyboard assembly workstation"}, - {hour_rate_electricity: elec_rate}, - {hour_rate_rent: rent}, - {hour_rate_consumable: consumable_rate}, - {hour_rate_labour: labour_rate}, - {working_hours: [ - [ - {enabled: 1}, - {start_time: '11:00:00'}, - {end_time: '18:00:00'} - ] - ]} - ] - ), - () => { - assert.ok(cur_frm.doc.workstation_name.includes('Keyboard assembly workstation'), - 'Keyboard assembly workstation created successfully'); - assert.equal(cur_frm.doc.hour_rate_electricity, elec_rate, - 'electricity rate set correctly'); - assert.equal(cur_frm.doc.hour_rate_rent, rent, - 'rent set correctly'); - assert.equal(cur_frm.doc.hour_rate_consumable, consumable_rate, - 'consumable rate set correctly'); - assert.equal(cur_frm.doc.hour_rate_labour, labour_rate, - 'labour rate set correctly'); - assert.equal(cur_frm.doc.working_hours[0].enabled, 1, - 'working hours enabled'); - assert.ok(cur_frm.doc.working_hours[0].start_time.includes('11:00:0'), - 'start time set correctly'); - assert.ok(cur_frm.doc.working_hours[0].end_time.includes('18:00:0'), - 'end time set correctly'); - assert.ok(cur_frm.doc.hour_rate_electricity+cur_frm.doc.hour_rate_rent+ - cur_frm.doc.hour_rate_consumable+cur_frm.doc.hour_rate_labour== - cur_frm.doc.hour_rate, 'Net hour rate set correctly'); - }, - - // Create a Screen workstation - () => frappe.tests.make( - "Workstation", [ - {workstation_name: "Screen assembly workstation"}, - {hour_rate_electricity: elec_rate}, - {hour_rate_rent: rent}, - {hour_rate_consumable: consumable_rate}, - {hour_rate_labour: labour_rate}, - {working_hours: [ - [ - {enabled: 1}, - {start_time: '11:00:00'}, - {end_time: '18:00:00'} - ] - ]} - ] - ), - - // Create a CPU workstation - () => frappe.tests.make( - "Workstation", [ - {workstation_name: "CPU assembly workstation"}, - {hour_rate_electricity: elec_rate}, - {hour_rate_rent: rent}, - {hour_rate_consumable: consumable_rate}, - {hour_rate_labour: labour_rate}, - {working_hours: [ - [ - {enabled: 1}, - {start_time: '11:00:00'}, - {end_time: '18:00:00'} - ] - ]} - ] - ), - - () => done() - ]); -}); diff --git a/erpnext/non_profit/doctype/donor/test_donor.js b/erpnext/non_profit/doctype/donor/test_donor.js deleted file mode 100644 index e478b343ec..0000000000 --- a/erpnext/non_profit/doctype/donor/test_donor.js +++ /dev/null @@ -1,27 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Donor", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(3); - - frappe.run_serially([ - // insert a new Member - () => frappe.tests.make('Donor', [ - // values to be set - {donor_name: 'Test Donor'}, - {donor_type: 'Test Organization'}, - {email: 'test@example.com'} - ]), - () => { - assert.equal(cur_frm.doc.donor_name, 'Test Donor'); - assert.equal(cur_frm.doc.donor_type, 'Test Organization'); - assert.equal(cur_frm.doc.email, 'test@example.com'); - }, - () => done() - ]); - -}); diff --git a/erpnext/non_profit/doctype/grant_application/test_grant_application.js b/erpnext/non_profit/doctype/grant_application/test_grant_application.js deleted file mode 100644 index 47230a52c0..0000000000 --- a/erpnext/non_profit/doctype/grant_application/test_grant_application.js +++ /dev/null @@ -1,30 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Grant Application", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(4); - - frappe.run_serially([ - // insert a new Member - () => frappe.tests.make('Grant Application', [ - // values to be set - {applicant_name: 'Test Organization'}, - {contact_person:'Test Applicant'}, - {email: 'test@example.com'}, - {grant_description:'Test message'}, - {amount: 150000} - ]), - () => { - assert.equal(cur_frm.doc.applicant_name, 'Test Organization'); - assert.equal(cur_frm.doc.contact_person, 'Test Applicant'); - assert.equal(cur_frm.doc.email, 'test@example.com'); - assert.equal(cur_frm.doc.amount, 150000); - }, - () => done() - ]); - -}); diff --git a/erpnext/non_profit/doctype/member/test_member.js b/erpnext/non_profit/doctype/member/test_member.js deleted file mode 100644 index f7cca97726..0000000000 --- a/erpnext/non_profit/doctype/member/test_member.js +++ /dev/null @@ -1,26 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Member", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(2); - - frappe.run_serially([ - // insert a new Member - () => frappe.tests.make('Member', [ - // values to be set - {member_name: 'Test Member'}, - {membership_type: 'Gold'}, - {email: 'test@example.com'} - ]), - () => { - assert.equal(cur_frm.doc.membership_type, 'Gold'); - assert.equal(cur_frm.doc.email, 'test@example.com'); - }, - () => done() - ]); - -}); diff --git a/erpnext/non_profit/doctype/membership_type/test_membership_type.js b/erpnext/non_profit/doctype/membership_type/test_membership_type.js deleted file mode 100644 index 6440df8473..0000000000 --- a/erpnext/non_profit/doctype/membership_type/test_membership_type.js +++ /dev/null @@ -1,25 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Membership Type", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(2); - - frappe.run_serially([ - // insert a new Member - () => frappe.tests.make('Membership Type', [ - // values to be set - {membership_type: 'Gold'}, - {amount:50000} - ]), - () => { - assert.equal(cur_frm.doc.membership_type, 'Gold'); - assert.equal(cur_frm.doc.amount, '50000'); - }, - () => done() - ]); - -}); diff --git a/erpnext/non_profit/doctype/volunteer/test_volunteer.js b/erpnext/non_profit/doctype/volunteer/test_volunteer.js deleted file mode 100644 index 45eb2813ee..0000000000 --- a/erpnext/non_profit/doctype/volunteer/test_volunteer.js +++ /dev/null @@ -1,34 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Volunteer", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(4); - - frappe.run_serially([ - // insert a new Member - () => frappe.tests.make('Volunteer', [ - // values to be set - {volunteer_name: 'Test Volunteer'}, - {volunteer_type:'Test Work'}, - {email:'test@example.com'}, - {'availability': 'Weekends'}, - {volunteer_skills:[ - [ - {'volunteer_skills': 'Fundraiser'}, - ] - ]}, - ]), - () => { - assert.equal(cur_frm.doc.volunteer_name, 'Test Volunteer'); - assert.equal(cur_frm.doc.volunteer_type, 'Test Work'); - assert.equal(cur_frm.doc.email, 'test@example.com'); - assert.equal(cur_frm.doc.availability, 'Weekends'); - }, - () => done() - ]); - -}); diff --git a/erpnext/non_profit/doctype/volunteer_type/test_volunteer_type.js b/erpnext/non_profit/doctype/volunteer_type/test_volunteer_type.js deleted file mode 100644 index 08baaf0bb3..0000000000 --- a/erpnext/non_profit/doctype/volunteer_type/test_volunteer_type.js +++ /dev/null @@ -1,27 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Volunteer Type", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(2); - - frappe.run_serially([ - // insert a new Member - () => { - return frappe.tests.make('Volunteer Type', [ - // values to be set - {__newname: 'Test Work'}, - {amount: 500} - ]); - }, - () => { - assert.equal(cur_frm.doc.name, 'Test Work'); - assert.equal(cur_frm.doc.amount, 500); - }, - () => done() - ]); - -}); diff --git a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.js deleted file mode 100644 index d24f243fc4..0000000000 --- a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.js +++ /dev/null @@ -1,62 +0,0 @@ -QUnit.module('HR'); - -QUnit.test("test: Payroll Entry", function (assert) { - assert.expect(5); - let done = assert.async(); - let employees, docname; - - frappe.run_serially([ - () => { - return frappe.tests.make('Payroll Entry', [ - {company: 'For Testing'}, - {posting_date: frappe.datetime.add_days(frappe.datetime.nowdate(), 0)}, - {payroll_frequency: 'Monthly'}, - {cost_center: 'Main - '+frappe.get_abbr(frappe.defaults.get_default("Company"))} - ]); - }, - - () => frappe.timeout(1), - () => { - assert.equal(cur_frm.doc.company, 'For Testing'); - assert.equal(cur_frm.doc.posting_date, frappe.datetime.add_days(frappe.datetime.nowdate(), 0)); - assert.equal(cur_frm.doc.cost_center, 'Main - FT'); - }, - () => frappe.click_button('Get Employee Details'), - () => { - employees = cur_frm.doc.employees.length; - docname = cur_frm.doc.name; - }, - - () => frappe.click_button('Submit'), - () => frappe.timeout(1), - () => frappe.click_button('Yes'), - () => frappe.timeout(5), - - () => frappe.click_button('View Salary Slip'), - () => frappe.timeout(2), - () => assert.equal(cur_list.data.length, employees), - - () => frappe.set_route('Form', 'Payroll Entry', docname), - () => frappe.timeout(2), - () => frappe.click_button('Submit Salary Slip'), - () => frappe.click_button('Yes'), - () => frappe.timeout(5), - - () => frappe.click_button('Close'), - () => frappe.timeout(1), - - () => frappe.click_button('View Salary Slip'), - () => frappe.timeout(2), - () => { - let count = 0; - for(var i = 0; i < employees; i++) { - if(cur_list.data[i].docstatus == 1){ - count++; - } - } - assert.equal(count, employees, "Salary Slip submitted for all employees"); - }, - - () => done() - ]); -}); diff --git a/erpnext/payroll/doctype/payroll_entry/test_set_salary_components.js b/erpnext/payroll/doctype/payroll_entry/test_set_salary_components.js deleted file mode 100644 index 092cbd8974..0000000000 --- a/erpnext/payroll/doctype/payroll_entry/test_set_salary_components.js +++ /dev/null @@ -1,61 +0,0 @@ -QUnit.module('HR'); - -QUnit.test("test: Set Salary Components", function (assert) { - assert.expect(5); - let done = assert.async(); - - frappe.run_serially([ - () => frappe.set_route('Form', 'Salary Component', 'Leave Encashment'), - () => { - var row = frappe.model.add_child(cur_frm.doc, "Salary Component Account", "accounts"); - row.company = 'For Testing'; - row.account = 'Salary - FT'; - }, - - () => cur_frm.save(), - () => frappe.timeout(2), - () => assert.equal(cur_frm.doc.accounts[0].account, 'Salary - FT'), - - () => frappe.set_route('Form', 'Salary Component', 'Basic'), - () => { - var row = frappe.model.add_child(cur_frm.doc, "Salary Component Account", "accounts"); - row.company = 'For Testing'; - row.account = 'Salary - FT'; - }, - - () => cur_frm.save(), - () => frappe.timeout(2), - () => assert.equal(cur_frm.doc.accounts[0].account, 'Salary - FT'), - - () => frappe.set_route('Form', 'Salary Component', 'Income Tax'), - () => { - var row = frappe.model.add_child(cur_frm.doc, "Salary Component Account", "accounts"); - row.company = 'For Testing'; - row.account = 'Salary - FT'; - }, - - () => cur_frm.save(), - () => frappe.timeout(2), - () => assert.equal(cur_frm.doc.accounts[0].account, 'Salary - FT'), - - () => frappe.set_route('Form', 'Salary Component', 'Arrear'), - () => { - var row = frappe.model.add_child(cur_frm.doc, "Salary Component Account", "accounts"); - row.company = 'For Testing'; - row.account = 'Salary - FT'; - }, - - () => cur_frm.save(), - () => frappe.timeout(2), - () => assert.equal(cur_frm.doc.accounts[0].account, 'Salary - FT'), - - () => frappe.set_route('Form', 'Company', 'For Testing'), - () => cur_frm.set_value('default_payroll_payable_account', 'Payroll Payable - FT'), - () => cur_frm.save(), - () => frappe.timeout(2), - () => assert.equal(cur_frm.doc.default_payroll_payable_account, 'Payroll Payable - FT'), - - () => done() - - ]); -}); diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.js b/erpnext/payroll/doctype/salary_slip/test_salary_slip.js deleted file mode 100644 index a47eba1887..0000000000 --- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.js +++ /dev/null @@ -1,55 +0,0 @@ -QUnit.test("test salary slip", function(assert) { - assert.expect(6); - let done = assert.async(); - let employee_name; - - let salary_slip = (ename) => { - frappe.run_serially([ - () => frappe.db.get_value('Employee', {'employee_name': ename}, 'name'), - (r) => { - employee_name = r.message.name; - }, - () => { - // Creating a salary slip for a employee - frappe.tests.make('Salary Slip', [ - { employee: employee_name} - ]); - }, - () => frappe.timeout(3), - () => { - // To check if all the calculations are correctly done - if(ename === 'Test Employee 1') - { - assert.ok(cur_frm.doc.gross_pay==24000, - 'Gross amount for first employee is correctly calculated'); - assert.ok(cur_frm.doc.total_deduction==4800, - 'Deduction amount for first employee is correctly calculated'); - assert.ok(cur_frm.doc.net_pay==19200, - 'Net amount for first employee is correctly calculated'); - } - if(ename === 'Test Employee 3') - { - assert.ok(cur_frm.doc.gross_pay==28800, - 'Gross amount for second employee is correctly calculated'); - assert.ok(cur_frm.doc.total_deduction==5760, - 'Deduction amount for second employee is correctly calculated'); - assert.ok(cur_frm.doc.net_pay==23040, - 'Net amount for second employee is correctly calculated'); - } - }, - ]); - }; - frappe.run_serially([ - () => salary_slip('Test Employee 1'), - () => frappe.timeout(6), - () => salary_slip('Test Employee 3'), - () => frappe.timeout(5), - () => frappe.set_route('List', 'Salary Slip', 'List'), - () => frappe.timeout(2), - () => {$('.list-row-checkbox').click();}, - () => frappe.timeout(2), - () => frappe.click_button('Delete'), - () => frappe.click_button('Yes'), - () => done() - ]); -}); diff --git a/erpnext/payroll/doctype/salary_structure/test_salary_structure.js b/erpnext/payroll/doctype/salary_structure/test_salary_structure.js deleted file mode 100644 index 542fa50354..0000000000 --- a/erpnext/payroll/doctype/salary_structure/test_salary_structure.js +++ /dev/null @@ -1,78 +0,0 @@ -QUnit.test("test Salary Structure", function(assert) { - assert.expect(7); - let done = assert.async(); - let employee_name1; - - frappe.run_serially([ - () => frappe.db.get_value('Employee', {'employee_name': "Test Employee 1"}, 'name', - (r) => { - employee_name1 = r.name; - } - ), - () => frappe.timeout(5), - () => frappe.db.get_value('Employee', {'employee_name': "Test Employee 3"}, 'name', - (r) => { - // Creating Salary Structure for employees); - return frappe.tests.make('Salary Structure', [ - { __newname: 'Test Salary Structure'}, - { company: 'For Testing'}, - { payroll_frequency: 'Monthly'}, - { employees: [ - [ - {employee: employee_name1}, - {from_date: '2017-07-01'}, - {base: 25000} - ], - [ - {employee: r.name}, - {from_date: '2017-07-01'}, - {base: 30000} - ] - ]}, - { earnings: [ - [ - {salary_component: 'Basic'}, - {formula: 'base * .80'} - ], - [ - {salary_component: 'Leave Encashment'}, - {formula: 'B * .20'} - ] - ]}, - { deductions: [ - [ - {salary_component: 'Income Tax'}, - {formula: '(B+LE) * .20'} - ] - ]}, - { payment_account: 'CASH - FT'}, - ]); - } - ), - () => frappe.timeout(15), - () => { - // To check if all the fields are correctly set - assert.ok(cur_frm.doc.employees[0].employee_name=='Test Employee 1', - 'Employee 1 name correctly set'); - - assert.ok(cur_frm.doc.employees[1].employee_name=='Test Employee 3', - 'Employee 2 name correctly set'); - - assert.ok(cur_frm.doc.employees[0].base==25000, - 'Base value for first employee is correctly set'); - - assert.ok(cur_frm.doc.employees[1].base==30000, - 'Base value for second employee is correctly set'); - - assert.ok(cur_frm.doc.earnings[0].formula.includes('base * .80'), - 'Formula for earnings as Basic is correctly set'); - - assert.ok(cur_frm.doc.earnings[1].formula.includes('B * .20'), - 'Formula for earnings as Leave Encashment is correctly set'); - - assert.ok(cur_frm.doc.deductions[0].formula.includes('(B+LE) * .20'), - 'Formula for deductions as Income Tax is correctly set'); - }, - () => done() - ]); -}); diff --git a/erpnext/projects/doctype/activity_type/test_activity_type.js b/erpnext/projects/doctype/activity_type/test_activity_type.js deleted file mode 100644 index 62be972bb2..0000000000 --- a/erpnext/projects/doctype/activity_type/test_activity_type.js +++ /dev/null @@ -1,21 +0,0 @@ -QUnit.test("test: Activity Type", function (assert) { - // number of asserts - assert.expect(1); - let done = assert.async(); - - frappe.run_serially([ - // insert a new Activity Type - () => frappe.set_route("List", "Activity Type", "List"), - () => frappe.new_doc("Activity Type"), - () => frappe.timeout(1), - () => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(), - () => frappe.timeout(1), - () => cur_frm.set_value("activity_type", "Test Activity"), - () => frappe.click_button('Save'), - () => frappe.timeout(1), - () => { - assert.equal(cur_frm.doc.name,"Test Activity"); - }, - () => done() - ]); -}); diff --git a/erpnext/projects/doctype/task/tests/test_task.js b/erpnext/projects/doctype/task/tests/test_task.js deleted file mode 100644 index 8a1a5bf682..0000000000 --- a/erpnext/projects/doctype/task/tests/test_task.js +++ /dev/null @@ -1,24 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Task", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(2); - - frappe.run_serially([ - // insert a new Task - () => frappe.tests.make('Task', [ - // values to be set - {subject: 'new task'} - ]), - () => { - assert.equal(cur_frm.doc.status, 'Open'); - assert.equal(cur_frm.doc.priority, 'Low'); - }, - () => done() - ]); - -}); diff --git a/erpnext/projects/doctype/task/tests/test_task_tree.js b/erpnext/projects/doctype/task/tests/test_task_tree.js deleted file mode 100644 index 27dccbfbed..0000000000 --- a/erpnext/projects/doctype/task/tests/test_task_tree.js +++ /dev/null @@ -1,88 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Task Tree", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(4); - - frappe.run_serially([ - // insert a new Task - () => frappe.set_route('Tree', 'Task'), - () => frappe.timeout(0.5), - - // Checking adding child without selecting any Node - () => frappe.tests.click_button('New'), - () => frappe.timeout(0.5), - () => {assert.equal($(`.msgprint`).text(), "Select a group node first.", "Error message success");}, - () => frappe.tests.click_button('Close'), - () => frappe.timeout(0.5), - - // Creating child nodes - () => frappe.tests.click_link('All Tasks'), - () => frappe.map_group.make('Test-1'), - () => frappe.map_group.make('Test-3', 1), - () => frappe.timeout(1), - () => frappe.tests.click_link('Test-3'), - () => frappe.map_group.make('Test-4', 0), - - // Checking Edit button - () => frappe.timeout(0.5), - () => frappe.tests.click_link('Test-1'), - () => frappe.tests.click_button('Edit'), - () => frappe.timeout(1), - () => frappe.db.get_value('Task', {'subject': 'Test-1'}, 'name'), - (task) => {assert.deepEqual(frappe.get_route(), ["Form", "Task", task.message.name], "Edit route checks");}, - - // Deleting child Node - () => frappe.set_route('Tree', 'Task'), - () => frappe.timeout(0.5), - () => frappe.tests.click_link('Test-1'), - () => frappe.tests.click_button('Delete'), - () => frappe.timeout(0.5), - () => frappe.tests.click_button('Yes'), - - // Deleting Group Node that has child nodes in it - () => frappe.timeout(0.5), - () => frappe.tests.click_link('Test-3'), - () => frappe.tests.click_button('Delete'), - () => frappe.timeout(0.5), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(1), - () => {assert.equal(cur_dialog.title, 'Message', 'Error thrown correctly');}, - () => frappe.tests.click_button('Close'), - - // Add multiple child tasks - () => frappe.tests.click_link('Test-3'), - () => frappe.timeout(0.5), - () => frappe.click_button('Add Multiple'), - () => frappe.timeout(1), - () => cur_dialog.set_value('tasks', 'Test-6\nTest-7'), - () => frappe.timeout(0.5), - () => frappe.click_button('Submit'), - () => frappe.timeout(2), - () => frappe.click_button('Expand All'), - () => frappe.timeout(1), - () => { - let count = $(`a:contains("Test-6"):visible`).length + $(`a:contains("Test-7"):visible`).length; - assert.equal(count, 2, "Multiple Tasks added successfully"); - }, - - () => done() - ]); -}); - -frappe.map_group = { - make:function(subject, is_group = 0){ - return frappe.run_serially([ - () => frappe.click_button('Add Child'), - () => frappe.timeout(1), - () => cur_dialog.set_value('is_group', is_group), - () => cur_dialog.set_value('subject', subject), - () => frappe.click_button('Create New'), - () => frappe.timeout(1.5) - ]); - } -}; diff --git a/erpnext/restaurant/doctype/restaurant/test_restaurant.js b/erpnext/restaurant/doctype/restaurant/test_restaurant.js deleted file mode 100644 index 8fe4e7b84d..0000000000 --- a/erpnext/restaurant/doctype/restaurant/test_restaurant.js +++ /dev/null @@ -1,50 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Restaurant", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(2); - let customer = { - "Test Customer 1": [ - {customer_name: "Test Customer 1"} - ], - "Test Customer 2": [ - {customer_name: "Test Customer 2"} - ] - }; - - frappe.run_serially([ - // insert a new Restaurant - () => frappe.tests.setup_doctype('Customer', customer), - () => { - return frappe.tests.make('Restaurant', [ - // values to be set - {__newname: 'Test Restaurant 1'}, - {company: 'Test Company'}, - {invoice_series_prefix: 'Test-Rest-1-Inv-'}, - {default_customer: 'Test Customer 1'} - ]) - }, - () => frappe.timeout(3), - () => { - assert.equal(cur_frm.doc.company, 'Test Company'); - }, - () => { - return frappe.tests.make('Restaurant', [ - // values to be set - {__newname: 'Test Restaurant 2'}, - {company: 'Test Company'}, - {invoice_series_prefix: 'Test-Rest-3-Inv-'}, - {default_customer: 'Test Customer 2'} - ]); - }, - () => frappe.timeout(3), - () => { - assert.equal(cur_frm.doc.company, 'Test Company'); - }, - () => done() - ]); -}); diff --git a/erpnext/restaurant/doctype/restaurant_menu/test_restaurant_menu.js b/erpnext/restaurant/doctype/restaurant_menu/test_restaurant_menu.js deleted file mode 100644 index f5ab9f0901..0000000000 --- a/erpnext/restaurant/doctype/restaurant_menu/test_restaurant_menu.js +++ /dev/null @@ -1,77 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Restaurant Menu", function (assert) { - let done = assert.async(); - - let items = { - "Food Item 1": [ - {item_code: "Food Item 1"}, - {item_group: "Products"}, - {is_stock_item: 1}, - ], - "Food Item 2": [ - {item_code: "Food Item 2"}, - {item_group: "Products"}, - {is_stock_item: 1}, - ], - "Food Item 3": [ - {item_code: "Food Item 3"}, - {item_group: "Products"}, - {is_stock_item: 1}, - ] - }; - - - // number of asserts - assert.expect(0); - - frappe.run_serially([ - // insert a new Restaurant Menu - () => frappe.tests.setup_doctype('Item', items), - () => { - return frappe.tests.make("Restaurant Menu", [ - {__newname: 'Restaurant Menu 1'}, - {restaurant: "Test Restaurant 1"}, - {items: [ - [ - {"item": "Food Item 1"}, - {"rate": 100} - ], - [ - {"item": "Food Item 2"}, - {"rate": 90} - ], - [ - {"item": "Food Item 3"}, - {"rate": 80} - ] - ]} - ]); - }, - () => frappe.timeout(2), - () => { - return frappe.tests.make("Restaurant Menu", [ - {__newname: 'Restaurant Menu 2'}, - {restaurant: "Test Restaurant 2"}, - {items: [ - [ - {"item": "Food Item 1"}, - {"rate": 105} - ], - [ - {"item": "Food Item 3"}, - {"rate": 85} - ] - ]} - ]); - }, - () => frappe.timeout(2), - () => frappe.set_route('Form', 'Restaurant', 'Test Restaurant 1'), - () => cur_frm.set_value('active_menu', 'Restaurant Menu 1'), - () => cur_frm.save(), - () => done() - ]); - -}); diff --git a/erpnext/restaurant/doctype/restaurant_order_entry/test_restaurant_order_entry.js b/erpnext/restaurant/doctype/restaurant_order_entry/test_restaurant_order_entry.js deleted file mode 100644 index fec2a2153b..0000000000 --- a/erpnext/restaurant/doctype/restaurant_order_entry/test_restaurant_order_entry.js +++ /dev/null @@ -1,53 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Restaurant Order Entry", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(5); - - frappe.run_serially([ - // insert a new Restaurant Order Entry - () => frappe.set_route('Form', 'Restaurant Settings'), - () => cur_frm.set_value('default_customer', 'Test Customer 1'), - () => cur_frm.save(), - () => frappe.set_route('Form', 'Restaurant Order Entry'), - () => frappe.click_button('Clear'), - () => frappe.timeout(2), - () => cur_frm.set_value('restaurant_table', 'Test-Restaurant-1-01'), - () => cur_frm.set_value('add_item', 'Food Item 1'), - () => frappe.timeout(0.5), - () => { - var e = $.Event( "keyup", {which: 13} ); - $('input[data-fieldname="add_item"]').trigger(e); - return frappe.timeout(0.5); - }, - () => cur_frm.set_value('add_item', 'Food Item 1'), - () => { - var e = $.Event( "keyup", {which: 13} ); - $('input[data-fieldname="add_item"]').trigger(e); - return frappe.timeout(0.5); - }, - () => cur_frm.set_value('add_item', 'Food Item 2'), - () => { - var e = $.Event( "keyup", {which: 13} ); - $('input[data-fieldname="add_item"]').trigger(e); - return frappe.timeout(0.5); - }, - () => { - assert.equal(cur_frm.doc.items[0].item, 'Food Item 1'); - assert.equal(cur_frm.doc.items[0].qty, 2); - assert.equal(cur_frm.doc.items[1].item, 'Food Item 2'); - assert.equal(cur_frm.doc.items[1].qty, 1); - }, - () => frappe.click_button('Update'), - () => frappe.timeout(2), - () => { - assert.equal(cur_frm.doc.grand_total, 290); - } - () => done() - ]); - -}); diff --git a/erpnext/restaurant/doctype/restaurant_reservation/test_restaurant_reservation.js b/erpnext/restaurant/doctype/restaurant_reservation/test_restaurant_reservation.js deleted file mode 100644 index eeea5a9f0b..0000000000 --- a/erpnext/restaurant/doctype/restaurant_reservation/test_restaurant_reservation.js +++ /dev/null @@ -1,27 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Restaurant Reservation", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Restaurant Reservation - () => frappe.tests.make('Restaurant Reservation', [ - // values to be set - {restaurant: 'Gokul - JP Nagar'}, - {customer_name: 'test customer'}, - {reservation_time: frappe.datetime.now_date() + " 19:00:00"}, - {no_of_people: 4}, - ]), - () => { - assert.equal(cur_frm.doc.reservation_end_time, - frappe.datetime.now_date() + ' 20:00:00'); - }, - () => done() - ]); - -}); diff --git a/erpnext/restaurant/doctype/restaurant_table/test_restaurant_table.js b/erpnext/restaurant/doctype/restaurant_table/test_restaurant_table.js deleted file mode 100644 index 16035f0c89..0000000000 --- a/erpnext/restaurant/doctype/restaurant_table/test_restaurant_table.js +++ /dev/null @@ -1,41 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Restaurant Table", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(0); - - frappe.run_serially([ - // insert a new Restaurant Table - () => frappe.tests.make('Restaurant Table', [ - // values to be set - {restaurant: 'Test Restaurant 1'}, - {no_of_seats: 4}, - ]), - () => frappe.tests.make('Restaurant Table', [ - // values to be set - {restaurant: 'Test Restaurant 1'}, - {no_of_seats: 5}, - ]), - () => frappe.tests.make('Restaurant Table', [ - // values to be set - {restaurant: 'Test Restaurant 1'}, - {no_of_seats: 2}, - ]), - () => frappe.tests.make('Restaurant Table', [ - // values to be set - {restaurant: 'Test Restaurant 1'}, - {no_of_seats: 2}, - ]), - () => frappe.tests.make('Restaurant Table', [ - // values to be set - {restaurant: 'Test Restaurant 1'}, - {no_of_seats: 6}, - ]), - () => done() - ]); - -}); diff --git a/erpnext/selling/doctype/product_bundle/test_product_bundle.js b/erpnext/selling/doctype/product_bundle/test_product_bundle.js deleted file mode 100644 index 0dc90ec211..0000000000 --- a/erpnext/selling/doctype/product_bundle/test_product_bundle.js +++ /dev/null @@ -1,35 +0,0 @@ -QUnit.test("test sales order", function(assert) { - assert.expect(4); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Product Bundle', [ - {new_item_code: 'Computer'}, - {items: [ - [ - {item_code:'CPU'}, - {qty:1} - ], - [ - {item_code:'Screen'}, - {qty:1} - ], - [ - {item_code:'Keyboard'}, - {qty:1} - ] - ]}, - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_code=='CPU', "Item Code correct"); - assert.ok(cur_frm.doc.items[1].item_code=='Screen', "Item Code correct"); - assert.ok(cur_frm.doc.items[2].item_code=='Keyboard', "Item Code correct"); - assert.ok(cur_frm.doc.new_item_code == "Computer", "Parent Item correct"); - }, - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation.js b/erpnext/selling/doctype/quotation/tests/test_quotation.js deleted file mode 100644 index ad942fe497..0000000000 --- a/erpnext/selling/doctype/quotation/tests/test_quotation.js +++ /dev/null @@ -1,58 +0,0 @@ -QUnit.test("test: quotation", function (assert) { - assert.expect(12); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make("Quotation", [ - {customer: "Test Customer 1"}, - {items: [ - [ - {"item_code": "Test Product 1"}, - {"qty": 5} - ]] - }, - {payment_terms_template: '_Test Payment Term Template UI'} - ]); - }, - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name == "Test Product 1", "Added Test Product 1"); - - // calculate_taxes_and_totals - assert.ok(cur_frm.doc.grand_total === 500, String(cur_frm.doc.grand_total)); - }, - () => cur_frm.set_value("customer_address", "Test1-Billing"), - () => cur_frm.set_value("shipping_address_name", "Test1-Warehouse"), - () => cur_frm.set_value("contact_person", "Contact 1-Test Customer 1"), - () => cur_frm.set_value("currency", "USD"), - () => frappe.timeout(0.3), - () => cur_frm.set_value("selling_price_list", "Test-Selling-USD"), - () => frappe.timeout(0.5), - () => cur_frm.doc.items[0].rate = 200, - () => frappe.timeout(0.3), - () => cur_frm.set_value("tc_name", "Test Term 1"), - () => cur_frm.set_value("payment_schedule", []), - () => frappe.timeout(0.5), - () => cur_frm.save(), - () => { - // Check Address and Contact Info - assert.ok(cur_frm.doc.address_display.includes("Billing Street 1"), "Address Changed"); - assert.ok(cur_frm.doc.shipping_address.includes("Warehouse Street 1"), "Address Changed"); - assert.ok(cur_frm.doc.contact_display == "Contact 1", "Contact info changed"); - - // Check Currency - assert.ok(cur_frm.doc.currency == "USD", "Currency Changed"); - assert.ok(cur_frm.doc.selling_price_list == "Test-Selling-USD", "Price List Changed"); - assert.ok(cur_frm.doc.items[0].rate == 200, "Price Changed Manually"); - assert.equal(cur_frm.doc.total, 1000, "New Total Calculated"); - - // Check Terms and Conditions - assert.ok(cur_frm.doc.tc_name == "Test Term 1", "Terms and Conditions Checked"); - - assert.ok(cur_frm.doc.payment_terms_template, "Payment Terms Template is correct"); - assert.ok(cur_frm.doc.payment_schedule.length > 0, "Payment Term Schedule is not empty"); - - }, - () => done() - ]); -}); diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_submit_cancel_amend.js b/erpnext/selling/doctype/quotation/tests/test_quotation_submit_cancel_amend.js deleted file mode 100644 index 26a099e4d6..0000000000 --- a/erpnext/selling/doctype/quotation/tests/test_quotation_submit_cancel_amend.js +++ /dev/null @@ -1,41 +0,0 @@ -QUnit.module('Quotation'); - -QUnit.test("test quotation submit cancel amend", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Quotation', [ - {customer: 'Test Customer 1'}, - {items: [ - [ - {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {'qty': 5}, - {'item_code': 'Test Product 1'} - ] - ]}, - {customer_address: 'Test1-Billing'}, - {shipping_address_name: 'Test1-Shipping'}, - {contact_person: 'Contact 1-Test Customer 1'} - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); - // get uom details - assert.ok(cur_frm.doc.grand_total== 500, "Grand total correct "); - - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(1), - () => frappe.tests.click_button('Close'), - () => frappe.tests.click_button('Cancel'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.5), - () => frappe.tests.click_button('Amend'), - () => cur_frm.save(), - () => done() - ]); -}); diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_discount_on_grand_total.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_discount_on_grand_total.js deleted file mode 100644 index b59bb0510e..0000000000 --- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_discount_on_grand_total.js +++ /dev/null @@ -1,43 +0,0 @@ -QUnit.module('Quotation'); - -QUnit.test("test quotation with additional discount in grand total", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Quotation', [ - {customer: 'Test Customer 1'}, - {items: [ - [ - {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {'qty': 5}, - {'item_code': 'Test Product 4'}, - ] - ]}, - {customer_address: 'Test1-Billing'}, - {shipping_address_name: 'Test1-Shipping'}, - {contact_person: 'Contact 1-Test Customer 1'}, - {payment_terms_template: '_Test Payment Term Template UI'} - ]); - }, - () => { - return frappe.tests.set_form_values(cur_frm, [ - {apply_discount_on:'Grand Total'}, - {additional_discount_percentage:10}, - {payment_schedule: []} - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct"); - // get grand_total details - assert.ok(cur_frm.doc.grand_total== 450, "Grand total correct "); - - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_item_wise_discount.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_item_wise_discount.js deleted file mode 100644 index f5172fbae2..0000000000 --- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_item_wise_discount.js +++ /dev/null @@ -1,37 +0,0 @@ -QUnit.module('Quotation'); - -QUnit.test("test quotation with item wise discount", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Quotation', [ - {customer: 'Test Customer 1'}, - {items: [ - [ - {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {'qty': 5}, - {'item_code': 'Test Product 4'}, - {'discount_percentage': 10}, - {'margin_type': 'Percentage'} - ] - ]}, - {customer_address: 'Test1-Billing'}, - {shipping_address_name: 'Test1-Shipping'}, - {contact_person: 'Contact 1-Test Customer 1'} - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct"); - // get grand_total details - assert.ok(cur_frm.doc.grand_total== 450, "Grand total correct "); - - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_margin.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_margin.js deleted file mode 100644 index 0d340997ad..0000000000 --- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_margin.js +++ /dev/null @@ -1,35 +0,0 @@ -QUnit.module('Selling'); - -QUnit.test("test quotation with margin", function(assert) { - assert.expect(3); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Quotation', [ - {customer: 'Test Customer 1'}, - {selling_price_list: 'Test-Selling-USD'}, - {currency: 'USD'}, - {items: [ - [ - {'item_code': 'Test Product 4'}, - {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {'qty': 1}, - {'margin_type': 'Percentage'}, - {'margin_rate_or_amount': 20} - ] - ]} - ]); - }, - () => cur_frm.save(), - () => { - assert.ok(cur_frm.doc.items[0].rate_with_margin == 240, "Margin rate correct"); - assert.ok(cur_frm.doc.items[0].base_rate_with_margin == cur_frm.doc.conversion_rate * 240, "Base margin rate correct"); - assert.ok(cur_frm.doc.total == 240, "Amount correct"); - - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_multi_uom.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_multi_uom.js deleted file mode 100644 index 84be56f460..0000000000 --- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_multi_uom.js +++ /dev/null @@ -1,38 +0,0 @@ -QUnit.module('Quotation'); - -QUnit.test("test quotation with multi uom", function(assert) { - assert.expect(3); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Quotation', [ - {customer: 'Test Customer 1'}, - {items: [ - [ - {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {'qty': 5}, - {'item_code': 'Test Product 4'}, - {'uom': 'unit'}, - ] - ]}, - {customer_address: 'Test1-Billing'}, - {shipping_address_name: 'Test1-Shipping'}, - {contact_person: 'Contact 1-Test Customer 1'} - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct"); - // get uom details - assert.ok(cur_frm.doc.items[0].uom=='Unit', "Multi Uom correct"); - // get grand_total details - assert.ok(cur_frm.doc.grand_total== 5000, "Grand total correct "); - - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_shipping_rule.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_shipping_rule.js deleted file mode 100644 index 17c5dd2b34..0000000000 --- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_shipping_rule.js +++ /dev/null @@ -1,35 +0,0 @@ -QUnit.module('Quotation'); - -QUnit.test("test quotation with shipping rule", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Quotation', [ - {customer: 'Test Customer 1'}, - {items: [ - [ - {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {'qty': 5}, - {'item_code': 'Test Product 4'}, - ] - ]}, - {customer_address: 'Test1-Billing'}, - {shipping_address_name: 'Test1-Shipping'}, - {contact_person: 'Contact 1-Test Customer 1'}, - {shipping_rule:'Next Day Shipping'} - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct"); - // get grand_total details - assert.ok(cur_frm.doc.grand_total== 550, "Grand total correct "); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_taxes_and_charges.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_taxes_and_charges.js deleted file mode 100644 index 5e21f81757..0000000000 --- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_taxes_and_charges.js +++ /dev/null @@ -1,40 +0,0 @@ -QUnit.module('Quotation'); - -QUnit.test("test quotation with taxes and charges", function(assert) { - assert.expect(3); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Quotation', [ - {customer: 'Test Customer 1'}, - {items: [ - [ - {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {'qty': 5}, - {'item_code': 'Test Product 4'}, - ] - ]}, - {customer_address: 'Test1-Billing'}, - {shipping_address_name: 'Test1-Shipping'}, - {contact_person: 'Contact 1-Test Customer 1'}, - {taxes_and_charges: 'TEST In State GST - FT'}, - {tc_name: 'Test Term 1'}, - {terms: 'This is Test'} - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct"); - // get tax details - assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct"); - // get tax account head details - assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct"); - - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order.js deleted file mode 100644 index c99f9ef2a9..0000000000 --- a/erpnext/selling/doctype/sales_order/tests/test_sales_order.js +++ /dev/null @@ -1,68 +0,0 @@ -QUnit.module('Sales Order'); - -QUnit.test("test sales order", function(assert) { - assert.expect(12); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Sales Order', [ - {customer: 'Test Customer 1'}, - {items: [ - [ - {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {'qty': 5.123}, - {'item_code': 'Test Product 3'}, - ] - ]}, - {customer_address: 'Test1-Billing'}, - {shipping_address_name: 'Test1-Shipping'}, - {contact_person: 'Contact 1-Test Customer 1'}, - {taxes_and_charges: 'TEST In State GST - FT'}, - {tc_name: 'Test Term 1'}, - {terms: 'This is Test'}, - {payment_terms_template: '_Test Payment Term Template UI'} - ]); - }, - () => { - return frappe.tests.set_form_values(cur_frm, [ - {selling_price_list:'Test-Selling-USD'}, - {currency: 'USD'} - ]); - }, - () => frappe.timeout(1.5), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 3', "Item name correct"); - // get tax details - assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct"); - // get tax account head details - assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct"); - }, - () => cur_frm.save(), - () => frappe.timeout(1), - () => cur_frm.print_doc(), - () => frappe.timeout(1), - () => { - // Payment Terms - assert.ok(cur_frm.doc.payment_terms_template, "Payment Terms Template is correct"); - assert.ok(cur_frm.doc.payment_schedule.length > 0, "Payment Term Schedule is not empty"); - - // totals - assert.ok(cur_frm.doc.items[0].price_list_rate==250, "Item 1 price_list_rate"); - assert.ok(cur_frm.doc.net_total== 1280.75, "net total correct "); - assert.ok(cur_frm.doc.base_grand_total== flt(1511.29* cur_frm.doc.conversion_rate, precision('base_grand_total')), String(flt(1511.29* cur_frm.doc.conversion_rate, precision('base_grand_total')) + ' ' + cur_frm.doc.base_grand_total)); - assert.ok(cur_frm.doc.grand_total== 1511.29 , "grand total correct "); - assert.ok(cur_frm.doc.rounded_total== 1511.30, "rounded total correct "); - - // print format - assert.ok($('.btn-print-print').is(':visible'), "Print Format Available"); - frappe.timeout(1); - assert.ok($(".section-break+ .section-break .column-break:nth-child(1) .data-field:nth-child(1) .value").text().includes("Billing Street 1"), "Print Preview Works As Expected"); - }, - () => cur_frm.print_doc(), - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_bypass_credit_limit_check.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_bypass_credit_limit_check.js deleted file mode 100644 index 79d798b944..0000000000 --- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_bypass_credit_limit_check.js +++ /dev/null @@ -1,58 +0,0 @@ -QUnit.module('Sales Order'); - -QUnit.test("test_sales_order_with_bypass_credit_limit_check", function(assert) { -//#PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => frappe.new_doc('Customer'), - () => frappe.timeout(1), - () => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(), - () => frappe.timeout(1), - () => cur_frm.set_value("customer_name", "Test Customer 10"), - () => cur_frm.add_child('credit_limits', { - 'company': cur_frm.doc.company || '_Test Company' - 'credit_limit': 1000, - 'bypass_credit_limit_check': 1}), - // save form - () => cur_frm.save(), - () => frappe.timeout(1), - - () => frappe.new_doc('Item'), - () => frappe.timeout(1), - () => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(), - () => frappe.timeout(1), - () => cur_frm.set_value("item_code", "Test Product 10"), - () => cur_frm.set_value("item_group", "Products"), - () => cur_frm.set_value("standard_rate", 100), - // save form - () => cur_frm.save(), - () => frappe.timeout(1), - - () => { - return frappe.tests.make('Sales Order', [ - {customer: 'Test Customer 5'}, - {items: [ - [ - {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {'qty': 5}, - {'item_code': 'Test Product 10'}, - ] - ]} - - ]); - }, - () => cur_frm.save(), - () => frappe.tests.click_button('Submit'), - () => assert.equal("Confirm", cur_dialog.title,'confirmation for submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(3), - () => { - - assert.ok(cur_frm.doc.status=="To Deliver and Bill", "It is submited. Credit limit is NOT checked for sales order"); - - - }, - () => done() - ]); -}); diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_discount_on_grand_total.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_discount_on_grand_total.js deleted file mode 100644 index de61a6112c..0000000000 --- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_discount_on_grand_total.js +++ /dev/null @@ -1,43 +0,0 @@ -QUnit.module('Sales Order'); - -QUnit.test("test sales order with additional discount in grand total", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Sales Order', [ - {customer: 'Test Customer 1'}, - {items: [ - [ - {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {'qty': 5}, - {'item_code': 'Test Product 4'}, - ] - ]}, - {customer_address: 'Test1-Billing'}, - {shipping_address_name: 'Test1-Shipping'}, - {contact_person: 'Contact 1-Test Customer 1'}, - {payment_terms_template: '_Test Payment Term Template UI'} - ]); - }, - () => { - return frappe.tests.set_form_values(cur_frm, [ - {apply_discount_on:'Grand Total'}, - {additional_discount_percentage:10}, - {payment_schedule: []} - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct"); - // get grand_total details - assert.ok(cur_frm.doc.grand_total== 450, "Grand total correct "); - - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_item_wise_discount.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_item_wise_discount.js deleted file mode 100644 index 2c48108308..0000000000 --- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_item_wise_discount.js +++ /dev/null @@ -1,38 +0,0 @@ -QUnit.module('Sales Order'); - -QUnit.test("test sales order", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Sales Order', [ - {customer: 'Test Customer 1'}, - {items: [ - [ - {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {'qty': 5}, - {'item_code': 'Test Product 4'}, - {'discount_percentage': 10}, - {'margin_type': 'Percentage'} - ] - ]}, - {customer_address: 'Test1-Billing'}, - {shipping_address_name: 'Test1-Shipping'}, - {contact_person: 'Contact 1-Test Customer 1'}, - {payment_terms_template: '_Test Payment Term Template UI'} - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct"); - // get grand_total details - assert.ok(cur_frm.doc.grand_total== 450, "Grand total correct "); - - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_margin.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_margin.js deleted file mode 100644 index 9eebfdaf21..0000000000 --- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_margin.js +++ /dev/null @@ -1,37 +0,0 @@ -QUnit.module('Selling'); - -QUnit.test("test sales order with margin", function(assert) { - assert.expect(3); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Sales Order', [ - {customer:'Test Customer 1'}, - {selling_price_list: 'Test-Selling-USD'}, - {currency: 'USD'}, - {items: [ - [ - {'item_code': 'Test Product 4'}, - {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {'qty': 1}, - {'margin_type': 'Amount'}, - {'margin_rate_or_amount': 20} - ] - ]}, - ]); - }, - - () => cur_frm.save(), - () => { - // get_rate_details - assert.ok(cur_frm.doc.items[0].rate_with_margin == 220, "Margin rate correct"); - assert.ok(cur_frm.doc.items[0].base_rate_with_margin == cur_frm.doc.conversion_rate * 220, "Base margin rate correct"); - assert.ok(cur_frm.doc.total == 220, "Amount correct"); - }, - - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multi_uom.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multi_uom.js deleted file mode 100644 index 84301f5a86..0000000000 --- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multi_uom.js +++ /dev/null @@ -1,38 +0,0 @@ -QUnit.module('Sales Order'); - -QUnit.test("test sales order", function(assert) { - assert.expect(3); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Sales Order', [ - {customer: 'Test Customer 1'}, - {items: [ - [ - {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {'qty': 5}, - {'item_code': 'Test Product 4'}, - {'uom': 'Unit'}, - ] - ]}, - {customer_address: 'Test1-Billing'}, - {shipping_address_name: 'Test1-Shipping'}, - {contact_person: 'Contact 1-Test Customer 1'} - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct"); - // get uom details - assert.ok(cur_frm.doc.items[0].uom=='Unit', "Multi Uom correct"); - // get grand_total details - assert.ok(cur_frm.doc.grand_total== 5000, "Grand total correct "); - - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multiple_delivery_date.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multiple_delivery_date.js deleted file mode 100644 index be76c49f84..0000000000 --- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multiple_delivery_date.js +++ /dev/null @@ -1,59 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Sales Order", function (assert) { - assert.expect(2); - let done = assert.async(); - let delivery_date = frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1); - - frappe.run_serially([ - // insert a new Sales Order - () => { - return frappe.tests.make('Sales Order', [ - {customer: "Test Customer 1"}, - {delivery_date: delivery_date}, - {order_type: 'Sales'}, - {items: [ - [ - {"item_code": "Test Product 1"}, - {"qty": 5}, - {'rate': 100}, - ]] - } - ]) - }, - () => { - assert.ok(cur_frm.doc.items[0].delivery_date == delivery_date); - }, - () => frappe.timeout(1), - // make SO without delivery date in parent, - // parent delivery date should be set based on final delivery date entered in item - () => { - return frappe.tests.make('Sales Order', [ - {customer: "Test Customer 1"}, - {order_type: 'Sales'}, - {items: [ - [ - {"item_code": "Test Product 1"}, - {"qty": 5}, - {'rate': 100}, - {'delivery_date': delivery_date} - ], - [ - {"item_code": "Test Product 2"}, - {"qty": 5}, - {'rate': 100}, - {'delivery_date': frappe.datetime.add_days(delivery_date, 5)} - ]] - } - ]) - }, - () => cur_frm.save(), - () => frappe.timeout(1), - () => { - assert.ok(cur_frm.doc.delivery_date == frappe.datetime.add_days(delivery_date, 5)); - }, - () => done() - ]); -}); diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_pricing_rule.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_pricing_rule.js deleted file mode 100644 index e91fb0143b..0000000000 --- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_pricing_rule.js +++ /dev/null @@ -1,34 +0,0 @@ -QUnit.module('Sales Order'); - -QUnit.test("test sales order with shipping rule", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Sales Order', [ - {customer: 'Test Customer 3'}, - {items: [ - [ - {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {'qty': 5}, - {'item_code': 'Test Product 2'}, - ] - ]}, - {customer_address: 'Test1-Billing'}, - {shipping_address_name: 'Test1-Shipping'}, - {contact_person: 'Contact 1-Test Customer 1'}, - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 2', "Item name correct"); - // get grand_total details - assert.ok(cur_frm.doc.grand_total== 675, "Grand total correct "); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_shipping_rule.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_shipping_rule.js deleted file mode 100644 index 7d1211f321..0000000000 --- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_shipping_rule.js +++ /dev/null @@ -1,35 +0,0 @@ -QUnit.module('Sales Order'); - -QUnit.test("test sales order with shipping rule", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Sales Order', [ - {customer: 'Test Customer 1'}, - {items: [ - [ - {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {'qty': 5}, - {'item_code': 'Test Product 4'}, - ] - ]}, - {customer_address: 'Test1-Billing'}, - {shipping_address_name: 'Test1-Shipping'}, - {contact_person: 'Contact 1-Test Customer 1'}, - {shipping_rule:'Next Day Shipping'} - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct"); - // get grand_total details - assert.ok(cur_frm.doc.grand_total== 550, "Grand total correct "); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_taxes_and_charges.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_taxes_and_charges.js deleted file mode 100644 index a3668ab2af..0000000000 --- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_taxes_and_charges.js +++ /dev/null @@ -1,40 +0,0 @@ -QUnit.module('Sales Order'); - -QUnit.test("test sales order with taxes and charges", function(assert) { - assert.expect(3); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Sales Order', [ - {customer: 'Test Customer 1'}, - {items: [ - [ - {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {'qty': 5}, - {'item_code': 'Test Product 4'}, - ] - ]}, - {customer_address: 'Test1-Billing'}, - {shipping_address_name: 'Test1-Shipping'}, - {contact_person: 'Contact 1-Test Customer 1'}, - {taxes_and_charges: 'TEST In State GST - FT'}, - {tc_name: 'Test Term 1'}, - {terms: 'This is Test'} - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct"); - // get tax details - assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct"); - // get tax account head details - assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct"); - - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_without_bypass_credit_limit_check.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_without_bypass_credit_limit_check.js deleted file mode 100644 index 8de39f9aa3..0000000000 --- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_without_bypass_credit_limit_check.js +++ /dev/null @@ -1,62 +0,0 @@ -QUnit.module('Sales Order'); - -QUnit.test("test_sales_order_without_bypass_credit_limit_check", function(assert) { -//#PR : 10861, Author : ashish-greycube & jigneshpshah, Email:mr.ashish.shah@gmail.com - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => frappe.new_doc('Customer'), - () => frappe.timeout(1), - () => frappe.quick_entry.dialog.$wrapper.find('.edit-full').click(), - () => frappe.timeout(1), - () => cur_frm.set_value("customer_name", "Test Customer 11"), - () => cur_frm.add_child('credit_limits', { - 'credit_limit': 1000, - 'company': '_Test Company', - 'bypass_credit_limit_check': 1}), - // save form - () => cur_frm.save(), - () => frappe.timeout(1), - - () => frappe.new_doc('Item'), - () => frappe.timeout(1), - () => frappe.click_link('Edit in full page'), - () => cur_frm.set_value("item_code", "Test Product 11"), - () => cur_frm.set_value("item_group", "Products"), - () => cur_frm.set_value("standard_rate", 100), - // save form - () => cur_frm.save(), - () => frappe.timeout(1), - - () => { - return frappe.tests.make('Sales Order', [ - {customer: 'Test Customer 11'}, - {items: [ - [ - {'delivery_date': frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)}, - {'qty': 5}, - {'item_code': 'Test Product 11'}, - ] - ]} - - ]); - }, - () => cur_frm.save(), - () => frappe.tests.click_button('Submit'), - () => assert.equal("Confirm", cur_dialog.title,'confirmation for submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(3), - () => { - - if (cur_dialog.body.innerText.match(/^Credit limit has been crossed for customer.*$/)) - { - /*Match found */ - assert.ok(true, "Credit Limit crossed message received"); - } - - - }, - () => cur_dialog.cancel(), - () => done() - ]); -}); diff --git a/erpnext/setup/doctype/company/tests/test_company.js b/erpnext/setup/doctype/company/tests/test_company.js deleted file mode 100644 index b568494c84..0000000000 --- a/erpnext/setup/doctype/company/tests/test_company.js +++ /dev/null @@ -1,25 +0,0 @@ -QUnit.module('setup'); - -QUnit.test("Test: Company [SetUp]", function (assert) { - assert.expect(2); - let done = assert.async(); - - frappe.run_serially([ - // test company creation - () => frappe.set_route("List", "Company", "List"), - () => frappe.new_doc("Company"), - () => frappe.timeout(1), - () => cur_frm.set_value("company_name", "Test Company"), - () => cur_frm.set_value("abbr", "TC"), - () => cur_frm.set_value("domain", "Services"), - () => cur_frm.set_value("default_currency", "INR"), - // save form - () => cur_frm.save(), - () => frappe.timeout(1), - () => assert.equal("Debtors - TC", cur_frm.doc.default_receivable_account, - 'chart of acounts created'), - () => assert.equal("Main - TC", cur_frm.doc.cost_center, - 'chart of cost centers created'), - () => done() - ]); -}); diff --git a/erpnext/setup/doctype/company/tests/test_company_production.js b/erpnext/setup/doctype/company/tests/test_company_production.js deleted file mode 100644 index a4c1e2e7de..0000000000 --- a/erpnext/setup/doctype/company/tests/test_company_production.js +++ /dev/null @@ -1,19 +0,0 @@ -QUnit.test("Test: Company", function (assert) { - assert.expect(0); - - let done = assert.async(); - - frappe.run_serially([ - // Added company for Work Order testing - () => frappe.set_route("List", "Company"), - () => frappe.new_doc("Company"), - () => frappe.timeout(1), - () => cur_frm.set_value("company_name", "For Testing"), - () => cur_frm.set_value("abbr", "RB"), - () => cur_frm.set_value("default_currency", "INR"), - () => cur_frm.save(), - () => frappe.timeout(1), - - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/batch/test_batch.js b/erpnext/stock/doctype/batch/test_batch.js deleted file mode 100644 index 2d2150b8ac..0000000000 --- a/erpnext/stock/doctype/batch/test_batch.js +++ /dev/null @@ -1,22 +0,0 @@ -QUnit.module('Stock'); - -QUnit.test("test Batch", function(assert) { - assert.expect(1); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Batch', [ - {batch_id:'TEST-BATCH-001'}, - {item:'Test Product 4'}, - {expiry_date:frappe.datetime.add_days(frappe.datetime.now_date(), 2)}, - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.batch_id=='TEST-BATCH-001', "Batch Id correct"); - }, - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.js b/erpnext/stock/doctype/delivery_note/test_delivery_note.js deleted file mode 100644 index 76f7989429..0000000000 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.js +++ /dev/null @@ -1,35 +0,0 @@ -QUnit.module('Stock'); - -QUnit.test("test delivery note", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Delivery Note', [ - {customer:'Test Customer 1'}, - {items: [ - [ - {'item_code': 'Test Product 1'}, - {'qty': 5}, - ] - ]}, - {shipping_address_name: 'Test1-Shipping'}, - {contact_person: 'Contact 1-Test Customer 1'}, - {taxes_and_charges: 'TEST In State GST - FT'}, - {tc_name: 'Test Term 1'}, - {transporter_name:'TEST TRANSPORT'}, - {lr_no:'MH-04-FG 1111'} - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); - assert.ok(cur_frm.doc.grand_total==590, " Grand Total correct"); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note_with_margin.js b/erpnext/stock/doctype/delivery_note/test_delivery_note_with_margin.js deleted file mode 100644 index 9f1375f563..0000000000 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note_with_margin.js +++ /dev/null @@ -1,36 +0,0 @@ -QUnit.module('Stock'); - -QUnit.test("test delivery note with margin", function(assert) { - assert.expect(3); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Delivery Note', [ - {customer:'Test Customer 1'}, - {selling_price_list: 'Test-Selling-USD'}, - {currency: 'USD'}, - {items: [ - [ - {'item_code': 'Test Product 4'}, - {'qty': 1}, - {'margin_type': 'Amount'}, - {'margin_rate_or_amount': 10} - ] - ]}, - ]); - }, - - () => cur_frm.save(), - () => { - // get_rate_details - assert.ok(cur_frm.doc.items[0].rate_with_margin == 210, "Margin rate correct"); - assert.ok(cur_frm.doc.items[0].base_rate_with_margin == cur_frm.doc.conversion_rate * 210, "Base margin rate correct"); - assert.ok(cur_frm.doc.total == 210, "Amount correct"); - }, - - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/item/tests/test_item.js b/erpnext/stock/doctype/item/tests/test_item.js deleted file mode 100644 index 7f7e72d5c0..0000000000 --- a/erpnext/stock/doctype/item/tests/test_item.js +++ /dev/null @@ -1,121 +0,0 @@ -QUnit.module('stock'); -QUnit.test("test: item", function (assert) { - assert.expect(6); - let done = assert.async(); - let keyboard_cost = 800; - let screen_cost = 4000; - let CPU_cost = 15000; - let scrap_cost = 100; - let no_of_items_to_stock = 100; - let is_stock_item = 1; - frappe.run_serially([ - // test item creation - () => frappe.set_route("List", "Item"), - - // Create a keyboard item - () => frappe.tests.make( - "Item", [ - {item_code: "Keyboard"}, - {item_group: "Products"}, - {is_stock_item: is_stock_item}, - {standard_rate: keyboard_cost}, - {opening_stock: no_of_items_to_stock}, - {default_warehouse: "Stores - FT"} - ] - ), - () => { - assert.ok(cur_frm.doc.item_name.includes('Keyboard'), - 'Item Keyboard created correctly'); - assert.ok(cur_frm.doc.item_code.includes('Keyboard'), - 'item_code for Keyboard set correctly'); - assert.ok(cur_frm.doc.item_group.includes('Products'), - 'item_group for Keyboard set correctly'); - assert.equal(cur_frm.doc.is_stock_item, is_stock_item, - 'is_stock_item for Keyboard set correctly'); - assert.equal(cur_frm.doc.standard_rate, keyboard_cost, - 'standard_rate for Keyboard set correctly'); - assert.equal(cur_frm.doc.opening_stock, no_of_items_to_stock, - 'opening_stock for Keyboard set correctly'); - }, - - // Create a Screen item - () => frappe.tests.make( - "Item", [ - {item_code: "Screen"}, - {item_group: "Products"}, - {is_stock_item: is_stock_item}, - {standard_rate: screen_cost}, - {opening_stock: no_of_items_to_stock}, - {default_warehouse: "Stores - FT"} - ] - ), - - // Create a CPU item - () => frappe.tests.make( - "Item", [ - {item_code: "CPU"}, - {item_group: "Products"}, - {is_stock_item: is_stock_item}, - {standard_rate: CPU_cost}, - {opening_stock: no_of_items_to_stock}, - {default_warehouse: "Stores - FT"} - ] - ), - - // Create a laptop item - () => frappe.tests.make( - "Item", [ - {item_code: "Laptop"}, - {item_group: "Products"}, - {default_warehouse: "Stores - FT"} - ] - ), - () => frappe.tests.make( - "Item", [ - {item_code: "Computer"}, - {item_group: "Products"}, - {is_stock_item: 0}, - ] - ), - - // Create a scrap item - () => frappe.tests.make( - "Item", [ - {item_code: "Scrap item"}, - {item_group: "Products"}, - {is_stock_item: is_stock_item}, - {standard_rate: scrap_cost}, - {opening_stock: no_of_items_to_stock}, - {default_warehouse: "Stores - FT"} - ] - ), - () => frappe.tests.make( - "Item", [ - {item_code: "Test Product 4"}, - {item_group: "Products"}, - {is_stock_item: 1}, - {has_batch_no: 1}, - {create_new_batch: 1}, - {uoms: - [ - [ - {uom:"Unit"}, - {conversion_factor: 10}, - ] - ] - }, - {taxes: - [ - [ - {tax_type:"SGST - "+frappe.get_abbr(frappe.defaults.get_default("Company"))}, - {tax_rate: 0}, - ] - ]}, - {has_serial_no: 1}, - {standard_rate: 100}, - {opening_stock: 100}, - ] - ), - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/item_price/test_item_price.js b/erpnext/stock/doctype/item_price/test_item_price.js deleted file mode 100644 index 49dbaa2051..0000000000 --- a/erpnext/stock/doctype/item_price/test_item_price.js +++ /dev/null @@ -1,22 +0,0 @@ -QUnit.module('Stock'); - -QUnit.test("test item price", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Item Price', [ - {price_list:'Test-Selling-USD'}, - {item_code: 'Test Product 4'}, - {price_list_rate: 200} - ]); - }, - () => cur_frm.save(), - () => { - assert.ok(cur_frm.doc.item_name == 'Test Product 4', "Item name correct"); - assert.ok(cur_frm.doc.price_list_rate == 200, "Price list rate correct"); - }, - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/material_request/tests/test_material_request.js b/erpnext/stock/doctype/material_request/tests/test_material_request.js deleted file mode 100644 index a2cd03b649..0000000000 --- a/erpnext/stock/doctype/material_request/tests/test_material_request.js +++ /dev/null @@ -1,39 +0,0 @@ -QUnit.module('Stock'); - -QUnit.test("test material request", function(assert) { - assert.expect(5); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Material Request', [ - {items: [ - [ - {'schedule_date': frappe.datetime.add_days(frappe.datetime.nowdate(), 5)}, - {'qty': 5}, - {'item_code': 'Test Product 1'}, - ], - [ - {'schedule_date': frappe.datetime.add_days(frappe.datetime.nowdate(), 6)}, - {'qty': 2}, - {'item_code': 'Test Product 2'}, - ] - ]}, - ]); - }, - () => cur_frm.save(), - () => { - assert.ok(cur_frm.doc.schedule_date == frappe.datetime.add_days(frappe.datetime.now_date(), 5), "Schedule Date correct"); - - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); - assert.ok(cur_frm.doc.items[0].schedule_date == frappe.datetime.add_days(frappe.datetime.now_date(), 5), "Schedule Date correct"); - - assert.ok(cur_frm.doc.items[1].item_name=='Test Product 2', "Item name correct"); - assert.ok(cur_frm.doc.items[1].schedule_date == frappe.datetime.add_days(frappe.datetime.now_date(), 6), "Schedule Date correct"); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/material_request/tests/test_material_request_from_bom.js b/erpnext/stock/doctype/material_request/tests/test_material_request_from_bom.js deleted file mode 100644 index 6fb55ae02a..0000000000 --- a/erpnext/stock/doctype/material_request/tests/test_material_request_from_bom.js +++ /dev/null @@ -1,27 +0,0 @@ -QUnit.module('manufacturing'); - -QUnit.test("test material request get items from BOM", function(assert) { - assert.expect(4); - let done = assert.async(); - frappe.run_serially([ - () => frappe.set_route('Form', 'BOM'), - () => frappe.timeout(3), - () => frappe.click_button('Get Items from BOM'), - () => frappe.timeout(3), - () => { - assert.ok(cur_dialog, 'dialog appeared'); - }, - () => cur_dialog.set_value('bom', 'Laptop'), - () => cur_dialog.set_value('warehouse', 'Laptop Scrap Warehouse'), - () => frappe.click_button('Get Items from BOM'), - () => frappe.timeout(3), - () => { - assert.ok(cur_frm.doc.items[0].item_code, "First row is not empty"); - assert.ok(cur_frm.doc.items[0].item_name, "Item name is not empty"); - assert.equal(cur_frm.doc.items[0].item_name, "Laptop", cur_frm.doc.items[0].item_name); - }, - () => cur_frm.doc.items[0].schedule_date = '2017-12-12', - () => cur_frm.save(), - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/material_request/tests/test_material_request_type_manufacture.js b/erpnext/stock/doctype/material_request/tests/test_material_request_type_manufacture.js deleted file mode 100644 index 137079b983..0000000000 --- a/erpnext/stock/doctype/material_request/tests/test_material_request_type_manufacture.js +++ /dev/null @@ -1,29 +0,0 @@ -QUnit.module('Stock'); - -QUnit.test("test material request", function(assert) { - assert.expect(1); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Material Request', [ - {material_request_type:'Manufacture'}, - {items: [ - [ - {'schedule_date': frappe.datetime.add_days(frappe.datetime.nowdate(), 5)}, - {'qty': 5}, - {'item_code': 'Test Product 1'}, - ] - ]}, - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_issue.js b/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_issue.js deleted file mode 100644 index b03a8543c6..0000000000 --- a/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_issue.js +++ /dev/null @@ -1,29 +0,0 @@ -QUnit.module('Stock'); - -QUnit.test("test material request for issue", function(assert) { - assert.expect(1); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Material Request', [ - {material_request_type:'Material Issue'}, - {items: [ - [ - {'schedule_date': frappe.datetime.add_days(frappe.datetime.nowdate(), 5)}, - {'qty': 5}, - {'item_code': 'Test Product 1'}, - ] - ]}, - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_transfer.js b/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_transfer.js deleted file mode 100644 index 7c62c2e63a..0000000000 --- a/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_transfer.js +++ /dev/null @@ -1,29 +0,0 @@ -QUnit.module('Stock'); - -QUnit.test("test material request for transfer", function(assert) { - assert.expect(1); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Material Request', [ - {material_request_type:'Manufacture'}, - {items: [ - [ - {'schedule_date': frappe.datetime.add_days(frappe.datetime.nowdate(), 5)}, - {'qty': 5}, - {'item_code': 'Test Product 1'}, - ] - ]}, - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/price_list/test_price_list_uom.js b/erpnext/stock/doctype/price_list/test_price_list_uom.js deleted file mode 100644 index 3896c0e59e..0000000000 --- a/erpnext/stock/doctype/price_list/test_price_list_uom.js +++ /dev/null @@ -1,58 +0,0 @@ -QUnit.module('Price List'); - -QUnit.test("test price list with uom dependancy", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - - () => frappe.set_route('Form', 'Price List', 'Standard Buying'), - () => { - cur_frm.set_value('price_not_uom_dependent','1'); - frappe.timeout(1); - }, - () => cur_frm.save(), - - () => frappe.timeout(1), - - () => { - return frappe.tests.make('Item Price', [ - {price_list:'Standard Buying'}, - {item_code: 'Test Product 3'}, - {price_list_rate: 200} - ]); - }, - - () => cur_frm.save(), - - () => { - return frappe.tests.make('Purchase Order', [ - {supplier: 'Test Supplier'}, - {currency: 'INR'}, - {buying_price_list: 'Standard Buying'}, - {items: [ - [ - {"item_code": 'Test Product 3'}, - {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 2)}, - {"uom": 'Nos'}, - {"conversion_factor": 3} - ] - ]}, - - ]); - }, - - () => cur_frm.save(), - () => frappe.timeout(0.3), - - () => { - assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 3', "Item code correct"); - assert.ok(cur_frm.doc.items[0].price_list_rate == 200, "Price list rate correct"); - }, - - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(1), - - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.js deleted file mode 100644 index d1f448536b..0000000000 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.js +++ /dev/null @@ -1,42 +0,0 @@ -QUnit.module('Stock'); - -QUnit.test("test Purchase Receipt", function(assert) { - assert.expect(4); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Purchase Receipt', [ - {supplier: 'Test Supplier'}, - {items: [ - [ - {'received_qty': 5}, - {'qty': 4}, - {'item_code': 'Test Product 1'}, - {'uom': 'Nos'}, - {'warehouse':'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))}, - {'rejected_warehouse':'Work In Progress - '+frappe.get_abbr(frappe.defaults.get_default('Company'))}, - ] - ]}, - {taxes_and_charges: 'TEST In State GST - FT'}, - {tc_name: 'Test Term 1'}, - {terms: 'This is Test'} - ]); - }, - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); - // get tax details - assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct"); - // get tax account head details - assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct"); - // grand_total Calculated - assert.ok(cur_frm.doc.grand_total==472, "Grad Total correct"); - - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_manufacture.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_manufacture.js deleted file mode 100644 index e51c90cf51..0000000000 --- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_manufacture.js +++ /dev/null @@ -1,26 +0,0 @@ -QUnit.module('Stock'); - -QUnit.test("test manufacture from bom", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make("Stock Entry", [ - { purpose: "Manufacture" }, - { from_bom: 1 }, - { bom_no: "BOM-_Test Item - Non Whole UOM-001" }, - { fg_completed_qty: 2 } - ]); - }, - () => cur_frm.save(), - () => frappe.click_button("Update Rate and Availability"), - () => { - assert.ok(cur_frm.doc.items[1] === 0.75, " Finished Item Qty correct"); - assert.ok(cur_frm.doc.items[2] === 0.25, " Process Loss Item Qty correct"); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue.js deleted file mode 100644 index a87a7fb7fd..0000000000 --- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue.js +++ /dev/null @@ -1,30 +0,0 @@ -QUnit.module('Stock'); - -QUnit.test("test material request", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Stock Entry', [ - {from_warehouse:'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))}, - {items: [ - [ - {'item_code': 'Test Product 1'}, - {'qty': 5}, - ] - ]}, - ]); - }, - () => cur_frm.save(), - () => frappe.click_button('Update Rate and Availability'), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); - assert.ok(cur_frm.doc.total_outgoing_value==500, " Outgoing Value correct"); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue_with_serialize_item.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue_with_serialize_item.js deleted file mode 100644 index cae318d8f2..0000000000 --- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue_with_serialize_item.js +++ /dev/null @@ -1,34 +0,0 @@ -QUnit.module('Stock'); - -QUnit.test("test material issue", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Stock Entry', [ - {from_warehouse:'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))}, - {items: [ - [ - {'item_code': 'Test Product 4'}, - {'qty': 1}, - {'batch_no':'TEST-BATCH-001'}, - {'serial_no':'Test-Product-003'}, - {'basic_rate':100}, - ] - ]}, - ]); - }, - () => cur_frm.save(), - () => frappe.click_button('Close'), - () => frappe.click_button('Update Rate and Availability'), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct"); - assert.ok(cur_frm.doc.total_outgoing_value==100, " Outgoing Value correct"); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt.js deleted file mode 100644 index ef0286fe1b..0000000000 --- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt.js +++ /dev/null @@ -1,31 +0,0 @@ -QUnit.module('Stock'); - -QUnit.test("test material request", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Stock Entry', [ - {purpose:'Material Receipt'}, - {to_warehouse:'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))}, - {items: [ - [ - {'item_code': 'Test Product 1'}, - {'qty': 5}, - ] - ]}, - ]); - }, - () => cur_frm.save(), - () => frappe.click_button('Update Rate and Availability'), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); - assert.ok(cur_frm.doc.total_incoming_value==500, " Incoming Value correct"); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt_for_serialize_item.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt_for_serialize_item.js deleted file mode 100644 index 54e1ac8121..0000000000 --- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt_for_serialize_item.js +++ /dev/null @@ -1,34 +0,0 @@ -QUnit.module('Stock'); - -QUnit.test("test material receipt", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Stock Entry', [ - {purpose:'Material Receipt'}, - {to_warehouse:'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))}, - {items: [ - [ - {'item_code': 'Test Product 4'}, - {'qty': 5}, - {'batch_no':'TEST-BATCH-001'}, - {'serial_no':'Test-Product-001\nTest-Product-002\nTest-Product-003\nTest-Product-004\nTest-Product-005'}, - {'basic_rate':100}, - ] - ]}, - ]); - }, - () => cur_frm.save(), - () => frappe.click_button('Update Rate and Availability'), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct"); - assert.ok(cur_frm.doc.total_incoming_value==500, " Incoming Value correct"); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer.js deleted file mode 100644 index fac0b4b892..0000000000 --- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer.js +++ /dev/null @@ -1,33 +0,0 @@ -QUnit.module('Stock'); - -QUnit.test("test material request", function(assert) { - assert.expect(3); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Stock Entry', [ - {purpose:'Material Transfer'}, - {from_warehouse:'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))}, - {to_warehouse:'Work In Progress - '+frappe.get_abbr(frappe.defaults.get_default('Company'))}, - {items: [ - [ - {'item_code': 'Test Product 1'}, - {'qty': 5}, - ] - ]}, - ]); - }, - () => cur_frm.save(), - () => frappe.click_button('Update Rate and Availability'), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); - assert.ok(cur_frm.doc.total_outgoing_value==500, " Outgoing Value correct"); - assert.ok(cur_frm.doc.total_incoming_value==500, " Incoming Value correct"); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer_for_manufacture.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer_for_manufacture.js deleted file mode 100644 index 9f85307270..0000000000 --- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer_for_manufacture.js +++ /dev/null @@ -1,33 +0,0 @@ -QUnit.module('Stock'); - -QUnit.test("test material Transfer to manufacture", function(assert) { - assert.expect(3); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Stock Entry', [ - {purpose:'Material Transfer for Manufacture'}, - {from_warehouse:'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))}, - {to_warehouse:'Work In Progress - '+frappe.get_abbr(frappe.defaults.get_default('Company'))}, - {items: [ - [ - {'item_code': 'Test Product 1'}, - {'qty': 1}, - ] - ]}, - ]); - }, - () => cur_frm.save(), - () => frappe.click_button('Update Rate and Availability'), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); - assert.ok(cur_frm.doc.total_outgoing_value==100, " Outgoing Value correct"); - assert.ok(cur_frm.doc.total_incoming_value==100, " Incoming Value correct"); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_repack.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_repack.js deleted file mode 100644 index 20f119ad61..0000000000 --- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_repack.js +++ /dev/null @@ -1,41 +0,0 @@ -QUnit.module('Stock'); - -QUnit.test("test repack", function(assert) { - assert.expect(2); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Stock Entry', [ - {purpose:'Repack'}, - {items: [ - [ - {'item_code': 'Test Product 1'}, - {'qty': 1}, - {'s_warehouse':'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))}, - ], - [ - {'item_code': 'Test Product 2'}, - {'qty': 1}, - {'s_warehouse':'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))}, - ], - [ - {'item_code': 'Test Product 3'}, - {'qty': 1}, - {'t_warehouse':'Work In Progress - '+frappe.get_abbr(frappe.defaults.get_default('Company'))}, - ], - ]}, - ]); - }, - () => cur_frm.save(), - () => frappe.click_button('Update Rate and Availability'), - () => { - // get_item_details - assert.ok(cur_frm.doc.total_outgoing_value==250, " Outgoing Value correct"); - assert.ok(cur_frm.doc.total_incoming_value==250, " Incoming Value correct"); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_subcontract.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_subcontract.js deleted file mode 100644 index 8243426032..0000000000 --- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_subcontract.js +++ /dev/null @@ -1,33 +0,0 @@ -QUnit.module('Stock'); - -QUnit.test("test material Transfer to manufacture", function(assert) { - assert.expect(3); - let done = assert.async(); - frappe.run_serially([ - () => { - return frappe.tests.make('Stock Entry', [ - {purpose:'Send to Subcontractor'}, - {from_warehouse:'Work In Progress - '+frappe.get_abbr(frappe.defaults.get_default('Company'))}, - {to_warehouse:'Finished Goods - '+frappe.get_abbr(frappe.defaults.get_default('Company'))}, - {items: [ - [ - {'item_code': 'Test Product 1'}, - {'qty': 1}, - ] - ]}, - ]); - }, - () => cur_frm.save(), - () => frappe.click_button('Update Rate and Availability'), - () => { - // get_item_details - assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); - assert.ok(cur_frm.doc.total_outgoing_value==100, " Outgoing Value correct"); - assert.ok(cur_frm.doc.total_incoming_value==100, " Incoming Value correct"); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js deleted file mode 100644 index 666d2c7144..0000000000 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js +++ /dev/null @@ -1,31 +0,0 @@ -QUnit.module('Stock'); - -QUnit.test("test Stock Reconciliation", function(assert) { - assert.expect(1); - let done = assert.async(); - frappe.run_serially([ - () => frappe.set_route('List', 'Stock Reconciliation'), - () => frappe.timeout(1), - () => frappe.click_button('New'), - () => cur_frm.set_value('company','For Testing'), - () => frappe.click_button('Items'), - () => {cur_dialog.set_value('warehouse','Stores - FT'); }, - () => frappe.timeout(0.5), - () => frappe.click_button('Update'), - () => { - cur_frm.doc.items[0].qty = 150; - cur_frm.refresh_fields('items');}, - () => frappe.timeout(0.5), - () => cur_frm.set_value('expense_account','Stock Adjustment - FT'), - () => cur_frm.set_value('cost_center','Main - FT'), - () => cur_frm.save(), - () => { - // get_item_details - assert.ok(cur_frm.doc.expense_account=='Stock Adjustment - FT', "expense_account correct"); - }, - () => frappe.tests.click_button('Submit'), - () => frappe.tests.click_button('Yes'), - () => frappe.timeout(0.3), - () => done() - ]); -}); diff --git a/erpnext/stock/doctype/warehouse/test_warehouse.js b/erpnext/stock/doctype/warehouse/test_warehouse.js deleted file mode 100644 index 850da1ee45..0000000000 --- a/erpnext/stock/doctype/warehouse/test_warehouse.js +++ /dev/null @@ -1,19 +0,0 @@ -QUnit.test("test: warehouse", function (assert) { - assert.expect(0); - let done = assert.async(); - - frappe.run_serially([ - // test warehouse creation - () => frappe.set_route("List", "Warehouse"), - - // Create a Laptop Scrap Warehouse - () => frappe.tests.make( - "Warehouse", [ - {warehouse_name: "Laptop Scrap Warehouse"}, - {company: "For Testing"} - ] - ), - - () => done() - ]); -}); From 66a10c0e73174d9469517350758504af7a146b5c Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Mon, 20 Dec 2021 18:12:57 +0530 Subject: [PATCH 066/193] Merge pull request #28440 from anupamvs/crm-lead-validate-cleanup-develop fix: lead validate function cleanup --- erpnext/crm/doctype/lead/lead.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index 9adbe8b6f1..c31b068a43 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -8,7 +8,6 @@ from frappe.contacts.address_and_contact import load_address_and_contact from frappe.email.inbox import link_communication_to_document from frappe.model.mapper import get_mapped_doc from frappe.utils import ( - cint, comma_and, cstr, get_link_to_form, @@ -39,11 +38,7 @@ class Lead(SellingController): self.check_email_id_is_unique() self.validate_email_id() self.validate_contact_date() - self._prev = frappe._dict({ - "contact_date": frappe.db.get_value("Lead", self.name, "contact_date") if (not cint(self.is_new())) else None, - "ends_on": frappe.db.get_value("Lead", self.name, "ends_on") if (not cint(self.is_new())) else None, - "contact_by": frappe.db.get_value("Lead", self.name, "contact_by") if (not cint(self.is_new())) else None, - }) + self.set_prev() def set_full_name(self): if self.first_name: @@ -75,6 +70,16 @@ class Lead(SellingController): self.add_calendar_event() self.update_prospects() + def set_prev(self): + if self.is_new(): + self._prev = frappe._dict({ + "contact_date": None, + "ends_on": None, + "contact_by": None + }) + else: + self._prev = frappe.db.get_value("Lead", self.name, ["contact_date", "ends_on", "contact_by"], as_dict=1) + def before_insert(self): self.contact_doc = self.create_contact() From ff9a6e8e899a8a2e23a9cad0220abf130a5d5ced Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 20 Dec 2021 15:07:41 +0530 Subject: [PATCH 067/193] fix: correct bin qty on backdated transactions When making a backdated transactions current balance qty depends on evaluation of whole ledger inbetween, instead of doing that just fetch the last sle's qty_after_transaction when future transactions are detected against SLE fix: don't update bin's actual_qty 1. it's already updated by repost_current_voucher 2. update if future sle exists --- erpnext/stock/doctype/bin/bin.py | 24 +++++++++++++++++------- erpnext/stock/stock_ledger.py | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index 37b54116a4..0ef7ce2923 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -130,8 +130,8 @@ def update_stock(bin_name, args, allow_negative_stock=False, via_landed_cost_vou """WARNING: This function is deprecated. Inline this function instead of using it.""" from erpnext.stock.stock_ledger import repost_current_voucher - update_qty(bin_name, args) repost_current_voucher(args, allow_negative_stock, via_landed_cost_voucher) + update_qty(bin_name, args) def get_bin_details(bin_name): return frappe.db.get_value('Bin', bin_name, ['actual_qty', 'ordered_qty', @@ -139,13 +139,23 @@ def get_bin_details(bin_name): 'reserved_qty_for_sub_contract'], as_dict=1) def update_qty(bin_name, args): - bin_details = get_bin_details(bin_name) + from erpnext.controllers.stock_controller import future_sle_exists - # update the stock values (for current quantities) - if args.get("voucher_type")=="Stock Reconciliation": - actual_qty = args.get('qty_after_transaction') - else: - actual_qty = bin_details.actual_qty + flt(args.get("actual_qty")) + bin_details = get_bin_details(bin_name) + # actual qty is already updated by processing current voucher + actual_qty = bin_details.actual_qty + + # actual qty is not up to date in case of backdated transaction + if future_sle_exists(args): + actual_qty = frappe.db.get_value("Stock Ledger Entry", + filters={ + "item_code": args.get("item_code"), + "warehouse": args.get("warehouse"), + "is_cancelled": 0 + }, + fieldname="qty_after_transaction", + order_by="posting_date desc, posting_time desc, creation desc", + ) or 0.0 ordered_qty = flt(bin_details.ordered_qty) + flt(args.get("ordered_qty")) reserved_qty = flt(bin_details.reserved_qty) + flt(args.get("reserved_qty")) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index f45bee1650..107bb23222 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -65,8 +65,8 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc is_stock_item = frappe.get_cached_value('Item', args.get("item_code"), 'is_stock_item') if is_stock_item: bin_name = get_or_make_bin(args.get("item_code"), args.get("warehouse")) - update_bin_qty(bin_name, args) repost_current_voucher(args, allow_negative_stock, via_landed_cost_voucher) + update_bin_qty(bin_name, args) else: frappe.msgprint(_("Item {0} ignored since it is not a stock item").format(args.get("item_code"))) From 2bb7bca464bbc1a6f5eb417c89c3f521f68638ee Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 20 Dec 2021 15:41:19 +0530 Subject: [PATCH 068/193] test: bin qty on backdated SR --- .../doctype/work_order/test_work_order.py | 2 - .../repost_item_valuation.py | 5 ++- .../test_stock_reconciliation.py | 41 ++++++++++++++++++- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 86c687fb7c..9926b15894 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -199,8 +199,6 @@ class TestWorkOrder(ERPNextTestCase): # no change in reserved / projected self.assertEqual(cint(bin1_on_end_production.reserved_qty_for_production), cint(bin1_on_start_production.reserved_qty_for_production)) - self.assertEqual(cint(bin1_on_end_production.projected_qty), - cint(bin1_on_end_production.projected_qty)) def test_backflush_qty_for_overpduction_manufacture(self): cancel_stock_entry = [] diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py index b2ad07f9c3..fb3b355fb7 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -46,7 +46,7 @@ class RepostItemValuation(Document): self.db_set('status', self.status) def on_submit(self): - if not frappe.flags.in_test or self.flags.dont_run_in_test: + if not frappe.flags.in_test or self.flags.dont_run_in_test or frappe.flags.dont_execute_stock_reposts: return frappe.enqueue(repost, timeout=1800, queue='long', @@ -97,7 +97,8 @@ def repost(doc): return doc.set_status('In Progress') - frappe.db.commit() + if not frappe.flags.in_test: + frappe.db.commit() repost_sl_entries(doc) repost_gl_entries(doc) diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 48e339ae56..c4ddc9e2d6 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -24,11 +24,15 @@ from erpnext.tests.utils import ERPNextTestCase, change_settings class TestStockReconciliation(ERPNextTestCase): @classmethod - def setUpClass(self): + def setUpClass(cls): super().setUpClass() create_batch_or_serial_no_items() frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1) + def tearDown(self): + frappe.flags.dont_execute_stock_reposts = None + + def test_reco_for_fifo(self): self._test_reco_sle_gle("FIFO") @@ -392,6 +396,41 @@ class TestStockReconciliation(ERPNextTestCase): repost_exists = bool(frappe.db.exists("Repost Item Valuation", {"voucher_no": sr.name})) self.assertFalse(repost_exists, msg="Negative stock validation not working on reco cancellation") + def test_intermediate_sr_bin_update(self): + """Bin should show correct qty even for backdated entries. + + ------------------------------------------- + | creation | Var | Doc | Qty | balance qty + ------------------------------------------- + | 1 | SR | Reco | 10 | 10 (posting date: today+10) + | 3 | SR2 | Reco | 11 | 11 (posting date: today+11) + | 2 | DN | DN | 5 | 6 <-- assert in BIN (posting date: today+12) + """ + from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note + + # repost will make this test useless, qty should update in realtime without reposts + frappe.flags.dont_execute_stock_reposts = True + frappe.db.rollback() + + item_code = "Backdated-Reco-Cancellation-Item" + warehouse = "_Test Warehouse - _TC" + create_item(item_code) + + sr = create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=10, rate=100, + posting_date=add_days(nowdate(), 10)) + + dn = create_delivery_note(item_code=item_code, warehouse=warehouse, qty=5, rate=120, + posting_date=add_days(nowdate(), 12)) + old_bin_qty = frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "actual_qty") + + sr2 = create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=11, rate=100, + posting_date=add_days(nowdate(), 11)) + new_bin_qty = frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "actual_qty") + + self.assertEqual(old_bin_qty + 1, new_bin_qty) + frappe.db.rollback() + + def test_valid_batch(self): create_batch_item_with_batch("Testing Batch Item 1", "001") create_batch_item_with_batch("Testing Batch Item 2", "002") From 0f43792dbbc2ef8f4d5e2f288b61034049d09dae Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 20 Dec 2021 21:33:59 +0530 Subject: [PATCH 069/193] fix: Stock Ageing Report - Negative Opening Stock - Consider negative opening stock in logic and neutralise it with +ve stock - minor code refactor: class for FIFOSlots to generate chronological FIFO queue --- .../stock/report/stock_ageing/stock_ageing.py | 323 +++++++++++------- .../report/stock_balance/stock_balance.py | 4 +- ...rehouse_wise_item_balance_age_and_value.py | 4 +- 3 files changed, 206 insertions(+), 125 deletions(-) diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 0ebe4f903f..0136007ca4 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -3,6 +3,7 @@ from operator import itemgetter +from typing import Dict, List, Tuple, Union import frappe from frappe import _ @@ -10,19 +11,29 @@ from frappe.utils import cint, date_diff, flt from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos +Filters = frappe._dict -def execute(filters=None): - columns = get_columns(filters) - item_details = get_fifo_queue(filters) +def execute(filters: Filters =None) -> Tuple: to_date = filters["to_date"] - _func = itemgetter(1) + columns = get_columns(filters) + item_details = FIFOSlots(filters).generate() + data = format_report_data(filters, item_details, to_date) + + chart_data = get_chart_data(data, filters) + + return columns, data, None, chart_data + +def format_report_data(filters: Filters, item_details: Dict, to_date: str) -> List[Dict]: + "Returns ordered, formatted data with ranges." + _func = itemgetter(1) data = [] + for item, item_dict in item_details.items(): earliest_age, latest_age = 0, 0 + details = item_dict["details"] fifo_queue = sorted(filter(_func, item_dict["fifo_queue"]), key=_func) - details = item_dict["details"] if not fifo_queue: continue @@ -31,23 +42,22 @@ def execute(filters=None): latest_age = date_diff(to_date, fifo_queue[-1][1]) range1, range2, range3, above_range3 = get_range_age(filters, fifo_queue, to_date, item_dict) - row = [details.name, details.item_name, - details.description, details.item_group, details.brand] + row = [details.name, details.item_name, details.description, + details.item_group, details.brand] if filters.get("show_warehouse_wise_stock"): row.append(details.warehouse) row.extend([item_dict.get("total_qty"), average_age, range1, range2, range3, above_range3, - earliest_age, latest_age, details.stock_uom]) + earliest_age, latest_age, + details.stock_uom]) data.append(row) - chart_data = get_chart_data(data, filters) + return data - return columns, data, None, chart_data - -def get_average_age(fifo_queue, to_date): +def get_average_age(fifo_queue: List, to_date: str) -> float: batch_age = age_qty = total_qty = 0.0 for batch in fifo_queue: batch_age = date_diff(to_date, batch[1]) @@ -61,7 +71,7 @@ def get_average_age(fifo_queue, to_date): return flt(age_qty / total_qty, 2) if total_qty else 0.0 -def get_range_age(filters, fifo_queue, to_date, item_dict): +def get_range_age(filters: Filters, fifo_queue: List, to_date: str, item_dict: Dict) -> Tuple: range1 = range2 = range3 = above_range3 = 0.0 for item in fifo_queue: @@ -79,7 +89,7 @@ def get_range_age(filters, fifo_queue, to_date, item_dict): return range1, range2, range3, above_range3 -def get_columns(filters): +def get_columns(filters: Filters) -> List[Dict]: range_columns = [] setup_ageing_columns(filters, range_columns) columns = [ @@ -164,106 +174,7 @@ def get_columns(filters): return columns -def get_fifo_queue(filters, sle=None): - item_details = {} - transferred_item_details = {} - serial_no_batch_purchase_details = {} - - if sle == None: - sle = get_stock_ledger_entries(filters) - - for d in sle: - key = (d.name, d.warehouse) if filters.get('show_warehouse_wise_stock') else d.name - item_details.setdefault(key, {"details": d, "fifo_queue": []}) - fifo_queue = item_details[key]["fifo_queue"] - - transferred_item_key = (d.voucher_no, d.name, d.warehouse) - transferred_item_details.setdefault(transferred_item_key, []) - - if d.voucher_type == "Stock Reconciliation": - d.actual_qty = flt(d.qty_after_transaction) - flt(item_details[key].get("qty_after_transaction", 0)) - - serial_no_list = get_serial_nos(d.serial_no) if d.serial_no else [] - - if d.actual_qty > 0: - if transferred_item_details.get(transferred_item_key): - batch = transferred_item_details[transferred_item_key][0] - fifo_queue.append(batch) - transferred_item_details[transferred_item_key].pop(0) - else: - if serial_no_list: - for serial_no in serial_no_list: - if serial_no_batch_purchase_details.get(serial_no): - fifo_queue.append([serial_no, serial_no_batch_purchase_details.get(serial_no)]) - else: - serial_no_batch_purchase_details.setdefault(serial_no, d.posting_date) - fifo_queue.append([serial_no, d.posting_date]) - else: - fifo_queue.append([d.actual_qty, d.posting_date]) - else: - if serial_no_list: - fifo_queue[:] = [serial_no for serial_no in fifo_queue if serial_no[0] not in serial_no_list] - else: - qty_to_pop = abs(d.actual_qty) - while qty_to_pop: - batch = fifo_queue[0] if fifo_queue else [0, None] - 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 -= flt(batch[0]) - transferred_item_details[transferred_item_key].append(fifo_queue.pop(0)) - else: - # all from current batch - batch[0] = flt(batch[0]) - qty_to_pop - transferred_item_details[transferred_item_key].append([qty_to_pop, batch[1]]) - qty_to_pop = 0 - - item_details[key]["qty_after_transaction"] = d.qty_after_transaction - - if "total_qty" not in item_details[key]: - item_details[key]["total_qty"] = d.actual_qty - else: - item_details[key]["total_qty"] += d.actual_qty - - item_details[key]["has_serial_no"] = d.has_serial_no - - return item_details - -def get_stock_ledger_entries(filters): - return frappe.db.sql("""select - item.name, item.item_name, item_group, brand, description, item.stock_uom, item.has_serial_no, - actual_qty, posting_date, voucher_type, voucher_no, serial_no, batch_no, qty_after_transaction, warehouse - from `tabStock Ledger Entry` sle, - (select name, item_name, description, stock_uom, brand, item_group, has_serial_no - from `tabItem` {item_conditions}) item - where item_code = item.name and - company = %(company)s and - posting_date <= %(to_date)s and - is_cancelled != 1 - {sle_conditions} - order by posting_date, posting_time, sle.creation, actual_qty""" #nosec - .format(item_conditions=get_item_conditions(filters), - sle_conditions=get_sle_conditions(filters)), filters, as_dict=True) - -def get_item_conditions(filters): - conditions = [] - if filters.get("item_code"): - conditions.append("item_code=%(item_code)s") - if filters.get("brand"): - conditions.append("brand=%(brand)s") - - return "where {}".format(" and ".join(conditions)) if conditions else "" - -def get_sle_conditions(filters): - conditions = [] - if filters.get("warehouse"): - lft, rgt = frappe.db.get_value('Warehouse', filters.get("warehouse"), ['lft', 'rgt']) - conditions.append("""warehouse in (select wh.name from `tabWarehouse` wh - where wh.lft >= {0} and rgt <= {1})""".format(lft, rgt)) - - return "and {}".format(" and ".join(conditions)) if conditions else "" - -def get_chart_data(data, filters): +def get_chart_data(data: List, filters: Filters) -> Dict: if not data: return [] @@ -294,17 +205,187 @@ def get_chart_data(data, filters): "type" : "bar" } -def setup_ageing_columns(filters, range_columns): - for i, label in enumerate(["0-{range1}".format(range1=filters["range1"]), - "{range1}-{range2}".format(range1=cint(filters["range1"])+ 1, range2=filters["range2"]), - "{range2}-{range3}".format(range2=cint(filters["range2"])+ 1, range3=filters["range3"]), - "{range3}-{above}".format(range3=cint(filters["range3"])+ 1, above=_("Above"))]): - add_column(range_columns, label="Age ("+ label +")", fieldname='range' + str(i+1)) +def setup_ageing_columns(filters: Filters, range_columns: List): + ranges = [ + f"0 - {filters['range1']}", + f"{cint(filters['range1']) + 1} - {cint(filters['range2'])}", + f"{cint(filters['range2']) + 1} - {cint(filters['range3'])}", + f"{cint(filters['range3']) + 1} - {_('Above')}" + ] + for i, label in enumerate(ranges): + fieldname = 'range' + str(i+1) + add_column(range_columns, label=f"Age ({label})",fieldname=fieldname) -def add_column(range_columns, label, fieldname, fieldtype='Float', width=140): +def add_column(range_columns: List, label:str, fieldname: str, fieldtype: str ='Float', width: int =140): range_columns.append(dict( label=label, fieldname=fieldname, fieldtype=fieldtype, width=width )) + + +class FIFOSlots: + "Returns FIFO computed slots of inwarded stock as per date." + + def __init__(self, filters: Dict =None , sle: List =None): + self.item_details = {} + self.transferred_item_details = {} + self.serial_no_batch_purchase_details = {} + self.filters = filters + self.sle = sle + + def generate(self) -> Dict: + """ + Returns dict of the foll.g structure: + Key = Item A / (Item A, Warehouse A) + Key: { + 'details' -> Dict: ** item details **, + 'fifo_queue' -> List: ** list of lists containing entries/slots for existing stock, + consumed/updated and maintained via FIFO. ** + } + """ + if self.sle == None: + self.sle = self.__get_stock_ledger_entries() + + for d in self.sle: + key, fifo_queue, transferred_item_key = self.__init_key_stores(d) + + if d.voucher_type == "Stock Reconciliation": + prev_balance_qty = self.item_details[key].get("qty_after_transaction", 0) + d.actual_qty = flt(d.qty_after_transaction) - flt(prev_balance_qty) + + serial_nos = get_serial_nos(d.serial_no) if d.serial_no else [] + + if d.actual_qty > 0: + self.__compute_incoming_stock(d, fifo_queue, transferred_item_key, serial_nos) + else: + self.__compute_outgoing_stock(d, fifo_queue, transferred_item_key, serial_nos) + + self.__update_balances(d, key) + + return self.item_details + + def __init_key_stores(self, row: Dict) -> Tuple: + "Initialise keys and FIFO Queue." + + key = (row.name, row.warehouse) if self.filters.get('show_warehouse_wise_stock') else row.name + self.item_details.setdefault(key, {"details": row, "fifo_queue": []}) + fifo_queue = self.item_details[key]["fifo_queue"] + + transferred_item_key = (row.voucher_no, row.name, row.warehouse) + self.transferred_item_details.setdefault(transferred_item_key, []) + + return key, fifo_queue, transferred_item_key + + def __compute_incoming_stock(self, row: Dict, fifo_queue: List, transfer_key: Tuple, serial_nos: List): + "Update FIFO Queue on inward stock." + + if self.transferred_item_details.get(transfer_key): + # inward/outward from same voucher, item & warehouse + slot = self.transferred_item_details[transfer_key].pop(0) + fifo_queue.append(slot) + else: + if not serial_nos: + if fifo_queue and fifo_queue[0][0] < 0: + # neutralize negative stock by adding positive stock + fifo_queue[0][0] += flt(row.actual_qty) + fifo_queue[0][1] = row.posting_date + else: + fifo_queue.append([row.actual_qty, row.posting_date]) + return + + for serial_no in serial_nos: + if self.serial_no_batch_purchase_details.get(serial_no): + fifo_queue.append([serial_no, self.serial_no_batch_purchase_details.get(serial_no)]) + else: + self.serial_no_batch_purchase_details.setdefault(serial_no, row.posting_date) + fifo_queue.append([serial_no, row.posting_date]) + + def __compute_outgoing_stock(self, row: Dict, fifo_queue: List, transfer_key: Tuple, serial_nos: List): + "Update FIFO Queue on outward stock." + if serial_nos: + fifo_queue[:] = [serial_no for serial_no in fifo_queue if serial_no[0] not in serial_nos] + return + + qty_to_pop = abs(row.actual_qty) + while qty_to_pop: + slot = fifo_queue[0] if fifo_queue else [0, None] + if 0 < flt(slot[0]) <= qty_to_pop: + # qty to pop >= slot qty + # if +ve and not enough or exactly same balance in current slot, consume whole slot + qty_to_pop -= flt(slot[0]) + self.transferred_item_details[transfer_key].append(fifo_queue.pop(0)) + elif not fifo_queue: + # negative stock, no balance but qty yet to consume + fifo_queue.append([-(qty_to_pop), row.posting_date]) + self.transferred_item_details[transfer_key].append([row.actual_qty, row.posting_date]) + qty_to_pop = 0 + else: + # qty to pop < slot qty, ample balance + # consume actual_qty from first slot + slot[0] = flt(slot[0]) - qty_to_pop + self.transferred_item_details[transfer_key].append([qty_to_pop, slot[1]]) + qty_to_pop = 0 + + def __update_balances(self, row: Dict, key: Union[Tuple, str]): + self.item_details[key]["qty_after_transaction"] = row.qty_after_transaction + + if "total_qty" not in self.item_details[key]: + self.item_details[key]["total_qty"] = row.actual_qty + else: + self.item_details[key]["total_qty"] += row.actual_qty + + self.item_details[key]["has_serial_no"] = row.has_serial_no + + def __get_stock_ledger_entries(self) -> List[Dict]: + return frappe.db.sql(""" + select + item.name, item.item_name, item_group, brand, description, + item.stock_uom, item.has_serial_no, + actual_qty, posting_date, voucher_type, voucher_no, + serial_no, batch_no, qty_after_transaction, warehouse + from + `tabStock Ledger Entry` sle, + ( + select name, item_name, description, stock_uom, + brand, item_group, has_serial_no + from `tabItem` {item_conditions} + ) item + where + item_code = item.name and + company = %(company)s and + posting_date <= %(to_date)s and + is_cancelled != 1 + {sle_conditions} + order by posting_date, posting_time, sle.creation, actual_qty""" #nosec + .format( + item_conditions=self.__get_item_conditions(), + sle_conditions=self.__get_sle_conditions() + ), + self.filters, + as_dict=True + ) + + def __get_item_conditions(self) -> str: + conditions = [] + if self.filters.get("item_code"): + conditions.append("item_code=%(item_code)s") + if self.filters.get("brand"): + conditions.append("brand=%(brand)s") + + return "where {}".format(" and ".join(conditions)) if conditions else "" + + def __get_sle_conditions(self) -> str: + conditions = [] + + if self.filters.get("warehouse"): + lft, rgt = frappe.db.get_value("Warehouse", self.filters.get("warehouse"), ['lft', 'rgt']) + conditions.append(""" + warehouse in ( + select wh.name from `tabWarehouse` wh + where wh.lft >= {0} and rgt <= {1} + ) + """.format(lft, rgt)) + + return "and {}".format(" and ".join(conditions)) if conditions else "" \ No newline at end of file diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index 3c7b26bb1b..c0dc3e8910 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -9,7 +9,7 @@ from frappe import _ from frappe.utils import cint, date_diff, flt, getdate import erpnext -from erpnext.stock.report.stock_ageing.stock_ageing import get_average_age, get_fifo_queue +from erpnext.stock.report.stock_ageing.stock_ageing import get_average_age, FIFOSlots from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition from erpnext.stock.utils import add_additional_uom_columns, is_reposting_item_valuation_in_progress @@ -33,7 +33,7 @@ def execute(filters=None): if filters.get('show_stock_ageing_data'): filters['show_warehouse_wise_stock'] = True - item_wise_fifo_queue = get_fifo_queue(filters, sle) + item_wise_fifo_queue = FIFOSlots().generate(filters, sle) # if no stock ledger entry found return if not sle: diff --git a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py index 4d1491b1b5..bd4e235e14 100644 --- a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py +++ b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py @@ -9,7 +9,7 @@ import frappe from frappe import _ from frappe.utils import flt -from erpnext.stock.report.stock_ageing.stock_ageing import get_average_age, get_fifo_queue +from erpnext.stock.report.stock_ageing.stock_ageing import get_average_age, FIFOSlots from erpnext.stock.report.stock_balance.stock_balance import ( get_item_details, get_item_warehouse_map, @@ -33,7 +33,7 @@ def execute(filters=None): item_map = get_item_details(items, sle, filters) iwb_map = get_item_warehouse_map(filters, sle) warehouse_list = get_warehouse_list(filters) - item_ageing = get_fifo_queue(filters) + item_ageing = FIFOSlots().generate(filters) data = [] item_balance = {} item_value = {} From 8951a5c2675bf9903b7b1fb3a4d241ca96456474 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 20 Dec 2021 21:53:47 +0530 Subject: [PATCH 070/193] fix: Linter (imports) --- erpnext/stock/report/stock_balance/stock_balance.py | 2 +- .../warehouse_wise_item_balance_age_and_value.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index c0dc3e8910..44d52f74bd 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -9,7 +9,7 @@ from frappe import _ from frappe.utils import cint, date_diff, flt, getdate import erpnext -from erpnext.stock.report.stock_ageing.stock_ageing import get_average_age, FIFOSlots +from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots, get_average_age from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition from erpnext.stock.utils import add_additional_uom_columns, is_reposting_item_valuation_in_progress diff --git a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py index bd4e235e14..e5003ee025 100644 --- a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py +++ b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py @@ -9,7 +9,7 @@ import frappe from frappe import _ from frappe.utils import flt -from erpnext.stock.report.stock_ageing.stock_ageing import get_average_age, FIFOSlots +from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots, get_average_age from erpnext.stock.report.stock_balance.stock_balance import ( get_item_details, get_item_warehouse_map, From d7148adee502a160e460720b4a7d9b54ae880cbf Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 20 Dec 2021 22:00:31 +0530 Subject: [PATCH 071/193] test: remove unnecessary creation of companies #28965 test: remove unnecessary creation of companies --- erpnext/__init__.py | 4 +-- .../test_opening_invoice_creation_tool.py | 2 +- .../setup/doctype/company/test_records.json | 6 ++-- .../stock/doctype/shipment/test_shipment.py | 29 ++++--------------- erpnext/tests/test_init.py | 9 ++---- 5 files changed, 14 insertions(+), 36 deletions(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index a5de50fb06..0b4696c803 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -55,9 +55,9 @@ def set_perpetual_inventory(enable=1, company=None): company.enable_perpetual_inventory = enable company.save() -def encode_company_abbr(name, company): +def encode_company_abbr(name, company=None, abbr=None): '''Returns name encoded with company abbreviation''' - company_abbr = frappe.get_cached_value('Company', company, "abbr") + company_abbr = abbr or frappe.get_cached_value('Company', company, "abbr") parts = name.rsplit(" - ", 1) if parts[-1].lower() != company_abbr.lower(): diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py index 07c72bde7b..b5aae9845b 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/test_opening_invoice_creation_tool.py @@ -148,7 +148,7 @@ def make_company(): company.company_name = "_Test Opening Invoice Company" company.abbr = "_TOIC" company.default_currency = "INR" - company.country = "India" + company.country = "Pakistan" company.insert() return company diff --git a/erpnext/setup/doctype/company/test_records.json b/erpnext/setup/doctype/company/test_records.json index 9e55702ddc..89be607d04 100644 --- a/erpnext/setup/doctype/company/test_records.json +++ b/erpnext/setup/doctype/company/test_records.json @@ -36,7 +36,7 @@ "abbr": "_TC3", "company_name": "_Test Company 3", "is_group": 1, - "country": "India", + "country": "Pakistan", "default_currency": "INR", "doctype": "Company", "domain": "Manufacturing", @@ -49,7 +49,7 @@ "company_name": "_Test Company 4", "parent_company": "_Test Company 3", "is_group": 1, - "country": "India", + "country": "Pakistan", "default_currency": "INR", "doctype": "Company", "domain": "Manufacturing", @@ -61,7 +61,7 @@ "abbr": "_TC5", "company_name": "_Test Company 5", "parent_company": "_Test Company 4", - "country": "India", + "country": "Pakistan", "default_currency": "INR", "doctype": "Company", "domain": "Manufacturing", diff --git a/erpnext/stock/doctype/shipment/test_shipment.py b/erpnext/stock/doctype/shipment/test_shipment.py index 705b2651f6..afe821845a 100644 --- a/erpnext/stock/doctype/shipment/test_shipment.py +++ b/erpnext/stock/doctype/shipment/test_shipment.py @@ -39,9 +39,9 @@ def create_test_delivery_note(): "description": 'Test delivery note for shipment', "qty": 5, "uom": 'Nos', - "warehouse": 'Stores - SC', + "warehouse": 'Stores - _TC', "rate": item.standard_rate, - "cost_center": 'Main - SC' + "cost_center": 'Main - _TC' } ) delivery_note.insert() @@ -127,13 +127,7 @@ def get_shipment_company_address(company_name): return create_shipment_address(address_title, company_name, 80331) def get_shipment_company(): - company_name = 'Shipment Company' - abbr = 'SC' - companies = frappe.get_all("Company", fields=["name"], filters = {"company_name": company_name}) - if len(companies): - return companies[0] - else: - return create_shipment_company(company_name, abbr) + return frappe.get_doc("Company", "_Test Company") def get_shipment_item(company_name): item_name = 'Testing Shipment item' @@ -182,17 +176,6 @@ def create_customer_contact(fname, lname): customer.insert() return customer - -def create_shipment_company(company_name, abbr): - company = frappe.new_doc("Company") - company.company_name = company_name - company.abbr = abbr - company.default_currency = 'EUR' - company.country = 'Germany' - company.enable_perpetual_inventory = 0 - company.insert() - return company - def create_shipment_customer(customer_name): customer = frappe.new_doc("Customer") customer.customer_name = customer_name @@ -211,12 +194,12 @@ def create_material_receipt(item, company): stock.posting_date = posting_date.strftime("%Y-%m-%d") stock.append('items', { - "t_warehouse": 'Stores - SC', + "t_warehouse": 'Stores - _TC', "item_code": item.name, "qty": 5, "uom": 'Nos', "basic_rate": item.standard_rate, - "cost_center": 'Main - SC' + "cost_center": 'Main - _TC' } ) stock.insert() @@ -233,7 +216,7 @@ def create_shipment_item(item_name, company_name): item.append('item_defaults', { "company": company_name, - "default_warehouse": 'Stores - SC' + "default_warehouse": 'Stores - _TC' } ) item.insert() diff --git a/erpnext/tests/test_init.py b/erpnext/tests/test_init.py index 36a9bf5e37..61849726ef 100644 --- a/erpnext/tests/test_init.py +++ b/erpnext/tests/test_init.py @@ -8,13 +8,8 @@ test_records = frappe.get_test_records('Company') class TestInit(unittest.TestCase): def test_encode_company_abbr(self): - company = frappe.new_doc("Company") - company.company_name = "New from Existing Company For Test" - company.abbr = "NFECT" - company.default_currency = "INR" - company.save() - abbr = company.abbr + abbr = "NFECT" names = [ "Warehouse Name", "ERPNext Foundation India", "Gold - Member - {a}".format(a=abbr), @@ -32,7 +27,7 @@ class TestInit(unittest.TestCase): ] for i in range(len(names)): - enc_name = encode_company_abbr(names[i], company.name) + enc_name = encode_company_abbr(names[i], abbr=abbr) self.assertTrue( enc_name == expected_names[i], "{enc} is not same as {exp}".format(enc=enc_name, exp=expected_names[i]) From 92e86f2f09aa7157d114ccde5e94853ef07546ea Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 20 Dec 2021 22:56:15 +0530 Subject: [PATCH 072/193] fix: fetch appointment letter content in the same order as template (#28968) --- .../appointment_letter/appointment_letter.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/erpnext/hr/doctype/appointment_letter/appointment_letter.py b/erpnext/hr/doctype/appointment_letter/appointment_letter.py index 0120188d31..71327bf1b0 100644 --- a/erpnext/hr/doctype/appointment_letter/appointment_letter.py +++ b/erpnext/hr/doctype/appointment_letter/appointment_letter.py @@ -12,14 +12,15 @@ class AppointmentLetter(Document): @frappe.whitelist() def get_appointment_letter_details(template): body = [] - intro= frappe.get_list("Appointment Letter Template", - fields = ['introduction', 'closing_notes'], - filters={'name': template - })[0] - content = frappe.get_list("Appointment Letter content", - fields = ['title', 'description'], - filters={'parent': template - }) + intro = frappe.get_list('Appointment Letter Template', + fields=['introduction', 'closing_notes'], + filters={'name': template} + )[0] + content = frappe.get_all('Appointment Letter content', + fields=['title', 'description'], + filters={'parent': template}, + order_by='idx' + ) body.append(intro) body.append({'description': content}) return body From e4ee55c9b9c9afc97097237615d6b87b50161616 Mon Sep 17 00:00:00 2001 From: Anupam Date: Tue, 21 Dec 2021 11:26:53 +0530 Subject: [PATCH 073/193] fix: test case --- erpnext/crm/doctype/opportunity/test_opportunity.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.py b/erpnext/crm/doctype/opportunity/test_opportunity.py index 0b3d4e0a0a..be5e5a21a0 100644 --- a/erpnext/crm/doctype/opportunity/test_opportunity.py +++ b/erpnext/crm/doctype/opportunity/test_opportunity.py @@ -49,7 +49,6 @@ class TestOpportunity(unittest.TestCase): contact.add_email(opp_doc.contact_email, is_primary=True) contact.insert(ignore_permissions=True) - opp_doc = frappe.get_doc(args).insert(ignore_permissions=True) self.assertTrue(opp_doc.party_name) self.assertEqual(opp_doc.opportunity_from, "Customer") self.assertEqual(opp_doc.party_name, customer.name) From 98d417602f77700b93f8eccd8d93ca27a5571887 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Tue, 21 Dec 2021 12:35:44 +0530 Subject: [PATCH 074/193] fix: Added filter for dispatch address (#28937) --- erpnext/public/js/queries.js | 7 +++++++ erpnext/selling/sales_common.js | 1 + 2 files changed, 8 insertions(+) diff --git a/erpnext/public/js/queries.js b/erpnext/public/js/queries.js index b635adcd44..b7d880ae40 100644 --- a/erpnext/public/js/queries.js +++ b/erpnext/public/js/queries.js @@ -83,6 +83,13 @@ $.extend(erpnext.queries, { }; }, + dispatch_address_query: function(doc) { + return { + query: 'frappe.contacts.doctype.address.address.address_query', + filters: { link_doctype: 'Company', link_name: doc.company || '' } + }; + }, + supplier_filter: function(doc) { if(!doc.supplier) { frappe.throw(__("Please set {0}", [__(frappe.meta.get_label(doc.doctype, "supplier", doc.name))])); diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index e2e0db4044..540aca234b 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -41,6 +41,7 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran me.frm.set_query('contact_person', erpnext.queries.contact_query); me.frm.set_query('customer_address', erpnext.queries.address_query); me.frm.set_query('shipping_address_name', erpnext.queries.address_query); + me.frm.set_query('dispatch_address_name', erpnext.queries.dispatch_address_query); if(this.frm.fields_dict.selling_price_list) { From 24a35c69c00565ecd6bf91120a9544c0fb7d5ef4 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 21 Dec 2021 12:32:55 +0530 Subject: [PATCH 075/193] fix: Sider and Server side test - args passed to wrong function - missing space around '=' --- erpnext/stock/report/stock_ageing/stock_ageing.py | 15 ++++++++------- .../stock/report/stock_balance/stock_balance.py | 2 +- .../warehouse_wise_item_balance_age_and_value.py | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 0136007ca4..75e235ac05 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -13,7 +13,7 @@ from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos Filters = frappe._dict -def execute(filters: Filters =None) -> Tuple: +def execute(filters: Filters = None) -> Tuple: to_date = filters["to_date"] columns = get_columns(filters) @@ -213,10 +213,10 @@ def setup_ageing_columns(filters: Filters, range_columns: List): f"{cint(filters['range3']) + 1} - {_('Above')}" ] for i, label in enumerate(ranges): - fieldname = 'range' + str(i+1) - add_column(range_columns, label=f"Age ({label})",fieldname=fieldname) + fieldname = 'range' + str(i+1) + add_column(range_columns, label=f"Age ({label})",fieldname=fieldname) -def add_column(range_columns: List, label:str, fieldname: str, fieldtype: str ='Float', width: int =140): +def add_column(range_columns: List, label:str, fieldname: str, fieldtype: str = 'Float', width: int = 140): range_columns.append(dict( label=label, fieldname=fieldname, @@ -228,7 +228,7 @@ def add_column(range_columns: List, label:str, fieldname: str, fieldtype: str =' class FIFOSlots: "Returns FIFO computed slots of inwarded stock as per date." - def __init__(self, filters: Dict =None , sle: List =None): + def __init__(self, filters: Dict = None , sle: List = None): self.item_details = {} self.transferred_item_details = {} self.serial_no_batch_purchase_details = {} @@ -245,7 +245,7 @@ class FIFOSlots: consumed/updated and maintained via FIFO. ** } """ - if self.sle == None: + if self.sle is None: self.sle = self.__get_stock_ledger_entries() for d in self.sle: @@ -358,7 +358,8 @@ class FIFOSlots: posting_date <= %(to_date)s and is_cancelled != 1 {sle_conditions} - order by posting_date, posting_time, sle.creation, actual_qty""" #nosec + order by posting_date, posting_time, sle.creation, actual_qty + """ #nosec .format( item_conditions=self.__get_item_conditions(), sle_conditions=self.__get_sle_conditions() diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index 44d52f74bd..b4f43a7fef 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -33,7 +33,7 @@ def execute(filters=None): if filters.get('show_stock_ageing_data'): filters['show_warehouse_wise_stock'] = True - item_wise_fifo_queue = FIFOSlots().generate(filters, sle) + item_wise_fifo_queue = FIFOSlots(filters, sle).generate() # if no stock ledger entry found return if not sle: diff --git a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py index e5003ee025..22bdb89198 100644 --- a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py +++ b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py @@ -33,7 +33,7 @@ def execute(filters=None): item_map = get_item_details(items, sle, filters) iwb_map = get_item_warehouse_map(filters, sle) warehouse_list = get_warehouse_list(filters) - item_ageing = FIFOSlots().generate(filters) + item_ageing = FIFOSlots(filters).generate() data = [] item_balance = {} item_value = {} From 107b4055604ee99fa53ee78522ee0a9bb0a21855 Mon Sep 17 00:00:00 2001 From: Anupam Date: Tue, 21 Dec 2021 13:35:15 +0530 Subject: [PATCH 076/193] fix: test case --- erpnext/crm/doctype/opportunity/test_opportunity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.py b/erpnext/crm/doctype/opportunity/test_opportunity.py index be5e5a21a0..2611e555bd 100644 --- a/erpnext/crm/doctype/opportunity/test_opportunity.py +++ b/erpnext/crm/doctype/opportunity/test_opportunity.py @@ -50,7 +50,7 @@ class TestOpportunity(unittest.TestCase): contact.insert(ignore_permissions=True) self.assertTrue(opp_doc.party_name) - self.assertEqual(opp_doc.opportunity_from, "Customer") + self.assertEqual(opp_doc.opportunity_from, "Lead") self.assertEqual(opp_doc.party_name, customer.name) def test_opportunity_item(self): From 12a65eef8a75ae8350eb7487dff2738fc489f2ab Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 17 Dec 2021 15:59:21 +0530 Subject: [PATCH 077/193] fix: Is Reverse Charge check in Tax Category (cherry picked from commit b33fd6acc769dbfaa43c665c19f378e8e041d010) # Conflicts: # erpnext/patches.txt --- erpnext/patches.txt | 7 ++++- .../v13_0/update_tax_category_for_rcm.py | 31 +++++++++++++++++++ erpnext/regional/india/setup.py | 4 ++- erpnext/regional/india/utils.py | 5 +-- 4 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 erpnext/patches/v13_0/update_tax_category_for_rcm.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d9cedab52a..8e74daa051 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -316,5 +316,10 @@ erpnext.patches.v13_0.create_ksa_vat_custom_fields erpnext.patches.v14_0.rename_ongoing_status_in_sla_documents erpnext.patches.v14_0.migrate_crm_settings erpnext.patches.v13_0.rename_ksa_qr_field +<<<<<<< HEAD erpnext.patches.v13_0.disable_ksa_print_format_for_others # 16-12-2021 -erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template \ No newline at end of file +erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template +======= +erpnext.patches.v13_0.disable_ksa_print_format_for_others +erpnext.patches.v13_0.update_tax_category_for_rcm #1 +>>>>>>> b33fd6acc7 (fix: Is Reverse Charge check in Tax Category) diff --git a/erpnext/patches/v13_0/update_tax_category_for_rcm.py b/erpnext/patches/v13_0/update_tax_category_for_rcm.py new file mode 100644 index 0000000000..7af2366bf0 --- /dev/null +++ b/erpnext/patches/v13_0/update_tax_category_for_rcm.py @@ -0,0 +1,31 @@ +import frappe +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields + +from erpnext.regional.india import states + + +def execute(): + company = frappe.get_all('Company', filters = {'country': 'India'}) + if not company: + return + + create_custom_fields({ + 'Tax Category': [ + dict(fieldname='is_inter_state', label='Is Inter State', + fieldtype='Check', insert_after='disabled', print_hide=1), + dict(fieldname='is_reverse_charge', label='Is Reverse Charge', fieldtype='Check', + insert_after='is_inter_state', print_hide=1), + dict(fieldname='tax_category_column_break', fieldtype='Column Break', + insert_after='is_reverse_charge'), + dict(fieldname='gst_state', label='Source State', fieldtype='Select', + options='\n'.join(states), insert_after='company') + ] + }, update=True) + + tax_category = frappe.qb.DocType("Tax Category") + + frappe.qb.update(tax_category).set( + tax_category.is_reverse_charge, 1 + ).where( + tax_category.name.isin(['Reverse Charge Out-State', 'Reverse Charge In-State']) + ).run() \ No newline at end of file diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 5865424028..c0dcb70b92 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -277,8 +277,10 @@ def get_custom_fields(): inter_state_gst_field = [ dict(fieldname='is_inter_state', label='Is Inter State', fieldtype='Check', insert_after='disabled', print_hide=1), + dict(fieldname='is_reverse_charge', label='Is Reverse Charge', fieldtype='Check', + insert_after='is_inter_state', print_hide=1), dict(fieldname='tax_category_column_break', fieldtype='Column Break', - insert_after='is_inter_state'), + insert_after='is_reverse_charge'), dict(fieldname='gst_state', label='Source State', fieldtype='Select', options='\n'.join(states), insert_after='company') ] diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index fd3ec3c08c..215b483c7a 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -67,7 +67,8 @@ def validate_pan_for_india(doc, method): frappe.throw(_("Invalid PAN No. The input you've entered doesn't match the format of PAN.")) def validate_tax_category(doc, method): - if doc.get('gst_state') and frappe.db.get_value('Tax Category', {'gst_state': doc.gst_state, 'is_inter_state': doc.is_inter_state}): + if doc.get('gst_state') and frappe.db.get_value('Tax Category', {'gst_state': doc.gst_state, 'is_inter_state': doc.is_inter_state, + 'is_reverse_charge': doc.is_reverse_charge}): if doc.is_inter_state: frappe.throw(_("Inter State tax category for GST State {0} already exists").format(doc.gst_state)) else: @@ -264,7 +265,7 @@ def get_tax_template_based_on_category(master_doctype, company, party_details): def get_tax_template(master_doctype, company, is_inter_state, state_code): tax_categories = frappe.get_all('Tax Category', fields = ['name', 'is_inter_state', 'gst_state'], - filters = {'is_inter_state': is_inter_state}) + filters = {'is_inter_state': is_inter_state, 'is_reverse_charge': 0}) default_tax = '' From cbef04fdedc6766e2a4373c98b5c4b16bebbfa25 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 17 Dec 2021 16:02:40 +0530 Subject: [PATCH 078/193] chore: Remove patch comment (cherry picked from commit 7c1bfe6b46ac3deedacfa666d0695b53b86ec3f6) # Conflicts: # erpnext/patches.txt --- erpnext/patches.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 8e74daa051..407d68384b 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -321,5 +321,9 @@ erpnext.patches.v13_0.disable_ksa_print_format_for_others # 16-12-2021 erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template ======= erpnext.patches.v13_0.disable_ksa_print_format_for_others +<<<<<<< HEAD erpnext.patches.v13_0.update_tax_category_for_rcm #1 >>>>>>> b33fd6acc7 (fix: Is Reverse Charge check in Tax Category) +======= +erpnext.patches.v13_0.update_tax_category_for_rcm +>>>>>>> 7c1bfe6b46 (chore: Remove patch comment) From 98ac47caa4720ba87b593df0c97c84ec115327a3 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 21 Dec 2021 12:54:40 +0530 Subject: [PATCH 079/193] fix: Add is reverse charge in country wise tax (cherry picked from commit 7e912db4b13ee2b1b88a68fc6110eb527a0375d5) --- erpnext/setup/setup_wizard/data/country_wise_tax.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/setup/setup_wizard/data/country_wise_tax.json b/erpnext/setup/setup_wizard/data/country_wise_tax.json index 14b79510c1..91e8eff89f 100644 --- a/erpnext/setup/setup_wizard/data/country_wise_tax.json +++ b/erpnext/setup/setup_wizard/data/country_wise_tax.json @@ -1178,11 +1178,13 @@ { "title": "Reverse Charge In-State", "is_inter_state": 0, + "is_reverse_charge": 1, "gst_state": "" }, { "title": "Reverse Charge Out-State", "is_inter_state": 1, + "is_reverse_charge": 1, "gst_state": "" }, { From 1ed30ee7c7f2f270989ab65ed79e60f06d0a48d0 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Tue, 21 Dec 2021 13:59:53 +0530 Subject: [PATCH 080/193] fix: Reset value_after_depreciation on reversing journal entry during Asset return --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 64712b550f..321b45323f 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1049,6 +1049,8 @@ class SalesInvoice(SellingController): frappe.flags.is_reverse_depr_entry = False asset.flags.ignore_validate_update_after_submit = True schedule.journal_entry = None + depreciation_amount = self.get_depreciation_amount_in_je(reverse_journal_entry) + asset.finance_books[0].value_after_depreciation += depreciation_amount asset.save() def get_posting_date_of_sales_invoice(self): @@ -1071,6 +1073,12 @@ class SalesInvoice(SellingController): return False + def get_depreciation_amount_in_je(self, journal_entry): + if journal_entry.accounts[0].debit_in_account_currency: + return journal_entry.accounts[0].debit_in_account_currency + else: + return journal_entry.accounts[0].credit_in_account_currency + @property def enable_discount_accounting(self): if not hasattr(self, "_enable_discount_accounting"): From 2abbf20da17ab10e1b3f2ee537e023b9271d2f08 Mon Sep 17 00:00:00 2001 From: Anupam Date: Tue, 21 Dec 2021 14:13:40 +0530 Subject: [PATCH 081/193] fix: test case --- erpnext/crm/doctype/opportunity/test_opportunity.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.py b/erpnext/crm/doctype/opportunity/test_opportunity.py index 2611e555bd..db44b6a3d5 100644 --- a/erpnext/crm/doctype/opportunity/test_opportunity.py +++ b/erpnext/crm/doctype/opportunity/test_opportunity.py @@ -49,10 +49,6 @@ class TestOpportunity(unittest.TestCase): contact.add_email(opp_doc.contact_email, is_primary=True) contact.insert(ignore_permissions=True) - self.assertTrue(opp_doc.party_name) - self.assertEqual(opp_doc.opportunity_from, "Lead") - self.assertEqual(opp_doc.party_name, customer.name) - def test_opportunity_item(self): opportunity_doc = make_opportunity(with_items=1, rate=1100, qty=2) self.assertEqual(opportunity_doc.total, 2200) From 076cb408db6577d3901e339d298064760f47edb0 Mon Sep 17 00:00:00 2001 From: Development for People <47140294+developmentforpeople@users.noreply.github.com> Date: Tue, 21 Dec 2021 10:18:31 +0000 Subject: [PATCH 082/193] fix: missed colon (#28979) --- erpnext/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 9ceb6267a7..1d11f20ab7 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -374,7 +374,7 @@ scheduler_events = { "erpnext.selling.doctype.quotation.quotation.set_expired_status", "erpnext.buying.doctype.supplier_quotation.supplier_quotation.set_expired_status", "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_auto_email", - "erpnext.non_profit.doctype.membership.membership.set_expired_status" + "erpnext.non_profit.doctype.membership.membership.set_expired_status", "erpnext.hr.doctype.interview.interview.send_daily_feedback_reminder" ], "daily_long": [ From 9c8f5235374d19a03a8f7f03e0ca73a7040568b7 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 21 Dec 2021 17:01:10 +0530 Subject: [PATCH 083/193] fix: typo --- erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py index 666e3f3e40..7d31e0aa19 100644 --- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py @@ -521,7 +521,7 @@ class TestPOSInvoice(unittest.TestCase): rounded_total = frappe.db.get_value("Sales Invoice", pos_inv2.consolidated_invoice, "rounded_total") self.assertEqual(rounded_total, 400) - def test_pos_batch_ietm_qty_validation(self): + def test_pos_batch_item_qty_validation(self): from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import ( create_batch_item_with_batch, ) From c3a9ca09d15893ca48e405617a9a5bc215ae92ba Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 21 Dec 2021 17:05:36 +0530 Subject: [PATCH 084/193] chore: remove extra whitespace --- erpnext/accounts/doctype/pos_invoice/pos_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index f99b87d047..6cda6266e4 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -142,7 +142,7 @@ class POSInvoice(SalesInvoice): frappe.throw(_("Row #{}: Batch No. {} of item {} has no stock available. Please select valid batch no.") .format(item.idx, bold_invalid_batch_no, bold_item_name), title=_("Item Unavailable")) elif (available_batch_qty - reserved_batch_qty - item.qty) < 0: - frappe.throw(_("Row #{}: Batch No. {} of item {} has less than required stock available, {} more required") + frappe.throw(_("Row #{}: Batch No. {} of item {} has less than required stock available, {} more required") .format(item.idx, bold_invalid_batch_no, bold_item_name, bold_extra_batch_qty_needed), title=_("Item Unavailable")) def validate_delivered_serial_nos(self, item): From db7aef2fef14a09053dfffe443b9099dae21c202 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 21 Dec 2021 17:39:27 +0530 Subject: [PATCH 085/193] style: better labels for SLE fields (#28988) * style: better labels for SLE fields * style: rename stock queue field [skip ci] Co-Authored-by: marination --- .../stock_ledger_entry/stock_ledger_entry.json | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) 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 2651407d16..46ce9debf3 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json @@ -150,7 +150,7 @@ "fieldtype": "Float", "in_filter": 1, "in_list_view": 1, - "label": "Actual Quantity", + "label": "Qty Change", "oldfieldname": "actual_qty", "oldfieldtype": "Currency", "print_width": "150px", @@ -189,7 +189,7 @@ "fieldname": "qty_after_transaction", "fieldtype": "Float", "in_filter": 1, - "label": "Actual Qty After Transaction", + "label": "Qty After Transaction", "oldfieldname": "bin_aqat", "oldfieldtype": "Currency", "print_width": "150px", @@ -210,7 +210,7 @@ { "fieldname": "stock_value", "fieldtype": "Currency", - "label": "Stock Value", + "label": "Balance Stock Value", "oldfieldname": "stock_value", "oldfieldtype": "Currency", "options": "Company:company:default_currency", @@ -219,14 +219,14 @@ { "fieldname": "stock_value_difference", "fieldtype": "Currency", - "label": "Stock Value Difference", + "label": "Change in Stock Value", "options": "Company:company:default_currency", "read_only": 1 }, { "fieldname": "stock_queue", "fieldtype": "Text", - "label": "Stock Queue (FIFO)", + "label": "FIFO Stock Queue (qty, rate)", "oldfieldname": "fcfs_stack", "oldfieldtype": "Text", "print_hide": 1, @@ -317,10 +317,11 @@ "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2021-10-08 13:42:51.857631", + "modified": "2021-12-21 06:25:30.040801", "modified_by": "Administrator", "module": "Stock", "name": "Stock Ledger Entry", + "naming_rule": "Expression (old style)", "owner": "Administrator", "permissions": [ { @@ -338,5 +339,6 @@ } ], "sort_field": "modified", - "sort_order": "DESC" -} + "sort_order": "DESC", + "states": [] +} \ No newline at end of file From 8dc1ce12d4d3590b4496d5aa3f25e9ee89abba89 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 22 Dec 2021 12:12:24 +0530 Subject: [PATCH 086/193] chore: simplify bug report form --- .github/ISSUE_TEMPLATE/bug_report.yaml | 27 ++++---------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index a6e16a03d8..8f938112a7 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -24,20 +24,6 @@ body: validations: required: true - - type: dropdown - id: version - attributes: - label: Version - description: Affected versions. - multiple: true - options: - - v12 - - v13 - - v14 - - develop - validations: - required: true - - type: dropdown id: module attributes: @@ -86,7 +72,7 @@ body: - manual install - FrappeCloud validations: - required: true + required: false - type: textarea id: logs @@ -95,12 +81,7 @@ body: description: Please copy and paste any relevant log output. This will be automatically formatted. render: shell - - - type: checkboxes - id: terms + - type: markdown attributes: - label: Code of Conduct - description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/frappe/erpnext/blob/develop/CODE_OF_CONDUCT.md) - options: - - label: I agree to follow this project's Code of Conduct - required: true + value: | + By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/frappe/erpnext/blob/develop/CODE_OF_CONDUCT.md) From 04ea42ce5267c7ae9d5d286631d938c106116ede Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 22 Dec 2021 13:23:42 +0530 Subject: [PATCH 087/193] fix(sales-invoice): cannot create debit note with zero qty --- erpnext/controllers/taxes_and_totals.py | 2 ++ erpnext/public/js/controllers/taxes_and_totals.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 746c6fd9a4..03f3a11416 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -139,6 +139,8 @@ class calculate_taxes_and_totals(object): if not item.qty and self.doc.get("is_return"): item.amount = flt(-1 * item.rate, item.precision("amount")) + elif not item.qty and self.doc.get("is_debit_note"): + item.amount = flt(item.rate, item.precision("amount")) else: item.amount = flt(item.rate * item.qty, item.precision("amount")) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 7c1c8c7e46..956fdca09e 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -114,6 +114,8 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { if ((!item.qty) && me.frm.doc.is_return) { item.amount = flt(item.rate * -1, precision("amount", item)); + } else if ((!item.qty) && me.frm.doc.is_debit_note) { + item.amount = flt(item.rate, precision("amount", item)); } else { item.amount = flt(item.rate * item.qty, precision("amount", item)); } From f6d55346276a81ed5d6d2425cb98015903968ece Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 22 Dec 2021 19:05:18 +0530 Subject: [PATCH 088/193] fix: set resolution by only if not on hold (#28995) --- erpnext/support/doctype/issue/test_issue.py | 1 + .../service_level_agreement/service_level_agreement.py | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py index 14cec46ad4..7a0a5e506f 100644 --- a/erpnext/support/doctype/issue/test_issue.py +++ b/erpnext/support/doctype/issue/test_issue.py @@ -98,6 +98,7 @@ class TestIssue(TestSetUp): issue.save() self.assertEqual(issue.on_hold_since, frappe.flags.current_time) + self.assertFalse(issue.resolution_by) creation = get_datetime("2020-03-04 5:00") frappe.flags.current_time = get_datetime("2020-03-04 5:00") diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index c94700bdc5..b3348f1e1e 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -476,7 +476,7 @@ def update_response_and_resolution_metrics(doc, apply_sla_for_resolution): priority = get_response_and_resolution_duration(doc) start_date_time = get_datetime(doc.get("service_level_agreement_creation") or doc.creation) set_response_by(doc, start_date_time, priority) - if apply_sla_for_resolution: + if apply_sla_for_resolution and not doc.get('on_hold_since'): # resolution_by is reset if on hold set_resolution_by(doc, start_date_time, priority) @@ -624,9 +624,6 @@ def reset_resolution_metrics(doc): if doc.meta.has_field("user_resolution_time"): doc.user_resolution_time = None - if doc.meta.has_field("agreement_status"): - doc.agreement_status = "First Response Due" - # called via hooks on communication update def on_communication_update(doc, status): From 2be5104848d367fc7e916ab9c3c10d7146338cd6 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 23 Dec 2021 11:52:10 +0530 Subject: [PATCH 089/193] fix: cannot save debit note with zero quantity --- erpnext/controllers/accounts_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 2c92820a74..85325ac6ec 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -112,7 +112,7 @@ class AccountsController(TransactionBase): _('{0} is blocked so this transaction cannot proceed').format(supplier_name), raise_exception=1) def validate(self): - if not self.get('is_return'): + if not self.get('is_return') and not self.get('is_debit_note'): self.validate_qty_is_not_zero() if self.get("_action") and self._action != "update_after_submit": From 7ad149f9fec06cd730d9734f3515608c1e4c1ed8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 23 Dec 2021 14:39:20 +0530 Subject: [PATCH 090/193] fix: Start date validation for deferred invoices --- 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 2c92820a74..c862774060 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -184,6 +184,8 @@ class AccountsController(TransactionBase): frappe.throw(_("Row #{0}: Service Start Date cannot be greater than Service End Date").format(d.idx)) elif getdate(self.posting_date) > getdate(d.service_end_date): frappe.throw(_("Row #{0}: Service End Date cannot be before Invoice Posting Date").format(d.idx)) + elif getdate(self.posting_date) > getdate(d.service_start_date): + frappe.throw(_("Row #{0}: Service Start Date cannot be before Invoice Posting Date").format(d.idx)) def validate_invoice_documents_schedule(self): self.validate_payment_schedule_dates() From ae929d7a63352e05a70c31ac182611f8c9fb89fb Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 23 Dec 2021 19:18:05 +0530 Subject: [PATCH 091/193] fix(test): Leave Allocation validation against Leave Application after submit (#29005) * fix(test): Leave Allocation validation against Leave Application after submit * chore: clean-up Leave Allocation tests * fix(test): set holiday list for leave allocation test --- .../leave_allocation/test_leave_allocation.py | 72 +++++++++---------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py index 46401a2dd8..6dbe2eca32 100644 --- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py @@ -4,6 +4,7 @@ import frappe from frappe.utils import add_days, add_months, getdate, nowdate import erpnext +from erpnext.hr.doctype.employee.test_employee import make_employee from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type @@ -11,18 +12,25 @@ from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type class TestLeaveAllocation(unittest.TestCase): @classmethod def setUpClass(cls): + from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list + frappe.db.sql("delete from `tabLeave Period`") + emp_id = make_employee("test_emp_leave_allocation@salary.com") + cls.employee = frappe.get_doc("Employee", emp_id) + + make_holiday_list() + frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List") + + def tearDown(self): + frappe.db.rollback() def test_overlapping_allocation(self): - frappe.db.sql("delete from `tabLeave Allocation`") - - employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0]) leaves = [ { "doctype": "Leave Allocation", "__islocal": 1, - "employee": employee.name, - "employee_name": employee.employee_name, + "employee": self.employee.name, + "employee_name": self.employee.employee_name, "leave_type": "_Test Leave Type", "from_date": getdate("2015-10-01"), "to_date": getdate("2015-10-31"), @@ -32,8 +40,8 @@ class TestLeaveAllocation(unittest.TestCase): { "doctype": "Leave Allocation", "__islocal": 1, - "employee": employee.name, - "employee_name": employee.employee_name, + "employee": self.employee.name, + "employee_name": self.employee.employee_name, "leave_type": "_Test Leave Type", "from_date": getdate("2015-09-01"), "to_date": getdate("2015-11-30"), @@ -45,40 +53,36 @@ class TestLeaveAllocation(unittest.TestCase): self.assertRaises(frappe.ValidationError, frappe.get_doc(leaves[1]).save) def test_invalid_period(self): - employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0]) - doc = frappe.get_doc({ "doctype": "Leave Allocation", "__islocal": 1, - "employee": employee.name, - "employee_name": employee.employee_name, + "employee": self.employee.name, + "employee_name": self.employee.employee_name, "leave_type": "_Test Leave Type", "from_date": getdate("2015-09-30"), "to_date": getdate("2015-09-1"), "new_leaves_allocated": 5 }) - #invalid period + # invalid period self.assertRaises(frappe.ValidationError, doc.save) def test_allocated_leave_days_over_period(self): - employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0]) doc = frappe.get_doc({ "doctype": "Leave Allocation", "__islocal": 1, - "employee": employee.name, - "employee_name": employee.employee_name, + "employee": self.employee.name, + "employee_name": self.employee.employee_name, "leave_type": "_Test Leave Type", "from_date": getdate("2015-09-1"), "to_date": getdate("2015-09-30"), "new_leaves_allocated": 35 }) - #allocated leave more than period + + # allocated leave more than period self.assertRaises(frappe.ValidationError, doc.save) def test_carry_forward_calculation(self): - frappe.db.sql("delete from `tabLeave Allocation`") - frappe.db.sql("delete from `tabLeave Ledger Entry`") leave_type = create_leave_type(leave_type_name="_Test_CF_leave", is_carry_forward=1) leave_type.maximum_carry_forwarded_leaves = 10 leave_type.max_leaves_allowed = 30 @@ -114,8 +118,6 @@ class TestLeaveAllocation(unittest.TestCase): self.assertEqual(leave_allocation_2.unused_leaves, 5) def test_carry_forward_leaves_expiry(self): - frappe.db.sql("delete from `tabLeave Allocation`") - frappe.db.sql("delete from `tabLeave Ledger Entry`") leave_type = create_leave_type( leave_type_name="_Test_CF_leave_expiry", is_carry_forward=1, @@ -151,8 +153,6 @@ class TestLeaveAllocation(unittest.TestCase): self.assertEqual(leave_allocation_1.unused_leaves, leave_allocation.new_leaves_allocated) def test_creation_of_leave_ledger_entry_on_submit(self): - frappe.db.sql("delete from `tabLeave Allocation`") - leave_allocation = create_leave_allocation() leave_allocation.submit() @@ -168,9 +168,6 @@ class TestLeaveAllocation(unittest.TestCase): self.assertFalse(frappe.db.exists("Leave Ledger Entry", {'transaction_name':leave_allocation.name})) def test_leave_addition_after_submit(self): - frappe.db.sql("delete from `tabLeave Allocation`") - frappe.db.sql("delete from `tabLeave Ledger Entry`") - leave_allocation = create_leave_allocation() leave_allocation.submit() self.assertTrue(leave_allocation.total_leaves_allocated, 15) @@ -179,8 +176,6 @@ class TestLeaveAllocation(unittest.TestCase): self.assertTrue(leave_allocation.total_leaves_allocated, 40) def test_leave_subtraction_after_submit(self): - frappe.db.sql("delete from `tabLeave Allocation`") - frappe.db.sql("delete from `tabLeave Ledger Entry`") leave_allocation = create_leave_allocation() leave_allocation.submit() self.assertTrue(leave_allocation.total_leaves_allocated, 15) @@ -188,17 +183,14 @@ class TestLeaveAllocation(unittest.TestCase): leave_allocation.submit() self.assertTrue(leave_allocation.total_leaves_allocated, 10) - def test_against_leave_application_validation_after_submit(self): - frappe.db.sql("delete from `tabLeave Allocation`") - frappe.db.sql("delete from `tabLeave Ledger Entry`") - + def test_validation_against_leave_application_after_submit(self): leave_allocation = create_leave_allocation() leave_allocation.submit() self.assertTrue(leave_allocation.total_leaves_allocated, 15) - employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0]) + leave_application = frappe.get_doc({ "doctype": 'Leave Application', - "employee": employee.name, + "employee": self.employee.name, "leave_type": "_Test Leave Type", "from_date": add_months(nowdate(), 2), "to_date": add_months(add_days(nowdate(), 10), 2), @@ -208,15 +200,20 @@ class TestLeaveAllocation(unittest.TestCase): "leave_approver": 'test@example.com' }) leave_application.submit() - leave_allocation.new_leaves_allocated = 8 - leave_allocation.total_leaves_allocated = 8 + leave_application.reload() + + # allocate less leaves than the ones which are already approved + leave_allocation.new_leaves_allocated = leave_application.total_leave_days - 1 + leave_allocation.total_leaves_allocated = leave_application.total_leave_days - 1 self.assertRaises(frappe.ValidationError, leave_allocation.submit) def create_leave_allocation(**args): args = frappe._dict(args) - employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0]) - leave_allocation = frappe.get_doc({ + emp_id = make_employee("test_emp_leave_allocation@salary.com") + employee = frappe.get_doc("Employee", emp_id) + + return frappe.get_doc({ "doctype": "Leave Allocation", "__islocal": 1, "employee": args.employee or employee.name, @@ -227,6 +224,5 @@ def create_leave_allocation(**args): "carry_forward": args.carry_forward or 0, "to_date": args.to_date or add_months(nowdate(), 12) }) - return leave_allocation test_dependencies = ["Employee", "Leave Type"] From 7987a4650947a2ac551b9b76fdca6bb5b4836295 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 23 Dec 2021 21:13:22 +0530 Subject: [PATCH 092/193] chore: add running stock value difference in invariant report (#29012) [skip ci] --- .../stock_ledger_invariant_check.js | 3 ++- .../stock_ledger_invariant_check.py | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.js b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.js index c484516a16..31f389f236 100644 --- a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.js +++ b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.js @@ -8,7 +8,8 @@ const DIFFERNCE_FIELD_NAMES = [ "fifo_value_diff", "fifo_valuation_diff", "valuation_diff", - "fifo_difference_diff" + "fifo_difference_diff", + "diff_value_diff" ]; frappe.query_reports["Stock Ledger Invariant Check"] = { diff --git a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py index ca47a1ec5b..48753b0edd 100644 --- a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py +++ b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py @@ -50,6 +50,7 @@ def get_stock_ledger_entries(filters): def add_invariant_check_fields(sles): balance_qty = 0.0 + balance_stock_value = 0.0 for idx, sle in enumerate(sles): queue = json.loads(sle.stock_queue) @@ -60,6 +61,7 @@ def add_invariant_check_fields(sles): fifo_value += qty * rate balance_qty += sle.actual_qty + balance_stock_value += sle.stock_value_difference if sle.voucher_type == "Stock Reconciliation" and not sle.batch_no: balance_qty = sle.qty_after_transaction @@ -70,6 +72,7 @@ def add_invariant_check_fields(sles): sle.stock_value / sle.qty_after_transaction if sle.qty_after_transaction else None ) sle.expected_qty_after_transaction = balance_qty + sle.stock_value_from_diff = balance_stock_value # set difference fields sle.difference_in_qty = sle.qty_after_transaction - sle.expected_qty_after_transaction @@ -81,6 +84,7 @@ def add_invariant_check_fields(sles): sle.valuation_diff = ( sle.valuation_rate - sle.balance_value_by_qty if sle.balance_value_by_qty else None ) + sle.diff_value_diff = sle.stock_value_from_diff - sle.stock_value if idx > 0: sle.fifo_stock_diff = sle.fifo_stock_value - sles[idx - 1].fifo_stock_value @@ -191,12 +195,21 @@ def get_columns(): "fieldtype": "Float", "label": "D - E", }, - { "fieldname": "stock_value_difference", "fieldtype": "Float", "label": "(F) Stock Value Difference", }, + { + "fieldname": "stock_value_from_diff", + "fieldtype": "Float", + "label": "Balance Stock Value using (F)", + }, + { + "fieldname": "diff_value_diff", + "fieldtype": "Float", + "label": "K - D", + }, { "fieldname": "fifo_stock_diff", "fieldtype": "Float", From e7ac774bbc753901abeb74b62881e710ebb14516 Mon Sep 17 00:00:00 2001 From: Anupam Date: Fri, 24 Dec 2021 14:12:53 +0530 Subject: [PATCH 093/193] fix: review chnages --- erpnext/crm/doctype/crm_settings/crm_settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/crm_settings/crm_settings.json b/erpnext/crm/doctype/crm_settings/crm_settings.json index c15f559ab3..a2a19b9e79 100644 --- a/erpnext/crm/doctype/crm_settings/crm_settings.json +++ b/erpnext/crm/doctype/crm_settings/crm_settings.json @@ -95,7 +95,7 @@ }, { "default": "0", - "description": "All the comments and Emails will be copied from one document to another newly created document(Lead -> Opportunity -> Quotation) throughout the sales cycle.", + "description": "All the Comments and Emails will be copied from one document to another newly created document(Lead -> Opportunity -> Quotation) throughout the CRM documents.", "fieldname": "carry_forward_communication_and_comments", "fieldtype": "Check", "label": "Carry Forward Communication and Comments" From a6d6de6c4f8a7ba676294bc94cf122d7a82f020c Mon Sep 17 00:00:00 2001 From: Anupam Date: Fri, 24 Dec 2021 15:34:24 +0530 Subject: [PATCH 094/193] fix: grouping project form custom buttons --- erpnext/projects/doctype/project/project.js | 44 +++++++++++++++------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index 31460f66ea..dc7cf81c76 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -59,22 +59,16 @@ frappe.ui.form.on("Project", { frm.trigger('show_dashboard'); } - frm.events.set_buttons(frm); + frm.trigger("set_custom_buttons"); }, - set_buttons: function(frm) { + set_custom_buttons: function(frm) { if (!frm.is_new()) { frm.add_custom_button(__('Duplicate Project with Tasks'), () => { frm.events.create_duplicate(frm); - }); + }, __("Actions")); - frm.add_custom_button(__('Completed'), () => { - frm.events.set_status(frm, 'Completed'); - }, __('Set Status')); - - frm.add_custom_button(__('Cancelled'), () => { - frm.events.set_status(frm, 'Cancelled'); - }, __('Set Status')); + frm.trigger("set_project_status_button"); if (frappe.model.can_read("Task")) { @@ -83,7 +77,7 @@ frappe.ui.form.on("Project", { "project": frm.doc.name }; frappe.set_route("List", "Task", "Gantt"); - }); + }, __("View")); frm.add_custom_button(__("Kanban Board"), () => { frappe.call('erpnext.projects.doctype.project.project.create_kanban_board_if_not_exists', { @@ -91,13 +85,35 @@ frappe.ui.form.on("Project", { }).then(() => { frappe.set_route('List', 'Task', 'Kanban', frm.doc.project_name); }); - }); + }, __("View")); } } }, + set_project_status_button: function(frm) { + frm.add_custom_button(__('Set Project Status'), () => { + let d = new frappe.ui.Dialog({ + "title": "Set Project Status", + "fields": [ + { + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "reqd": 1, + "options": "Completed\nCancelled", + }, + ], + primary_action: function() { + frm.events.set_status(frm, d.get_values().status); + d.hide(); + }, + primary_action_label: __("Set Project Status") + }).show(); + }, __("Actions")); + }, + create_duplicate: function(frm) { return new Promise(resolve => { frappe.prompt('Project Name', (data) => { @@ -117,7 +133,9 @@ frappe.ui.form.on("Project", { set_status: function(frm, status) { frappe.confirm(__('Set Project and all Tasks to status {0}?', [status.bold()]), () => { frappe.xcall('erpnext.projects.doctype.project.project.set_project_status', - {project: frm.doc.name, status: status}).then(() => { /* page will auto reload */ }); + {project: frm.doc.name, status: status}).then(() => { + frm.reload_doc() + }); }); }, From f152a40b2876fb30fc2d47a9944300c03a28776e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 24 Dec 2021 16:23:33 +0530 Subject: [PATCH 095/193] feat: Provision to assign multiple payroll cost centers against a single employee --- erpnext/hr/doctype/department/department.js | 9 ++++ erpnext/hr/doctype/employee/employee.js | 9 ++++ erpnext/hr/doctype/employee/employee.py | 18 +++++--- erpnext/patches.txt | 3 +- .../patches/v14_0/set_payroll_cost_centers.py | 31 +++++++++++++ .../doctype/employee_cost_center/__init__.py | 0 .../employee_cost_center.json | 43 ++++++++++++++++++ .../employee_cost_center.py | 8 ++++ .../doctype/payroll_entry/payroll_entry.py | 45 ++++++++++++++++--- .../payroll_entry/test_payroll_entry.py | 33 ++++++++++---- .../doctype/salary_slip/salary_slip.json | 12 +---- .../salary_structure/salary_structure.py | 5 +-- .../salary_structure_assignment.js | 25 ++++++----- .../salary_structure_assignment.json | 25 +++++++++-- .../salary_structure_assignment.py | 31 ++++++++++++- 15 files changed, 244 insertions(+), 53 deletions(-) create mode 100644 erpnext/patches/v14_0/set_payroll_cost_centers.py create mode 100644 erpnext/payroll/doctype/employee_cost_center/__init__.py create mode 100644 erpnext/payroll/doctype/employee_cost_center/employee_cost_center.json create mode 100644 erpnext/payroll/doctype/employee_cost_center/employee_cost_center.py diff --git a/erpnext/hr/doctype/department/department.js b/erpnext/hr/doctype/department/department.js index 7db8cfbd60..eb759a323e 100644 --- a/erpnext/hr/doctype/department/department.js +++ b/erpnext/hr/doctype/department/department.js @@ -6,6 +6,15 @@ frappe.ui.form.on('Department', { frm.set_query("parent_department", function(){ return {"filters": [["Department", "is_group", "=", 1]]}; }); + + frm.set_query("payroll_cost_center", function() { + return { + filters: { + "company": frm.doc.company, + "is_group": 0 + } + } + }); }, refresh: function(frm) { // read-only for root department diff --git a/erpnext/hr/doctype/employee/employee.js b/erpnext/hr/doctype/employee/employee.js index 13b33e2e74..4181e0f8bb 100755 --- a/erpnext/hr/doctype/employee/employee.js +++ b/erpnext/hr/doctype/employee/employee.js @@ -47,6 +47,15 @@ frappe.ui.form.on('Employee', { } }; }); + + frm.set_query("payroll_cost_center", function() { + return { + filters: { + "company": frm.doc.company, + "is_group": 0 + } + } + }); }, onload: function (frm) { frm.set_query("department", function() { diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index 88e5ca9d4c..a2df26c3e2 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -68,12 +68,18 @@ class Employee(NestedSet): self.employee_name = ' '.join(filter(lambda x: x, [self.first_name, self.middle_name, self.last_name])) def validate_user_details(self): - data = frappe.db.get_value('User', - self.user_id, ['enabled', 'user_image'], as_dict=1) - if data.get("user_image") and self.image == '': - self.image = data.get("user_image") - self.validate_for_enabled_user_id(data.get("enabled", 0)) - self.validate_duplicate_user_id() + if self.user_id: + data = frappe.db.get_value('User', + self.user_id, ['enabled', 'user_image'], as_dict=1) + + if not data: + self.user_id = None + return + + if data.get("user_image") and self.image == '': + self.image = data.get("user_image") + self.validate_for_enabled_user_id(data.get("enabled", 0)) + self.validate_duplicate_user_id() def update_nsm_model(self): frappe.utils.nestedset.update_nsm(self) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d9cedab52a..ff8b6b3503 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -317,4 +317,5 @@ erpnext.patches.v14_0.rename_ongoing_status_in_sla_documents erpnext.patches.v14_0.migrate_crm_settings erpnext.patches.v13_0.rename_ksa_qr_field erpnext.patches.v13_0.disable_ksa_print_format_for_others # 16-12-2021 -erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template \ No newline at end of file +erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template +erpnext.patches.v14_0.set_payroll_cost_centers \ No newline at end of file diff --git a/erpnext/patches/v14_0/set_payroll_cost_centers.py b/erpnext/patches/v14_0/set_payroll_cost_centers.py new file mode 100644 index 0000000000..db5baa5d20 --- /dev/null +++ b/erpnext/patches/v14_0/set_payroll_cost_centers.py @@ -0,0 +1,31 @@ +import frappe + +def execute(): + frappe.reload_doc('payroll', 'doctype', 'employee_cost_center') + frappe.reload_doc('payroll', 'doctype', 'salary_structure_assignment') + + employees = frappe.get_all("Employee", fields=["department", "payroll_cost_center", "name"]) + + employee_cost_center = {} + for d in employees: + cost_center = d.payroll_cost_center + if not cost_center and d.department: + cost_center = frappe.get_cached_value("Department", d.department, "payroll_cost_center") + + if cost_center: + employee_cost_center.setdefault(d.name, cost_center) + + salary_structure_assignments = frappe.get_all("Salary Structure Assignment", + filters = {"docstatus": ["!=", 2]}, + fields=["name", "employee"]) + + for d in salary_structure_assignments: + cost_center = employee_cost_center.get(d.employee) + if cost_center: + assignment = frappe.get_doc("Salary Structure Assignment", d.name) + if not assignment.get("payroll_cost_centers"): + assignment.append("payroll_cost_centers", { + "cost_center": cost_center, + "percentage": 100 + }) + assignment.save() \ No newline at end of file diff --git a/erpnext/payroll/doctype/employee_cost_center/__init__.py b/erpnext/payroll/doctype/employee_cost_center/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.json b/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.json new file mode 100644 index 0000000000..8fed9f7752 --- /dev/null +++ b/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.json @@ -0,0 +1,43 @@ +{ + "actions": [], + "creation": "2021-12-23 12:44:38.389283", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "cost_center", + "percentage" + ], + "fields": [ + { + "allow_on_submit": 1, + "fieldname": "cost_center", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Cost Center", + "options": "Cost Center", + "reqd": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "percentage", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Percentage (%)", + "non_negative": 1, + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-12-23 17:39:03.410924", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Cost Center", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.py b/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.py new file mode 100644 index 0000000000..91bcc21d18 --- /dev/null +++ b/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class EmployeeCostCenter(Document): + pass diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py index 84c59a2c2b..6aa11bc117 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py @@ -158,7 +158,7 @@ class PayrollEntry(Document): """ ss_list = frappe.db.sql(""" - select t1.name, t1.salary_structure, t1.payroll_cost_center from `tabSalary Slip` t1 + select t1.name, t1.salary_structure from `tabSalary Slip` t1 where t1.docstatus = %s and t1.start_date >= %s and t1.end_date <= %s and t1.payroll_entry = %s and (t1.journal_entry is null or t1.journal_entry = "") and ifnull(salary_slip_based_on_timesheet,0) = %s """, (ss_status, self.start_date, self.end_date, self.name, self.salary_slip_based_on_timesheet), as_dict=as_dict) @@ -192,9 +192,15 @@ class PayrollEntry(Document): salary_slips = self.get_sal_slip_list(ss_status = 1, as_dict = True) if salary_slips: salary_components = frappe.db.sql(""" - select ssd.salary_component, ssd.amount, ssd.parentfield, ss.payroll_cost_center - from `tabSalary Slip` ss, `tabSalary Detail` ssd - where ss.name = ssd.parent and ssd.parentfield = '%s' and ss.name in (%s) + SELECT + ssd.salary_component, ssd.amount, ssd.parentfield, ss.salary_structure, ss.employee + FROM + `tabSalary Slip` ss, + `tabSalary Detail` ssd + WHERE + ss.name = ssd.parent + and ssd.parentfield = '%s' + and ss.name in (%s) """ % (component_type, ', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=True) @@ -204,18 +210,43 @@ class PayrollEntry(Document): salary_components = self.get_salary_components(component_type) if salary_components: component_dict = {} + self.employee_cost_centers = {} for item in salary_components: + employee_cost_centers = self.get_payroll_cost_centers_for_employee(item.employee, item.salary_structure) + add_component_to_accrual_jv_entry = True if component_type == "earnings": - is_flexible_benefit, only_tax_impact = frappe.db.get_value("Salary Component", item['salary_component'], ['is_flexible_benefit', 'only_tax_impact']) + is_flexible_benefit, only_tax_impact = \ + frappe.get_cached_value("Salary Component",item['salary_component'], ['is_flexible_benefit', 'only_tax_impact']) if is_flexible_benefit == 1 and only_tax_impact ==1: add_component_to_accrual_jv_entry = False + if add_component_to_accrual_jv_entry: - component_dict[(item.salary_component, item.payroll_cost_center)] \ - = component_dict.get((item.salary_component, item.payroll_cost_center), 0) + flt(item.amount) + for cost_center, percentage in employee_cost_centers.items(): + amount_against_cost_center = flt(item.amount) * percentage / 100 + component_dict[(item.salary_component, cost_center)] \ + = component_dict.get((item.salary_component, cost_center), 0) + amount_against_cost_center + account_details = self.get_account(component_dict = component_dict) return account_details + def get_payroll_cost_centers_for_employee(self, employee, salary_structure): + if not self.employee_cost_centers.get(employee): + ss_assignment_name = frappe.db.get_value("Salary Structure Assignment", + {"employee": employee, "salary_structure": salary_structure, "docstatus": 1}, 'name') + + if ss_assignment_name: + cost_centers = dict(frappe.get_all("Employee Cost Center", {"parent": ss_assignment_name}, + ["cost_center", "percentage"], as_list=1)) + if not cost_centers: + cost_centers = { + self.cost_center: 100 + } + + self.employee_cost_centers.setdefault(employee, cost_centers) + + return self.employee_cost_centers.get(employee, {}) + def get_account(self, component_dict = None): account_dict = {} for key, amount in component_dict.items(): diff --git a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py index c6f3897288..a3bc164950 100644 --- a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py @@ -120,8 +120,7 @@ class TestPayrollEntry(unittest.TestCase): employee1 = make_employee("test_employee1@example.com", payroll_cost_center="_Test Cost Center - _TC", department="cc - _TC", company="_Test Company") - employee2 = make_employee("test_employee2@example.com", payroll_cost_center="_Test Cost Center 2 - _TC", - department="cc - _TC", company="_Test Company") + employee2 = make_employee("test_employee2@example.com", department="cc - _TC", company="_Test Company") if not frappe.db.exists("Account", "_Test Payroll Payable - _TC"): create_account(account_name="_Test Payroll Payable", @@ -132,8 +131,26 @@ class TestPayrollEntry(unittest.TestCase): frappe.db.set_value("Company", "_Test Company", "default_payroll_payable_account", "_Test Payroll Payable - _TC") currency=frappe.db.get_value("Company", "_Test Company", "default_currency") - make_salary_structure("_Test Salary Structure 1", "Monthly", employee1, company="_Test Company", currency=currency, test_tax=False) - make_salary_structure("_Test Salary Structure 2", "Monthly", employee2, company="_Test Company", currency=currency, test_tax=False) + + ss1 = make_salary_structure("_Test Salary Structure 1", "Monthly", employee1, company="_Test Company", currency=currency, test_tax=False) + ss2 = make_salary_structure("_Test Salary Structure 2", "Monthly", employee2, company="_Test Company", currency=currency, test_tax=False) + + # update cost centers in salary structure assignment for employee2 + ssa = frappe.db.get_value("Salary Structure Assignment", + {"employee": employee2, "salary_structure": ss2.name, "docstatus": 1}, 'name') + + ssa_doc = frappe.get_doc("Salary Structure Assignment", ssa) + ssa_doc.payroll_cost_centers = [] + ssa_doc.append("payroll_cost_centers", { + "cost_center": "_Test Cost Center - _TC", + "percentage": 60 + }) + ssa_doc.append("payroll_cost_centers", { + "cost_center": "_Test Cost Center 2 - _TC", + "percentage": 40 + }) + + ssa_doc.save() dates = get_start_end_dates('Monthly', nowdate()) if not frappe.db.get_value("Salary Slip", {"start_date": dates.start_date, "end_date": dates.end_date}): @@ -148,10 +165,10 @@ class TestPayrollEntry(unittest.TestCase): """, je) expected_je = ( ('_Test Payroll Payable - _TC', 'Main - _TC', 0.0, 155600.0), - ('Salary - _TC', '_Test Cost Center - _TC', 78000.0, 0.0), - ('Salary - _TC', '_Test Cost Center 2 - _TC', 78000.0, 0.0), - ('Salary Deductions - _TC', '_Test Cost Center - _TC', 0.0, 200.0), - ('Salary Deductions - _TC', '_Test Cost Center 2 - _TC', 0.0, 200.0) + ('Salary - _TC', '_Test Cost Center - _TC', 124800.0, 0.0), + ('Salary - _TC', '_Test Cost Center 2 - _TC', 31200.0, 0.0), + ('Salary Deductions - _TC', '_Test Cost Center - _TC', 0.0, 320.0), + ('Salary Deductions - _TC', '_Test Cost Center 2 - _TC', 0.0, 80.0) ) self.assertEqual(je_entries, expected_je) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.json b/erpnext/payroll/doctype/salary_slip/salary_slip.json index 7a80e69374..4e40e13be0 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.json +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.json @@ -12,7 +12,6 @@ "department", "designation", "branch", - "payroll_cost_center", "column_break1", "status", "journal_entry", @@ -462,15 +461,6 @@ "print_hide": 1, "read_only": 1 }, - { - "fetch_from": "employee.payroll_cost_center", - "fetch_if_empty": 1, - "fieldname": "payroll_cost_center", - "fieldtype": "Link", - "label": "Payroll Cost Center", - "options": "Cost Center", - "read_only": 1 - }, { "fieldname": "mode_of_payment", "fieldtype": "Select", @@ -647,7 +637,7 @@ "idx": 9, "is_submittable": 1, "links": [], - "modified": "2021-10-08 11:47:47.098248", + "modified": "2021-12-23 11:47:47.098248", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Slip", diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.py b/erpnext/payroll/doctype/salary_structure/salary_structure.py index ae83c046a5..4cbf9484bd 100644 --- a/erpnext/payroll/doctype/salary_structure/salary_structure.py +++ b/erpnext/payroll/doctype/salary_structure/salary_structure.py @@ -167,15 +167,12 @@ def make_salary_slip(source_name, target_doc = None, employee = None, as_print = def postprocess(source, target): if employee: employee_details = frappe.db.get_value("Employee", employee, - ["employee_name", "branch", "designation", "department", "payroll_cost_center"], as_dict=1) + ["employee_name", "branch", "designation", "department"], as_dict=1) target.employee = employee target.employee_name = employee_details.employee_name target.branch = employee_details.branch target.designation = employee_details.designation target.department = employee_details.department - target.payroll_cost_center = employee_details.payroll_cost_center - if not target.payroll_cost_center and target.department: - target.payroll_cost_center = frappe.db.get_value("Department", target.department, "payroll_cost_center") target.run_method('process_salary_structure', for_preview=for_preview) diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js index 6cd897e95d..f6cb503881 100644 --- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js +++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js @@ -40,28 +40,29 @@ frappe.ui.form.on('Salary Structure Assignment', { } } }); + + frm.set_query("cost_center", "payroll_cost_centers", function() { + return { + filters: { + "company": frm.doc.company, + "is_group": 0 + } + } + }); }, employee: function(frm) { if(frm.doc.employee){ frappe.call({ - method: "frappe.client.get_value", - args:{ - doctype: "Employee", - fieldname: "company", - filters:{ - name: frm.doc.employee - } - }, + method: "set_payroll_cost_centers", + doc: frm.doc, callback: function(data) { - if(data.message){ - frm.set_value("company", data.message.company); - } + refresh_field("payroll_cost_centers"); } }); } else{ - frm.set_value("company", null); + frm.set_value("payroll_cost_centers", []); } }, diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json index c8b98e5aaf..197ab5f25b 100644 --- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json +++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json @@ -22,7 +22,9 @@ "base", "column_break_9", "variable", - "amended_from" + "amended_from", + "section_break_17", + "payroll_cost_centers" ], "fields": [ { @@ -90,7 +92,8 @@ }, { "fieldname": "section_break_7", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Base & Variable" }, { "fieldname": "base", @@ -141,14 +144,29 @@ "fieldtype": "Link", "label": "Payroll Payable Account", "options": "Account" + }, + { + "collapsible": 1, + "depends_on": "employee", + "fieldname": "section_break_17", + "fieldtype": "Section Break", + "label": "Payroll Cost Centers" + }, + { + "allow_on_submit": 1, + "fieldname": "payroll_cost_centers", + "fieldtype": "Table", + "label": "Cost Centers", + "options": "Employee Cost Center" } ], "is_submittable": 1, "links": [], - "modified": "2021-03-31 22:44:46.267974", + "modified": "2021-12-23 17:28:09.794444", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Structure Assignment", + "naming_rule": "Expression (old style)", "owner": "Administrator", "permissions": [ { @@ -193,6 +211,7 @@ ], "sort_field": "modified", "sort_order": "DESC", + "states": [], "title_field": "employee_name", "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py index e1ff9ca9f0..a7cee453ac 100644 --- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py +++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py @@ -5,7 +5,7 @@ import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import getdate +from frappe.utils import getdate, flt class DuplicateAssignment(frappe.ValidationError): pass @@ -15,6 +15,10 @@ class SalaryStructureAssignment(Document): self.validate_dates() self.validate_income_tax_slab() self.set_payroll_payable_account() + if not self.get("payroll_cost_centers"): + self.set_payroll_cost_centers() + + self.validate_cost_center_distribution() def validate_dates(self): joining_date, relieving_date = frappe.db.get_value("Employee", self.employee, @@ -50,6 +54,31 @@ class SalaryStructureAssignment(Document): "account_name": _("Payroll Payable"), "company": self.company, "account_currency": frappe.db.get_value( "Company", self.company, "default_currency"), "is_group": 0}) self.payroll_payable_account = payroll_payable_account + + @frappe.whitelist() + def set_payroll_cost_centers(self): + self.payroll_cost_centers = [] + default_payroll_cost_center = self.get_payroll_cost_center() + if default_payroll_cost_center: + self.append("payroll_cost_centers", { + "cost_center": default_payroll_cost_center, + "percentage": 100 + }) + + def get_payroll_cost_center(self): + payroll_cost_center = frappe.db.get_value("Employee", self.employee, "payroll_cost_center") + if not payroll_cost_center and self.department: + payroll_cost_center = frappe.db.get_value("Department", self.department, "payroll_cost_center") + + return payroll_cost_center + + def validate_cost_center_distribution(self): + if self.get("payroll_cost_centers"): + total_percentage = sum([flt(d.percentage) for d in self.get("payroll_cost_centers", [])]) + if total_percentage != 100: + frappe.throw(_("Total percentage against cost centers should be 100")) + + def get_assigned_salary_structure(employee, on_date): if not employee or not on_date: From 9c0f6d1306ac352bbc7314f1d03b662f7f0b785d Mon Sep 17 00:00:00 2001 From: Anupam Date: Fri, 24 Dec 2021 16:51:50 +0530 Subject: [PATCH 096/193] fix: sider issue --- erpnext/projects/doctype/project/project.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index dc7cf81c76..6399a50f48 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -133,9 +133,9 @@ frappe.ui.form.on("Project", { set_status: function(frm, status) { frappe.confirm(__('Set Project and all Tasks to status {0}?', [status.bold()]), () => { frappe.xcall('erpnext.projects.doctype.project.project.set_project_status', - {project: frm.doc.name, status: status}).then(() => { - frm.reload_doc() - }); + {project: frm.doc.name, status: status}).then(() => { + frm.reload_doc(); + }); }); }, From 6eeea0a77a0fbb9d014831250c7f1e917b769903 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 24 Dec 2021 17:20:23 +0530 Subject: [PATCH 097/193] fix: ignore links while setting default notification templates in Settings (#29019) --- .../patches/v11_0/add_default_dispatch_notification_template.py | 1 + .../v13_0/add_default_interview_notification_templates.py | 1 + .../add_default_exit_questionnaire_notification_template.py | 1 + 3 files changed, 3 insertions(+) diff --git a/erpnext/patches/v11_0/add_default_dispatch_notification_template.py b/erpnext/patches/v11_0/add_default_dispatch_notification_template.py index 08006ad01b..c7771a5f19 100644 --- a/erpnext/patches/v11_0/add_default_dispatch_notification_template.py +++ b/erpnext/patches/v11_0/add_default_dispatch_notification_template.py @@ -22,4 +22,5 @@ def execute(): delivery_settings = frappe.get_doc("Delivery Settings") delivery_settings.dispatch_template = _("Dispatch Notification") + delivery_settings.flags.ignore_links = True delivery_settings.save() diff --git a/erpnext/patches/v13_0/add_default_interview_notification_templates.py b/erpnext/patches/v13_0/add_default_interview_notification_templates.py index 0208ca914e..6b5de52e2b 100644 --- a/erpnext/patches/v13_0/add_default_interview_notification_templates.py +++ b/erpnext/patches/v13_0/add_default_interview_notification_templates.py @@ -32,4 +32,5 @@ def execute(): hr_settings = frappe.get_doc('HR Settings') hr_settings.interview_reminder_template = _('Interview Reminder') hr_settings.feedback_reminder_notification_template = _('Interview Feedback Reminder') + hr_settings.flags.ignore_links = True hr_settings.save() diff --git a/erpnext/patches/v14_0/add_default_exit_questionnaire_notification_template.py b/erpnext/patches/v14_0/add_default_exit_questionnaire_notification_template.py index 8b1752b2c7..120182a80e 100644 --- a/erpnext/patches/v14_0/add_default_exit_questionnaire_notification_template.py +++ b/erpnext/patches/v14_0/add_default_exit_questionnaire_notification_template.py @@ -24,4 +24,5 @@ def execute(): hr_settings = frappe.get_doc("HR Settings") hr_settings.exit_questionnaire_notification_template = template + hr_settings.flags.ignore_links = True hr_settings.save() From f867f1974a7b34afda4d44e74297fb1dc3f09ca1 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 24 Dec 2021 17:34:34 +0530 Subject: [PATCH 098/193] fix: get fallback cost center from Employee/Department --- erpnext/hr/doctype/department/department.js | 2 +- erpnext/hr/doctype/employee/employee.js | 2 +- erpnext/payroll/doctype/payroll_entry/payroll_entry.py | 8 +++++++- .../payroll/doctype/payroll_entry/test_payroll_entry.py | 6 +++--- .../salary_structure_assignment.js | 6 +++--- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/erpnext/hr/doctype/department/department.js b/erpnext/hr/doctype/department/department.js index eb759a323e..46cfbdad56 100644 --- a/erpnext/hr/doctype/department/department.js +++ b/erpnext/hr/doctype/department/department.js @@ -13,7 +13,7 @@ frappe.ui.form.on('Department', { "company": frm.doc.company, "is_group": 0 } - } + }; }); }, refresh: function(frm) { diff --git a/erpnext/hr/doctype/employee/employee.js b/erpnext/hr/doctype/employee/employee.js index 4181e0f8bb..8c73e9c9c5 100755 --- a/erpnext/hr/doctype/employee/employee.js +++ b/erpnext/hr/doctype/employee/employee.js @@ -54,7 +54,7 @@ frappe.ui.form.on('Employee', { "company": frm.doc.company, "is_group": 0 } - } + }; }); }, onload: function (frm) { diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py index 6aa11bc117..f61e68896b 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py @@ -239,8 +239,14 @@ class PayrollEntry(Document): cost_centers = dict(frappe.get_all("Employee Cost Center", {"parent": ss_assignment_name}, ["cost_center", "percentage"], as_list=1)) if not cost_centers: + default_cost_center, department = frappe.get_cached_value("Employee", employee, ["payroll_cost_center", "department"]) + if not default_cost_center and department: + default_cost_center = frappe.get_cached_value("Department", department, "payroll_cost_center") + if not default_cost_center: + default_cost_center = self.cost_center + cost_centers = { - self.cost_center: 100 + default_cost_center: 100 } self.employee_cost_centers.setdefault(employee, cost_centers) diff --git a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py index a3bc164950..e88a2ca9ed 100644 --- a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py @@ -132,12 +132,12 @@ class TestPayrollEntry(unittest.TestCase): "_Test Payroll Payable - _TC") currency=frappe.db.get_value("Company", "_Test Company", "default_currency") - ss1 = make_salary_structure("_Test Salary Structure 1", "Monthly", employee1, company="_Test Company", currency=currency, test_tax=False) - ss2 = make_salary_structure("_Test Salary Structure 2", "Monthly", employee2, company="_Test Company", currency=currency, test_tax=False) + make_salary_structure("_Test Salary Structure 1", "Monthly", employee1, company="_Test Company", currency=currency, test_tax=False) + ss = make_salary_structure("_Test Salary Structure 2", "Monthly", employee2, company="_Test Company", currency=currency, test_tax=False) # update cost centers in salary structure assignment for employee2 ssa = frappe.db.get_value("Salary Structure Assignment", - {"employee": employee2, "salary_structure": ss2.name, "docstatus": 1}, 'name') + {"employee": employee2, "salary_structure": ss.name, "docstatus": 1}, 'name') ssa_doc = frappe.get_doc("Salary Structure Assignment", ssa) ssa_doc.payroll_cost_centers = [] diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js index f6cb503881..220bfbfd65 100644 --- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js +++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js @@ -47,12 +47,12 @@ frappe.ui.form.on('Salary Structure Assignment', { "company": frm.doc.company, "is_group": 0 } - } + }; }); }, employee: function(frm) { - if(frm.doc.employee){ + if (frm.doc.employee) { frappe.call({ method: "set_payroll_cost_centers", doc: frm.doc, @@ -61,7 +61,7 @@ frappe.ui.form.on('Salary Structure Assignment', { } }); } - else{ + else { frm.set_value("payroll_cost_centers", []); } }, From 9c4455f77c133b142d40397141240b86e836bd70 Mon Sep 17 00:00:00 2001 From: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> Date: Fri, 24 Dec 2021 18:52:35 +0530 Subject: [PATCH 099/193] fix: Removed ERPNext Integration Settings Workspace (#29023) --- .../erpnext_integrations_settings.json | 78 ------------------- erpnext/patches.txt | 3 +- 2 files changed, 2 insertions(+), 79 deletions(-) delete mode 100644 erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json diff --git a/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json b/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json deleted file mode 100644 index 5efafd67fe..0000000000 --- a/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "charts": [], - "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Integrations Settings\", \"col\": 4}}]", - "creation": "2020-07-31 10:38:54.021237", - "docstatus": 0, - "doctype": "Workspace", - "for_user": "", - "hide_custom": 0, - "icon": "setting", - "idx": 0, - "label": "ERPNext Integrations Settings", - "links": [ - { - "hidden": 0, - "is_query_report": 0, - "label": "Integrations Settings", - "link_count": 0, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Woocommerce Settings", - "link_count": 0, - "link_to": "Woocommerce Settings", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Amazon MWS Settings", - "link_count": 0, - "link_to": "Amazon MWS Settings", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Plaid Settings", - "link_count": 0, - "link_to": "Plaid Settings", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Exotel Settings", - "link_count": 0, - "link_to": "Exotel Settings", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - } - ], - "modified": "2021-11-23 04:30:33.106991", - "modified_by": "Administrator", - "module": "ERPNext Integrations", - "name": "ERPNext Integrations Settings", - "owner": "Administrator", - "parent_page": "", - "public": 1, - "restrict_to_domain": "", - "roles": [], - "sequence_id": 11, - "shortcuts": [], - "title": "ERPNext Integrations Settings" -} \ No newline at end of file diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d9cedab52a..c75606afaa 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -317,4 +317,5 @@ erpnext.patches.v14_0.rename_ongoing_status_in_sla_documents erpnext.patches.v14_0.migrate_crm_settings erpnext.patches.v13_0.rename_ksa_qr_field erpnext.patches.v13_0.disable_ksa_print_format_for_others # 16-12-2021 -erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template \ No newline at end of file +erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template +execute:frappe.delete_doc_if_exists('Workspace', 'ERPNext Integrations Settings') \ No newline at end of file From cfb6b26e4776d164b4c9786bfe3cf517abe1432e Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Fri, 24 Dec 2021 19:13:28 +0530 Subject: [PATCH 100/193] fix: contact duplication on converting lead to customer (#29001) * fix: contact duplication on converting lead to customer * fix: added test case --- erpnext/crm/doctype/lead/test_lead.py | 11 ++++++++++ erpnext/selling/doctype/customer/customer.py | 23 ++++++++++---------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/erpnext/crm/doctype/lead/test_lead.py b/erpnext/crm/doctype/lead/test_lead.py index 56bfc8f145..3882974022 100644 --- a/erpnext/crm/doctype/lead/test_lead.py +++ b/erpnext/crm/doctype/lead/test_lead.py @@ -23,6 +23,17 @@ class TestLead(unittest.TestCase): customer.customer_group = "_Test Customer Group" customer.insert() + #check whether lead contact is carried forward to the customer. + contact = frappe.db.get_value('Dynamic Link', { + "parenttype": "Contact", + "link_doctype": "Lead", + "link_name": customer.lead_name, + }, "parent") + + if contact: + contact_doc = frappe.get_doc("Contact", contact) + self.assertEqual(contact_doc.has_link(customer.doctype, customer.name), True) + def test_make_customer_from_organization(self): from erpnext.crm.doctype.lead.lead import make_customer diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 0c8c53aabe..b7f74df105 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -196,20 +196,19 @@ class Customer(TransactionBase): if not lead.lead_name: frappe.throw(_("Please mention the Lead Name in Lead {0}").format(self.lead_name)) - if lead.company_name: - contact_names = frappe.get_all('Dynamic Link', filters={ - "parenttype":"Contact", - "link_doctype":"Lead", - "link_name":self.lead_name - }, fields=["parent as name"]) + contact_names = frappe.get_all('Dynamic Link', filters={ + "parenttype":"Contact", + "link_doctype":"Lead", + "link_name":self.lead_name + }, fields=["parent as name"]) - for contact_name in contact_names: - contact = frappe.get_doc('Contact', contact_name.get('name')) - if not contact.has_link('Customer', self.name): - contact.append('links', dict(link_doctype='Customer', link_name=self.name)) - contact.save(ignore_permissions=self.flags.ignore_permissions) + for contact_name in contact_names: + contact = frappe.get_doc('Contact', contact_name.get('name')) + if not contact.has_link('Customer', self.name): + contact.append('links', dict(link_doctype='Customer', link_name=self.name)) + contact.save(ignore_permissions=self.flags.ignore_permissions) - else: + if not contact_names: lead.lead_name = lead.lead_name.lstrip().split(" ") lead.first_name = lead.lead_name[0] lead.last_name = " ".join(lead.lead_name[1:]) From ab09dc545e77e93a918cea6fd93ab3c730739d9b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 20 Dec 2021 12:16:14 +0530 Subject: [PATCH 101/193] fix(UX): Optimize rate updation of changing price list (cherry picked from commit 6087d5a6038d6e636ce1ba006ebd59e820b3cd8e) --- erpnext/public/js/controllers/transaction.js | 7 +++++-- erpnext/stock/get_item_details.py | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 773d53c552..2019bc0ae6 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1586,17 +1586,19 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe var items_rule_dict = {}; for(var i=0, l=children.length; i Date: Mon, 20 Dec 2021 13:26:16 +0530 Subject: [PATCH 102/193] fix: Linting issues (cherry picked from commit 0980c2f9816b3e4a11b5410b5997a72b1cad28fd) --- 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 2019bc0ae6..92a6113e0f 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1587,7 +1587,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe for(var i=0, l=children.length; i Date: Wed, 22 Dec 2021 11:26:19 +0530 Subject: [PATCH 103/193] fix: Recalculate taxes irrespective of price list rate changed or not (cherry picked from commit 233f79bf960381b1c2bd753d783afd3020b377e0) # Conflicts: # erpnext/public/js/controllers/transaction.js --- erpnext/public/js/controllers/transaction.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 92a6113e0f..d04802b2fb 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1582,7 +1582,6 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe _set_values_for_item_list(children) { var me = this; - var price_list_rate_changed = false; var items_rule_dict = {}; for(var i=0, l=children.length; i>>>>>> 233f79bf96 (fix: Recalculate taxes irrespective of price list rate changed or not) apply_rule_on_other_items(args) { const me = this; From 2d0208ba1fc6808cad18de509ebb2e2cfa5cd152 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 22 Dec 2021 12:01:39 +0530 Subject: [PATCH 104/193] fix: Add round floats for price list rate (cherry picked from commit b60fbf5ba95350e79463f922b0e8dce518780383) --- erpnext/public/js/controllers/transaction.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index d04802b2fb..794e912eda 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -680,7 +680,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe var item = frappe.get_doc(cdt, cdn); frappe.model.round_floats_in(item, ["price_list_rate", "discount_percentage"]); - // check if child doctype is Sales Order Item/Qutation Item and calculate the rate + // check if child doctype is Sales Order Item/Quotation Item and calculate the rate if (in_list(["Quotation Item", "Sales Order Item", "Delivery Note Item", "Sales Invoice Item", "POS Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Purchase Receipt Item"]), cdt) this.apply_pricing_rule_on_item(item); else @@ -1601,6 +1601,8 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } } + frappe.model.round_floats_in(item_row, ["price_list_rate", "discount_percentage"]); + // if pricing rule set as blank from an existing value, apply price_list if(!me.frm.doc.ignore_pricing_rule && existing_pricing_rule && !d.pricing_rules) { me.apply_price_list(frappe.get_doc(d.doctype, d.name)); From 6eb3d6a814a1a86a6d606946779278488c0a8b68 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Sat, 25 Dec 2021 09:04:34 +0530 Subject: [PATCH 105/193] fix: Conflicts --- erpnext/public/js/controllers/transaction.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 794e912eda..3791741663 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1622,13 +1622,8 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe me.frm.refresh_field('items'); me.apply_rule_on_other_items(items_rule_dict); -<<<<<<< HEAD - if(!price_list_rate_changed) me.calculate_taxes_and_totals(); - } -======= me.calculate_taxes_and_totals(); - }, ->>>>>>> 233f79bf96 (fix: Recalculate taxes irrespective of price list rate changed or not) + } apply_rule_on_other_items(args) { const me = this; From 52397c97714fd5fd5ef75b32908006fc7c9af1e0 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Sun, 26 Dec 2021 02:15:57 +0100 Subject: [PATCH 106/193] fix: avoid `"string" in None` condition --- erpnext/patches/v12_0/create_itc_reversal_custom_fields.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/patches/v12_0/create_itc_reversal_custom_fields.py b/erpnext/patches/v12_0/create_itc_reversal_custom_fields.py index d157aad8f2..d4fbded5a3 100644 --- a/erpnext/patches/v12_0/create_itc_reversal_custom_fields.py +++ b/erpnext/patches/v12_0/create_itc_reversal_custom_fields.py @@ -97,6 +97,8 @@ def execute(): 'itc_central_tax': 0, 'itc_cess_amount': 0 }) + if not gst_accounts: + continue if d.account_head in gst_accounts.get('igst_account'): amount_map[d.parent]['itc_integrated_tax'] += d.amount From 8e4ea7e99790a2fbe83a4462fe6020eead87c07f Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Sun, 26 Dec 2021 10:23:36 +0530 Subject: [PATCH 107/193] fix: Merge Conflicts --- erpnext/patches.txt | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 407d68384b..47be13087a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -316,14 +316,7 @@ erpnext.patches.v13_0.create_ksa_vat_custom_fields erpnext.patches.v14_0.rename_ongoing_status_in_sla_documents erpnext.patches.v14_0.migrate_crm_settings erpnext.patches.v13_0.rename_ksa_qr_field -<<<<<<< HEAD erpnext.patches.v13_0.disable_ksa_print_format_for_others # 16-12-2021 erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template -======= -erpnext.patches.v13_0.disable_ksa_print_format_for_others -<<<<<<< HEAD -erpnext.patches.v13_0.update_tax_category_for_rcm #1 ->>>>>>> b33fd6acc7 (fix: Is Reverse Charge check in Tax Category) -======= erpnext.patches.v13_0.update_tax_category_for_rcm ->>>>>>> 7c1bfe6b46 (chore: Remove patch comment) + From 8415ec4d0c6b8ba83f5e9fb99694dfd13fc647cb Mon Sep 17 00:00:00 2001 From: Wolfram Schmidt Date: Sun, 26 Dec 2021 12:46:11 +0100 Subject: [PATCH 108/193] Update de.csv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Especially to allign Sales Orders (Aufträge) Sales Invoices (Ausgangsrechnungen) to Purchase Orders (Bestellungen) Purchase Invoice (Eingangsrechnungen). Both are a definate opposite and are kept short. --- erpnext/translations/de.csv | 268 ++++++++++++++++++------------------ 1 file changed, 134 insertions(+), 134 deletions(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index d46ffb5609..24e54fd035 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -242,7 +242,7 @@ Apply Now,Jetzt bewerben, Appointment Confirmation,Terminbestätigung, Appointment Duration (mins),Termindauer (Min.), Appointment Type,Termin-Typ, -Appointment {0} and Sales Invoice {1} cancelled,Termin {0} und Verkaufsrechnung {1} wurden storniert, +Appointment {0} and Sales Invoice {1} cancelled,Termin {0} und Ausgangsrechnung {1} wurden storniert, Appointments and Encounters,Termine und Begegnungen, Appointments and Patient Encounters,Termine und Patienten-Begegnungen, Appraisal {0} created for Employee {1} in the given date range,Bewertung {0} für Mitarbeiter {1} im angegebenen Datumsbereich erstellt, @@ -427,7 +427,7 @@ Buying Price List,Kauf Preisliste, Buying Rate,Kaufrate, "Buying must be checked, if Applicable For is selected as {0}","Einkauf muss ausgewählt sein, wenn ""Anwenden auf"" auf {0} gesetzt wurde", By {0},Von {0}, -Bypass credit check at Sales Order ,Kreditprüfung im Kundenauftrag umgehen, +Bypass credit check at Sales Order ,Kreditprüfung im Auftrag umgehen, C-Form records,Kontakt-Formular Datensätze, C-form is not applicable for Invoice: {0},Kontaktformular nicht anwendbar auf Rechnung: {0}, CEO,CEO, @@ -474,11 +474,11 @@ Cannot deduct when category is for 'Valuation' or 'Vaulation and Total',"Kann ni "Cannot delete Serial No {0}, as it is used in stock transactions","Die Seriennummer {0} kann nicht gelöscht werden, da sie in Lagertransaktionen verwendet wird", Cannot enroll more than {0} students for this student group.,Kann nicht mehr als {0} Studenten für diese Studentengruppe einschreiben., Cannot find active Leave Period,Aktive Abwesenheitszeit kann nicht gefunden werden, -Cannot produce more Item {0} than Sales Order quantity {1},"Es können nicht mehr Artikel {0} produziert werden, als die über Kundenaufträge bestellte Stückzahl {1}", +Cannot produce more Item {0} than Sales Order quantity {1},"Es können nicht mehr Artikel {0} produziert werden, als die über den Auftrag bestellte Stückzahl {1}", Cannot promote Employee with status Left,Mitarbeiter mit Status "Links" kann nicht gefördert werden, Cannot refer row number greater than or equal to current row number for this Charge type,"Für diese Berechnungsart kann keine Zeilennummern zugeschrieben werden, die größer oder gleich der aktuellen Zeilennummer ist", Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row,"Die Berechnungsart kann für die erste Zeile nicht auf ""bezogen auf Menge der vorhergenden Zeile"" oder auf ""bezogen auf Gesamtmenge der vorhergenden Zeile"" gesetzt werden", -Cannot set as Lost as Sales Order is made.,"Kann nicht als verloren gekennzeichnet werden, da ein Kundenauftrag dazu existiert.", +Cannot set as Lost as Sales Order is made.,"Kann nicht als verloren gekennzeichnet werden, da ein Auftrag dazu existiert.", Cannot set authorization on basis of Discount for {0},Genehmigung kann nicht auf der Basis des Rabattes für {0} festgelegt werden, Cannot set multiple Item Defaults for a company.,Es können nicht mehrere Artikelstandards für ein Unternehmen festgelegt werden., Cannot set quantity less than delivered quantity,Menge kann nicht kleiner als gelieferte Menge sein, @@ -663,7 +663,7 @@ Create Leads,Leads erstellen, Create Maintenance Visit,Wartungsbesuch anlegen, Create Material Request,Materialanforderung erstellen, Create Multiple,Erstellen Sie mehrere, -Create Opening Sales and Purchase Invoices,Erstellen Sie Eingangsverkaufs- und Einkaufsrechnungen, +Create Opening Sales and Purchase Invoices,Erstellen Sie die eröffnungs Ein- und Ausgangsrechnungen, Create Payment Entries,Zahlungseinträge erstellen, Create Payment Entry,Zahlungseintrag erstellen, Create Print Format,Druckformat erstellen, @@ -672,9 +672,9 @@ Create Purchase Orders,Bestellungen erstellen, Create Quotation,Angebot erstellen, Create Salary Slip,Gehaltsabrechnung erstellen, Create Salary Slips,Gehaltszettel erstellen, -Create Sales Invoice,Verkaufsrechnung erstellen, -Create Sales Order,Kundenauftrag anlegen, -Create Sales Orders to help you plan your work and deliver on-time,"Erstellen Sie Kundenaufträge, um Ihre Arbeit zu planen und pünktlich zu liefern", +Create Sales Invoice,Ausgangsrechnung erstellen, +Create Sales Order,Auftrag anlegen, +Create Sales Orders to help you plan your work and deliver on-time,"Erstellen Sie Aufträge, um Ihre Arbeit zu planen und pünktlich zu liefern", Create Sample Retention Stock Entry,Legen Sie einen Muster-Retention-Stock-Eintrag an, Create Student,Schüler erstellen, Create Student Batch,Studentenstapel erstellen, @@ -808,7 +808,7 @@ Delivery Date,Liefertermin, Delivery Note,Lieferschein, Delivery Note {0} is not submitted,Lieferschein {0} ist nicht gebucht, Delivery Note {0} must not be submitted,Lieferschein {0} darf nicht gebucht sein, -Delivery Notes {0} must be cancelled before cancelling this Sales Order,Lieferscheine {0} müssen vor Löschung dieser Kundenaufträge storniert werden, +Delivery Notes {0} must be cancelled before cancelling this Sales Order,Lieferscheine {0} müssen vor Löschung dieser Aufträge storniert werden, Delivery Notes {0} updated,Lieferhinweise {0} aktualisiert, Delivery Status,Lieferstatus, Delivery Trip,Liefertrip, @@ -981,7 +981,7 @@ Execution,Ausführung, Executive Search,Direktsuche, Expand All,Alle ausklappen, Expected Delivery Date,Geplanter Liefertermin, -Expected Delivery Date should be after Sales Order Date,Voraussichtlicher Liefertermin sollte nach Kundenauftragsdatum erfolgen, +Expected Delivery Date should be after Sales Order Date,Voraussichtlicher Liefertermin sollte nach Auftragsdatum erfolgen, Expected End Date,Voraussichtliches Enddatum, Expected Hrs,Erwartete Stunden, Expected Start Date,Voraussichtliches Startdatum, @@ -1235,7 +1235,7 @@ ITC Reversed,ITC rückgängig gemacht, Identifying Decision Makers,Entscheidungsträger identifizieren, "If Auto Opt In is checked, then the customers will be automatically linked with the concerned Loyalty Program (on save)","Wenn Automatische Anmeldung aktiviert ist, werden die Kunden automatisch mit dem betreffenden Treueprogramm verknüpft (beim Speichern)", "If multiple Pricing Rules continue to prevail, users are asked to set Priority manually to resolve conflict.","Wenn mehrere Preisregeln weiterhin gleichrangig gelten, werden die Benutzer aufgefordert, Vorrangregelungen manuell zu erstellen, um den Konflikt zu lösen.", -"If selected Pricing Rule is made for 'Rate', it will overwrite Price List. Pricing Rule rate is the final rate, so no further discount should be applied. Hence, in transactions like Sales Order, Purchase Order etc, it will be fetched in 'Rate' field, rather than 'Price List Rate' field.","Wenn die ausgewählte Preisregel für "Rate" festgelegt wurde, wird die Preisliste überschrieben. Der Preisregelpreis ist der Endpreis, daher sollte kein weiterer Rabatt angewendet werden. Daher wird es in Transaktionen wie Kundenauftrag, Bestellung usw. im Feld 'Preis' und nicht im Feld 'Preislistenpreis' abgerufen.", +"If selected Pricing Rule is made for 'Rate', it will overwrite Price List. Pricing Rule rate is the final rate, so no further discount should be applied. Hence, in transactions like Sales Order, Purchase Order etc, it will be fetched in 'Rate' field, rather than 'Price List Rate' field.","Wenn die ausgewählte Preisregel für "Rate" festgelegt wurde, wird die Preisliste überschrieben. Der Preisregelpreis ist der Endpreis, daher sollte kein weiterer Rabatt angewendet werden. Daher wird es in Transaktionen wie Auftrag, Bestellung usw. im Feld 'Preis' und nicht im Feld 'Preislistenpreis' abgerufen.", "If two or more Pricing Rules are found based on the above conditions, Priority is applied. Priority is a number between 0 to 20 while default value is zero (blank). Higher number means it will take precedence if there are multiple Pricing Rules with same conditions.","Wenn zwei oder mehrere Preisregeln basierend auf den oben genannten Bedingungen gefunden werden, wird eine Vorrangregelung angewandt. Priorität ist eine Zahl zwischen 0 und 20, wobei der Standardwert Null (leer) ist. Die höhere Zahl hat Vorrang, wenn es mehrere Preisregeln zu den gleichen Bedingungen gibt.", "If unlimited expiry for the Loyalty Points, keep the Expiry Duration empty or 0.","Wenn die Treuepunkte unbegrenzt ablaufen, lassen Sie die Ablaufdauer leer oder 0.", "If you have any questions, please get back to us.","Wenn Sie Fragen haben, wenden Sie sich bitte an uns.", @@ -1386,7 +1386,7 @@ Item {0} must be a Sub-contracted Item,Artikel {0} muss ein unterbeauftragter Ar Item {0} must be a non-stock item,Artikel {0} darf kein Lagerartikel sein, Item {0} must be a stock Item,Artikel {0} muss ein Lagerartikel sein, Item {0} not found,Artikel {0} nicht gefunden, -Item {0} not found in 'Raw Materials Supplied' table in Purchase Order {1},"Artikel {0} in Tabelle ""Rohmaterialien geliefert"" des Lieferantenauftrags {1} nicht gefunden", +Item {0} not found in 'Raw Materials Supplied' table in Purchase Order {1},"Artikel {0} in Tabelle ""Rohmaterialien geliefert"" der Bestellung {1} nicht gefunden", Item {0}: Ordered qty {1} cannot be less than minimum order qty {2} (defined in Item).,Artikel {0}: Bestellmenge {1} kann nicht weniger als Mindestbestellmenge {2} (im Artikel definiert) sein., Item: {0} does not exist in the system,Artikel: {0} ist nicht im System vorhanden, Items,Artikel, @@ -1489,7 +1489,7 @@ Lower Income,Niedrigeres Einkommen, Loyalty Amount,Loyalitätsbetrag, Loyalty Point Entry,Loyalitätspunkteintrag, Loyalty Points,Treuepunkte, -"Loyalty Points will be calculated from the spent done (via the Sales Invoice), based on collection factor mentioned.","Treuepunkte werden aus dem ausgegebenen Betrag (über die Verkaufsrechnung) berechnet, basierend auf dem genannten Sammelfaktor.", +"Loyalty Points will be calculated from the spent done (via the Sales Invoice), based on collection factor mentioned.","Treuepunkte werden aus dem ausgegebenen Betrag (über die Ausgangsrechnung) berechnet, basierend auf dem genannten Sammelfaktor.", Loyalty Points: {0},Treuepunkte: {0}, Loyalty Program,Treueprogramm, Main,Haupt, @@ -1499,11 +1499,11 @@ Maintenance Manager,Leiter der Wartung, Maintenance Schedule,Wartungsplan, Maintenance Schedule is not generated for all the items. Please click on 'Generate Schedule',"Wartungsplan wird nicht für alle Elemente erzeugt. Bitte klicken Sie auf ""Zeitplan generieren""", Maintenance Schedule {0} exists against {1},Wartungsplan {0} existiert gegen {1}, -Maintenance Schedule {0} must be cancelled before cancelling this Sales Order,Wartungsplan {0} muss vor Stornierung dieses Kundenauftrages aufgehoben werden, +Maintenance Schedule {0} must be cancelled before cancelling this Sales Order,Wartungsplan {0} muss vor Stornierung dieses Auftrags aufgehoben werden, Maintenance Status has to be Cancelled or Completed to Submit,Der Wartungsstatus muss abgebrochen oder zum Senden abgeschlossen werden, Maintenance User,Nutzer Instandhaltung, Maintenance Visit,Wartungsbesuch, -Maintenance Visit {0} must be cancelled before cancelling this Sales Order,Wartungsbesuch {0} muss vor Stornierung dieses Kundenauftrages abgebrochen werden, +Maintenance Visit {0} must be cancelled before cancelling this Sales Order,Wartungsbesuch {0} muss vor Stornierung dieses Auftrags abgebrochen werden, Maintenance start date can not be before delivery date for Serial No {0},Startdatum der Wartung kann nicht vor dem Liefertermin für Seriennummer {0} liegen, Make,Erstellen, Make Payment,Zahlung ausführen, @@ -1549,8 +1549,8 @@ Material Request,Materialanfrage, Material Request Date,Material Auftragsdatum, Material Request No,Materialanfragenr., "Material Request not created, as quantity for Raw Materials already available.","Materialanforderung nicht angelegt, da Menge für Rohstoffe bereits vorhanden.", -Material Request of maximum {0} can be made for Item {1} against Sales Order {2},Materialanfrage von maximal {0} kann für Artikel {1} zum Kundenauftrag {2} gemacht werden, -Material Request to Purchase Order,Von der Materialanfrage zum Lieferantenauftrag, +Material Request of maximum {0} can be made for Item {1} against Sales Order {2},Materialanfrage von maximal {0} kann für Artikel {1} zum Auftrag {2} gemacht werden, +Material Request to Purchase Order,Von der Materialanfrage zur Bestellung, Material Request {0} is cancelled or stopped,Materialanfrage {0} wird storniert oder gestoppt, Material Request {0} submitted.,Materialanfrage {0} gesendet., Material Transfer,Materialübertrag, @@ -2224,7 +2224,7 @@ Publishing,Veröffentlichung, Purchase,Einkauf, Purchase Amount,Gesamtbetrag des Einkaufs, Purchase Date,Kaufdatum, -Purchase Invoice,Einkaufsrechnung, +Purchase Invoice,Eingangsrechnung, Purchase Invoice {0} is already submitted,Eingangsrechnung {0} wurde bereits übertragen, Purchase Manager,Einkaufsleiter, Purchase Master Manager,Einkaufsstammdaten-Manager, @@ -2233,11 +2233,11 @@ Purchase Order Amount,Bestellbetrag, Purchase Order Amount(Company Currency),Bestellbetrag (Firmenwährung), Purchase Order Date,Bestelldatum, Purchase Order Items not received on time,Bestellpositionen nicht rechtzeitig erhalten, -Purchase Order number required for Item {0},Lieferantenauftragsnummer ist für den Artikel {0} erforderlich, -Purchase Order to Payment,Vom Lieferantenauftrag zur Zahlung, -Purchase Order {0} is not submitted,Lieferantenauftrag {0} wurde nicht übertragen, +Purchase Order number required for Item {0},Bestellnummer ist für den Artikel {0} erforderlich, +Purchase Order to Payment,Von der Bestellung zur Zahlung, +Purchase Order {0} is not submitted,Bestellung {0} wurde nicht übertragen, Purchase Orders are not allowed for {0} due to a scorecard standing of {1}.,Kaufaufträge sind für {0} wegen einer Scorecard von {1} nicht erlaubt., -Purchase Orders given to Suppliers.,An Lieferanten erteilte Lieferantenaufträge, +Purchase Orders given to Suppliers.,An Lieferanten erteilte Bestellungen, Purchase Price List,Einkaufspreisliste, Purchase Receipt,Kaufbeleg, Purchase Receipt {0} is not submitted,Kaufbeleg {0} wurde nicht übertragen, @@ -2440,7 +2440,7 @@ Row #{0}: Duplicate entry in References {1} {2},Zeile # {0}: Eintrag in Referenz Row #{0}: Expected Delivery Date cannot be before Purchase Order Date,Row # {0}: Voraussichtlicher Liefertermin kann nicht vor Bestelldatum sein, Row #{0}: Item added,Zeile # {0}: Element hinzugefügt, Row #{0}: Journal Entry {1} does not have account {2} or already matched against another voucher,Row # {0}: Journal Entry {1} nicht Konto {2} oder bereits abgestimmt gegen einen anderen Gutschein, -Row #{0}: Not allowed to change Supplier as Purchase Order already exists,"Zeile #{0}: Es ist nicht erlaubt den Lieferanten zu wechseln, da bereits ein Lieferantenauftrag vorhanden ist", +Row #{0}: Not allowed to change Supplier as Purchase Order already exists,"Zeile #{0}: Es ist nicht erlaubt den Lieferanten zu wechseln, da bereits eine Bestellung vorhanden ist", Row #{0}: Please set reorder quantity,Zeile #{0}: Bitte Nachbestellmenge angeben, Row #{0}: Please specify Serial No for Item {1},Zeile #{0}: Bitte Seriennummer für Artikel {1} angeben, Row #{0}: Qty increased by 1,Zeile # {0}: Menge um 1 erhöht, @@ -2483,7 +2483,7 @@ Row {0}: Hours value must be greater than zero.,Row {0}: Stunden-Wert muss grö Row {0}: Invalid reference {1},Zeile {0}: Ungültige Referenz {1}, Row {0}: Party / Account does not match with {1} / {2} in {3} {4},Zeile {0}: Gruppe / Konto stimmt nicht mit {1} / {2} in {3} {4} überein, Row {0}: Party Type and Party is required for Receivable / Payable account {1},Zeile {0}: Gruppen-Typ und Gruppe sind für Forderungen-/Verbindlichkeiten-Konto {1} zwingend erforderlich, -Row {0}: Payment against Sales/Purchase Order should always be marked as advance,"Zeile {0}: ""Zahlung zu Kunden-/Lieferantenauftrag"" sollte immer als ""Vorkasse"" eingestellt werden", +Row {0}: Payment against Sales/Purchase Order should always be marked as advance,"Zeile {0}: ""Zahlung zu Auftrag bzw. Bestellung"" sollte immer als ""Vorkasse"" eingestellt werden", Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.,"Zeile {0}: Wenn es sich um eine Vorkasse-Buchung handelt, bitte ""Ist Vorkasse"" zu Konto {1} anklicken, .", Row {0}: Please set at Tax Exemption Reason in Sales Taxes and Charges,Zeile {0}: Bitte setzen Sie den Steuerbefreiungsgrund in den Umsatzsteuern und -gebühren, Row {0}: Please set the Mode of Payment in Payment Schedule,Zeile {0}: Bitte legen Sie die Zahlungsart im Zahlungsplan fest, @@ -2518,19 +2518,19 @@ Sales,Vertrieb, Sales Account,Verkaufskonto, Sales Expenses,Vertriebskosten, Sales Funnel,Verkaufstrichter, -Sales Invoice,Verkaufsrechnung, +Sales Invoice,Ausgangsrechnung, Sales Invoice {0} has already been submitted,Ausgangsrechnung {0} wurde bereits übertragen, -Sales Invoice {0} must be cancelled before cancelling this Sales Order,Ausgangsrechnung {0} muss vor Stornierung dieses Kundenauftrags abgebrochen werden, +Sales Invoice {0} must be cancelled before cancelling this Sales Order,Ausgangsrechnung {0} muss vor Stornierung dieses Auftrags abgebrochen werden, Sales Manager,Vertriebsleiter, Sales Master Manager,Hauptvertriebsleiter, -Sales Order,Auftragsbestätigung, -Sales Order Item,Kundenauftrags-Artikel, -Sales Order required for Item {0},Kundenauftrag für den Artikel {0} erforderlich, -Sales Order to Payment,Vom Kundenauftrag zum Zahlungseinang, -Sales Order {0} is not submitted,Kundenauftrag {0} wurde nicht übertragen, -Sales Order {0} is not valid,Kundenauftrag {0} ist nicht gültig, -Sales Order {0} is {1},Kundenauftrag {0} ist {1}, -Sales Orders,Kundenaufträge, +Sales Order,Auftrag, +Sales Order Item,Auftrags-Artikel, +Sales Order required for Item {0},Auftrag für den Artikel {0} erforderlich, +Sales Order to Payment,Vom Auftrag zum Zahlungseinang, +Sales Order {0} is not submitted,Auftrag {0} wurde nicht übertragen, +Sales Order {0} is not valid,Auftrag {0} ist nicht gültig, +Sales Order {0} is {1},Auftrag {0} ist {1}, +Sales Orders,Aufträge, Sales Partner,Vertriebspartner, Sales Pipeline,Vertriebspipeline, Sales Price List,Verkaufspreisliste, @@ -2541,7 +2541,7 @@ Sales Team,Verkaufsteam, Sales User,Nutzer Vertrieb, Sales and Returns,Verkauf und Retouren, Sales campaigns.,Vertriebskampagnen, -Sales orders are not available for production,Kundenaufträge sind für die Produktion nicht verfügbar, +Sales orders are not available for production,Aufträge sind für die Produktion nicht verfügbar, Salutation,Anrede, Same Company is entered more than once,Das selbe Unternehmen wurde mehrfach angegeben, Same item cannot be entered multiple times.,Das gleiche Einzelteil kann nicht mehrfach eingegeben werden., @@ -2650,7 +2650,7 @@ Serial No {0} not found,Seriennummer {0} wurde nicht gefunden, Serial No {0} not in stock,Seriennummer {0} ist nicht auf Lager, Serial No {0} quantity {1} cannot be a fraction,Seriennummer {0} mit Menge {1} kann nicht eine Teilmenge sein, Serial Nos Required for Serialized Item {0},Seriennummern sind erforderlich für den Artikel mit Seriennummer {0}, -Serial Number: {0} is already referenced in Sales Invoice: {1},Seriennummer: {0} wird bereits in der Verkaufsrechnung referenziert: {1}, +Serial Number: {0} is already referenced in Sales Invoice: {1},Seriennummer: {0} wird bereits in der Ausgangsrechnung referenziert: {1}, Serial Numbers,Seriennummer, Serial Numbers in row {0} does not match with Delivery Note,Seriennummern in Zeile {0} stimmt nicht mit der Lieferschein überein, Serial no {0} has been already returned,Seriennr. {0} wurde bereits zurückgegeben, @@ -3278,7 +3278,7 @@ Warning: Invalid SSL certificate on attachment {0},Warnung: Ungültiges SSL-Zert Warning: Invalid attachment {0},Warnung: Ungültige Anlage {0}, Warning: Leave application contains following block dates,Achtung: Die Urlaubsverwaltung enthält die folgenden gesperrten Daten, Warning: Material Requested Qty is less than Minimum Order Qty,Achtung : Materialanfragemenge ist geringer als die Mindestbestellmenge, -Warning: Sales Order {0} already exists against Customer's Purchase Order {1},Warnung: Kundenauftrag {0} zu Kunden-Bestellung bereits vorhanden {1}, +Warning: Sales Order {0} already exists against Customer's Purchase Order {1},Warnung: Auftrag {0} zu Kunden-Bestellung bereits vorhanden {1}, Warning: System will not check overbilling since amount for Item {0} in {1} is zero,"Achtung: Das System erkennt keine überhöhten Rechnungen, da der Betrag für Artikel {0} in {1} gleich Null ist", Warranty,Garantie, Warranty Claim,Garantieanspruch, @@ -3308,7 +3308,7 @@ Work Order already created for all items with BOM,Arbeitsauftrag wurde bereits f Work Order cannot be raised against a Item Template,Arbeitsauftrag kann nicht gegen eine Artikelbeschreibungsvorlage ausgelöst werden, Work Order has been {0},Arbeitsauftrag wurde {0}, Work Order not created,Arbeitsauftrag wurde nicht erstellt, -Work Order {0} must be cancelled before cancelling this Sales Order,Der Arbeitsauftrag {0} muss vor dem Stornieren dieses Kundenauftrags storniert werden, +Work Order {0} must be cancelled before cancelling this Sales Order,Der Arbeitsauftrag {0} muss vor dem Stornieren dieses Auftrags storniert werden, Work Order {0} must be submitted,Arbeitsauftrag {0} muss eingereicht werden, Work Orders Created: {0},Arbeitsaufträge erstellt: {0}, Work Summary for {0},Arbeitszusammenfassung für {0}, @@ -3382,9 +3382,9 @@ on,Am, {0} Student Groups created.,{0} Schülergruppen erstellt., {0} Students have been enrolled,{0} Studenten wurden angemeldet, {0} against Bill {1} dated {2},{0} zu Rechnung {1} vom {2}, -{0} against Purchase Order {1},{0} zu Lieferantenauftrag {1}, -{0} against Sales Invoice {1},{0} zu Verkaufsrechnung {1}, -{0} against Sales Order {1},{0} zu Kundenauftrag{1}, +{0} against Purchase Order {1},{0} zu Bestellung {1}, +{0} against Sales Invoice {1},{0} zu Ausgangsrechnung {1}, +{0} against Sales Order {1},{0} zu Auftrag{1}, {0} already allocated for Employee {1} for period {2} to {3},{0} bereits an Mitarbeiter {1} zugeteilt für den Zeitraum {2} bis {3}, {0} applicable after {1} working days,{0} gilt nach {1} Werktagen, {0} asset cannot be transferred,{0} Anlagevermögen kann nicht übertragen werden, @@ -3833,7 +3833,7 @@ Location,Ort, Log Type is required for check-ins falling in the shift: {0}.,Der Protokolltyp ist für Eincheckvorgänge in der Schicht erforderlich: {0}., Looks like someone sent you to an incomplete URL. Please ask them to look into it.,"Sieht aus wie jemand, den Sie zu einer unvollständigen URL gesendet. Bitte fragen Sie sie, sich in sie.", Make Journal Entry,Buchungssatz erstellen, -Make Purchase Invoice,Einkaufsrechnung erstellen, +Make Purchase Invoice,Eingangsrechnung erstellen, Manufactured,Hergestellt, Mark Work From Home,Markieren Sie Work From Home, Master,Vorlage, @@ -4302,7 +4302,7 @@ Row {}: Asset Naming Series is mandatory for the auto creation for item {},Zeile Assets not created for {0}. You will have to create asset manually.,Assets nicht für {0} erstellt. Sie müssen das Asset manuell erstellen., {0} {1} has accounting entries in currency {2} for company {3}. Please select a receivable or payable account with currency {2}.,{0} {1} hat Buchhaltungseinträge in Währung {2} für Firma {3}. Bitte wählen Sie ein Debitoren- oder Kreditorenkonto mit der Währung {2} aus., Invalid Account,Ungültiger Account, -Purchase Order Required,Lieferantenauftrag erforderlich, +Purchase Order Required,Bestellung erforderlich, Purchase Receipt Required,Kaufbeleg notwendig, Account Missing,Konto fehlt, Requested,Angefordert, @@ -4889,7 +4889,7 @@ Transaction Currency,Transaktionswährung, Subscription Plans,Abonnementpläne, SWIFT Number,SWIFT-Nummer, Recipient Message And Payment Details,Empfänger der Nachricht und Zahlungsdetails, -Make Sales Invoice,Verkaufsrechnung erstellen, +Make Sales Invoice,Ausgangsrechnung erstellen, Mute Email,Mute Email, payment_url,payment_url, Payment Gateway Details,Payment Gateway-Details, @@ -4988,7 +4988,7 @@ Apply Tax Withholding Amount,Steuereinbehaltungsbetrag anwenden, Accounting Dimensions ,Buchhaltung Dimensionen, Supplier Invoice Details,Lieferant Rechnungsdetails, Supplier Invoice Date,Lieferantenrechnungsdatum, -Return Against Purchase Invoice,Zurück zur Einkaufsrechnung, +Return Against Purchase Invoice,Zurück zur Eingangsrechnung, Select Supplier Address,Lieferantenadresse auswählen, Contact Person,Kontaktperson, Select Shipping Address,Lieferadresse auswählen, @@ -5081,7 +5081,7 @@ Service End Date,Service-Enddatum, Allow Zero Valuation Rate,Nullbewertung zulassen, Item Tax Rate,Artikelsteuersatz, Tax detail table fetched from item master as a string and stored in this field.\nUsed for Taxes and Charges,Die Tabelle Steuerdetails wird aus dem Artikelstamm als Zeichenfolge entnommen und in diesem Feld gespeichert. Wird verwendet für Steuern und Abgaben, -Purchase Order Item,Lieferantenauftrags-Artikel, +Purchase Order Item,Bestellartikel, Purchase Receipt Detail,Kaufbelegdetail, Item Weight Details,Artikel Gewicht Details, Weight Per Unit,Gewicht pro Einheit, @@ -5110,10 +5110,10 @@ Include Payment (POS),(POS) Zahlung einschließen, Offline POS Name,Offline-Verkaufsstellen-Name, Is Return (Credit Note),ist Rücklieferung (Gutschrift), Return Against Sales Invoice,Zurück zur Kundenrechnung, -Update Billed Amount in Sales Order,Aktualisierung des Rechnungsbetrags im Kundenauftrag, -Customer PO Details,Kundenauftragsdetails, -Customer's Purchase Order,Kundenauftrag, -Customer's Purchase Order Date,Kundenauftragsdatum, +Update Billed Amount in Sales Order,Aktualisierung des Rechnungsbetrags im Auftrag, +Customer PO Details,Auftragsdetails, +Customer's Purchase Order,Bestellung des Kunden, +Customer's Purchase Order Date,Bestelldatum des Kunden, Customer Address,Kundenadresse, Shipping Address Name,Lieferadresse Bezeichnung, Company Address Name,Bezeichnung der Anschrift des Unternehmens, @@ -5483,7 +5483,7 @@ Sets 'Warehouse' in each row of the Items table.,Legt 'Warehouse' in jed Supply Raw Materials,Rohmaterial bereitstellen, Purchase Order Pricing Rule,Preisregel für Bestellungen, Set Reserve Warehouse,Legen Sie das Reservelager fest, -In Words will be visible once you save the Purchase Order.,"""In Worten"" wird sichtbar, sobald Sie den Lieferantenauftrag speichern.", +In Words will be visible once you save the Purchase Order.,"""In Worten"" wird sichtbar, sobald Sie die Bestellung speichern.", Advance Paid,Angezahlt, Tracking,Verfolgung, % Billed,% verrechnet, @@ -5500,7 +5500,7 @@ Against Blanket Order,Gegen Pauschalauftrag, Blanket Order,Blankoauftrag, Blanket Order Rate,Pauschale Bestellrate, Returned Qty,Zurückgegebene Menge, -Purchase Order Item Supplied,Lieferantenauftrags-Artikel geliefert, +Purchase Order Item Supplied,Bestellartikel geliefert, BOM Detail No,Stückliste Detailnr., Stock Uom,Lagermaßeinheit, Raw Material Item Code,Rohmaterial-Artikelnummer, @@ -6011,7 +6011,7 @@ Get financial breakup of Taxes and charges data by Amazon ,Erhalten Sie finanzie Sync Products,Produkte synchronisieren, Always sync your products from Amazon MWS before synching the Orders details,"Synchronisieren Sie Ihre Produkte immer mit Amazon MWS, bevor Sie die Bestelldetails synchronisieren", Sync Orders,Bestellungen synchronisieren, -Click this button to pull your Sales Order data from Amazon MWS.,"Klicken Sie auf diese Schaltfläche, um Ihre Kundenauftragsdaten von Amazon MWS abzurufen.", +Click this button to pull your Sales Order data from Amazon MWS.,"Klicken Sie auf diese Schaltfläche, um Ihre Auftragsdaten von Amazon MWS abzurufen.", Enable Scheduled Sync,Aktivieren Sie die geplante Synchronisierung, Check this to enable a scheduled Daily synchronization routine via scheduler,"Aktivieren Sie diese Option, um eine geplante tägliche Synchronisierungsroutine über den Scheduler zu aktivieren", Max Retry Limit,Max. Wiederholungslimit, @@ -6060,14 +6060,14 @@ Customer Settings,Kundeneinstellungen, Default Customer,Standardkunde, Customer Group will set to selected group while syncing customers from Shopify,Die Kundengruppe wird bei der Synchronisierung von Kunden von Shopify auf die ausgewählte Gruppe festgelegt, For Company,Für Unternehmen, -Cash Account will used for Sales Invoice creation,Cash Account wird für die Erstellung von Verkaufsrechnungen verwendet, +Cash Account will used for Sales Invoice creation,Cash Account wird für die Erstellung von Ausgangsrechnungen verwendet, Update Price from Shopify To ERPNext Price List,Preis von Shopify auf ERPNext Preisliste aktualisieren, -Default Warehouse to to create Sales Order and Delivery Note,Standard Warehouse zum Erstellen von Kundenauftrag und Lieferschein, -Sales Order Series,Kundenauftragsreihen, +Default Warehouse to to create Sales Order and Delivery Note,Standard Lager zum Erstellen von Auftrag und Lieferschein, +Sales Order Series,Auftragsnummernkreis, Import Delivery Notes from Shopify on Shipment,Lieferscheine von Shopify bei Versand importieren, Delivery Note Series,Lieferschein-Serie, -Import Sales Invoice from Shopify if Payment is marked,"Verkaufsrechnung aus Shopify importieren, wenn Zahlung markiert ist", -Sales Invoice Series,Verkaufsrechnung Serie, +Import Sales Invoice from Shopify if Payment is marked,"Ausgangsrechnung aus Shopify importieren, wenn Zahlung markiert ist", +Sales Invoice Series,Nummernkreis Ausgangsrechnung, Shopify Tax Account,Steuerkonto erstellen, Shopify Tax/Shipping Title,Steuern / Versand Titel, ERPNext Account,ERPNext Konto, @@ -6106,13 +6106,13 @@ API consumer secret,API-Konsumentengeheimnis, Tax Account,Steuerkonto, Freight and Forwarding Account,Fracht- und Speditionskonto, Creation User,Erstellungsbenutzer, -"The user that will be used to create Customers, Items and Sales Orders. This user should have the relevant permissions.","Der Benutzer, der zum Erstellen von Kunden, Artikeln und Kundenaufträgen verwendet wird. Dieser Benutzer sollte über die entsprechenden Berechtigungen verfügen.", -"This warehouse will be used to create Sales Orders. The fallback warehouse is ""Stores"".",Dieses Lager wird zum Erstellen von Kundenaufträgen verwendet. Das Fallback-Lager ist "Stores"., +"The user that will be used to create Customers, Items and Sales Orders. This user should have the relevant permissions.","Der Benutzer, der zum Erstellen von Kunden, Artikeln und Aufträgen verwendet wird. Dieser Benutzer sollte über die entsprechenden Berechtigungen verfügen.", +"This warehouse will be used to create Sales Orders. The fallback warehouse is ""Stores"".",Dieses Lager wird zum Erstellen von Aufträgen verwendet. Das Fallback-Lager ist "Stores"., "The fallback series is ""SO-WOO-"".",Die Fallback-Serie heißt "SO-WOO-"., -This company will be used to create Sales Orders.,Diese Firma wird zum Erstellen von Kundenaufträgen verwendet., +This company will be used to create Sales Orders.,Diese Firma wird zum Erstellen von Aufträgen verwendet., Delivery After (Days),Lieferung nach (Tage), -This is the default offset (days) for the Delivery Date in Sales Orders. The fallback offset is 7 days from the order placement date.,Dies ist der Standardversatz (Tage) für das Lieferdatum in Kundenaufträgen. Der Fallback-Offset beträgt 7 Tage ab Bestelldatum., -"This is the default UOM used for items and Sales orders. The fallback UOM is ""Nos"".","Dies ist die Standard-ME, die für Artikel und Kundenaufträge verwendet wird. Die Fallback-UOM lautet "Nos".", +This is the default offset (days) for the Delivery Date in Sales Orders. The fallback offset is 7 days from the order placement date.,Dies ist der Standardversatz (Tage) für das Lieferdatum in Aufträgen. Der Fallback-Offset beträgt 7 Tage ab Bestelldatum., +"This is the default UOM used for items and Sales orders. The fallback UOM is ""Nos"".","Dies ist die Standard-ME, die für Artikel und Aufträge verwendet wird. Die Fallback-UOM lautet "Nos".", Endpoints,Endpunkte, Endpoint,Endpunkt, Antibiotic Name,Antibiotika-Name, @@ -6230,8 +6230,8 @@ Appointment Reminder,Termin Erinnerung, Reminder Message,Erinnerungsmeldung, Remind Before,Vorher erinnern, Laboratory Settings,Laboreinstellungen, -Create Lab Test(s) on Sales Invoice Submission,Erstellen Sie Labortests für die Übermittlung von Verkaufsrechnungen, -Checking this will create Lab Test(s) specified in the Sales Invoice on submission.,"Wenn Sie dies aktivieren, werden Labortests erstellt, die bei der Übermittlung in der Verkaufsrechnung angegeben sind.", +Create Lab Test(s) on Sales Invoice Submission,Erstellen Sie Labortests für die Übermittlung von Ausgangsrechnungen, +Checking this will create Lab Test(s) specified in the Sales Invoice on submission.,"Wenn Sie dies aktivieren, werden Labortests erstellt, die bei der Übermittlung in der Ausgangsrechnung angegeben sind.", Create Sample Collection document for Lab Test,Erstellen Sie ein Probensammeldokument für den Labortest, Checking this will create a Sample Collection document every time you create a Lab Test,"Wenn Sie dies aktivieren, wird jedes Mal, wenn Sie einen Labortest erstellen, ein Probensammeldokument erstellt", Employee name and designation in print,Name und Bezeichnung des Mitarbeiters im Druck, @@ -6315,7 +6315,7 @@ Therapy,Therapie, Get Prescribed Therapies,Holen Sie sich verschriebene Therapien, Appointment Datetime,Termin Datum / Uhrzeit, Duration (In Minutes),Dauer (in Minuten), -Reference Sales Invoice,Referenzverkaufsrechnung, +Reference Sales Invoice,Referenzausgangsrechnung, More Info,Weitere Informationen, Referring Practitioner,Überweisender Praktiker, Reminded,Erinnert, @@ -7268,7 +7268,7 @@ Default Warehouses for Production,Standardlager für die Produktion, Default Work In Progress Warehouse,Standard-Fertigungslager, Default Finished Goods Warehouse,Standard-Fertigwarenlager, Default Scrap Warehouse,Standard-Schrottlager, -Overproduction Percentage For Sales Order,Überproduktionsprozentsatz für Kundenauftrag, +Overproduction Percentage For Sales Order,Überproduktionsprozentsatz für Auftrag, Overproduction Percentage For Work Order,Überproduktionsprozentsatz für Arbeitsauftrag, Other Settings,Weitere Einstellungen, Update BOM Cost Automatically,Stücklisten-Kosten automatisch aktualisieren, @@ -7281,7 +7281,7 @@ Default Workstation,Standard-Arbeitsplatz, Production Plan,Produktionsplan, MFG-PP-.YYYY.-,MFG-PP-.YYYY.-, Get Items From,Holen Sie Elemente aus, -Get Sales Orders,Kundenaufträge aufrufen, +Get Sales Orders,Aufträge aufrufen, Material Request Detail,Materialanforderungsdetail, Get Material Request,Get-Material anfordern, Material Requests,Materialwünsche, @@ -7304,8 +7304,8 @@ Quantity and Description,Menge und Beschreibung, material_request_item,material_request_item, Product Bundle Item,Produkt-Bundle-Artikel, Production Plan Material Request,Produktionsplan-Material anfordern, -Production Plan Sales Order,Produktionsplan für Kundenauftrag, -Sales Order Date,Kundenauftrags-Datum, +Production Plan Sales Order,Produktionsplan für Auftrag, +Sales Order Date,Auftragsdatum, Routing Name,Routing-Name, MFG-WO-.YYYY.-,MFG-WO-.YYYY.-, Item To Manufacture,Zu fertigender Artikel, @@ -7482,12 +7482,12 @@ Copied From,Kopiert von, Start and End Dates,Start- und Enddatum, Actual Time (in Hours),Tatsächliche Zeit (in Stunden), Costing and Billing,Kalkulation und Abrechnung, -Total Costing Amount (via Timesheets),Gesamtkalkulationsbetrag (über Arbeitszeittabellen), -Total Expense Claim (via Expense Claims),Gesamtbetrag der Aufwandsabrechnung (über Aufwandsabrechnungen), -Total Purchase Cost (via Purchase Invoice),Summe Einkaufskosten (über Einkaufsrechnung), -Total Sales Amount (via Sales Order),Gesamtverkaufsbetrag (über Kundenauftrag), -Total Billable Amount (via Timesheets),Gesamter abrechenbarer Betrag (über Arbeitszeittabellen), -Total Billed Amount (via Sales Invoices),Gesamtabrechnungsbetrag (über Verkaufsrechnungen), +Total Costing Amount (via Timesheets),Gesamtkalkulationsbetrag (über Zeiterfassung), +Total Expense Claim (via Expense Claims),Gesamtbetrag der Auslagenabrechnung (über Auslagenabrechnungen), +Total Purchase Cost (via Purchase Invoice),Summe Einkaufskosten (über Eingangsrechnung), +Total Sales Amount (via Sales Order),Auftragssumme (über Auftrag), +Total Billable Amount (via Timesheets),Abrechenbare Summe (über Zeiterfassung), +Total Billed Amount (via Sales Invoices),Abgerechnete Summe (über Ausgangsrechnungen), Total Consumed Material Cost (via Stock Entry),Summe der verbrauchten Materialkosten (über die Bestandsbuchung), Gross Margin,Handelsspanne, Gross Margin %,Handelsspanne %, @@ -7497,9 +7497,9 @@ Frequency To Collect Progress,"Häufigkeit, um Fortschritte zu sammeln", Twice Daily,Zweimal täglich, First Email,Erste E-Mail, Second Email,Zweite E-Mail, -Time to send,Zeit zu senden, -Day to Send,Tag zum Senden, -Message will be sent to the users to get their status on the Project,"Es wird eine Nachricht an die Benutzer gesendet, um deren Status für das Projekt zu erhalten", +Time to send,Sendezeit, +Day to Send,Sendetag, +Message will be sent to the users to get their status on the Project,"Es wird eine Nachricht an die Benutzer gesendet, um über den Projektstatus zu informieren", Projects Manager,Projektleiter, Project Template,Projektvorlage, Project Template Task,Projektvorlagenaufgabe, @@ -7518,27 +7518,27 @@ Timeline,Zeitleiste, Expected Time (in hours),Voraussichtliche Zeit (in Stunden), % Progress,% Fortschritt, Is Milestone,Ist Meilenstein, -Task Description,Aufgabenbeschreibung, +Task Description,Vorgangsbeschreibung, Dependencies,Abhängigkeiten, -Dependent Tasks,Abhängige Aufgaben, +Dependent Tasks,Abhängige Vorgänge, Depends on Tasks,Abhängig von Vorgang, Actual Start Date (via Time Sheet),Das tatsächliche Startdatum (durch Zeiterfassung), Actual Time (in hours),Tatsächliche Zeit (in Stunden), Actual End Date (via Time Sheet),Das tatsächliche Enddatum (durch Zeiterfassung), -Total Costing Amount (via Time Sheet),Gesamtkostenbetrag (über Arbeitszeitblatt), -Total Expense Claim (via Expense Claim),Gesamtbetrag der Aufwandsabrechnung (über Aufwandsabrechnung), -Total Billing Amount (via Time Sheet),Gesamtrechnungsbetrag (über Arbeitszeitblatt), +Total Costing Amount (via Time Sheet),Gesamtkosten (über Zeiterfassung), +Total Expense Claim (via Expense Claim),Summe der Auslagen (über Auslagenabrechnung), +Total Billing Amount (via Time Sheet),Gesamtrechnungsbetrag (über Zeiterfassung), Review Date,Überprüfungsdatum, Closing Date,Abschlussdatum, Task Depends On,Vorgang hängt ab von, -Task Type,Aufgabentyp, +Task Type,Vorgangstyp, TS-.YYYY.-,TS-.YYYY.-, Employee Detail,Mitarbeiterdetails, Billing Details,Rechnungsdetails, -Total Billable Hours,Insgesamt abrechenbare Stunden, -Total Billed Hours,Insgesamt Angekündigt Stunden, +Total Billable Hours,Summe abrechenbare Stunden, +Total Billed Hours,Summe Angekündigt Stunden, Total Costing Amount,Gesamtkalkulation Betrag, -Total Billable Amount,Insgesamt abrechenbare Betrag, +Total Billable Amount,Summe abrechenbare Betrag, Total Billed Amount,Gesamtrechnungsbetrag, % Amount Billed,% des Betrages berechnet, Hrs,Std, @@ -7555,13 +7555,13 @@ Quality Goal,Qualitätsziel, Monitoring Frequency,Überwachungsfrequenz, Weekday,Wochentag, Objectives,Ziele, -Quality Goal Objective,Qualitätsziel Ziel, +Quality Goal Objective,Qualitätsziel, Objective,Zielsetzung, Agenda,Agenda, -Minutes,Protokoll, +Minutes,Protokolle, Quality Meeting Agenda,Qualitätstreffen Agenda, Quality Meeting Minutes,Qualitätssitzungsprotokoll, -Minute,Minute, +Minute,Protokoll, Parent Procedure,Übergeordnetes Verfahren, Processes,Prozesse, Quality Procedure Process,Qualitätsprozess, @@ -7627,7 +7627,7 @@ Restaurant Menu Item,Restaurant-Menüpunkt, Restaurant Order Entry,Restaurantbestellung, Restaurant Table,Restaurant-Tisch, Click Enter To Add,Klicken Sie zum Hinzufügen auf Hinzufügen., -Last Sales Invoice,Letzte Verkaufsrechnung, +Last Sales Invoice,Letzte Ausgangsrechnung, Current Order,Aktueller Auftrag, Restaurant Order Entry Item,Restaurantbestellzugangsposten, Served,Serviert, @@ -7639,7 +7639,7 @@ Reservation Time,Reservierungszeit, Reservation End Time,Reservierungsendzeit, No of Seats,Anzahl der Sitze, Minimum Seating,Mindestbestuhlung, -"Keep Track of Sales Campaigns. Keep track of Leads, Quotations, Sales Order etc from Campaigns to gauge Return on Investment. ","Verkaufskampagne verfolgen: Leads, Angebote, Kundenaufträge usw. von Kampagnen beobachten um die Kapitalverzinsung (RoI) zu messen.", +"Keep Track of Sales Campaigns. Keep track of Leads, Quotations, Sales Order etc from Campaigns to gauge Return on Investment. ","Verkaufskampagne verfolgen: Leads, Angebote, Aufträge usw. von Kampagnen beobachten um die Kapitalverzinsung (RoI) zu messen.", SAL-CAM-.YYYY.-,SAL-CAM-.YYYY.-, Campaign Schedules,Kampagnenpläne, Buyer of Goods and Services.,Käufer von Waren und Dienstleistungen., @@ -7647,8 +7647,8 @@ CUST-.YYYY.-,CUST-.YYYY.-, Default Company Bank Account,Standard-Bankkonto des Unternehmens, From Lead,Von Lead, Account Manager,Buchhalter, -Allow Sales Invoice Creation Without Sales Order,Ermöglichen Sie die Erstellung von Kundenrechnungen ohne Kundenauftrag, -Allow Sales Invoice Creation Without Delivery Note,Ermöglichen Sie die Erstellung einer Verkaufsrechnung ohne Lieferschein, +Allow Sales Invoice Creation Without Sales Order,Ermöglichen Sie die Erstellung von Kundenrechnungen ohne Auftrag, +Allow Sales Invoice Creation Without Delivery Note,Ermöglichen Sie die Erstellung einer Ausgangsrechnung ohne Lieferschein, Default Price List,Standardpreisliste, Primary Address and Contact Detail,Primäre Adresse und Kontaktdetails, "Select, to make the customer searchable with these fields","Wählen Sie, um den Kunden mit diesen Feldern durchsuchbar zu machen", @@ -7665,7 +7665,7 @@ Commission Rate,Provisionssatz, Sales Team Details,Verkaufsteamdetails, Customer POS id,Kunden-POS-ID, Customer Credit Limit,Kundenkreditlimit, -Bypass Credit Limit Check at Sales Order,Kreditlimitprüfung im Kundenauftrag umgehen, +Bypass Credit Limit Check at Sales Order,Kreditlimitprüfung im Auftrag umgehen, Industry Type,Wirtschaftsbranche, MAT-INS-.YYYY.-,MAT-INS-.YYYY.-, Installation Date,Datum der Installation, @@ -7701,16 +7701,16 @@ Against Docname,Zu Dokumentenname, Additional Notes,Zusätzliche Bemerkungen, SAL-ORD-.YYYY.-,SAL-ORD-.YYYY.-, Skip Delivery Note,Lieferschein überspringen, -In Words will be visible once you save the Sales Order.,"""In Worten"" wird sichtbar, sobald Sie den Kundenauftrag speichern.", -Track this Sales Order against any Project,Diesen Kundenauftrag in jedem Projekt nachverfolgen, +In Words will be visible once you save the Sales Order.,"""In Worten"" wird sichtbar, sobald Sie den Auftrag speichern.", +Track this Sales Order against any Project,Diesen Auftrag in jedem Projekt nachverfolgen, Billing and Delivery Status,Abrechnungs- und Lieferstatus, Not Delivered,Nicht geliefert, Fully Delivered,Komplett geliefert, Partly Delivered,Teilweise geliefert, Not Applicable,Nicht andwendbar, % Delivered,% geliefert, -% of materials delivered against this Sales Order,% der für diesen Kundenauftrag gelieferten Materialien, -% of materials billed against this Sales Order,% der Materialien welche zu diesem Kundenauftrag gebucht wurden, +% of materials delivered against this Sales Order,% der für diesen Auftrag gelieferten Materialien, +% of materials billed against this Sales Order,% der Materialien welche zu diesem Auftrag gebucht wurden, Not Billed,Nicht abgerechnet, Fully Billed,Voll berechnet, Partly Billed,Teilweise abgerechnet, @@ -7845,13 +7845,13 @@ Bank Balance,Kontostand, Bank Credit Balance,Bankguthaben, Receivables,Forderungen, Payables,Verbindlichkeiten, -Sales Orders to Bill,Kundenaufträge an Rechnung, +Sales Orders to Bill,Aufträge an Rechnung, Purchase Orders to Bill,Bestellungen an Rechnung, -New Sales Orders,Neue Kundenaufträge, +New Sales Orders,Neue Aufträge, New Purchase Orders,Neue Bestellungen an Lieferanten, -Sales Orders to Deliver,Kundenaufträge zu liefern, -Purchase Orders to Receive,Bestellungen zu empfangen, -New Purchase Invoice,Neue Kaufrechnung, +Sales Orders to Deliver,Auszuliefernde Aufträge, +Purchase Orders to Receive,Anzuliefernde Bestellungen, +New Purchase Invoice,Neue Ausgangsrechnung, New Quotations,Neue Angebote, Open Quotations,Angebote öffnen, Open Issues,Offene Punkte, @@ -7972,7 +7972,7 @@ MAT-DN-.YYYY.-,MAT-DN-.YYYY.-, Is Return,Ist Rückgabe, Issue Credit Note,Gutschrift ausgeben, Return Against Delivery Note,Zurück zum Lieferschein, -Customer's Purchase Order No,Kundenauftragsnr., +Customer's Purchase Order No,Bestell Nr. des Kunden, Billing Address Name,Name der Rechnungsadresse, Required only for sample item.,Nur erforderlich für Probeartikel., "If you have created a standard template in Sales Taxes and Charges Template, select one and click on the button below.","Wenn eine Standardvorlage unter den Vorlagen ""Steuern und Abgaben beim Verkauf"" erstellt wurde, bitte eine Vorlage auswählen und auf die Schaltfläche unten klicken.", @@ -7989,8 +7989,8 @@ Installation Status,Installationsstatus, Excise Page Number,Seitenzahl entfernen, Instructions,Anweisungen, From Warehouse,Ab Lager, -Against Sales Order,Zu Kundenauftrag, -Against Sales Order Item,Zu Kundenauftrags-Position, +Against Sales Order,Zu Auftrag, +Against Sales Order Item,Zu Auftragsposition, Against Sales Invoice,Zu Ausgangsrechnung, Against Sales Invoice Item,Zu Ausgangsrechnungs-Position, Available Batch Qty at From Warehouse,Verfügbare Chargenmenge im Ausgangslager, @@ -8063,7 +8063,7 @@ Serial Number Series,Serie der Seriennummer, "Example: ABCD.#####\nIf series is set and Serial No is not mentioned in transactions, then automatic serial number will be created based on this series. If you always want to explicitly mention Serial Nos for this item. leave this blank.","Beispiel: ABCD.##### \n Wenn ""Serie"" eingestellt ist und ""Seriennummer"" in den Transaktionen nicht aufgeführt ist, dann wird eine Seriennummer automatisch auf der Grundlage dieser Serie erstellt. Wenn immer explizit Seriennummern für diesen Artikel aufgeführt werden sollen, muss das Feld leer gelassen werden.", Variants,Varianten, Has Variants,Hat Varianten, -"If this item has variants, then it cannot be selected in sales orders etc.","Wenn dieser Artikel Varianten hat, dann kann er bei den Kundenaufträgen, etc. nicht ausgewählt werden", +"If this item has variants, then it cannot be selected in sales orders etc.","Wenn dieser Artikel Varianten hat, dann kann er bei den Aufträgen, etc. nicht ausgewählt werden", Variant Based On,Variante basierend auf, Item Attribute,Artikelattribut, "Sales, Purchase, Accounting Defaults","Verkauf, Einkauf, Buchhaltungsvorgaben", @@ -8529,7 +8529,7 @@ Open Work Orders,Arbeitsaufträge öffnen, Qty to Deliver,Zu liefernde Menge, Patient Appointment Analytics,Analyse von Patiententerminen, Payment Period Based On Invoice Date,Zahlungszeitraum basierend auf Rechnungsdatum, -Pending SO Items For Purchase Request,Ausstehende Artikel aus Kundenaufträgen für Lieferantenanfrage, +Pending SO Items For Purchase Request,Ausstehende Artikel aus Aufträgen für Lieferantenanfrage, Procurement Tracker,Beschaffungs-Tracker, Product Bundle Balance,Produkt-Bundle-Balance, Production Analytics,Produktions-Analysen, @@ -8544,7 +8544,7 @@ Purchase Invoice Trends,Trendanalyse Eingangsrechnungen, Qty to Receive,Anzunehmende Menge, Received Qty Amount,Erhaltene Menge Menge, Billed Qty,Rechnungsmenge, -Purchase Order Trends,Entwicklung Lieferantenaufträge, +Purchase Order Trends,Entwicklung Bestellungen, Purchase Receipt Trends,Trendanalyse Kaufbelege, Purchase Register,Übersicht über Einkäufe, Quotation Trends,Trendanalyse Angebote, @@ -8555,7 +8555,7 @@ Qty to Transfer,Zu versendende Menge, Salary Register,Gehalt Register, Sales Analytics,Vertriebsanalyse, Sales Invoice Trends,Ausgangsrechnung-Trendanalyse, -Sales Order Trends,Trendanalyse Kundenaufträge, +Sales Order Trends,Trendanalyse Aufträge, Sales Partner Commission Summary,Zusammenfassung der Vertriebspartnerprovision, Sales Partner Target Variance based on Item Group,Zielabweichung des Vertriebspartners basierend auf Artikelgruppe, Sales Partner Transaction Summary,Sales Partner Transaction Summary, @@ -8706,7 +8706,7 @@ Dunning Letter,Mahnbrief, Reference Detail No,Referenz Detail Nr, Custom Remarks,Benutzerdefinierte Bemerkungen, Please select a Company first.,Bitte wählen Sie zuerst eine Firma aus., -"Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice, Journal Entry or Dunning","Zeile # {0}: Der Referenzdokumenttyp muss Kundenauftrag, Verkaufsrechnung, Journaleintrag oder Mahnwesen sein", +"Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice, Journal Entry or Dunning","Zeile # {0}: Der Referenzdokumenttyp muss Auftrag, Ausgangsrechnung, Journaleintrag oder Mahnwesen sein", POS Closing Entry,POS Closing Entry, POS Opening Entry,POS-Eröffnungseintrag, POS Transactions,POS-Transaktionen, @@ -8716,7 +8716,7 @@ Closing Amount,Schlussbetrag, POS Closing Entry Taxes,POS Closing Entry Taxes, POS Invoice,POS-Rechnung, ACC-PSINV-.YYYY.-,ACC-PSINV-.YYYY.-, -Consolidated Sales Invoice,Konsolidierte Verkaufsrechnung, +Consolidated Sales Invoice,Konsolidierte Ausgangsrechnung, Return Against POS Invoice,Gegen POS-Rechnung zurücksenden, Consolidated,Konsolidiert, POS Invoice Item,POS-Rechnungsposition, @@ -8826,7 +8826,7 @@ Depreciation Posting Date,Buchungsdatum der Abschreibung, "By default, the Supplier Name is set as per the Supplier Name entered. If you want Suppliers to be named by a ","Standardmäßig wird der Lieferantenname gemäß dem eingegebenen Lieferantennamen festgelegt. Wenn Sie möchten, dass Lieferanten von a benannt werden", choose the 'Naming Series' option.,Wählen Sie die Option "Naming Series"., Configure the default Price List when creating a new Purchase transaction. Item prices will be fetched from this Price List.,Konfigurieren Sie die Standardpreisliste beim Erstellen einer neuen Kauftransaktion. Artikelpreise werden aus dieser Preisliste abgerufen., -"If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in the Supplier master.","Wenn diese Option auf "Ja" konfiguriert ist, verhindert ERPNext, dass Sie eine Kaufrechnung oder einen Beleg erstellen können, ohne zuvor eine Bestellung zu erstellen. Diese Konfiguration kann für einen bestimmten Lieferanten überschrieben werden, indem das Kontrollkästchen "Erstellung von Einkaufsrechnungen ohne Bestellung zulassen" im Lieferantenstamm aktiviert wird.", +"If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in the Supplier master.","Wenn diese Option auf "Ja" konfiguriert ist, verhindert ERPNext, dass Sie eine Kaufrechnung oder einen Beleg erstellen können, ohne zuvor eine Bestellung zu erstellen. Diese Konfiguration kann für einen bestimmten Lieferanten überschrieben werden, indem das Kontrollkästchen "Erstellung von Eingangsrechnungen ohne Bestellung zulassen" im Lieferantenstamm aktiviert wird.", "If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in the Supplier master.","Wenn diese Option auf "Ja" konfiguriert ist, verhindert ERPNext, dass Sie eine Kaufrechnung erstellen können, ohne zuvor einen Kaufbeleg zu erstellen. Diese Konfiguration kann für einen bestimmten Lieferanten überschrieben werden, indem das Kontrollkästchen "Erstellung von Kaufrechnungen ohne Kaufbeleg zulassen" im Lieferantenstamm aktiviert wird.", Quantity & Stock,Menge & Lager, Call Details,Anrufdetails, @@ -8903,7 +8903,7 @@ Set the Out Patient Consulting Charge for this Practitioner.,Legen Sie die Gebü "If checked, a customer will be created for every Patient. Patient Invoices will be created against this Customer. You can also select existing Customer while creating a Patient. This field is checked by default.","Wenn diese Option aktiviert ist, wird für jeden Patienten ein Kunde erstellt. Für diesen Kunden werden Patientenrechnungen erstellt. Sie können beim Erstellen eines Patienten auch einen vorhandenen Kunden auswählen. Dieses Feld ist standardmäßig aktiviert.", Collect Registration Fee,Registrierungsgebühr sammeln, "If your Healthcare facility bills registrations of Patients, you can check this and set the Registration Fee in the field below. Checking this will create new Patients with a Disabled status by default and will only be enabled after invoicing the Registration Fee.","Wenn Ihre Gesundheitseinrichtung die Registrierung von Patienten in Rechnung stellt, können Sie dies überprüfen und die Registrierungsgebühr im Feld unten festlegen. Wenn Sie dies aktivieren, werden standardmäßig neue Patienten mit dem Status "Deaktiviert" erstellt und erst nach Rechnungsstellung der Registrierungsgebühr aktiviert.", -Checking this will automatically create a Sales Invoice whenever an appointment is booked for a Patient.,"Wenn Sie dies aktivieren, wird automatisch eine Verkaufsrechnung erstellt, wenn ein Termin für einen Patienten gebucht wird.", +Checking this will automatically create a Sales Invoice whenever an appointment is booked for a Patient.,"Wenn Sie dies aktivieren, wird automatisch eine Ausgangsrechnung erstellt, wenn ein Termin für einen Patienten gebucht wird.", Healthcare Service Items,Artikel im Gesundheitswesen, "You can create a service item for Inpatient Visit Charge and set it here. Similarly, you can set up other Healthcare Service Items for billing in this section. Click ",Sie können ein Serviceelement für die Gebühr für stationäre Besuche erstellen und hier festlegen. Ebenso können Sie in diesem Abschnitt andere Gesundheitsposten für die Abrechnung einrichten. Klicken, Set up default Accounts for the Healthcare Facility,Richten Sie Standardkonten für die Gesundheitseinrichtung ein, @@ -8958,7 +8958,7 @@ Lab Test Group Template,Labortestgruppenvorlage, Add New Line,Neue Zeile hinzufügen, Secondary UOM,Sekundäre UOM, "Single: Results which require only a single input.\n
\nCompound: Results which require multiple event inputs.\n
\nDescriptive: Tests which have multiple result components with manual result entry.\n
\nGrouped: Test templates which are a group of other test templates.\n
\nNo Result: Tests with no results, can be ordered and billed but no Lab Test will be created. e.g.. Sub Tests for Grouped results","Single : Ergebnisse, die nur eine einzige Eingabe erfordern.
Verbindung : Ergebnisse, die mehrere Ereigniseingaben erfordern.
Beschreibend : Tests mit mehreren Ergebniskomponenten mit manueller Ergebniseingabe.
Gruppiert : Testvorlagen, die eine Gruppe anderer Testvorlagen sind.
Kein Ergebnis : Tests ohne Ergebnisse können bestellt und in Rechnung gestellt werden, es wird jedoch kein Labortest erstellt. z.B. Untertests für gruppierte Ergebnisse", -"If unchecked, the item will not be available in Sales Invoices for billing but can be used in group test creation. ","Wenn diese Option deaktiviert ist, ist der Artikel in den Verkaufsrechnungen nicht zur Abrechnung verfügbar, kann jedoch für die Erstellung von Gruppentests verwendet werden.", +"If unchecked, the item will not be available in Sales Invoices for billing but can be used in group test creation. ","Wenn diese Option deaktiviert ist, ist der Artikel in den Ausgangsrechnungen nicht zur Abrechnung verfügbar, kann jedoch für die Erstellung von Gruppentests verwendet werden.", Description ,Beschreibung, Descriptive Test,Beschreibender Test, Group Tests,Gruppentests, @@ -9084,8 +9084,8 @@ Feedback By,Feedback von, Manufacturing Section,Fertigungsabteilung, "By default, the Customer Name is set as per the Full Name entered. If you want Customers to be named by a ","Standardmäßig wird der Kundenname gemäß dem eingegebenen vollständigen Namen festgelegt. Wenn Sie möchten, dass Kunden von a benannt werden", Configure the default Price List when creating a new Sales transaction. Item prices will be fetched from this Price List.,Konfigurieren Sie die Standardpreisliste beim Erstellen einer neuen Verkaufstransaktion. Artikelpreise werden aus dieser Preisliste abgerufen., -"If this option is configured 'Yes', ERPNext will prevent you from creating a Sales Invoice or Delivery Note without creating a Sales Order first. This configuration can be overridden for a particular Customer by enabling the 'Allow Sales Invoice Creation Without Sales Order' checkbox in the Customer master.","Wenn diese Option auf "Ja" konfiguriert ist, verhindert ERPNext, dass Sie eine Verkaufsrechnung oder einen Lieferschein erstellen, ohne zuvor einen Kundenauftrag zu erstellen. Diese Konfiguration kann für einen bestimmten Kunden überschrieben werden, indem das Kontrollkästchen "Erstellung von Verkaufsrechnungen ohne Kundenauftrag zulassen" im Kundenstamm aktiviert wird.", -"If this option is configured 'Yes', ERPNext will prevent you from creating a Sales Invoice without creating a Delivery Note first. This configuration can be overridden for a particular Customer by enabling the 'Allow Sales Invoice Creation Without Delivery Note' checkbox in the Customer master.","Wenn diese Option auf "Ja" konfiguriert ist, verhindert ERPNext, dass Sie eine Verkaufsrechnung erstellen, ohne zuvor einen Lieferschein zu erstellen. Diese Konfiguration kann für einen bestimmten Kunden überschrieben werden, indem das Kontrollkästchen "Erstellung von Verkaufsrechnungen ohne Lieferschein zulassen" im Kundenstamm aktiviert wird.", +"If this option is configured 'Yes', ERPNext will prevent you from creating a Sales Invoice or Delivery Note without creating a Sales Order first. This configuration can be overridden for a particular Customer by enabling the 'Allow Sales Invoice Creation Without Sales Order' checkbox in the Customer master.","Wenn diese Option auf "Ja" konfiguriert ist, verhindert ERPNext, dass Sie eine Ausgangsrechnung oder einen Lieferschein erstellen, ohne zuvor einen Auftrag zu erstellen. Diese Konfiguration kann für einen bestimmten Kunden überschrieben werden, indem das Kontrollkästchen "Erstellung von Ausgangsrechnung ohne Auftrag zulassen" im Kundenstamm aktiviert wird.", +"If this option is configured 'Yes', ERPNext will prevent you from creating a Sales Invoice without creating a Delivery Note first. This configuration can be overridden for a particular Customer by enabling the 'Allow Sales Invoice Creation Without Delivery Note' checkbox in the Customer master.","Wenn diese Option auf "Ja" konfiguriert ist, verhindert ERPNext, dass Sie eine Ausgangsrechnung erstellen, ohne zuvor einen Lieferschein zu erstellen. Diese Konfiguration kann für einen bestimmten Kunden überschrieben werden, indem das Kontrollkästchen "Erstellung von Ausgangsrechnungen ohne Lieferschein zulassen" im Kundenstamm aktiviert wird.", Default Warehouse for Sales Return,Standardlager für Retouren, Default In Transit Warehouse,Standard im Transit Warehouse, Enable Perpetual Inventory For Non Stock Items,Aktivieren Sie das ewige Inventar für nicht vorrätige Artikel, @@ -9114,7 +9114,7 @@ Set a Default Warehouse for Inventory Transactions. This will be fetched into th Choose between FIFO and Moving Average Valuation Methods. Click ,Wählen Sie zwischen FIFO- und Moving Average-Bewertungsmethoden. Klicken, to know more about them.,um mehr über sie zu erfahren., Show 'Scan Barcode' field above every child table to insert Items with ease.,"Zeigen Sie das Feld "Barcode scannen" über jeder untergeordneten Tabelle an, um Elemente problemlos einzufügen.", -"Serial numbers for stock will be set automatically based on the Items entered based on first in first out in transactions like Purchase/Sales Invoices, Delivery Notes, etc.","Seriennummern für Lagerbestände werden automatisch basierend auf den Artikeln festgelegt, die basierend auf First-In-First-Out in Transaktionen wie Kauf- / Verkaufsrechnungen, Lieferscheinen usw. eingegeben wurden.", +"Serial numbers for stock will be set automatically based on the Items entered based on first in first out in transactions like Purchase/Sales Invoices, Delivery Notes, etc.","Seriennummern für Lagerbestände werden automatisch basierend auf den Artikeln festgelegt, die basierend auf First-In-First-Out in Transaktionen wie Ein- und Ausgangsrechnungen, Lieferscheinen usw. eingegeben wurden.", "If blank, parent Warehouse Account or company default will be considered in transactions","Wenn leer, wird das übergeordnete Lagerkonto oder der Firmenstandard bei Transaktionen berücksichtigt", Service Level Agreement Details,Details zum Service Level Agreement, Service Level Agreement Status,Status des Service Level Agreements, @@ -9263,10 +9263,10 @@ Salary Payments via ECS,Gehaltszahlungen über ECS, Account No,Konto Nr, IFSC,IFSC, MICR,MICR, -Sales Order Analysis,Kundenauftragsanalyse, +Sales Order Analysis,Auftragsanalyse, Amount Delivered,Gelieferter Betrag, Delay (in Days),Verzögerung (in Tagen), -Group by Sales Order,Nach Kundenauftrag gruppieren, +Group by Sales Order,Nach Auftrag gruppieren, Sales Value,Verkaufswert, Stock Qty vs Serial No Count,Lagermenge vs Seriennummer, Serial No Count,Seriennummer nicht gezählt, @@ -9456,8 +9456,8 @@ Total Forecast (Future Data),Gesamtprognose (zukünftige Daten), Based On Document,Basierend auf Dokument, Based On Data ( in years ),Basierend auf Daten (in Jahren), Smoothing Constant,Glättungskonstante, -Please fill the Sales Orders table,Bitte füllen Sie die Tabelle Kundenaufträge aus, -Sales Orders Required,Kundenaufträge erforderlich, +Please fill the Sales Orders table,Bitte füllen Sie die Tabelle Aufträge aus, +Sales Orders Required,Aufträge erforderlich, Please fill the Material Requests table,Bitte füllen Sie die Materialanforderungstabelle aus, Material Requests Required,Materialanforderungen erforderlich, Items to Manufacture are required to pull the Raw Materials associated with it.,"Zu fertigende Gegenstände sind erforderlich, um die damit verbundenen Rohstoffe zu ziehen.", @@ -9486,7 +9486,7 @@ To date can not be greater than employee's relieving date.,Bisher kann das Entla Payroll date can not be greater than employee's relieving date.,Das Abrechnungsdatum darf nicht größer sein als das Entlastungsdatum des Mitarbeiters., Row #{0}: Please enter the result value for {1},Zeile # {0}: Bitte geben Sie den Ergebniswert für {1} ein, Mandatory Results,Obligatorische Ergebnisse, -Sales Invoice or Patient Encounter is required to create Lab Tests,Für die Erstellung von Labortests ist eine Verkaufsrechnung oder eine Patientenbegegnung erforderlich, +Sales Invoice or Patient Encounter is required to create Lab Tests,Für die Erstellung von Labortests ist eine Ausgangsrechnung oder eine Patientenbegegnung erforderlich, Insufficient Data,Unzureichende Daten, Lab Test(s) {0} created successfully,Labortest (e) {0} erfolgreich erstellt, Test :,Prüfung :, @@ -9634,16 +9634,16 @@ Time Between Operations (Mins),Zeit zwischen Operationen (Minuten), Default: 10 mins,Standard: 10 Minuten, Overproduction for Sales and Work Order,Überproduktion für Kunden- und Arbeitsauftrag, "Update BOM cost automatically via scheduler, based on the latest Valuation Rate/Price List Rate/Last Purchase Rate of raw materials","Aktualisieren Sie die Stücklistenkosten automatisch über den Planer, basierend auf der neuesten Bewertungsrate / Preislistenrate / letzten Kaufrate der Rohstoffe", -Purchase Order already created for all Sales Order items,Bestellung bereits für alle Kundenauftragspositionen angelegt, +Purchase Order already created for all Sales Order items,Bestellung bereits für alle Auftragspositionen angelegt, Select Items,Gegenstände auswählen, Against Default Supplier,Gegen Standardlieferanten, Auto close Opportunity after the no. of days mentioned above,Gelegenheit zum automatischen Schließen nach der Nr. der oben genannten Tage, -Is Sales Order Required for Sales Invoice & Delivery Note Creation?,Ist ein Kundenauftrag für die Erstellung von Kundenrechnungen und Lieferscheinen erforderlich?, -Is Delivery Note Required for Sales Invoice Creation?,Ist für die Erstellung der Verkaufsrechnung ein Lieferschein erforderlich?, +Is Sales Order Required for Sales Invoice & Delivery Note Creation?,Ist ein Auftrag für die Erstellung von Kundenrechnungen und Lieferscheinen erforderlich?, +Is Delivery Note Required for Sales Invoice Creation?,Ist für die Erstellung der Ausgangsrechnung ein Lieferschein erforderlich?, How often should Project and Company be updated based on Sales Transactions?,Wie oft sollten Projekt und Unternehmen basierend auf Verkaufstransaktionen aktualisiert werden?, Allow User to Edit Price List Rate in Transactions,Benutzer darf Preisliste in Transaktionen bearbeiten, Allow Item to Be Added Multiple Times in a Transaction,"Zulassen, dass ein Element in einer Transaktion mehrmals hinzugefügt wird", -Allow Multiple Sales Orders Against a Customer's Purchase Order,Erlauben Sie mehrere Kundenaufträge für die Bestellung eines Kunden, +Allow Multiple Sales Orders Against a Customer's Purchase Order,Erlauben Sie mehrere Aufträge für die Bestellung eines Kunden, Validate Selling Price for Item Against Purchase Rate or Valuation Rate,Überprüfen Sie den Verkaufspreis für den Artikel anhand der Kauf- oder Bewertungsrate, Hide Customer's Tax ID from Sales Transactions,Steuer-ID des Kunden vor Verkaufstransaktionen ausblenden, "The percentage you are allowed to receive or deliver more against the quantity ordered. For example, if you have ordered 100 units, and your Allowance is 10%, then you are allowed to receive 110 units.","Der Prozentsatz, den Sie mehr gegen die bestellte Menge erhalten oder liefern dürfen. Wenn Sie beispielsweise 100 Einheiten bestellt haben und Ihre Zulage 10% beträgt, können Sie 110 Einheiten erhalten.", @@ -9653,8 +9653,8 @@ Automatically Set Serial Nos Based on FIFO,Seriennummern basierend auf FIFO auto Set Qty in Transactions Based on Serial No Input,Stellen Sie die Menge in Transaktionen basierend auf Seriennummer ohne Eingabe ein, Raise Material Request When Stock Reaches Re-order Level,"Erhöhen Sie die Materialanforderung, wenn der Lagerbestand die Nachbestellmenge erreicht", Notify by Email on Creation of Automatic Material Request,Benachrichtigen Sie per E-Mail über die Erstellung einer automatischen Materialanforderung, -Allow Material Transfer from Delivery Note to Sales Invoice,Materialübertragung vom Lieferschein zur Verkaufsrechnung zulassen, -Allow Material Transfer from Purchase Receipt to Purchase Invoice,Materialübertragung vom Kaufbeleg zur Kaufrechnung zulassen, +Allow Material Transfer from Delivery Note to Sales Invoice,Materialübertragung vom Lieferschein zur Ausgangsrechnung zulassen, +Allow Material Transfer from Purchase Receipt to Purchase Invoice,Materialübertragung vom Kaufbeleg zur Eingangsrechnung zulassen, Freeze Stocks Older Than (Days),Aktien einfrieren älter als (Tage), Role Allowed to Edit Frozen Stock,Rolle darf eingefrorenes Material bearbeiten, The unallocated amount of Payment Entry {0} is greater than the Bank Transaction's unallocated amount,Der nicht zugewiesene Betrag der Zahlungseingabe {0} ist größer als der nicht zugewiesene Betrag der Banküberweisung, @@ -9694,7 +9694,7 @@ You had {} errors while creating opening invoices. Check {} for more details,Bei Error Occured,Fehler aufgetreten, Opening Invoice Creation In Progress,Öffnen der Rechnungserstellung läuft, Creating {} out of {} {},{} Aus {} {} erstellen, -(Serial No: {0}) cannot be consumed as it's reserverd to fullfill Sales Order {1}.,"(Seriennummer: {0}) kann nicht verwendet werden, da es zum Ausfüllen des Kundenauftrags {1} reserviert ist.", +(Serial No: {0}) cannot be consumed as it's reserverd to fullfill Sales Order {1}.,"(Seriennummer: {0}) kann nicht verwendet werden, da es zum Ausfüllen des Auftrags {1} reserviert ist.", Item {0} {1},Gegenstand {0} {1}, Last Stock Transaction for item {0} under warehouse {1} was on {2}.,Die letzte Lagertransaktion für Artikel {0} unter Lager {1} war am {2}., Stock Transactions for Item {0} under warehouse {1} cannot be posted before this time.,Lagertransaktionen für Artikel {0} unter Lager {1} können nicht vor diesem Zeitpunkt gebucht werden., @@ -9822,8 +9822,8 @@ Invalid Parent Account,Ungültiges übergeordnetes Konto, "If you {0} {1} worth item {2}, the scheme {3} will be applied on the item.","Wenn Sie {0} {1} Gegenstand {2} wert sind, wird das Schema {3} auf den Gegenstand angewendet.", "As the field {0} is enabled, the field {1} is mandatory.","Da das Feld {0} aktiviert ist, ist das Feld {1} obligatorisch.", "As the field {0} is enabled, the value of the field {1} should be more than 1.","Wenn das Feld {0} aktiviert ist, sollte der Wert des Feldes {1} größer als 1 sein.", -Cannot deliver Serial No {0} of item {1} as it is reserved to fullfill Sales Order {2},"Die Seriennummer {0} von Artikel {1} kann nicht geliefert werden, da sie für die Erfüllung des Kundenauftrags {2} reserviert ist.", -"Sales Order {0} has reservation for the item {1}, you can only deliver reserved {1} against {0}.","Kundenauftrag {0} hat eine Reservierung für den Artikel {1}, Sie können reservierte {1} nur gegen {0} liefern.", +Cannot deliver Serial No {0} of item {1} as it is reserved to fullfill Sales Order {2},"Die Seriennummer {0} von Artikel {1} kann nicht geliefert werden, da sie für die Erfüllung des Auftrags {2} reserviert ist.", +"Sales Order {0} has reservation for the item {1}, you can only deliver reserved {1} against {0}.","Auftrag {0} hat eine Reservierung für den Artikel {1}, Sie können reservierte {1} nur gegen {0} liefern.", {0} Serial No {1} cannot be delivered,{0} Seriennummer {1} kann nicht zugestellt werden, Row {0}: Subcontracted Item is mandatory for the raw material {1},Zeile {0}: Unterauftragsartikel sind für den Rohstoff {1} obligatorisch., "As there are sufficient raw materials, Material Request is not required for Warehouse {0}.","Da genügend Rohstoffe vorhanden sind, ist für Warehouse {0} keine Materialanforderung erforderlich.", From 21b07385baa4855972637b07b1129cdd65196f94 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 27 Dec 2021 16:57:36 +0530 Subject: [PATCH 109/193] fix: flaky HR tests (#29017) * fix(test): use root company in Expense Claim tests * fix(test): set Holiday List for Leave Allocation * fix(test): set Holiday List for company --- .../expense_claim/test_expense_claim.py | 24 +++++----- .../doctype/expense_claim/test_records.json | 1 - .../leave_allocation/test_leave_allocation.py | 45 ++++++++++++++----- 3 files changed, 48 insertions(+), 22 deletions(-) delete mode 100644 erpnext/hr/doctype/expense_claim/test_records.json diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py index ec703614c8..2a079201b7 100644 --- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py @@ -10,15 +10,17 @@ from erpnext.accounts.doctype.account.test_account import create_account from erpnext.hr.doctype.employee.test_employee import make_employee from erpnext.hr.doctype.expense_claim.expense_claim import make_bank_entry -test_records = frappe.get_test_records('Expense Claim') test_dependencies = ['Employee'] -company_name = '_Test Company 4' +company_name = '_Test Company 3' class TestExpenseClaim(unittest.TestCase): + def tearDown(self): + frappe.db.rollback() + def test_total_expense_claim_for_project(self): - frappe.db.sql("""delete from `tabTask` where project = "_Test Project 1" """) - frappe.db.sql("""delete from `tabProject` where name = "_Test Project 1" """) + frappe.db.sql("""delete from `tabTask`""") + frappe.db.sql("""delete from `tabProject`""") frappe.db.sql("update `tabExpense Claim` set project = '', task = ''") project = frappe.get_doc({ @@ -37,12 +39,12 @@ class TestExpenseClaim(unittest.TestCase): task_name = task.name payable_account = get_payable_account(company_name) - make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", project.name, task_name) + make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC3", project.name, task_name) self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200) self.assertEqual(frappe.db.get_value("Project", project.name, "total_expense_claim"), 200) - expense_claim2 = make_expense_claim(payable_account, 600, 500, company_name, "Travel Expenses - _TC4", project.name, task_name) + expense_claim2 = make_expense_claim(payable_account, 600, 500, company_name, "Travel Expenses - _TC3", project.name, task_name) self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 700) self.assertEqual(frappe.db.get_value("Project", project.name, "total_expense_claim"), 700) @@ -54,7 +56,7 @@ class TestExpenseClaim(unittest.TestCase): def test_expense_claim_status(self): payable_account = get_payable_account(company_name) - expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4") + expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC3") je_dict = make_bank_entry("Expense Claim", expense_claim.name) je = frappe.get_doc(je_dict) @@ -73,7 +75,7 @@ class TestExpenseClaim(unittest.TestCase): def test_expense_claim_gl_entry(self): payable_account = get_payable_account(company_name) taxes = generate_taxes() - expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", + expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC3", do_not_submit=True, taxes=taxes) expense_claim.submit() @@ -84,9 +86,9 @@ class TestExpenseClaim(unittest.TestCase): self.assertTrue(gl_entries) expected_values = dict((d[0], d) for d in [ - ['Output Tax CGST - _TC4',18.0, 0.0], + ['Output Tax CGST - _TC3',18.0, 0.0], [payable_account, 0.0, 218.0], - ["Travel Expenses - _TC4", 200.0, 0.0] + ["Travel Expenses - _TC3", 200.0, 0.0] ]) for gle in gl_entries: @@ -102,7 +104,7 @@ class TestExpenseClaim(unittest.TestCase): "payable_account": payable_account, "approval_status": "Rejected", "expenses": - [{ "expense_type": "Travel", "default_account": "Travel Expenses - _TC4", "amount": 300, "sanctioned_amount": 200 }] + [{"expense_type": "Travel", "default_account": "Travel Expenses - _TC3", "amount": 300, "sanctioned_amount": 200}] }) expense_claim.submit() diff --git a/erpnext/hr/doctype/expense_claim/test_records.json b/erpnext/hr/doctype/expense_claim/test_records.json deleted file mode 100644 index fe51488c70..0000000000 --- a/erpnext/hr/doctype/expense_claim/test_records.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py index 6dbe2eca32..1fe91399a0 100644 --- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py @@ -12,15 +12,11 @@ from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type class TestLeaveAllocation(unittest.TestCase): @classmethod def setUpClass(cls): - from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list - frappe.db.sql("delete from `tabLeave Period`") + emp_id = make_employee("test_emp_leave_allocation@salary.com") cls.employee = frappe.get_doc("Employee", emp_id) - make_holiday_list() - frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List") - def tearDown(self): frappe.db.rollback() @@ -90,6 +86,8 @@ class TestLeaveAllocation(unittest.TestCase): # initial leave allocation = 15 leave_allocation = create_leave_allocation( + employee=self.employee.name, + employee_name=self.employee.employee_name, leave_type="_Test_CF_leave", from_date=add_months(nowdate(), -12), to_date=add_months(nowdate(), -1), @@ -99,6 +97,8 @@ class TestLeaveAllocation(unittest.TestCase): # carry forwarded leaves considering maximum_carry_forwarded_leaves # new_leaves = 15, carry_forwarded = 10 leave_allocation_1 = create_leave_allocation( + employee=self.employee.name, + employee_name=self.employee.employee_name, leave_type="_Test_CF_leave", carry_forward=1) leave_allocation_1.submit() @@ -110,6 +110,8 @@ class TestLeaveAllocation(unittest.TestCase): # carry forwarded leaves considering max_leave_allowed # max_leave_allowed = 30, new_leaves = 25, carry_forwarded = 5 leave_allocation_2 = create_leave_allocation( + employee=self.employee.name, + employee_name=self.employee.employee_name, leave_type="_Test_CF_leave", carry_forward=1, new_leaves_allocated=25) @@ -126,6 +128,8 @@ class TestLeaveAllocation(unittest.TestCase): # initial leave allocation leave_allocation = create_leave_allocation( + employee=self.employee.name, + employee_name=self.employee.employee_name, leave_type="_Test_CF_leave_expiry", from_date=add_months(nowdate(), -24), to_date=add_months(nowdate(), -12), @@ -133,6 +137,8 @@ class TestLeaveAllocation(unittest.TestCase): leave_allocation.submit() leave_allocation = create_leave_allocation( + employee=self.employee.name, + employee_name=self.employee.employee_name, leave_type="_Test_CF_leave_expiry", from_date=add_days(nowdate(), -90), to_date=add_days(nowdate(), 100), @@ -144,6 +150,8 @@ class TestLeaveAllocation(unittest.TestCase): # leave allocation with carry forward of only new leaves allocated leave_allocation_1 = create_leave_allocation( + employee=self.employee.name, + employee_name=self.employee.employee_name, leave_type="_Test_CF_leave_expiry", carry_forward=1, from_date=add_months(nowdate(), 6), @@ -153,7 +161,10 @@ class TestLeaveAllocation(unittest.TestCase): self.assertEqual(leave_allocation_1.unused_leaves, leave_allocation.new_leaves_allocated) def test_creation_of_leave_ledger_entry_on_submit(self): - leave_allocation = create_leave_allocation() + leave_allocation = create_leave_allocation( + employee=self.employee.name, + employee_name=self.employee.employee_name + ) leave_allocation.submit() leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=dict(transaction_name=leave_allocation.name)) @@ -168,7 +179,10 @@ class TestLeaveAllocation(unittest.TestCase): self.assertFalse(frappe.db.exists("Leave Ledger Entry", {'transaction_name':leave_allocation.name})) def test_leave_addition_after_submit(self): - leave_allocation = create_leave_allocation() + leave_allocation = create_leave_allocation( + employee=self.employee.name, + employee_name=self.employee.employee_name + ) leave_allocation.submit() self.assertTrue(leave_allocation.total_leaves_allocated, 15) leave_allocation.new_leaves_allocated = 40 @@ -176,7 +190,10 @@ class TestLeaveAllocation(unittest.TestCase): self.assertTrue(leave_allocation.total_leaves_allocated, 40) def test_leave_subtraction_after_submit(self): - leave_allocation = create_leave_allocation() + leave_allocation = create_leave_allocation( + employee=self.employee.name, + employee_name=self.employee.employee_name + ) leave_allocation.submit() self.assertTrue(leave_allocation.total_leaves_allocated, 15) leave_allocation.new_leaves_allocated = 10 @@ -184,7 +201,15 @@ class TestLeaveAllocation(unittest.TestCase): self.assertTrue(leave_allocation.total_leaves_allocated, 10) def test_validation_against_leave_application_after_submit(self): - leave_allocation = create_leave_allocation() + from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list + + make_holiday_list() + frappe.db.set_value("Company", self.employee.company, "default_holiday_list", "Salary Slip Test Holiday List") + + leave_allocation = create_leave_allocation( + employee=self.employee.name, + employee_name=self.employee.employee_name + ) leave_allocation.submit() self.assertTrue(leave_allocation.total_leaves_allocated, 15) @@ -194,7 +219,7 @@ class TestLeaveAllocation(unittest.TestCase): "leave_type": "_Test Leave Type", "from_date": add_months(nowdate(), 2), "to_date": add_months(add_days(nowdate(), 10), 2), - "company": erpnext.get_default_company() or "_Test Company", + "company": self.employee.company, "docstatus": 1, "status": "Approved", "leave_approver": 'test@example.com' From 878fd377c2584f80022dc96e899261e2bcbe493f Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 27 Dec 2021 21:09:05 +0530 Subject: [PATCH 110/193] chore: Use frappe.qb for query --- .../stock/report/stock_ageing/stock_ageing.py | 108 ++++++++++-------- 1 file changed, 61 insertions(+), 47 deletions(-) diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 75e235ac05..8062a1c98e 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -287,7 +287,7 @@ class FIFOSlots: fifo_queue.append(slot) else: if not serial_nos: - if fifo_queue and fifo_queue[0][0] < 0: + if fifo_queue and flt(fifo_queue[0][0]) < 0: # neutralize negative stock by adding positive stock fifo_queue[0][0] += flt(row.actual_qty) fifo_queue[0][1] = row.posting_date @@ -339,54 +339,68 @@ class FIFOSlots: self.item_details[key]["has_serial_no"] = row.has_serial_no def __get_stock_ledger_entries(self) -> List[Dict]: - return frappe.db.sql(""" - select - item.name, item.item_name, item_group, brand, description, + sle = frappe.qb.DocType("Stock Ledger Entry") + item = self.__get_item_query() # used as derived table in sle query + + sle_query = ( + frappe.qb.from_(sle).from_(item) + .select( + item.name, item.item_name, item.item_group, + item.brand, item.description, item.stock_uom, item.has_serial_no, - actual_qty, posting_date, voucher_type, voucher_no, - serial_no, batch_no, qty_after_transaction, warehouse - from - `tabStock Ledger Entry` sle, - ( - select name, item_name, description, stock_uom, - brand, item_group, has_serial_no - from `tabItem` {item_conditions} - ) item - where - item_code = item.name and - company = %(company)s and - posting_date <= %(to_date)s and - is_cancelled != 1 - {sle_conditions} - order by posting_date, posting_time, sle.creation, actual_qty - """ #nosec - .format( - item_conditions=self.__get_item_conditions(), - sle_conditions=self.__get_sle_conditions() - ), - self.filters, - as_dict=True + sle.actual_qty, sle.posting_date, + sle.voucher_type, sle.voucher_no, + sle.serial_no, sle.batch_no, + sle.qty_after_transaction, sle.warehouse + ).where( + (sle.item_code == item.name) + & (sle.company == self.filters.get("company")) + & (sle.posting_date <= self.filters.get("to_date")) + & (sle.is_cancelled != 1) + ) ) - def __get_item_conditions(self) -> str: - conditions = [] - if self.filters.get("item_code"): - conditions.append("item_code=%(item_code)s") - if self.filters.get("brand"): - conditions.append("brand=%(brand)s") - - return "where {}".format(" and ".join(conditions)) if conditions else "" - - def __get_sle_conditions(self) -> str: - conditions = [] - if self.filters.get("warehouse"): - lft, rgt = frappe.db.get_value("Warehouse", self.filters.get("warehouse"), ['lft', 'rgt']) - conditions.append(""" - warehouse in ( - select wh.name from `tabWarehouse` wh - where wh.lft >= {0} and rgt <= {1} - ) - """.format(lft, rgt)) + sle_query = self.__get_warehouse_conditions(sle, sle_query) - return "and {}".format(" and ".join(conditions)) if conditions else "" \ No newline at end of file + sle_query = sle_query.orderby( + sle.posting_date, sle.posting_time, sle.creation, sle.actual_qty + ) + + return sle_query.run(as_dict=True) + + def __get_item_query(self) -> str: + item_table = frappe.qb.DocType("Item") + + item = frappe.qb.from_("Item").select( + "name", "item_name", "description", "stock_uom", + "brand", "item_group", "has_serial_no" + ) + + if self.filters.get("item_code"): + item = item.where(item_table.item_code == self.filters.get("item_code")) + + if self.filters.get("brand"): + item = item.where(item_table.brand == self.filters.get("brand")) + + return item + + def __get_warehouse_conditions(self, sle, sle_query) -> str: + warehouse = frappe.qb.DocType("Warehouse") + lft, rgt = frappe.db.get_value( + "Warehouse", + self.filters.get("warehouse"), + ['lft', 'rgt'] + ) + + warehouse_results = ( + frappe.qb.from_(warehouse) + .select("name") + .where( + (warehouse.lft >= lft) + & (warehouse.rgt <= rgt) + ).run() + ) + warehouse_results = [x[0] for x in warehouse_results] + + return sle_query.where(sle.warehouse.isin(warehouse_results)) From 25f4de80b374ed1526d3490e613bb6cfdf4bba39 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 27 Dec 2021 21:15:40 +0530 Subject: [PATCH 111/193] fix: filter out Claimed employee advances in Expense Claim (#29046) --- erpnext/hr/doctype/expense_claim/expense_claim.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index 665556301b..047945787d 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -171,7 +171,7 @@ frappe.ui.form.on("Expense Claim", { ['docstatus', '=', 1], ['employee', '=', frm.doc.employee], ['paid_amount', '>', 0], - ['paid_amount', '>', 'claimed_amount'] + ['status', '!=', 'Claimed'] ] }; }); From c007f84ade706e1738e4d964f291bfe4ab3aa948 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 28 Dec 2021 01:27:24 +0530 Subject: [PATCH 112/193] chore: Added .md file to explain stock ageing business logic --- .../stock/report/stock_ageing/stock_ageing.py | 3 +- .../stock_ageing/stock_ageing_fifo_logic.md | 73 +++++++++++++++++++ 2 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 erpnext/stock/report/stock_ageing/stock_ageing_fifo_logic.md diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 8062a1c98e..88e0712fd7 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -395,8 +395,7 @@ class FIFOSlots: warehouse_results = ( frappe.qb.from_(warehouse) - .select("name") - .where( + .select("name").where( (warehouse.lft >= lft) & (warehouse.rgt <= rgt) ).run() diff --git a/erpnext/stock/report/stock_ageing/stock_ageing_fifo_logic.md b/erpnext/stock/report/stock_ageing/stock_ageing_fifo_logic.md new file mode 100644 index 0000000000..5ffe97fd74 --- /dev/null +++ b/erpnext/stock/report/stock_ageing/stock_ageing_fifo_logic.md @@ -0,0 +1,73 @@ +### Concept of FIFO Slots + +Since we need to know age-wise remaining stock, we maintain all the inward entries as slots. So each time stock comes in, a slot is added for the same. + +Eg. For Item A: +---------------------- +Date | Qty | Queue +---------------------- +1st | +50 | [[50, 1-12-2021]] +2nd | +20 | [[50, 1-12-2021], [20, 2-12-2021]] +---------------------- + +Now the queue can tell us the total stock and also how old the stock is. +Here, the balance qty is 70. +50 qty is (today-the 1st) days old +20 qty is (today-the 2nd) days old + +### Calculation of FIFO Slots + +#### Case 1: Outward from sufficient balance qty +---------------------- +Date | Qty | Queue +---------------------- +1st | +50 | [[50, 1-12-2021]] +2nd | -20 | [[30, 1-12-2021]] +2nd | +20 | [[30, 1-12-2021], [20, 2-12-2021]] + +Here after the first entry, while issuing 20 qty: +- **since 20 is lesser than the balance**, **qty_to_pop (20)** is simply consumed from first slot (FIFO consumption) +- Any inward entry after as usual will get its own slot added to the queue + +#### Case 2: Outward from sufficient cumulative (slots) balance qty +---------------------- +Date | Qty | Queue +---------------------- +1st | +50 | [[50, 1-12-2021]] +2nd | +20 | [[50, 1-12-2021], [20, 2-12-2021]] +2nd | -60 | [[10, 2-12-2021]] + +- Consumption happens slot wise. First slot 1 is consumed +- Since **qty_to_pop (60) is greater than slot 1 qty (50)**, the entire slot is consumed and popped +- Now the queue is [[20, 2-12-2021]], and **qty_to_pop=10** (remaining qty to pop) +- It then goes ahead to the next slot and consumes 10 from it +- Now the queue is [[10, 2-12-2021]] + +#### Case 3: Outward from insufficient balance qty +> This case is possible only if **Allow Negative Stock** was enabled at some point/is enabled. + +---------------------- +Date | Qty | Queue +---------------------- +1st | +50 | [[50, 1-12-2021]] +2nd | -60 | [[-10, 1-12-2021]] + +- Since **qty_to_pop (60)** is more than the balance in slot 1, the entire slot is consumed and popped +- Now the queue is **empty**, and **qty_to_pop=10** (remaining qty to pop) +- Since we still have more to consume, we append the balance since 60 is issued from 50 i.e. -10. +- We register this negative value, since the stock issue has caused the balance to become negative + +Now when stock is inwarded: +- Instead of adding a slot we check if there are any negative balances. +- If yes, we keep adding positive stock to it until we make the balance positive. +- Once the balance is positive, the next inward entry will add a new slot in the queue + +Eg: +---------------------- +Date | Qty | Queue +---------------------- +1st | +50 | [[50, 1-12-2021]] +2nd | -60 | [[-10, 1-12-2021]] +3rd | +5 | [[-5, 3-12-2021]] +4th | +10 | [[5, 4-12-2021]] +4th | +20 | [[5, 4-12-2021], [20, 4-12-2021]] \ No newline at end of file From 098f72e7ec43ee8ccc65236956e496be01ec8d18 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 28 Dec 2021 02:33:54 +0530 Subject: [PATCH 113/193] test: Stock Ageing FIFO Slot generation --- .../stock/report/stock_ageing/stock_ageing.py | 10 +- .../report/stock_ageing/test_stock_ageing.py | 125 ++++++++++++++++++ 2 files changed, 130 insertions(+), 5 deletions(-) create mode 100644 erpnext/stock/report/stock_ageing/test_stock_ageing.py diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 88e0712fd7..e6dfc97a99 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -292,7 +292,7 @@ class FIFOSlots: fifo_queue[0][0] += flt(row.actual_qty) fifo_queue[0][1] = row.posting_date else: - fifo_queue.append([row.actual_qty, row.posting_date]) + fifo_queue.append([flt(row.actual_qty), row.posting_date]) return for serial_no in serial_nos: @@ -395,10 +395,10 @@ class FIFOSlots: warehouse_results = ( frappe.qb.from_(warehouse) - .select("name").where( - (warehouse.lft >= lft) - & (warehouse.rgt <= rgt) - ).run() + .select("name").where( + (warehouse.lft >= lft) + & (warehouse.rgt <= rgt) + ).run() ) warehouse_results = [x[0] for x in warehouse_results] diff --git a/erpnext/stock/report/stock_ageing/test_stock_ageing.py b/erpnext/stock/report/stock_ageing/test_stock_ageing.py new file mode 100644 index 0000000000..0e355a5c38 --- /dev/null +++ b/erpnext/stock/report/stock_ageing/test_stock_ageing.py @@ -0,0 +1,125 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import frappe + +from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots +from erpnext.tests.utils import ERPNextTestCase + +class TestStockAgeing(ERPNextTestCase): + def setUp(self) -> None: + self.filters = frappe._dict( + company="_Test Company", + to_date="2021-12-10" + ) + + def test_normal_inward_outward_queue(self): + "Reference: Case 1 in stock_ageing_fifo_logic.md" + sle = [ + frappe._dict( + name="Flask Item", + actual_qty=30, qty_after_transaction=30, + posting_date="2021-12-01", voucher_type="Stock Entry", + voucher_no="001", + has_serial_no=False, serial_no=None + ), + frappe._dict( + name="Flask Item", + actual_qty=20, qty_after_transaction=50, + posting_date="2021-12-02", voucher_type="Stock Entry", + voucher_no="002", + has_serial_no=False, serial_no=None + ), + frappe._dict( + name="Flask Item", + actual_qty=(-10), qty_after_transaction=40, + posting_date="2021-12-03", voucher_type="Stock Entry", + voucher_no="003", + has_serial_no=False, serial_no=None + ) + ] + + slots = FIFOSlots(self.filters, sle).generate() + + self.assertTrue(slots["Flask Item"]["fifo_queue"]) + result = slots["Flask Item"] + queue = result["fifo_queue"] + + self.assertEqual(result["qty_after_transaction"], result["total_qty"]) + self.assertEqual(queue[0][0], 20.0) + + def test_insufficient_balance(self): + "Reference: Case 3 in stock_ageing_fifo_logic.md" + sle = [ + frappe._dict( + name="Flask Item", + actual_qty=(-30), qty_after_transaction=(-30), + posting_date="2021-12-01", voucher_type="Stock Entry", + voucher_no="001", + has_serial_no=False, serial_no=None + ), + frappe._dict( + name="Flask Item", + actual_qty=20, qty_after_transaction=(-10), + posting_date="2021-12-02", voucher_type="Stock Entry", + voucher_no="002", + has_serial_no=False, serial_no=None + ), + frappe._dict( + name="Flask Item", + actual_qty=20, qty_after_transaction=10, + posting_date="2021-12-03", voucher_type="Stock Entry", + voucher_no="003", + has_serial_no=False, serial_no=None + ), + frappe._dict( + name="Flask Item", + actual_qty=10, qty_after_transaction=20, + posting_date="2021-12-03", voucher_type="Stock Entry", + voucher_no="004", + has_serial_no=False, serial_no=None + ) + ] + + slots = FIFOSlots(self.filters, sle).generate() + + result = slots["Flask Item"] + queue = result["fifo_queue"] + + self.assertEqual(result["qty_after_transaction"], result["total_qty"]) + self.assertEqual(queue[0][0], 10.0) + self.assertEqual(queue[1][0], 10.0) + + def test_stock_reconciliation(self): + sle = [ + frappe._dict( + name="Flask Item", + actual_qty=30, qty_after_transaction=30, + posting_date="2021-12-01", voucher_type="Stock Entry", + voucher_no="001", + has_serial_no=False, serial_no=None + ), + frappe._dict( + name="Flask Item", + actual_qty=0, qty_after_transaction=50, + posting_date="2021-12-02", voucher_type="Stock Reconciliation", + voucher_no="002", + has_serial_no=False, serial_no=None + ), + frappe._dict( + name="Flask Item", + actual_qty=(-10), qty_after_transaction=40, + posting_date="2021-12-03", voucher_type="Stock Entry", + voucher_no="003", + has_serial_no=False, serial_no=None + ) + ] + + slots = FIFOSlots(self.filters, sle).generate() + + result = slots["Flask Item"] + queue = result["fifo_queue"] + + self.assertEqual(result["qty_after_transaction"], result["total_qty"]) + self.assertEqual(queue[0][0], 20.0) + self.assertEqual(queue[1][0], 20.0) From 8226ea65c81b432dcd71e9eaa872e15c7c729cde Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 28 Dec 2021 08:54:05 +0530 Subject: [PATCH 114/193] fix: linter issues --- .../patches/v14_0/set_payroll_cost_centers.py | 49 ++++++++++--------- .../employee_cost_center.py | 1 + .../doctype/payroll_entry/payroll_entry.py | 4 +- .../payroll_entry/test_payroll_entry.py | 4 +- .../salary_structure_assignment.py | 10 ++-- 5 files changed, 35 insertions(+), 33 deletions(-) diff --git a/erpnext/patches/v14_0/set_payroll_cost_centers.py b/erpnext/patches/v14_0/set_payroll_cost_centers.py index db5baa5d20..89b305bb6f 100644 --- a/erpnext/patches/v14_0/set_payroll_cost_centers.py +++ b/erpnext/patches/v14_0/set_payroll_cost_centers.py @@ -1,31 +1,32 @@ import frappe + def execute(): - frappe.reload_doc('payroll', 'doctype', 'employee_cost_center') - frappe.reload_doc('payroll', 'doctype', 'salary_structure_assignment') + frappe.reload_doc('payroll', 'doctype', 'employee_cost_center') + frappe.reload_doc('payroll', 'doctype', 'salary_structure_assignment') - employees = frappe.get_all("Employee", fields=["department", "payroll_cost_center", "name"]) + employees = frappe.get_all("Employee", fields=["department", "payroll_cost_center", "name"]) - employee_cost_center = {} - for d in employees: - cost_center = d.payroll_cost_center - if not cost_center and d.department: - cost_center = frappe.get_cached_value("Department", d.department, "payroll_cost_center") + employee_cost_center = {} + for d in employees: + cost_center = d.payroll_cost_center + if not cost_center and d.department: + cost_center = frappe.get_cached_value("Department", d.department, "payroll_cost_center") - if cost_center: - employee_cost_center.setdefault(d.name, cost_center) - - salary_structure_assignments = frappe.get_all("Salary Structure Assignment", - filters = {"docstatus": ["!=", 2]}, - fields=["name", "employee"]) + if cost_center: + employee_cost_center.setdefault(d.name, cost_center) - for d in salary_structure_assignments: - cost_center = employee_cost_center.get(d.employee) - if cost_center: - assignment = frappe.get_doc("Salary Structure Assignment", d.name) - if not assignment.get("payroll_cost_centers"): - assignment.append("payroll_cost_centers", { - "cost_center": cost_center, - "percentage": 100 - }) - assignment.save() \ No newline at end of file + salary_structure_assignments = frappe.get_all("Salary Structure Assignment", + filters = {"docstatus": ["!=", 2]}, + fields=["name", "employee"]) + + for d in salary_structure_assignments: + cost_center = employee_cost_center.get(d.employee) + if cost_center: + assignment = frappe.get_doc("Salary Structure Assignment", d.name) + if not assignment.get("payroll_cost_centers"): + assignment.append("payroll_cost_centers", { + "cost_center": cost_center, + "percentage": 100 + }) + assignment.save() \ No newline at end of file diff --git a/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.py b/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.py index 91bcc21d18..6c5be9744b 100644 --- a/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.py +++ b/erpnext/payroll/doctype/employee_cost_center/employee_cost_center.py @@ -4,5 +4,6 @@ # import frappe from frappe.model.document import Document + class EmployeeCostCenter(Document): pass diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py index f61e68896b..a9a95546fa 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py @@ -234,7 +234,7 @@ class PayrollEntry(Document): if not self.employee_cost_centers.get(employee): ss_assignment_name = frappe.db.get_value("Salary Structure Assignment", {"employee": employee, "salary_structure": salary_structure, "docstatus": 1}, 'name') - + if ss_assignment_name: cost_centers = dict(frappe.get_all("Employee Cost Center", {"parent": ss_assignment_name}, ["cost_center", "percentage"], as_list=1)) @@ -244,7 +244,7 @@ class PayrollEntry(Document): default_cost_center = frappe.get_cached_value("Department", department, "payroll_cost_center") if not default_cost_center: default_cost_center = self.cost_center - + cost_centers = { default_cost_center: 100 } diff --git a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py index e88a2ca9ed..4f097fa2c3 100644 --- a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py @@ -131,14 +131,14 @@ class TestPayrollEntry(unittest.TestCase): frappe.db.set_value("Company", "_Test Company", "default_payroll_payable_account", "_Test Payroll Payable - _TC") currency=frappe.db.get_value("Company", "_Test Company", "default_currency") - + make_salary_structure("_Test Salary Structure 1", "Monthly", employee1, company="_Test Company", currency=currency, test_tax=False) ss = make_salary_structure("_Test Salary Structure 2", "Monthly", employee2, company="_Test Company", currency=currency, test_tax=False) # update cost centers in salary structure assignment for employee2 ssa = frappe.db.get_value("Salary Structure Assignment", {"employee": employee2, "salary_structure": ss.name, "docstatus": 1}, 'name') - + ssa_doc = frappe.get_doc("Salary Structure Assignment", ssa) ssa_doc.payroll_cost_centers = [] ssa_doc.append("payroll_cost_centers", { diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py index a7cee453ac..8359478d0b 100644 --- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py +++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py @@ -5,7 +5,7 @@ import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import getdate, flt +from frappe.utils import flt, getdate class DuplicateAssignment(frappe.ValidationError): pass @@ -54,7 +54,7 @@ class SalaryStructureAssignment(Document): "account_name": _("Payroll Payable"), "company": self.company, "account_currency": frappe.db.get_value( "Company", self.company, "default_currency"), "is_group": 0}) self.payroll_payable_account = payroll_payable_account - + @frappe.whitelist() def set_payroll_cost_centers(self): self.payroll_cost_centers = [] @@ -69,7 +69,7 @@ class SalaryStructureAssignment(Document): payroll_cost_center = frappe.db.get_value("Employee", self.employee, "payroll_cost_center") if not payroll_cost_center and self.department: payroll_cost_center = frappe.db.get_value("Department", self.department, "payroll_cost_center") - + return payroll_cost_center def validate_cost_center_distribution(self): @@ -77,8 +77,7 @@ class SalaryStructureAssignment(Document): total_percentage = sum([flt(d.percentage) for d in self.get("payroll_cost_centers", [])]) if total_percentage != 100: frappe.throw(_("Total percentage against cost centers should be 100")) - - + def get_assigned_salary_structure(employee, on_date): if not employee or not on_date: @@ -93,6 +92,7 @@ def get_assigned_salary_structure(employee, on_date): }) return salary_structure[0][0] if salary_structure else None + @frappe.whitelist() def get_employee_currency(employee): employee_currency = frappe.db.get_value('Salary Structure Assignment', {'employee': employee}, 'currency') From baa12bcee613b7bf03853fa78abc1eef91094917 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 28 Dec 2021 10:04:14 +0530 Subject: [PATCH 115/193] fix: convert raw queries with frappe ORM --- .../doctype/payroll_entry/payroll_entry.py | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py index a9a95546fa..5bb32cf909 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py @@ -7,6 +7,7 @@ from dateutil.relativedelta import relativedelta from frappe import _ from frappe.desk.reportview import get_filters_cond, get_match_cond from frappe.model.document import Document +from frappe.query_builder.functions import Coalesce from frappe.utils import ( DATE_FORMAT, add_days, @@ -157,11 +158,20 @@ class PayrollEntry(Document): Returns list of salary slips based on selected criteria """ - ss_list = frappe.db.sql(""" - select t1.name, t1.salary_structure from `tabSalary Slip` t1 - where t1.docstatus = %s and t1.start_date >= %s and t1.end_date <= %s and t1.payroll_entry = %s - and (t1.journal_entry is null or t1.journal_entry = "") and ifnull(salary_slip_based_on_timesheet,0) = %s - """, (ss_status, self.start_date, self.end_date, self.name, self.salary_slip_based_on_timesheet), as_dict=as_dict) + ss = frappe.qb.DocType("Salary Slip") + ss_list = ( + frappe.qb.from_(ss) + .select(ss.name, ss.salary_structure) + .where( + (ss.docstatus == ss_status) + & (ss.start_date >= self.start_date) + & (ss.end_date <= self.end_date) + & (ss.payroll_entry == self.name) + & ((ss.journal_entry.isnull()) | (ss.journal_entry == "")) + & (Coalesce(ss.salary_slip_based_on_timesheet, 0) == self.salary_slip_based_on_timesheet) + ) + ).run(as_dict=as_dict) + return ss_list @frappe.whitelist() @@ -190,19 +200,20 @@ class PayrollEntry(Document): def get_salary_components(self, component_type): salary_slips = self.get_sal_slip_list(ss_status = 1, as_dict = True) + if salary_slips: - salary_components = frappe.db.sql(""" - SELECT - ssd.salary_component, ssd.amount, ssd.parentfield, ss.salary_structure, ss.employee - FROM - `tabSalary Slip` ss, - `tabSalary Detail` ssd - WHERE - ss.name = ssd.parent - and ssd.parentfield = '%s' - and ss.name in (%s) - """ % (component_type, ', '.join(['%s']*len(salary_slips))), - tuple([d.name for d in salary_slips]), as_dict=True) + ss = frappe.qb.DocType("Salary Slip") + ssd = frappe.qb.DocType("Salary Detail") + salary_components = ( + frappe.qb.from_(ss) + .join(ssd) + .on(ss.name == ssd.parent) + .select(ssd.salary_component, ssd.amount, ssd.parentfield, ss.salary_structure, ss.employee) + .where( + (ssd.parentfield == component_type) + & (ss.name.isin(tuple([d.name for d in salary_slips]))) + ) + ).run(as_dict=True) return salary_components From a0863d1cb13ac6a3d1b6233ff98b5474df740664 Mon Sep 17 00:00:00 2001 From: MohsinAli Date: Tue, 28 Dec 2021 10:27:19 +0530 Subject: [PATCH 116/193] feat: Add currency in import download statement Add currency in bank statement import --- .../doctype/bank_statement_import/bank_statement_import.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js index 0a2e0bc9ba..990d6d9c8d 100644 --- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js +++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js @@ -239,7 +239,8 @@ frappe.ui.form.on("Bank Statement Import", { "withdrawal", "description", "reference_number", - "bank_account" + "bank_account", + "currency" ], }, }); From 077e2c646721e24eccdf7937715f5250cc6c22ca Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 28 Dec 2021 11:08:13 +0530 Subject: [PATCH 117/193] Update erpnext/projects/doctype/project/project.js --- erpnext/projects/doctype/project/project.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index 6399a50f48..4f19bbd516 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -95,7 +95,7 @@ frappe.ui.form.on("Project", { set_project_status_button: function(frm) { frm.add_custom_button(__('Set Project Status'), () => { let d = new frappe.ui.Dialog({ - "title": "Set Project Status", + "title": __("Set Project Status"), "fields": [ { "fieldname": "status", From 6ca978c18b4565a696356280448a4fab2d47f94c Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 28 Dec 2021 11:46:46 +0530 Subject: [PATCH 118/193] fix: Linter (extra line after import) --- erpnext/stock/report/stock_ageing/test_stock_ageing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/report/stock_ageing/test_stock_ageing.py b/erpnext/stock/report/stock_ageing/test_stock_ageing.py index 0e355a5c38..949bb7c15a 100644 --- a/erpnext/stock/report/stock_ageing/test_stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/test_stock_ageing.py @@ -6,6 +6,7 @@ import frappe from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots from erpnext.tests.utils import ERPNextTestCase + class TestStockAgeing(ERPNextTestCase): def setUp(self) -> None: self.filters = frappe._dict( From 7d0340e660780e3e678e110ef36b9afc2b51c98e Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 28 Dec 2021 13:06:35 +0530 Subject: [PATCH 119/193] fix: Sider --- erpnext/stock/doctype/stock_entry/stock_entry.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index eae509e927..fc686d1781 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -35,11 +35,16 @@ from erpnext.stock.stock_ledger import NegativeStockError, get_previous_sle, get from erpnext.stock.utils import get_bin, get_incoming_rate -class FinishedGoodError(frappe.ValidationError): pass -class IncorrectValuationRateError(frappe.ValidationError): pass -class DuplicateEntryForWorkOrderError(frappe.ValidationError): pass -class OperationsNotCompleteError(frappe.ValidationError): pass -class MaxSampleAlreadyRetainedError(frappe.ValidationError): pass +class FinishedGoodError(frappe.ValidationError): + pass +class IncorrectValuationRateError(frappe.ValidationError): + pass +class DuplicateEntryForWorkOrderError(frappe.ValidationError): + pass +class OperationsNotCompleteError(frappe.ValidationError): + pass +class MaxSampleAlreadyRetainedError(frappe.ValidationError): + pass from erpnext.controllers.stock_controller import StockController From 22809a28381ac92336b1fbdb0b1caca2c7b3de7e Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 28 Dec 2021 13:23:27 +0530 Subject: [PATCH 120/193] fix: Avoid Impact on Repack Entry - Repack entries must be considered for fg/scrap checkbox auto set - This must be avoided only for Manufacture entries, due to confusion between scrap and fg - No scrap in repack, so its straight forward --- 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 fc686d1781..93e303c9a7 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -707,7 +707,7 @@ class StockEntry(StockController): finished_item = self.get_finished_item() - if not finished_item: + if not finished_item and self.purpose == "Manufacture": # In case of independent Manufacture entry, don't auto set # user must decide and set return From d807d7831333df9ef462b130ab0b0def229f26e5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 22 Dec 2021 18:04:49 +0530 Subject: [PATCH 121/193] fix: Issues in Bank Reconciliation tool (cherry picked from commit ff183ee895fb0dd1ca595eb76e2286695063315a) --- .../bank_reconciliation_tool.py | 16 ++++++++-------- .../doctype/bank_transaction/bank_transaction.py | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py index e7371fbe43..4211bd0169 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py @@ -218,6 +218,8 @@ def reconcile_vouchers(bank_transaction_name, vouchers): # updated clear date of all the vouchers based on the bank transaction vouchers = json.loads(vouchers) transaction = frappe.get_doc("Bank Transaction", bank_transaction_name) + company_account = frappe.db.get_value('Bank Account', transaction.bank_account, 'account') + if transaction.unallocated_amount == 0: frappe.throw(_("This bank transaction is already fully reconciled")) total_amount = 0 @@ -226,7 +228,7 @@ def reconcile_vouchers(bank_transaction_name, vouchers): total_amount += get_paid_amount(frappe._dict({ 'payment_document': voucher['payment_doctype'], 'payment_entry': voucher['payment_name'], - }), transaction.currency) + }), transaction.currency, company_account) if total_amount > transaction.unallocated_amount: frappe.throw(_("The Sum Total of Amounts of All Selected Vouchers Should be Less than the Unallocated Amount of the Bank Transaction")) @@ -261,7 +263,7 @@ def get_linked_payments(bank_transaction_name, document_types = None): return matching def check_matching(bank_account, company, transaction, document_types): - # combine all types of vocuhers + # combine all types of vouchers subquery = get_queries(bank_account, company, transaction, document_types) filters = { "amount": transaction.unallocated_amount, @@ -343,13 +345,11 @@ def get_pe_matching_query(amount_condition, account_from_to, transaction): def get_je_matching_query(amount_condition, transaction): # get matching journal entry query + # We have mapping at the bank level + # So one bank could have both types of bank accounts like asset and liability + # So cr_or_dr should be judged only on basis of withdrawal and deposit and not account type company_account = frappe.get_value("Bank Account", transaction.bank_account, "account") - root_type = frappe.get_value("Account", company_account, "root_type") - - if root_type == "Liability": - cr_or_dr = "debit" if transaction.withdrawal > 0 else "credit" - else: - cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit" + cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit" return f""" diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index 4620087304..e877765fe6 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -102,7 +102,7 @@ def get_total_allocated_amount(payment_entry): AND bt.docstatus = 1""", (payment_entry.payment_document, payment_entry.payment_entry), as_dict=True) -def get_paid_amount(payment_entry, currency): +def get_paid_amount(payment_entry, currency, bank_account): if payment_entry.payment_document in ["Payment Entry", "Sales Invoice", "Purchase Invoice"]: paid_amount_field = "paid_amount" @@ -115,7 +115,7 @@ def get_paid_amount(payment_entry, currency): payment_entry.payment_entry, paid_amount_field) elif payment_entry.payment_document == "Journal Entry": - return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "total_credit") + return frappe.db.get_value('Journal Entry Account', {'parent': payment_entry.payment_entry, 'account': bank_account}, "sum(credit_in_account_currency)", debug=1) elif payment_entry.payment_document == "Expense Claim": return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "total_amount_reimbursed") From 057cfb22d693a289a9d01559d8c175c4bdf1a293 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 28 Dec 2021 13:18:17 +0530 Subject: [PATCH 122/193] fix: Remove debug statement (cherry picked from commit becb39da902e7e113c3dacb7eee7921144b8e99d) --- erpnext/accounts/doctype/bank_transaction/bank_transaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index e877765fe6..44cea31ed3 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -115,7 +115,7 @@ def get_paid_amount(payment_entry, currency, bank_account): payment_entry.payment_entry, paid_amount_field) elif payment_entry.payment_document == "Journal Entry": - return frappe.db.get_value('Journal Entry Account', {'parent': payment_entry.payment_entry, 'account': bank_account}, "sum(credit_in_account_currency)", debug=1) + return frappe.db.get_value('Journal Entry Account', {'parent': payment_entry.payment_entry, 'account': bank_account}, "sum(credit_in_account_currency)") elif payment_entry.payment_document == "Expense Claim": return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "total_amount_reimbursed") From 095a6e186f4ace26bf8e71bb0d91c43bdaa26d67 Mon Sep 17 00:00:00 2001 From: Development for People <47140294+developmentforpeople@users.noreply.github.com> Date: Tue, 28 Dec 2021 08:38:22 +0000 Subject: [PATCH 123/193] fix: rename non existent doctype field to the right one (#29055) --- erpnext/non_profit/doctype/membership/membership.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py index beb38e2110..f9b295a223 100644 --- a/erpnext/non_profit/doctype/membership/membership.py +++ b/erpnext/non_profit/doctype/membership/membership.py @@ -409,7 +409,7 @@ def get_plan_from_razorpay_id(plan_id): def set_expired_status(): frappe.db.sql(""" UPDATE - `tabMembership` SET `status` = 'Expired' + `tabMembership` SET `membership_status` = 'Expired' WHERE - `status` not in ('Cancelled') AND `to_date` < %s + `membership_status` not in ('Cancelled') AND `to_date` < %s """, (nowdate())) From 6f933db3cc03b48e9eaf904d42a5ab31a36a76d7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 28 Dec 2021 14:16:19 +0530 Subject: [PATCH 124/193] fix: Deferred revenue test case --- .../accounts/doctype/purchase_invoice/test_purchase_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index aa2408e092..cb18dd3b17 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -986,7 +986,7 @@ class TestPurchaseInvoice(unittest.TestCase): pi = make_purchase_invoice(item=item.name, qty=1, rate=100, do_not_save=True) pi.set_posting_time = 1 - pi.posting_date = '2019-03-15' + pi.posting_date = '2019-01-10' pi.items[0].enable_deferred_expense = 1 pi.items[0].service_start_date = "2019-01-10" pi.items[0].service_end_date = "2019-03-15" From 087ebcae5a6ac6c8f4bafde9a46c0bda735247bb Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 28 Dec 2021 15:34:50 +0530 Subject: [PATCH 125/193] Merge pull request #29014 from ankush/rename_sr_no fix!: disable renaming on serial nos --- erpnext/stock/doctype/serial_no/serial_no.json | 5 +++-- erpnext/stock/doctype/serial_no/serial_no.py | 17 ----------------- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/erpnext/stock/doctype/serial_no/serial_no.json b/erpnext/stock/doctype/serial_no/serial_no.json index a3d44af494..6e1e0d461a 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", @@ -434,10 +433,11 @@ "icon": "fa fa-barcode", "idx": 1, "links": [], - "modified": "2021-01-08 14:31:15.375996", + "modified": "2021-12-23 10:44:30.299450", "modified_by": "Administrator", "module": "Stock", "name": "Serial No", + "naming_rule": "By fieldname", "owner": "Administrator", "permissions": [ { @@ -476,5 +476,6 @@ "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 38291d19ec..2947fafe52 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -194,23 +194,6 @@ class SerialNo(StockController): if sle_exists: frappe.throw(_("Cannot delete Serial No {0}, as it is used in stock transactions").format(self.name)) - def before_rename(self, old, new, merge=False): - if merge: - frappe.throw(_("Sorry, Serial Nos cannot be merged")) - - def after_rename(self, old, new, merge=False): - """rename serial_no text fields""" - for dt in frappe.db.sql("""select parent from tabDocField - where fieldname='serial_no' and fieldtype in ('Text', 'Small Text', 'Long Text')"""): - - for item in frappe.db.sql("""select name, serial_no from `tab%s` - where serial_no like %s""" % (dt[0], frappe.db.escape('%' + old + '%'))): - - serial_nos = map(lambda i: new if i.upper()==old.upper() else i, item[1].split('\n')) - frappe.db.sql("""update `tab%s` set serial_no = %s - where name=%s""" % (dt[0], '%s', '%s'), - ('\n'.join(list(serial_nos)), item[0])) - 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")) From 88df5ce5306552739315be78ed5f667054c7b1f8 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 28 Dec 2021 17:09:46 +0530 Subject: [PATCH 126/193] fix: tests (#29051) * fix(test): create salary slip as per timesheet dates for Project Profitability * fix(test): reload salary slip before checking for payment days --- erpnext/payroll/doctype/salary_slip/test_salary_slip.py | 1 + .../project_profitability/test_project_profitability.py | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py index 3052a2b727..6e8fae0c1a 100644 --- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py @@ -171,6 +171,7 @@ class TestSalarySlip(unittest.TestCase): salary_slip.end_date = month_end_date salary_slip.save() salary_slip.submit() + salary_slip.reload() no_of_days = self.get_no_of_days() days_in_month = no_of_days[0] diff --git a/erpnext/projects/report/project_profitability/test_project_profitability.py b/erpnext/projects/report/project_profitability/test_project_profitability.py index 04156902f7..1eb3d0d717 100644 --- a/erpnext/projects/report/project_profitability/test_project_profitability.py +++ b/erpnext/projects/report/project_profitability/test_project_profitability.py @@ -25,6 +25,7 @@ class TestProjectProfitability(unittest.TestCase): self.timesheet = make_timesheet(emp, is_billable=1) self.salary_slip = make_salary_slip(self.timesheet.name) + self.salary_slip.start_date = self.timesheet.start_date holidays = self.salary_slip.get_holidays_for_employee(date, date) if holidays: @@ -41,8 +42,8 @@ class TestProjectProfitability(unittest.TestCase): def test_project_profitability(self): filters = { 'company': '_Test Company', - 'start_date': add_days(getdate(), -3), - 'end_date': getdate() + 'start_date': add_days(self.timesheet.start_date, -3), + 'end_date': self.timesheet.start_date } report = execute(filters) From f78bf4c6ef513fb4e0dde26b4eccecd1075c337d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 28 Dec 2021 17:40:05 +0530 Subject: [PATCH 127/193] fix: Test cases --- erpnext/loan_management/doctype/loan/test_loan.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py index f66cc54a13..b232b8761c 100644 --- a/erpnext/loan_management/doctype/loan/test_loan.py +++ b/erpnext/loan_management/doctype/loan/test_loan.py @@ -311,9 +311,9 @@ class TestLoan(unittest.TestCase): loan.load_from_db() - self.assertEqual(flt(loan.get('repayment_schedule')[3].principal_amount, 2), 32151.83) - self.assertEqual(flt(loan.get('repayment_schedule')[3].interest_amount, 2), 225.06) - self.assertEqual(flt(loan.get('repayment_schedule')[3].total_payment, 2), 32376.89) + self.assertEqual(flt(loan.get('repayment_schedule')[3].principal_amount, 2), 41369.83) + self.assertEqual(flt(loan.get('repayment_schedule')[3].interest_amount, 2), 289.59) + self.assertEqual(flt(loan.get('repayment_schedule')[3].total_payment, 2), 41659.41) self.assertEqual(flt(loan.get('repayment_schedule')[3].balance_loan_amount, 2), 0) def test_security_shortfall(self): From 68d49817a1da8bdd42502d86fb51c7d8df3289eb Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 28 Dec 2021 18:10:52 +0530 Subject: [PATCH 128/193] fix: Add test for loan repayment cancellation --- erpnext/loan_management/doctype/loan/test_loan.py | 8 ++++++++ .../doctype/loan_repayment/loan_repayment.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py index b232b8761c..1676c218c8 100644 --- a/erpnext/loan_management/doctype/loan/test_loan.py +++ b/erpnext/loan_management/doctype/loan/test_loan.py @@ -218,6 +218,14 @@ class TestLoan(unittest.TestCase): self.assertEqual(flt(loan.total_principal_paid, 0), flt(repayment_entry.amount_paid - penalty_amount - total_interest_paid, 0)) + # Check Repayment Entry cancel + repayment_entry.load_from_db() + repayment_entry.cancel() + + loan.load_from_db() + self.assertEqual(loan.total_principal_paid, 0) + self.assertEqual(loan.total_principal_paid, 0) + def test_loan_closure(self): pledge = [{ "loan_security": "Test Security 1", diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index f7d9e6602e..2abb3957b2 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -134,7 +134,7 @@ class LoanRepayment(AccountsController): }) pending_principal_amount = get_pending_principal_amount(loan) - if not loan.is_secured_loan and pending_principal_amount < 0: + if not loan.is_secured_loan and pending_principal_amount <= 0: loan.update({'status': 'Loan Closure Requested'}) for payment in self.repayment_details: From d9b9f4e8b76d529ade481483b161df0a50ce3530 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 29 Dec 2021 10:14:03 +0530 Subject: [PATCH 129/193] fix: optimize patch for update bom in SO and MR --- erpnext/patches/v12_0/update_bom_in_so_mr.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v12_0/update_bom_in_so_mr.py b/erpnext/patches/v12_0/update_bom_in_so_mr.py index 37d850fab4..132f3bd3b1 100644 --- a/erpnext/patches/v12_0/update_bom_in_so_mr.py +++ b/erpnext/patches/v12_0/update_bom_in_so_mr.py @@ -6,7 +6,7 @@ def execute(): frappe.reload_doc("selling", "doctype", "sales_order_item") for doctype in ["Sales Order", "Material Request"]: - condition = " and child_doc.stock_qty > child_doc.produced_qty" + condition = " and child_doc.stock_qty > child_doc.produced_qty and doc.per_delivered < 100" if doctype == "Material Request": condition = " and doc.per_ordered < 100 and doc.material_request_type = 'Manufacture'" @@ -15,5 +15,6 @@ def execute(): child_doc.bom_no = item.default_bom WHERE child_doc.item_code = item.name and child_doc.docstatus < 2 + and child_doc.parent = doc.name and item.default_bom is not null and item.default_bom != '' {cond} """.format(doc = doctype, cond = condition)) From 8c4cdf7afc5fba383eae93074a70647c6bdff7fe Mon Sep 17 00:00:00 2001 From: Kanchan Chauhan Date: Wed, 29 Dec 2021 16:44:20 +0530 Subject: [PATCH 130/193] refactor: Payment Request added to PO Dashboard --- .../buying/doctype/purchase_order/purchase_order_dashboard.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py b/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py index 0163595b51..d288f881de 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order_dashboard.py @@ -7,6 +7,7 @@ def get_data(): 'non_standard_fieldnames': { 'Journal Entry': 'reference_name', 'Payment Entry': 'reference_name', + 'Payment Request': 'reference_name', 'Auto Repeat': 'reference_document' }, 'internal_links': { @@ -21,7 +22,7 @@ def get_data(): }, { 'label': _('Payment'), - 'items': ['Payment Entry', 'Journal Entry'] + 'items': ['Payment Entry', 'Journal Entry', 'Payment Request'] }, { 'label': _('Reference'), From dbbc8d8aedce1d476d7466b06d54355841334263 Mon Sep 17 00:00:00 2001 From: Vaibhav Chopra <53619134+sudo-vaibhav@users.noreply.github.com> Date: Fri, 31 Dec 2021 13:34:20 +0530 Subject: [PATCH 131/193] fix: leave_allocation variable not being defined (#29086) --- .../leave_policy_assignment/leave_policy_assignment.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py index dca7e4895e..655e3ac53e 100644 --- a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py +++ b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py @@ -56,9 +56,7 @@ class LeavePolicyAssignment(Document): leave_policy_detail.leave_type, leave_policy_detail.annual_allocation, leave_type_details, date_of_joining ) - - leave_allocations[leave_policy_detail.leave_type] = {"name": leave_allocation, "leaves": new_leaves_allocated} - + leave_allocations[leave_policy_detail.leave_type] = {"name": leave_allocation, "leaves": new_leaves_allocated} self.db_set("leaves_allocated", 1) return leave_allocations From 538dffa6174cb22e7d5182f407856f585e467f20 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Fri, 31 Dec 2021 14:12:28 +0100 Subject: [PATCH 132/193] fix: apply suggestions from review --- erpnext/translations/de.csv | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 24e54fd035..551f6203c9 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -7536,9 +7536,9 @@ TS-.YYYY.-,TS-.YYYY.-, Employee Detail,Mitarbeiterdetails, Billing Details,Rechnungsdetails, Total Billable Hours,Summe abrechenbare Stunden, -Total Billed Hours,Summe Angekündigt Stunden, +Total Billed Hours,Summe abgerechneter Stunden, Total Costing Amount,Gesamtkalkulation Betrag, -Total Billable Amount,Summe abrechenbare Betrag, +Total Billable Amount,Summe abrechenbarer Betrag, Total Billed Amount,Gesamtrechnungsbetrag, % Amount Billed,% des Betrages berechnet, Hrs,Std, @@ -7851,7 +7851,7 @@ New Sales Orders,Neue Aufträge, New Purchase Orders,Neue Bestellungen an Lieferanten, Sales Orders to Deliver,Auszuliefernde Aufträge, Purchase Orders to Receive,Anzuliefernde Bestellungen, -New Purchase Invoice,Neue Ausgangsrechnung, +New Purchase Invoice,Neue Eingangsrechnung, New Quotations,Neue Angebote, Open Quotations,Angebote öffnen, Open Issues,Offene Punkte, @@ -7972,7 +7972,7 @@ MAT-DN-.YYYY.-,MAT-DN-.YYYY.-, Is Return,Ist Rückgabe, Issue Credit Note,Gutschrift ausgeben, Return Against Delivery Note,Zurück zum Lieferschein, -Customer's Purchase Order No,Bestell Nr. des Kunden, +Customer's Purchase Order No,Bestellnummer des Kunden, Billing Address Name,Name der Rechnungsadresse, Required only for sample item.,Nur erforderlich für Probeartikel., "If you have created a standard template in Sales Taxes and Charges Template, select one and click on the button below.","Wenn eine Standardvorlage unter den Vorlagen ""Steuern und Abgaben beim Verkauf"" erstellt wurde, bitte eine Vorlage auswählen und auf die Schaltfläche unten klicken.", From a4e3d79bc1c8d79a6f303a31164e43f438cfcfa7 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Fri, 31 Dec 2021 17:43:10 +0100 Subject: [PATCH 133/193] revert: german translation of "Minute" --- erpnext/translations/de.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 551f6203c9..0aca1a07bd 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -7561,7 +7561,7 @@ Agenda,Agenda, Minutes,Protokolle, Quality Meeting Agenda,Qualitätstreffen Agenda, Quality Meeting Minutes,Qualitätssitzungsprotokoll, -Minute,Protokoll, +Minute,Minute, Parent Procedure,Übergeordnetes Verfahren, Processes,Prozesse, Quality Procedure Process,Qualitätsprozess, From 466e549842c0e8a4c6b325240a9fda75090db28a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 2 Jan 2022 17:53:15 +0530 Subject: [PATCH 134/193] fix(India): Tax and Charges template not getting fetched based on tax category assigned (cherry picked from commit 7a5937a98c3d74bdb1da79c3d5a8f49ef76ded2d) --- erpnext/regional/india/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 215b483c7a..d443f9c15c 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -215,7 +215,7 @@ def get_regional_address_details(party_details, doctype, company): if tax_template_by_category: party_details['taxes_and_charges'] = tax_template_by_category - return + return party_details if not party_details.place_of_supply: return party_details if not party_details.company_gstin: return party_details From 4b6217a6832af1d61ef193f49e4cbf9ebdb6cc24 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 2 Jan 2022 18:39:59 +0530 Subject: [PATCH 135/193] fix: Test Case (cherry picked from commit 342658ea7028cc0d890200364cc08cf97e55d6d0) # Conflicts: # erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py --- .../doctype/purchase_invoice/test_purchase_invoice.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index cb18dd3b17..699af60825 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1236,7 +1236,11 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date): def update_tax_witholding_category(company, account): from erpnext.accounts.utils import get_fiscal_year +<<<<<<< HEAD fiscal_year = get_fiscal_year(fiscal_year='2021') +======= + fiscal_year = get_fiscal_year(date=nowdate()) +>>>>>>> 342658ea70 (fix: Test Case) if not frappe.db.get_value('Tax Withholding Rate', {'parent': 'TDS - 194 - Dividends - Individual', 'from_date': ('>=', fiscal_year[1]), From c3d890f3f18f209cc2a6ba3ccb75f66470d14616 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Sun, 2 Jan 2022 19:10:49 +0530 Subject: [PATCH 136/193] fix: get fiscal year based on date --- .../doctype/purchase_invoice/test_purchase_invoice.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 699af60825..21846bb76c 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1236,11 +1236,7 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date): def update_tax_witholding_category(company, account): from erpnext.accounts.utils import get_fiscal_year -<<<<<<< HEAD - fiscal_year = get_fiscal_year(fiscal_year='2021') -======= fiscal_year = get_fiscal_year(date=nowdate()) ->>>>>>> 342658ea70 (fix: Test Case) if not frappe.db.get_value('Tax Withholding Rate', {'parent': 'TDS - 194 - Dividends - Individual', 'from_date': ('>=', fiscal_year[1]), From d2074b16dbd8fc54d4d435001787d95aba1cde85 Mon Sep 17 00:00:00 2001 From: Conor Date: Sun, 2 Jan 2022 12:03:10 -0600 Subject: [PATCH 137/193] ci: postgres unittests configurations (#28952) Co-authored-by: Ankush Menat --- .github/helper/install.sh | 29 +++-- ...e_config.json => site_config_mariadb.json} | 2 +- .github/helper/site_config_postgres.json | 18 +++ .github/workflows/patch.yml | 3 + ...ver-tests.yml => server-tests-mariadb.yml} | 6 +- .github/workflows/server-tests-postgres.yml | 105 ++++++++++++++++++ 6 files changed, 152 insertions(+), 11 deletions(-) rename .github/helper/{site_config.json => site_config_mariadb.json} (99%) create mode 100644 .github/helper/site_config_postgres.json rename .github/workflows/{server-tests.yml => server-tests-mariadb.yml} (95%) create mode 100644 .github/workflows/server-tests-postgres.yml diff --git a/.github/helper/install.sh b/.github/helper/install.sh index 85f146d351..903196847d 100644 --- a/.github/helper/install.sh +++ b/.github/helper/install.sh @@ -12,17 +12,30 @@ git clone https://github.com/frappe/frappe --branch "${GITHUB_BASE_REF:-${GITHUB bench init --skip-assets --frappe-path ~/frappe --python "$(which python)" frappe-bench mkdir ~/frappe-bench/sites/test_site -cp -r "${GITHUB_WORKSPACE}/.github/helper/site_config.json" ~/frappe-bench/sites/test_site/ -mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL character_set_server = 'utf8mb4'" -mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'" +if [ "$DB" == "mariadb" ];then + cp -r "${GITHUB_WORKSPACE}/.github/helper/site_config_mariadb.json" ~/frappe-bench/sites/test_site/site_config.json +else + cp -r "${GITHUB_WORKSPACE}/.github/helper/site_config_postgres.json" ~/frappe-bench/sites/test_site/site_config.json +fi -mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe'" -mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE DATABASE test_frappe" -mysql --host 127.0.0.1 --port 3306 -u root -e "GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost'" -mysql --host 127.0.0.1 --port 3306 -u root -e "UPDATE mysql.user SET Password=PASSWORD('travis') WHERE User='root'" -mysql --host 127.0.0.1 --port 3306 -u root -e "FLUSH PRIVILEGES" +if [ "$DB" == "mariadb" ];then + mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL character_set_server = 'utf8mb4'" + mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'" + + mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe'" + mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE DATABASE test_frappe" + mysql --host 127.0.0.1 --port 3306 -u root -e "GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost'" + + mysql --host 127.0.0.1 --port 3306 -u root -e "UPDATE mysql.user SET Password=PASSWORD('travis') WHERE User='root'" + mysql --host 127.0.0.1 --port 3306 -u root -e "FLUSH PRIVILEGES" +fi + +if [ "$DB" == "postgres" ];then + echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE DATABASE test_frappe" -U postgres; + echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE USER test_frappe WITH PASSWORD 'test_frappe'" -U postgres; +fi wget -O /tmp/wkhtmltox.tar.xz https://github.com/frappe/wkhtmltopdf/raw/master/wkhtmltox-0.12.3_linux-generic-amd64.tar.xz tar -xf /tmp/wkhtmltox.tar.xz -C /tmp diff --git a/.github/helper/site_config.json b/.github/helper/site_config_mariadb.json similarity index 99% rename from .github/helper/site_config.json rename to .github/helper/site_config_mariadb.json index 60ef80cbad..948ad08bab 100644 --- a/.github/helper/site_config.json +++ b/.github/helper/site_config_mariadb.json @@ -13,4 +13,4 @@ "host_name": "http://test_site:8000", "install_apps": ["erpnext"], "throttle_user_limit": 100 -} \ No newline at end of file +} diff --git a/.github/helper/site_config_postgres.json b/.github/helper/site_config_postgres.json new file mode 100644 index 0000000000..c82905fea0 --- /dev/null +++ b/.github/helper/site_config_postgres.json @@ -0,0 +1,18 @@ +{ + "db_host": "127.0.0.1", + "db_port": 5432, + "db_name": "test_frappe", + "db_password": "test_frappe", + "db_type": "postgres", + "allow_tests": true, + "auto_email_id": "test@example.com", + "mail_server": "smtp.example.com", + "mail_login": "test@example.com", + "mail_password": "test", + "admin_password": "admin", + "root_login": "postgres", + "root_password": "travis", + "host_name": "http://test_site:8000", + "install_apps": ["erpnext"], + "throttle_user_limit": 100 +} diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml index 97bccf5d56..33a28ac9bb 100644 --- a/.github/workflows/patch.yml +++ b/.github/workflows/patch.yml @@ -80,6 +80,9 @@ jobs: - name: Install run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh + env: + DB: mariadb + TYPE: server - name: Run Patch Tests run: | diff --git a/.github/workflows/server-tests.yml b/.github/workflows/server-tests-mariadb.yml similarity index 95% rename from .github/workflows/server-tests.yml rename to .github/workflows/server-tests-mariadb.yml index 77c0aee195..186f95e6ec 100644 --- a/.github/workflows/server-tests.yml +++ b/.github/workflows/server-tests-mariadb.yml @@ -1,10 +1,11 @@ -name: Server +name: Server (Mariadb) on: pull_request: paths-ignore: - '**.js' - '**.md' + - '**.html' workflow_dispatch: push: branches: [ develop ] @@ -13,7 +14,7 @@ on: - '**.md' concurrency: - group: server-develop-${{ github.event.number }} + group: server-mariadb-develop-${{ github.event.number }} cancel-in-progress: true jobs: @@ -92,6 +93,7 @@ jobs: - name: Install run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh env: + DB: mariadb TYPE: server - name: Run Tests diff --git a/.github/workflows/server-tests-postgres.yml b/.github/workflows/server-tests-postgres.yml new file mode 100644 index 0000000000..3bbf6a91f5 --- /dev/null +++ b/.github/workflows/server-tests-postgres.yml @@ -0,0 +1,105 @@ +name: Server (Postgres) + +on: + pull_request: + paths-ignore: + - '**.js' + - '**.md' + - '**.html' + types: [opened, labelled, synchronize, reopened] + +concurrency: + group: server-postgres-develop-${{ github.event.number }} + cancel-in-progress: true + +jobs: + test: + if: ${{ contains(github.event.pull_request.labels.*.name, 'postgres') }} + runs-on: ubuntu-latest + timeout-minutes: 60 + + strategy: + fail-fast: false + matrix: + container: [1, 2, 3] + + name: Python Unit Tests + + services: + postgres: + image: postgres:13.3 + env: + POSTGRES_PASSWORD: travis + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + + - name: Clone + uses: actions/checkout@v2 + + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: 3.7 + + - name: Setup Node + uses: actions/setup-node@v2 + with: + node-version: 14 + check-latest: true + + - name: Add to Hosts + run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts + + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + ${{ runner.os }}- + + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v2 + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + + - name: Install + run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh + env: + DB: postgres + TYPE: server + + - name: Run Tests + run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --use-orchestrator + env: + TYPE: server + CI_BUILD_ID: ${{ github.run_id }} + ORCHESTRATOR_URL: http://test-orchestrator.frappe.io From d0f5beeb3490123050026b685c9e12b96f346bd9 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 3 Jan 2022 11:33:03 +0530 Subject: [PATCH 138/193] fix: filter query in bank reconciliation tool (#29098) --- .../bank_reconciliation_tool/bank_reconciliation_tool.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js index 78e7ff6ea2..335f8502c7 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js @@ -7,7 +7,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", { frm.set_query("bank_account", function () { return { filters: { - company: ["in", frm.doc.company], + company: frm.doc.company, 'is_company_account': 1 }, }; From f02e6b463188a35b0798cdd6174f374b24bbc81b Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 3 Jan 2022 14:28:34 +0530 Subject: [PATCH 139/193] fix: incorrect posting time fetching incorrect qty (#29103) --- 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 3b1ae3b43c..3c70b41eda 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -86,8 +86,8 @@ def get_stock_balance(item_code, warehouse, posting_date=None, posting_time=None from erpnext.stock.stock_ledger import get_previous_sle - if not posting_date: posting_date = nowdate() - if not posting_time: posting_time = nowtime() + if posting_date is None: posting_date = nowdate() + if posting_time is None: posting_time = nowtime() args = { "item_code": item_code, From 267da4888900c7c2b6796637dc19d68ff442f07f Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Mon, 3 Jan 2022 16:40:11 +0530 Subject: [PATCH 140/193] fix: added Sum() in query --- erpnext/stock/doctype/batch/batch.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index ebca87e71d..77e53c569d 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -322,8 +322,9 @@ def get_pos_reserved_batch_qty(filters): p = frappe.qb.DocType("POS Invoice").as_("p") item = frappe.qb.DocType("POS Invoice Item").as_("item") + sum_qty = frappe.query_builder.functions.Sum(item.qty).as_("qty") - pos_transacted_batch_nos = frappe.qb.from_(p).from_(item).select(item.qty).where( + reserved_batch_qty = frappe.qb.from_(p).from_(item).select(sum_qty).where( (p.name == item.parent) & (p.consolidated_invoice.isnull()) & (p.status != "Consolidated") & @@ -332,10 +333,6 @@ def get_pos_reserved_batch_qty(filters): (item.item_code == filters.get('item_code')) & (item.warehouse == filters.get('warehouse')) & (item.batch_no == filters.get('batch_no')) - ).run(as_dict=True) + ).run() - reserved_batch_qty = 0.0 - for d in pos_transacted_batch_nos: - reserved_batch_qty += d.qty - - return reserved_batch_qty \ No newline at end of file + return reserved_batch_qty[0][0] \ No newline at end of file From 73854972198e99ba529651211ad264e9c50ea5ab Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Mon, 3 Jan 2022 16:57:00 +0530 Subject: [PATCH 141/193] fix: using get_batch_qty method to get available_qty --- erpnext/accounts/doctype/pos_invoice/pos_invoice.py | 4 ++-- erpnext/stock/doctype/batch/batch.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 6cda6266e4..11d59bcf70 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -15,7 +15,7 @@ from erpnext.accounts.doctype.sales_invoice.sales_invoice import ( update_multi_mode_option, ) from erpnext.accounts.party import get_due_date, get_party_account -from erpnext.stock.doctype.batch.batch import get_pos_reserved_batch_qty +from erpnext.stock.doctype.batch.batch import get_batch_qty, get_pos_reserved_batch_qty from erpnext.stock.doctype.serial_no.serial_no import get_pos_reserved_serial_nos, get_serial_nos @@ -131,7 +131,7 @@ class POSInvoice(SalesInvoice): def validate_pos_reserved_batch_qty(self, item): filters = {"item_code": item.item_code, "warehouse": item.warehouse, "batch_no":item.batch_no} - available_batch_qty = frappe.db.get_value('Batch', item.batch_no, 'batch_qty') + available_batch_qty = get_batch_qty(item.batch_no, item.warehouse, item.item_code) reserved_batch_qty = get_pos_reserved_batch_qty(filters) bold_item_name = frappe.bold(item.item_name) diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index 77e53c569d..5593101575 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -335,4 +335,5 @@ def get_pos_reserved_batch_qty(filters): (item.batch_no == filters.get('batch_no')) ).run() - return reserved_batch_qty[0][0] \ No newline at end of file + flt_reserved_batch_qty = flt(reserved_batch_qty[0][0]) + return flt_reserved_batch_qty \ No newline at end of file From 11cfa1615864ecabce230858cb4cc48a77d7db2a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 4 Jan 2022 11:38:49 +0530 Subject: [PATCH 142/193] fix: future recurring period calculation (#29083) Co-authored-by: Rucha Mahabal --- erpnext/payroll/doctype/salary_slip/salary_slip.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index b035292c0b..a4e75acfde 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -932,8 +932,11 @@ class SalarySlip(TransactionBase): def get_future_recurring_additional_amount(self, additional_salary, monthly_additional_amount): future_recurring_additional_amount = 0 to_date = frappe.db.get_value("Additional Salary", additional_salary, 'to_date') + # future month count excluding current - future_recurring_period = (getdate(to_date).month - getdate(self.start_date).month) + from_date, to_date = getdate(self.start_date), getdate(to_date) + future_recurring_period = ((to_date.year - from_date.year) * 12) + (to_date.month - from_date.month) + if future_recurring_period > 0: future_recurring_additional_amount = monthly_additional_amount * future_recurring_period # Used earning.additional_amount to consider the amount for the full month return future_recurring_additional_amount @@ -1032,7 +1035,8 @@ class SalarySlip(TransactionBase): data.update({"annual_taxable_earning": annual_taxable_earning}) tax_amount = 0 for slab in tax_slab.slabs: - if slab.condition and not self.eval_tax_slab_condition(slab.condition, data): + cond = cstr(slab.condition).strip() + if cond and not self.eval_tax_slab_condition(cond, data): continue 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 From 3aa1817f7bec944ba30409b03430eb6ae5da67b5 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 4 Jan 2022 14:43:02 +0530 Subject: [PATCH 143/193] fix: cannot create reverse journal entry --- .../accounts/doctype/journal_entry/journal_entry.js | 2 +- .../accounts/doctype/journal_entry/journal_entry.json | 11 ++++++++++- .../accounts/doctype/journal_entry/journal_entry.py | 10 ++++------ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 957a50f013..617b376128 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -31,7 +31,7 @@ frappe.ui.form.on("Journal Entry", { if(frm.doc.docstatus==1) { frm.add_custom_button(__('Reverse Journal Entry'), function() { return erpnext.journal_entry.reverse_journal_entry(frm); - }, __('Make')); + }, __('Actions')); } if (frm.doc.__islocal) { diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json index 20678d787b..335fd350de 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.json +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json @@ -13,6 +13,7 @@ "voucher_type", "naming_series", "finance_book", + "reversal_of", "tax_withholding_category", "column_break1", "from_template", @@ -515,13 +516,21 @@ "fieldname": "apply_tds", "fieldtype": "Check", "label": "Apply Tax Withholding Amount " + }, + { + "depends_on": "eval:doc.docstatus", + "fieldname": "reversal_of", + "fieldtype": "Link", + "label": "Reversal Of", + "options": "Journal Entry", + "read_only": 1 } ], "icon": "fa fa-file-text", "idx": 176, "is_submittable": 1, "links": [], - "modified": "2021-09-09 15:31:14.484029", + "modified": "2022-01-04 13:39:36.485954", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry", diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index ca17265078..8fc4e8c5e3 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -1157,9 +1157,8 @@ def make_inter_company_journal_entry(name, voucher_type, company): def make_reverse_journal_entry(source_name, target_doc=None): from frappe.model.mapper import get_mapped_doc - def update_accounts(source, target, source_parent): - target.reference_type = "Journal Entry" - target.reference_name = source_parent.name + def post_process(source, target): + target.reversal_of = source.name doclist = get_mapped_doc("Journal Entry", source_name, { "Journal Entry": { @@ -1177,9 +1176,8 @@ def make_reverse_journal_entry(source_name, target_doc=None): "debit": "credit", "credit_in_account_currency": "debit_in_account_currency", "credit": "debit", - }, - "postprocess": update_accounts, + } }, - }, target_doc) + }, target_doc, post_process) return doclist From e73c51bee613f31446d0a5bcfa850610cd897aca Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 4 Jan 2022 17:23:03 +0530 Subject: [PATCH 144/193] test: fix test broken by todo schema change --- erpnext/projects/doctype/task/test_task.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/projects/doctype/task/test_task.py b/erpnext/projects/doctype/task/test_task.py index a0ac7c1497..5f5b519ba4 100644 --- a/erpnext/projects/doctype/task/test_task.py +++ b/erpnext/projects/doctype/task/test_task.py @@ -78,11 +78,11 @@ class TestTask(unittest.TestCase): return frappe.db.get_value("ToDo", filters={"reference_type": task.doctype, "reference_name": task.name, "description": "Close this task"}, - fieldname=("owner", "status"), as_dict=True) + fieldname=("allocated_to", "status"), as_dict=True) assign() todo = get_owner_and_status() - self.assertEqual(todo.owner, "test@example.com") + self.assertEqual(todo.allocated_to, "test@example.com") self.assertEqual(todo.status, "Open") # assignment should be @@ -90,7 +90,7 @@ class TestTask(unittest.TestCase): task.status = "Completed" task.save() todo = get_owner_and_status() - self.assertEqual(todo.owner, "test@example.com") + self.assertEqual(todo.allocated_to, "test@example.com") self.assertEqual(todo.status, "Closed") def test_overdue(self): From 0900c3d65579b4c8de411ff04898994e546261f0 Mon Sep 17 00:00:00 2001 From: Chillar Anand Date: Tue, 4 Jan 2022 17:26:11 +0530 Subject: [PATCH 145/193] refactor: Removed agriculture module from ERPNext (#29022) --- .../doctype/pricing_rule/test_pricing_rule.py | 2 +- erpnext/agriculture/__init__.py | 0 erpnext/agriculture/doctype/__init__.py | 0 .../agriculture_analysis_criteria/__init__.py | 0 .../agriculture_analysis_criteria.js | 8 - .../agriculture_analysis_criteria.json | 182 --- .../agriculture_analysis_criteria.py | 9 - .../test_agriculture_analysis_criteria.py | 8 - .../doctype/agriculture_task/__init__.py | 0 .../agriculture_task/agriculture_task.js | 8 - .../agriculture_task/agriculture_task.json | 212 ---- .../agriculture_task/agriculture_task.py | 9 - .../agriculture_task/test_agriculture_task.py | 8 - erpnext/agriculture/doctype/crop/__init__.py | 0 erpnext/agriculture/doctype/crop/crop.js | 55 - erpnext/agriculture/doctype/crop/crop.json | 1110 ----------------- erpnext/agriculture/doctype/crop/crop.py | 31 - .../doctype/crop/crop_dashboard.py | 12 - erpnext/agriculture/doctype/crop/test_crop.py | 13 - .../doctype/crop/test_records.json | 80 -- .../doctype/crop_cycle/__init__.py | 0 .../doctype/crop_cycle/crop_cycle.js | 48 - .../doctype/crop_cycle/crop_cycle.json | 904 -------------- .../doctype/crop_cycle/crop_cycle.py | 126 -- .../doctype/crop_cycle/test_crop_cycle.py | 72 -- .../doctype/crop_cycle/test_records.json | 15 - .../doctype/detected_disease/__init__.py | 0 .../detected_disease/detected_disease.json | 142 --- .../detected_disease/detected_disease.py | 9 - .../agriculture/doctype/disease/__init__.py | 0 .../agriculture/doctype/disease/disease.js | 8 - .../agriculture/doctype/disease/disease.json | 308 ----- .../agriculture/doctype/disease/disease.py | 19 - .../doctype/disease/test_disease.py | 12 - .../doctype/disease/test_records.json | 18 - .../doctype/fertilizer/__init__.py | 0 .../doctype/fertilizer/fertilizer.js | 8 - .../doctype/fertilizer/fertilizer.json | 307 ----- .../doctype/fertilizer/fertilizer.py | 14 - .../doctype/fertilizer/test_fertilizer.py | 11 - .../doctype/fertilizer/test_records.json | 13 - .../doctype/fertilizer_content/__init__.py | 0 .../fertilizer_content.json | 103 -- .../fertilizer_content/fertilizer_content.py | 9 - .../doctype/linked_location/__init__.py | 0 .../linked_location/linked_location.json | 77 -- .../linked_location/linked_location.py | 9 - .../doctype/linked_plant_analysis/__init__.py | 0 .../linked_plant_analysis.json | 77 -- .../linked_plant_analysis.py | 9 - .../doctype/linked_soil_analysis/__init__.py | 0 .../linked_soil_analysis.json | 77 -- .../linked_soil_analysis.py | 9 - .../doctype/linked_soil_texture/__init__.py | 0 .../linked_soil_texture.json | 77 -- .../linked_soil_texture.py | 9 - .../doctype/plant_analysis/__init__.py | 0 .../doctype/plant_analysis/plant_analysis.js | 17 - .../plant_analysis/plant_analysis.json | 372 ------ .../doctype/plant_analysis/plant_analysis.py | 14 - .../plant_analysis/test_plant_analysis.py | 8 - .../plant_analysis_criteria/__init__.py | 0 .../plant_analysis_criteria.json | 173 --- .../plant_analysis_criteria.py | 9 - .../doctype/soil_analysis/__init__.py | 0 .../doctype/soil_analysis/soil_analysis.js | 17 - .../doctype/soil_analysis/soil_analysis.json | 593 --------- .../doctype/soil_analysis/soil_analysis.py | 14 - .../soil_analysis/test_soil_analysis.py | 8 - .../soil_analysis_criteria/__init__.py | 0 .../soil_analysis_criteria.json | 173 --- .../soil_analysis_criteria.py | 9 - .../doctype/soil_texture/__init__.py | 0 .../doctype/soil_texture/soil_texture.js | 59 - .../doctype/soil_texture/soil_texture.json | 533 -------- .../doctype/soil_texture/soil_texture.py | 71 -- .../doctype/soil_texture/test_records.json | 9 - .../doctype/soil_texture/test_soil_texture.py | 14 - .../doctype/soil_texture_criteria/__init__.py | 0 .../soil_texture_criteria.json | 173 --- .../soil_texture_criteria.py | 9 - .../doctype/water_analysis/__init__.py | 0 .../water_analysis/test_water_analysis.py | 8 - .../doctype/water_analysis/water_analysis.js | 18 - .../water_analysis/water_analysis.json | 594 --------- .../doctype/water_analysis/water_analysis.py | 26 - .../water_analysis_criteria/__init__.py | 0 .../water_analysis_criteria.json | 173 --- .../water_analysis_criteria.py | 9 - .../agriculture/doctype/weather/__init__.py | 0 .../doctype/weather/test_weather.py | 8 - .../agriculture/doctype/weather/weather.js | 8 - .../agriculture/doctype/weather/weather.json | 307 ----- .../agriculture/doctype/weather/weather.py | 14 - .../doctype/weather_parameter/__init__.py | 0 .../weather_parameter/weather_parameter.json | 173 --- .../weather_parameter/weather_parameter.py | 9 - erpnext/agriculture/setup.py | 429 ------- .../workspace/agriculture/agriculture.json | 171 --- erpnext/demo/domains.py | 3 - erpnext/domains/agriculture.py | 26 - erpnext/hooks.py | 13 - erpnext/modules.txt | 1 - erpnext/patches.txt | 4 +- .../v13_0/agriculture_deprecation_warning.py | 10 + .../v14_0/delete_agriculture_doctypes.py | 19 + erpnext/public/js/setup_wizard.js | 1 - .../operations/install_fixtures.py | 1 - 108 files changed, 33 insertions(+), 8504 deletions(-) delete mode 100644 erpnext/agriculture/__init__.py delete mode 100644 erpnext/agriculture/doctype/__init__.py delete mode 100644 erpnext/agriculture/doctype/agriculture_analysis_criteria/__init__.py delete mode 100644 erpnext/agriculture/doctype/agriculture_analysis_criteria/agriculture_analysis_criteria.js delete mode 100644 erpnext/agriculture/doctype/agriculture_analysis_criteria/agriculture_analysis_criteria.json delete mode 100644 erpnext/agriculture/doctype/agriculture_analysis_criteria/agriculture_analysis_criteria.py delete mode 100644 erpnext/agriculture/doctype/agriculture_analysis_criteria/test_agriculture_analysis_criteria.py delete mode 100644 erpnext/agriculture/doctype/agriculture_task/__init__.py delete mode 100644 erpnext/agriculture/doctype/agriculture_task/agriculture_task.js delete mode 100644 erpnext/agriculture/doctype/agriculture_task/agriculture_task.json delete mode 100644 erpnext/agriculture/doctype/agriculture_task/agriculture_task.py delete mode 100644 erpnext/agriculture/doctype/agriculture_task/test_agriculture_task.py delete mode 100644 erpnext/agriculture/doctype/crop/__init__.py delete mode 100644 erpnext/agriculture/doctype/crop/crop.js delete mode 100644 erpnext/agriculture/doctype/crop/crop.json delete mode 100644 erpnext/agriculture/doctype/crop/crop.py delete mode 100644 erpnext/agriculture/doctype/crop/crop_dashboard.py delete mode 100644 erpnext/agriculture/doctype/crop/test_crop.py delete mode 100644 erpnext/agriculture/doctype/crop/test_records.json delete mode 100644 erpnext/agriculture/doctype/crop_cycle/__init__.py delete mode 100644 erpnext/agriculture/doctype/crop_cycle/crop_cycle.js delete mode 100644 erpnext/agriculture/doctype/crop_cycle/crop_cycle.json delete mode 100644 erpnext/agriculture/doctype/crop_cycle/crop_cycle.py delete mode 100644 erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.py delete mode 100644 erpnext/agriculture/doctype/crop_cycle/test_records.json delete mode 100644 erpnext/agriculture/doctype/detected_disease/__init__.py delete mode 100644 erpnext/agriculture/doctype/detected_disease/detected_disease.json delete mode 100644 erpnext/agriculture/doctype/detected_disease/detected_disease.py delete mode 100644 erpnext/agriculture/doctype/disease/__init__.py delete mode 100644 erpnext/agriculture/doctype/disease/disease.js delete mode 100644 erpnext/agriculture/doctype/disease/disease.json delete mode 100644 erpnext/agriculture/doctype/disease/disease.py delete mode 100644 erpnext/agriculture/doctype/disease/test_disease.py delete mode 100644 erpnext/agriculture/doctype/disease/test_records.json delete mode 100644 erpnext/agriculture/doctype/fertilizer/__init__.py delete mode 100644 erpnext/agriculture/doctype/fertilizer/fertilizer.js delete mode 100644 erpnext/agriculture/doctype/fertilizer/fertilizer.json delete mode 100644 erpnext/agriculture/doctype/fertilizer/fertilizer.py delete mode 100644 erpnext/agriculture/doctype/fertilizer/test_fertilizer.py delete mode 100644 erpnext/agriculture/doctype/fertilizer/test_records.json delete mode 100644 erpnext/agriculture/doctype/fertilizer_content/__init__.py delete mode 100644 erpnext/agriculture/doctype/fertilizer_content/fertilizer_content.json delete mode 100644 erpnext/agriculture/doctype/fertilizer_content/fertilizer_content.py delete mode 100644 erpnext/agriculture/doctype/linked_location/__init__.py delete mode 100644 erpnext/agriculture/doctype/linked_location/linked_location.json delete mode 100644 erpnext/agriculture/doctype/linked_location/linked_location.py delete mode 100644 erpnext/agriculture/doctype/linked_plant_analysis/__init__.py delete mode 100644 erpnext/agriculture/doctype/linked_plant_analysis/linked_plant_analysis.json delete mode 100644 erpnext/agriculture/doctype/linked_plant_analysis/linked_plant_analysis.py delete mode 100644 erpnext/agriculture/doctype/linked_soil_analysis/__init__.py delete mode 100644 erpnext/agriculture/doctype/linked_soil_analysis/linked_soil_analysis.json delete mode 100644 erpnext/agriculture/doctype/linked_soil_analysis/linked_soil_analysis.py delete mode 100644 erpnext/agriculture/doctype/linked_soil_texture/__init__.py delete mode 100644 erpnext/agriculture/doctype/linked_soil_texture/linked_soil_texture.json delete mode 100644 erpnext/agriculture/doctype/linked_soil_texture/linked_soil_texture.py delete mode 100644 erpnext/agriculture/doctype/plant_analysis/__init__.py delete mode 100644 erpnext/agriculture/doctype/plant_analysis/plant_analysis.js delete mode 100644 erpnext/agriculture/doctype/plant_analysis/plant_analysis.json delete mode 100644 erpnext/agriculture/doctype/plant_analysis/plant_analysis.py delete mode 100644 erpnext/agriculture/doctype/plant_analysis/test_plant_analysis.py delete mode 100644 erpnext/agriculture/doctype/plant_analysis_criteria/__init__.py delete mode 100644 erpnext/agriculture/doctype/plant_analysis_criteria/plant_analysis_criteria.json delete mode 100644 erpnext/agriculture/doctype/plant_analysis_criteria/plant_analysis_criteria.py delete mode 100644 erpnext/agriculture/doctype/soil_analysis/__init__.py delete mode 100644 erpnext/agriculture/doctype/soil_analysis/soil_analysis.js delete mode 100644 erpnext/agriculture/doctype/soil_analysis/soil_analysis.json delete mode 100644 erpnext/agriculture/doctype/soil_analysis/soil_analysis.py delete mode 100644 erpnext/agriculture/doctype/soil_analysis/test_soil_analysis.py delete mode 100644 erpnext/agriculture/doctype/soil_analysis_criteria/__init__.py delete mode 100644 erpnext/agriculture/doctype/soil_analysis_criteria/soil_analysis_criteria.json delete mode 100644 erpnext/agriculture/doctype/soil_analysis_criteria/soil_analysis_criteria.py delete mode 100644 erpnext/agriculture/doctype/soil_texture/__init__.py delete mode 100644 erpnext/agriculture/doctype/soil_texture/soil_texture.js delete mode 100644 erpnext/agriculture/doctype/soil_texture/soil_texture.json delete mode 100644 erpnext/agriculture/doctype/soil_texture/soil_texture.py delete mode 100644 erpnext/agriculture/doctype/soil_texture/test_records.json delete mode 100644 erpnext/agriculture/doctype/soil_texture/test_soil_texture.py delete mode 100644 erpnext/agriculture/doctype/soil_texture_criteria/__init__.py delete mode 100644 erpnext/agriculture/doctype/soil_texture_criteria/soil_texture_criteria.json delete mode 100644 erpnext/agriculture/doctype/soil_texture_criteria/soil_texture_criteria.py delete mode 100644 erpnext/agriculture/doctype/water_analysis/__init__.py delete mode 100644 erpnext/agriculture/doctype/water_analysis/test_water_analysis.py delete mode 100644 erpnext/agriculture/doctype/water_analysis/water_analysis.js delete mode 100644 erpnext/agriculture/doctype/water_analysis/water_analysis.json delete mode 100644 erpnext/agriculture/doctype/water_analysis/water_analysis.py delete mode 100644 erpnext/agriculture/doctype/water_analysis_criteria/__init__.py delete mode 100644 erpnext/agriculture/doctype/water_analysis_criteria/water_analysis_criteria.json delete mode 100644 erpnext/agriculture/doctype/water_analysis_criteria/water_analysis_criteria.py delete mode 100644 erpnext/agriculture/doctype/weather/__init__.py delete mode 100644 erpnext/agriculture/doctype/weather/test_weather.py delete mode 100644 erpnext/agriculture/doctype/weather/weather.js delete mode 100644 erpnext/agriculture/doctype/weather/weather.json delete mode 100644 erpnext/agriculture/doctype/weather/weather.py delete mode 100644 erpnext/agriculture/doctype/weather_parameter/__init__.py delete mode 100644 erpnext/agriculture/doctype/weather_parameter/weather_parameter.json delete mode 100644 erpnext/agriculture/doctype/weather_parameter/weather_parameter.py delete mode 100644 erpnext/agriculture/setup.py delete mode 100644 erpnext/agriculture/workspace/agriculture/agriculture.json delete mode 100644 erpnext/domains/agriculture.py create mode 100644 erpnext/patches/v13_0/agriculture_deprecation_warning.py create mode 100644 erpnext/patches/v14_0/delete_agriculture_doctypes.py diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index d8b860671f..314c89424b 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -166,7 +166,7 @@ class TestPricingRule(unittest.TestCase): "item_group": "Products", }, { - "item_group": "Seed", + "item_group": "_Test Item Group", }, ], "selling": 1, diff --git a/erpnext/agriculture/__init__.py b/erpnext/agriculture/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/__init__.py b/erpnext/agriculture/doctype/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/agriculture_analysis_criteria/__init__.py b/erpnext/agriculture/doctype/agriculture_analysis_criteria/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/agriculture_analysis_criteria/agriculture_analysis_criteria.js b/erpnext/agriculture/doctype/agriculture_analysis_criteria/agriculture_analysis_criteria.js deleted file mode 100644 index e236cc6675..0000000000 --- a/erpnext/agriculture/doctype/agriculture_analysis_criteria/agriculture_analysis_criteria.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Agriculture Analysis Criteria', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/agriculture/doctype/agriculture_analysis_criteria/agriculture_analysis_criteria.json b/erpnext/agriculture/doctype/agriculture_analysis_criteria/agriculture_analysis_criteria.json deleted file mode 100644 index bb5e4d9108..0000000000 --- a/erpnext/agriculture/doctype/agriculture_analysis_criteria/agriculture_analysis_criteria.json +++ /dev/null @@ -1,182 +0,0 @@ -{ - "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": "2017-12-05 16:37:46.599982", - "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, - "unique": 1 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "standard", - "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": "Standard", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "linked_doctype", - "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": "Linked Doctype", - "length": 0, - "no_copy": 0, - "options": "\nWater Analysis\nSoil Analysis\nPlant Analysis\nFertilizer\nSoil Texture\nWeather", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-11-04 03:27:36.678832", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Agriculture Analysis Criteria", - "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": "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, - "restrict_to_domain": "Agriculture", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/agriculture/doctype/agriculture_analysis_criteria/agriculture_analysis_criteria.py b/erpnext/agriculture/doctype/agriculture_analysis_criteria/agriculture_analysis_criteria.py deleted file mode 100644 index 19459927a5..0000000000 --- a/erpnext/agriculture/doctype/agriculture_analysis_criteria/agriculture_analysis_criteria.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class AgricultureAnalysisCriteria(Document): - pass diff --git a/erpnext/agriculture/doctype/agriculture_analysis_criteria/test_agriculture_analysis_criteria.py b/erpnext/agriculture/doctype/agriculture_analysis_criteria/test_agriculture_analysis_criteria.py deleted file mode 100644 index 91e6f3fa0c..0000000000 --- a/erpnext/agriculture/doctype/agriculture_analysis_criteria/test_agriculture_analysis_criteria.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestAgricultureAnalysisCriteria(unittest.TestCase): - pass diff --git a/erpnext/agriculture/doctype/agriculture_task/__init__.py b/erpnext/agriculture/doctype/agriculture_task/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/agriculture_task/agriculture_task.js b/erpnext/agriculture/doctype/agriculture_task/agriculture_task.js deleted file mode 100644 index 4d6b9597a2..0000000000 --- a/erpnext/agriculture/doctype/agriculture_task/agriculture_task.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Agriculture Task', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/agriculture/doctype/agriculture_task/agriculture_task.json b/erpnext/agriculture/doctype/agriculture_task/agriculture_task.json deleted file mode 100644 index d943d77167..0000000000 --- a/erpnext/agriculture/doctype/agriculture_task/agriculture_task.json +++ /dev/null @@ -1,212 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "AG-TASK-.#####", - "beta": 0, - "creation": "2017-10-26 15:51:19.602452", - "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": "task_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": "Task 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, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "start_day", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Start Day", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "end_day", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "End Day", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Ignore holidays", - "fieldname": "holiday_management", - "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": "Holiday Management", - "length": 0, - "no_copy": 0, - "options": "Ignore holidays\nPrevious Business Day\nNext Business Day", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Low", - "fieldname": "priority", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Priority", - "length": 0, - "no_copy": 0, - "options": "Low\nMedium\nHigh\nUrgent", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-11-04 03:28:08.679157", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Agriculture Task", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Agriculture", - "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/agriculture/doctype/agriculture_task/agriculture_task.py b/erpnext/agriculture/doctype/agriculture_task/agriculture_task.py deleted file mode 100644 index dab2998935..0000000000 --- a/erpnext/agriculture/doctype/agriculture_task/agriculture_task.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class AgricultureTask(Document): - pass diff --git a/erpnext/agriculture/doctype/agriculture_task/test_agriculture_task.py b/erpnext/agriculture/doctype/agriculture_task/test_agriculture_task.py deleted file mode 100644 index 94d7915d62..0000000000 --- a/erpnext/agriculture/doctype/agriculture_task/test_agriculture_task.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestAgricultureTask(unittest.TestCase): - pass diff --git a/erpnext/agriculture/doctype/crop/__init__.py b/erpnext/agriculture/doctype/crop/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/crop/crop.js b/erpnext/agriculture/doctype/crop/crop.js deleted file mode 100644 index 550824636b..0000000000 --- a/erpnext/agriculture/doctype/crop/crop.js +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.provide("erpnext.crop"); - -frappe.ui.form.on('Crop', { - refresh: (frm) => { - frm.fields_dict.materials_required.grid.set_column_disp('bom_no', false); - } -}); - -frappe.ui.form.on("BOM Item", { - item_code: (frm, cdt, cdn) => { - erpnext.crop.update_item_rate_uom(frm, cdt, cdn); - }, - qty: (frm, cdt, cdn) => { - erpnext.crop.update_item_qty_amount(frm, cdt, cdn); - }, - rate: (frm, cdt, cdn) => { - erpnext.crop.update_item_qty_amount(frm, cdt, cdn); - } -}); - -erpnext.crop.update_item_rate_uom = function(frm, cdt, cdn) { - let material_list = ['materials_required', 'produce', 'byproducts']; - material_list.forEach((material) => { - frm.doc[material].forEach((item, index) => { - if (item.name == cdn && item.item_code){ - frappe.call({ - method:'erpnext.agriculture.doctype.crop.crop.get_item_details', - args: { - item_code: item.item_code - }, - callback: (r) => { - frappe.model.set_value('BOM Item', item.name, 'uom', r.message.uom); - frappe.model.set_value('BOM Item', item.name, 'rate', r.message.rate); - } - }); - } - }); - }); -}; - -erpnext.crop.update_item_qty_amount = function(frm, cdt, cdn) { - let material_list = ['materials_required', 'produce', 'byproducts']; - material_list.forEach((material) => { - frm.doc[material].forEach((item, index) => { - if (item.name == cdn){ - if (!frappe.model.get_value('BOM Item', item.name, 'qty')) - frappe.model.set_value('BOM Item', item.name, 'qty', 1); - frappe.model.set_value('BOM Item', item.name, 'amount', item.qty * item.rate); - } - }); - }); -}; diff --git a/erpnext/agriculture/doctype/crop/crop.json b/erpnext/agriculture/doctype/crop/crop.json deleted file mode 100644 index e357abb98b..0000000000 --- a/erpnext/agriculture/doctype/crop/crop.json +++ /dev/null @@ -1,1110 +0,0 @@ -{ - "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": "2017-10-20 01:16:17.606174", - "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": 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": "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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "crop_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": "Crop 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 - }, - { - "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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "scientific_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": "Scientific 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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "You can define all the tasks which need to carried out for this crop here. The day field is used to mention the day on which the task needs to be carried out, 1 being the 1st day, etc.. ", - "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": "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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "agriculture_task", - "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": "Agriculture Task", - "length": 0, - "no_copy": 0, - "options": "Agriculture 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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "period", - "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": "Period", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "crop_spacing", - "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": "Crop Spacing", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "crop_spacing_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": "Crop Spacing 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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_12", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "row_spacing", - "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": "Row Spacing", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "row_spacing_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": "Row Spacing 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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "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_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Type", - "length": 0, - "no_copy": 0, - "options": "Annual\nPerennial\nBiennial", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_6", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "category", - "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": "Category", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "target_warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Target Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "planting_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": "Planting 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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "planting_area", - "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": "Planting Area", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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_14", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "yield_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": "Yield 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 - }, - { - "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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_17", - "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": "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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "materials_required", - "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": "Materials Required", - "length": 0, - "no_copy": 0, - "options": "BOM Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_19", - "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": "Produced Items", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "produce", - "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": "Produce", - "length": 0, - "no_copy": 0, - "options": "BOM Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_18", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Byproducts", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "byproducts", - "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": "Byproducts", - "length": 0, - "no_copy": 0, - "options": "BOM 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 - } - ], - "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-04 03:27:10.651075", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Crop", - "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": "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": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Agriculture", - "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/agriculture/doctype/crop/crop.py b/erpnext/agriculture/doctype/crop/crop.py deleted file mode 100644 index ed2073cebf..0000000000 --- a/erpnext/agriculture/doctype/crop/crop.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document - - -class Crop(Document): - def validate(self): - self.validate_crop_tasks() - - def validate_crop_tasks(self): - for task in self.agriculture_task: - if task.start_day > task.end_day: - frappe.throw(_("Start day is greater than end day in task '{0}'").format(task.task_name)) - - # Verify that the crop period is correct - max_crop_period = max([task.end_day for task in self.agriculture_task]) - self.period = max(self.period, max_crop_period) - - # Sort the crop tasks based on start days, - # maintaining the order for same-day tasks - self.agriculture_task.sort(key=lambda task: task.start_day) - - -@frappe.whitelist() -def get_item_details(item_code): - item = frappe.get_doc('Item', item_code) - return {"uom": item.stock_uom, "rate": item.valuation_rate} diff --git a/erpnext/agriculture/doctype/crop/crop_dashboard.py b/erpnext/agriculture/doctype/crop/crop_dashboard.py deleted file mode 100644 index 37cdbb2df5..0000000000 --- a/erpnext/agriculture/doctype/crop/crop_dashboard.py +++ /dev/null @@ -1,12 +0,0 @@ -from frappe import _ - - -def get_data(): - return { - 'transactions': [ - { - 'label': _('Crop Cycle'), - 'items': ['Crop Cycle'] - } - ] - } diff --git a/erpnext/agriculture/doctype/crop/test_crop.py b/erpnext/agriculture/doctype/crop/test_crop.py deleted file mode 100644 index c79a367219..0000000000 --- a/erpnext/agriculture/doctype/crop/test_crop.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe - -test_dependencies = ["Fertilizer"] - -class TestCrop(unittest.TestCase): - def test_crop_period(self): - basil = frappe.get_doc('Crop', 'Basil from seed') - self.assertEqual(basil.period, 15) diff --git a/erpnext/agriculture/doctype/crop/test_records.json b/erpnext/agriculture/doctype/crop/test_records.json deleted file mode 100644 index 41ddb9af22..0000000000 --- a/erpnext/agriculture/doctype/crop/test_records.json +++ /dev/null @@ -1,80 +0,0 @@ -[ - { - "doctype": "Item", - "item_code": "Basil Seeds", - "item_name": "Basil Seeds", - "item_group": "Seed" - }, - { - "doctype": "Item", - "item_code": "Twigs", - "item_name": "Twigs", - "item_group": "By-product" - }, - { - "doctype": "Item", - "item_code": "Basil Leaves", - "item_name": "Basil Leaves", - "item_group": "Produce" - }, - { - "doctype": "Crop", - "title": "Basil from seed", - "crop_name": "Basil", - "scientific_name": "Ocimum basilicum", - "materials_required": [{ - "item_code": "Basil Seeds", - "qty": "25", - "uom": "Nos", - "rate": "1" - }, { - "item_code": "Urea", - "qty": "5", - "uom": "Kg", - "rate": "10" - }], - "byproducts": [{ - "item_code": "Twigs", - "qty": "25", - "uom": "Nos", - "rate": "1" - }], - "produce": [{ - "item_code": "Basil Leaves", - "qty": "100", - "uom": "Nos", - "rate": "1" - }], - "agriculture_task": [{ - "task_name": "Plough the field", - "start_day": 1, - "end_day": 1, - "holiday_management": "Ignore holidays" - }, { - "task_name": "Plant the seeds", - "start_day": 2, - "end_day": 3, - "holiday_management": "Ignore holidays" - }, { - "task_name": "Water the field", - "start_day": 4, - "end_day": 4, - "holiday_management": "Ignore holidays" - }, { - "task_name": "First harvest", - "start_day": 8, - "end_day": 8, - "holiday_management": "Ignore holidays" - }, { - "task_name": "Add the fertilizer", - "start_day": 10, - "end_day": 12, - "holiday_management": "Ignore holidays" - }, { - "task_name": "Final cut", - "start_day": 15, - "end_day": 15, - "holiday_management": "Ignore holidays" - }] - } -] \ No newline at end of file diff --git a/erpnext/agriculture/doctype/crop_cycle/__init__.py b/erpnext/agriculture/doctype/crop_cycle/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.js b/erpnext/agriculture/doctype/crop_cycle/crop_cycle.js deleted file mode 100644 index 94392e7261..0000000000 --- a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.js +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Crop Cycle', { - refresh: (frm) => { - if (!frm.doc.__islocal) - frm.add_custom_button(__('Reload Linked Analysis'), () => frm.call("reload_linked_analysis")); - - frappe.realtime.on("List of Linked Docs", (output) => { - let analysis_doctypes = ['Soil Texture', 'Plant Analysis', 'Soil Analysis']; - let analysis_doctypes_docs = ['soil_texture', 'plant_analysis', 'soil_analysis']; - let obj_to_append = {soil_analysis: [], soil_texture: [], plant_analysis: []}; - output['Location'].forEach( (land_doc) => { - analysis_doctypes.forEach( (doctype) => { - output[doctype].forEach( (analysis_doc) => { - let point_to_be_tested = JSON.parse(analysis_doc.location).features[0].geometry.coordinates; - let poly_of_land = JSON.parse(land_doc.location).features[0].geometry.coordinates[0]; - if (is_in_land_unit(point_to_be_tested, poly_of_land)){ - obj_to_append[analysis_doctypes_docs[analysis_doctypes.indexOf(doctype)]].push(analysis_doc.name); - } - }); - }); - }); - frm.call('append_to_child', { - obj_to_append: obj_to_append - }); - }); - } -}); - -function is_in_land_unit(point, vs) { - // ray-casting algorithm based on - // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html - - var x = point[0], y = point[1]; - - var inside = false; - for (var i = 0, j = vs.length - 1; i < vs.length; j = i++) { - var xi = vs[i][0], yi = vs[i][1]; - var xj = vs[j][0], yj = vs[j][1]; - - var intersect = ((yi > y) != (yj > y)) - && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - if (intersect) inside = !inside; - } - - return inside; -}; diff --git a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.json b/erpnext/agriculture/doctype/crop_cycle/crop_cycle.json deleted file mode 100644 index a076718919..0000000000 --- a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.json +++ /dev/null @@ -1,904 +0,0 @@ -{ - "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": "2017-11-02 03:09:35.449880", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "title", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Title", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 1 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "crop", - "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": "Crop", - "length": 0, - "no_copy": 0, - "options": "Crop", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "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, - "label": "Linked 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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "A link to all the Locations in which the Crop is growing", - "fieldname": "linked_location", - "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": "Linked Location", - "length": 0, - "no_copy": 0, - "options": "Linked 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": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_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, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.__islocal", - "fieldname": "project", - "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": "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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_12", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "This will be day 1 of the crop cycle", - "fieldname": "start_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Start Date", - "length": 0, - "no_copy": 0, - "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, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "project.expected_end_date", - "fieldname": "end_date", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "End 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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_7", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "iso_8601_standard", - "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": "ISO 8601 standard", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_5", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "cycle_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": "Cycle Type", - "length": 0, - "no_copy": 0, - "options": "Yearly\nLess than a 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 - }, - { - "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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "The minimum length between each plant in the field for optimum growth", - "fetch_from": "crop.crop_spacing", - "fieldname": "crop_spacing", - "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": "Crop Spacing", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "crop_spacing_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": "Crop Spacing 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 - }, - { - "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, - "description": "The minimum distance between rows of plants for optimum growth", - "fetch_from": "crop.row_spacing", - "fieldname": "row_spacing", - "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": "Row Spacing", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "row_spacing_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": "Row Spacing 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 - }, - { - "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": "List of diseases detected on the field. When selected it'll automatically add a list of tasks to deal with the disease ", - "fieldname": "section_break_14", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Detected Diseases", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "detected_disease", - "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": "Detected Disease", - "length": 0, - "no_copy": 0, - "options": "Detected Disease", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "eval:false", - "columns": 0, - "fieldname": "section_break_22", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "LInked Analysis", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "soil_texture", - "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": "Soil Texture", - "length": 0, - "no_copy": 0, - "options": "Linked Soil Texture", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "soil_analysis", - "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": "Soil Analysis", - "length": 0, - "no_copy": 0, - "options": "Linked Soil Analysis", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "plant_analysis", - "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": "Plant Analysis", - "length": 0, - "no_copy": 0, - "options": "Linked Plant Analysis", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-11-04 03:31:47.602312", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Crop Cycle", - "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": "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": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Agriculture", - "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/agriculture/doctype/crop_cycle/crop_cycle.py b/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py deleted file mode 100644 index 43c5bbde82..0000000000 --- a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import ast - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import add_days - - -class CropCycle(Document): - def validate(self): - self.set_missing_values() - - def after_insert(self): - self.create_crop_cycle_project() - self.create_tasks_for_diseases() - - def on_update(self): - self.create_tasks_for_diseases() - - def set_missing_values(self): - crop = frappe.get_doc('Crop', self.crop) - - if not self.crop_spacing_uom: - self.crop_spacing_uom = crop.crop_spacing_uom - - if not self.row_spacing_uom: - self.row_spacing_uom = crop.row_spacing_uom - - def create_crop_cycle_project(self): - crop = frappe.get_doc('Crop', self.crop) - - self.project = self.create_project(crop.period, crop.agriculture_task) - self.create_task(crop.agriculture_task, self.project, self.start_date) - - def create_tasks_for_diseases(self): - for disease in self.detected_disease: - if not disease.tasks_created: - self.import_disease_tasks(disease.disease, disease.start_date) - disease.tasks_created = True - - frappe.msgprint(_("Tasks have been created for managing the {0} disease (on row {1})").format(disease.disease, disease.idx)) - - def import_disease_tasks(self, disease, start_date): - disease_doc = frappe.get_doc('Disease', disease) - self.create_task(disease_doc.treatment_task, self.project, start_date) - - def create_project(self, period, crop_tasks): - project = frappe.get_doc({ - "doctype": "Project", - "project_name": self.title, - "expected_start_date": self.start_date, - "expected_end_date": add_days(self.start_date, period - 1) - }).insert() - - return project.name - - def create_task(self, crop_tasks, project_name, start_date): - for crop_task in crop_tasks: - frappe.get_doc({ - "doctype": "Task", - "subject": crop_task.get("task_name"), - "priority": crop_task.get("priority"), - "project": project_name, - "exp_start_date": add_days(start_date, crop_task.get("start_day") - 1), - "exp_end_date": add_days(start_date, crop_task.get("end_day") - 1) - }).insert() - - @frappe.whitelist() - def reload_linked_analysis(self): - linked_doctypes = ['Soil Texture', 'Soil Analysis', 'Plant Analysis'] - required_fields = ['location', 'name', 'collection_datetime'] - output = {} - - for doctype in linked_doctypes: - output[doctype] = frappe.get_all(doctype, fields=required_fields) - - output['Location'] = [] - - for location in self.linked_location: - output['Location'].append(frappe.get_doc('Location', location.location)) - - frappe.publish_realtime("List of Linked Docs", - output, user=frappe.session.user) - - @frappe.whitelist() - def append_to_child(self, obj_to_append): - for doctype in obj_to_append: - for doc_name in set(obj_to_append[doctype]): - self.append(doctype, {doctype: doc_name}) - - self.save() - - -def get_coordinates(doc): - return ast.literal_eval(doc.location).get('features')[0].get('geometry').get('coordinates') - - -def get_geometry_type(doc): - return ast.literal_eval(doc.location).get('features')[0].get('geometry').get('type') - - -def is_in_location(point, vs): - x, y = point - inside = False - - j = len(vs) - 1 - i = 0 - - while i < len(vs): - xi, yi = vs[i] - xj, yj = vs[j] - - intersect = ((yi > y) != (yj > y)) and ( - x < (xj - xi) * (y - yi) / (yj - yi) + xi) - - if intersect: - inside = not inside - - i = j - j += 1 - - return inside diff --git a/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.py b/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.py deleted file mode 100644 index e4765a57c0..0000000000 --- a/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe -from frappe.utils import datetime - -test_dependencies = ["Crop", "Fertilizer", "Location", "Disease"] - - -class TestCropCycle(unittest.TestCase): - def test_crop_cycle_creation(self): - cycle = frappe.get_doc('Crop Cycle', 'Basil from seed 2017') - self.assertTrue(frappe.db.exists('Crop Cycle', 'Basil from seed 2017')) - - # check if the tasks were created - self.assertEqual(check_task_creation(), True) - self.assertEqual(check_project_creation(), True) - - -def check_task_creation(): - all_task_dict = { - "Survey and find the aphid locations": { - "exp_start_date": datetime.date(2017, 11, 21), - "exp_end_date": datetime.date(2017, 11, 22) - }, - "Apply Pesticides": { - "exp_start_date": datetime.date(2017, 11, 23), - "exp_end_date": datetime.date(2017, 11, 23) - }, - "Plough the field": { - "exp_start_date": datetime.date(2017, 11, 11), - "exp_end_date": datetime.date(2017, 11, 11) - }, - "Plant the seeds": { - "exp_start_date": datetime.date(2017, 11, 12), - "exp_end_date": datetime.date(2017, 11, 13) - }, - "Water the field": { - "exp_start_date": datetime.date(2017, 11, 14), - "exp_end_date": datetime.date(2017, 11, 14) - }, - "First harvest": { - "exp_start_date": datetime.date(2017, 11, 18), - "exp_end_date": datetime.date(2017, 11, 18) - }, - "Add the fertilizer": { - "exp_start_date": datetime.date(2017, 11, 20), - "exp_end_date": datetime.date(2017, 11, 22) - }, - "Final cut": { - "exp_start_date": datetime.date(2017, 11, 25), - "exp_end_date": datetime.date(2017, 11, 25) - } - } - - all_tasks = frappe.get_all('Task') - - for task in all_tasks: - sample_task = frappe.get_doc('Task', task.name) - - if sample_task.subject in list(all_task_dict): - if sample_task.exp_start_date != all_task_dict[sample_task.subject]['exp_start_date'] or sample_task.exp_end_date != all_task_dict[sample_task.subject]['exp_end_date']: - return False - all_task_dict.pop(sample_task.subject) - - return True if not all_task_dict else False - - -def check_project_creation(): - return True if frappe.db.exists('Project', {'project_name': 'Basil from seed 2017'}) else False diff --git a/erpnext/agriculture/doctype/crop_cycle/test_records.json b/erpnext/agriculture/doctype/crop_cycle/test_records.json deleted file mode 100644 index 5c79f1030a..0000000000 --- a/erpnext/agriculture/doctype/crop_cycle/test_records.json +++ /dev/null @@ -1,15 +0,0 @@ -[ - { - "doctype": "Crop Cycle", - "title": "Basil from seed 2017", - "linked_location": [{ - "location": "Basil Farm" - }], - "crop": "Basil from seed", - "start_date": "2017-11-11", - "detected_disease": [{ - "disease": "Aphids", - "start_date": "2017-11-21" - }] - } -] \ No newline at end of file diff --git a/erpnext/agriculture/doctype/detected_disease/__init__.py b/erpnext/agriculture/doctype/detected_disease/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/detected_disease/detected_disease.json b/erpnext/agriculture/doctype/detected_disease/detected_disease.json deleted file mode 100644 index f670cd31b8..0000000000 --- a/erpnext/agriculture/doctype/detected_disease/detected_disease.json +++ /dev/null @@ -1,142 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-11-20 17:31:30.772779", - "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": "disease", - "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": "Disease", - "length": 0, - "no_copy": 0, - "options": "Disease", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "start_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Start Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 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": "tasks_created", - "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": "Tasks Created", - "length": 0, - "no_copy": 1, - "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 - } - ], - "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:27:47.463994", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Detected Disease", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Agriculture", - "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/agriculture/doctype/detected_disease/detected_disease.py b/erpnext/agriculture/doctype/detected_disease/detected_disease.py deleted file mode 100644 index e507add7f9..0000000000 --- a/erpnext/agriculture/doctype/detected_disease/detected_disease.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class DetectedDisease(Document): - pass diff --git a/erpnext/agriculture/doctype/disease/__init__.py b/erpnext/agriculture/doctype/disease/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/disease/disease.js b/erpnext/agriculture/doctype/disease/disease.js deleted file mode 100644 index f6b678c1a9..0000000000 --- a/erpnext/agriculture/doctype/disease/disease.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Disease', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/agriculture/doctype/disease/disease.json b/erpnext/agriculture/doctype/disease/disease.json deleted file mode 100644 index 16b735a660..0000000000 --- a/erpnext/agriculture/doctype/disease/disease.json +++ /dev/null @@ -1,308 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:common_name", - "beta": 0, - "creation": "2017-11-20 17:16:54.496355", - "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": "common_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": "Common 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": 1 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "scientific_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": "Scientific 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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "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, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "treatment_task", - "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": "Treatment Task", - "length": 0, - "no_copy": 0, - "options": "Agriculture Task", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "treatment_period", - "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": "Treatment Period", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_2", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Treatment Task", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Long Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-11-04 03:27:25.076490", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Disease", - "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": "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": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Agriculture", - "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/agriculture/doctype/disease/disease.py b/erpnext/agriculture/doctype/disease/disease.py deleted file mode 100644 index 30ab298376..0000000000 --- a/erpnext/agriculture/doctype/disease/disease.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document - - -class Disease(Document): - def validate(self): - max_period = 0 - for task in self.treatment_task: - # validate start_day is not > end_day - if task.start_day > task.end_day: - frappe.throw(_("Start day is greater than end day in task '{0}'").format(task.task_name)) - # to calculate the period of the Crop Cycle - if task.end_day > max_period: max_period = task.end_day - self.treatment_period = max_period diff --git a/erpnext/agriculture/doctype/disease/test_disease.py b/erpnext/agriculture/doctype/disease/test_disease.py deleted file mode 100644 index 6a6f1e70a9..0000000000 --- a/erpnext/agriculture/doctype/disease/test_disease.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe - - -class TestDisease(unittest.TestCase): - def test_treatment_period(self): - disease = frappe.get_doc('Disease', 'Aphids') - self.assertEqual(disease.treatment_period, 3) diff --git a/erpnext/agriculture/doctype/disease/test_records.json b/erpnext/agriculture/doctype/disease/test_records.json deleted file mode 100644 index e91a61190d..0000000000 --- a/erpnext/agriculture/doctype/disease/test_records.json +++ /dev/null @@ -1,18 +0,0 @@ -[ - { - "doctype": "Disease", - "common_name": "Aphids", - "scientific_name": "Aphidoidea", - "treatment_task": [{ - "task_name": "Survey and find the aphid locations", - "start_day": 1, - "end_day": 2, - "holiday_management": "Ignore holidays" - }, { - "task_name": "Apply Pesticides", - "start_day": 3, - "end_day": 3, - "holiday_management": "Ignore holidays" - }] - } -] \ No newline at end of file diff --git a/erpnext/agriculture/doctype/fertilizer/__init__.py b/erpnext/agriculture/doctype/fertilizer/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/fertilizer/fertilizer.js b/erpnext/agriculture/doctype/fertilizer/fertilizer.js deleted file mode 100644 index 357e089af2..0000000000 --- a/erpnext/agriculture/doctype/fertilizer/fertilizer.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Fertilizer', { - onload: (frm) => { - if (frm.doc.fertilizer_contents == undefined) frm.call('load_contents'); - } -}); diff --git a/erpnext/agriculture/doctype/fertilizer/fertilizer.json b/erpnext/agriculture/doctype/fertilizer/fertilizer.json deleted file mode 100644 index 6a1877344b..0000000000 --- a/erpnext/agriculture/doctype/fertilizer/fertilizer.json +++ /dev/null @@ -1,307 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:fertilizer_name", - "beta": 0, - "creation": "2017-10-17 18:17:06.175062", - "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": "fertilizer_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": "Fertilizer 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": 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": 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": 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": "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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "density", - "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": "Density (if liquid)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_4", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_28", - "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": "Fertilizer Contents", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "fertilizer_contents", - "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": "Fertilizer Content", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-11-04 03:26:29.211792", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Fertilizer", - "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": "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": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Agriculture", - "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/agriculture/doctype/fertilizer/fertilizer.py b/erpnext/agriculture/doctype/fertilizer/fertilizer.py deleted file mode 100644 index 2408302bd1..0000000000 --- a/erpnext/agriculture/doctype/fertilizer/fertilizer.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe.model.document import Document - - -class Fertilizer(Document): - @frappe.whitelist() - def load_contents(self): - docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Fertilizer'}) - for doc in docs: - self.append('fertilizer_contents', {'title': str(doc.name)}) diff --git a/erpnext/agriculture/doctype/fertilizer/test_fertilizer.py b/erpnext/agriculture/doctype/fertilizer/test_fertilizer.py deleted file mode 100644 index c8630ef1f8..0000000000 --- a/erpnext/agriculture/doctype/fertilizer/test_fertilizer.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe - - -class TestFertilizer(unittest.TestCase): - def test_fertilizer_creation(self): - self.assertEqual(frappe.db.exists('Fertilizer', 'Urea'), 'Urea') diff --git a/erpnext/agriculture/doctype/fertilizer/test_records.json b/erpnext/agriculture/doctype/fertilizer/test_records.json deleted file mode 100644 index ba735cd985..0000000000 --- a/erpnext/agriculture/doctype/fertilizer/test_records.json +++ /dev/null @@ -1,13 +0,0 @@ -[ - { - "doctype": "Item", - "item_code": "Urea", - "item_name": "Urea", - "item_group": "Fertilizer" - }, - { - "doctype": "Fertilizer", - "fertilizer_name": "Urea", - "item": "Urea" - } -] \ No newline at end of file diff --git a/erpnext/agriculture/doctype/fertilizer_content/__init__.py b/erpnext/agriculture/doctype/fertilizer_content/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/fertilizer_content/fertilizer_content.json b/erpnext/agriculture/doctype/fertilizer_content/fertilizer_content.json deleted file mode 100644 index bf222abaca..0000000000 --- a/erpnext/agriculture/doctype/fertilizer_content/fertilizer_content.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-12-05 16:54:17.071914", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "title", - "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": "Title", - "length": 0, - "no_copy": 0, - "options": "Agriculture Analysis 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, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "value", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_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 - } - ], - "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-12-05 19:20:38.892231", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Fertilizer Content", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Agriculture", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/agriculture/doctype/fertilizer_content/fertilizer_content.py b/erpnext/agriculture/doctype/fertilizer_content/fertilizer_content.py deleted file mode 100644 index 967c3e02de..0000000000 --- a/erpnext/agriculture/doctype/fertilizer_content/fertilizer_content.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class FertilizerContent(Document): - pass diff --git a/erpnext/agriculture/doctype/linked_location/__init__.py b/erpnext/agriculture/doctype/linked_location/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/linked_location/linked_location.json b/erpnext/agriculture/doctype/linked_location/linked_location.json deleted file mode 100644 index a14ae3d5c4..0000000000 --- a/erpnext/agriculture/doctype/linked_location/linked_location.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-11-22 14:34:59.461273", - "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": "location", - "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": "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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-11-04 03:27:58.120962", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Linked Location", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Agriculture", - "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/agriculture/doctype/linked_location/linked_location.py b/erpnext/agriculture/doctype/linked_location/linked_location.py deleted file mode 100644 index e1257f3db1..0000000000 --- a/erpnext/agriculture/doctype/linked_location/linked_location.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class LinkedLocation(Document): - pass diff --git a/erpnext/agriculture/doctype/linked_plant_analysis/__init__.py b/erpnext/agriculture/doctype/linked_plant_analysis/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/linked_plant_analysis/linked_plant_analysis.json b/erpnext/agriculture/doctype/linked_plant_analysis/linked_plant_analysis.json deleted file mode 100644 index 57d2aab7b2..0000000000 --- a/erpnext/agriculture/doctype/linked_plant_analysis/linked_plant_analysis.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-11-22 15:04:25.180446", - "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": "plant_analysis", - "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": "Plant Analysis", - "length": 0, - "no_copy": 0, - "options": "Plant Analysis", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-11-04 03:25:15.359130", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Linked Plant Analysis", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Agriculture", - "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/agriculture/doctype/linked_plant_analysis/linked_plant_analysis.py b/erpnext/agriculture/doctype/linked_plant_analysis/linked_plant_analysis.py deleted file mode 100644 index 0bc04af640..0000000000 --- a/erpnext/agriculture/doctype/linked_plant_analysis/linked_plant_analysis.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class LinkedPlantAnalysis(Document): - pass diff --git a/erpnext/agriculture/doctype/linked_soil_analysis/__init__.py b/erpnext/agriculture/doctype/linked_soil_analysis/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/linked_soil_analysis/linked_soil_analysis.json b/erpnext/agriculture/doctype/linked_soil_analysis/linked_soil_analysis.json deleted file mode 100644 index 38e5030d85..0000000000 --- a/erpnext/agriculture/doctype/linked_soil_analysis/linked_soil_analysis.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-11-22 15:00:37.259063", - "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": "soil_analysis", - "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": "Soil Analysis", - "length": 0, - "no_copy": 0, - "options": "Soil Analysis", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-11-04 03:25:27.670079", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Linked Soil Analysis", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Agriculture", - "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/agriculture/doctype/linked_soil_analysis/linked_soil_analysis.py b/erpnext/agriculture/doctype/linked_soil_analysis/linked_soil_analysis.py deleted file mode 100644 index 0d290556bf..0000000000 --- a/erpnext/agriculture/doctype/linked_soil_analysis/linked_soil_analysis.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class LinkedSoilAnalysis(Document): - pass diff --git a/erpnext/agriculture/doctype/linked_soil_texture/__init__.py b/erpnext/agriculture/doctype/linked_soil_texture/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/linked_soil_texture/linked_soil_texture.json b/erpnext/agriculture/doctype/linked_soil_texture/linked_soil_texture.json deleted file mode 100644 index 80682b07a5..0000000000 --- a/erpnext/agriculture/doctype/linked_soil_texture/linked_soil_texture.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-11-22 14:58:52.818040", - "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": "soil_texture", - "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": "Soil Texture", - "length": 0, - "no_copy": 0, - "options": "Soil Texture", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-11-04 03:26:17.877616", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Linked Soil Texture", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Agriculture", - "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/agriculture/doctype/linked_soil_texture/linked_soil_texture.py b/erpnext/agriculture/doctype/linked_soil_texture/linked_soil_texture.py deleted file mode 100644 index 1438853690..0000000000 --- a/erpnext/agriculture/doctype/linked_soil_texture/linked_soil_texture.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class LinkedSoilTexture(Document): - pass diff --git a/erpnext/agriculture/doctype/plant_analysis/__init__.py b/erpnext/agriculture/doctype/plant_analysis/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/plant_analysis/plant_analysis.js b/erpnext/agriculture/doctype/plant_analysis/plant_analysis.js deleted file mode 100644 index 3914f832a5..0000000000 --- a/erpnext/agriculture/doctype/plant_analysis/plant_analysis.js +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Plant Analysis', { - onload: (frm) => { - if (frm.doc.plant_analysis_criteria == undefined) frm.call('load_contents'); - }, - refresh: (frm) => { - let map_tools = ["a.leaflet-draw-draw-polyline", - "a.leaflet-draw-draw-polygon", - "a.leaflet-draw-draw-rectangle", - "a.leaflet-draw-draw-circle", - "a.leaflet-draw-draw-circlemarker"]; - - map_tools.forEach((element) => $(element).hide()); - } -}); diff --git a/erpnext/agriculture/doctype/plant_analysis/plant_analysis.json b/erpnext/agriculture/doctype/plant_analysis/plant_analysis.json deleted file mode 100644 index ceb1a5ba5f..0000000000 --- a/erpnext/agriculture/doctype/plant_analysis/plant_analysis.json +++ /dev/null @@ -1,372 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "AG-PLA-.YYYY.-.#####", - "beta": 0, - "creation": "2017-10-18 12:45:13.575986", - "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": "crop", - "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": "Crop", - "length": 0, - "no_copy": 0, - "options": "Crop", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "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": 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": "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 - }, - { - "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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "collection_datetime", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Collection Datetime", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "laboratory_testing_datetime", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Laboratory Testing Datetime", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "result_datetime", - "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": "Result Datetime", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_2", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Plant Analysis Criterias", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "plant_analysis_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": "", - "length": 0, - "no_copy": 0, - "options": "Plant Analysis 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": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-11-04 03:28:48.087828", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Plant Analysis", - "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": "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": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Agriculture", - "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/agriculture/doctype/plant_analysis/plant_analysis.py b/erpnext/agriculture/doctype/plant_analysis/plant_analysis.py deleted file mode 100644 index 9a939cde0b..0000000000 --- a/erpnext/agriculture/doctype/plant_analysis/plant_analysis.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe.model.document import Document - - -class PlantAnalysis(Document): - @frappe.whitelist() - def load_contents(self): - docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Plant Analysis'}) - for doc in docs: - self.append('plant_analysis_criteria', {'title': str(doc.name)}) diff --git a/erpnext/agriculture/doctype/plant_analysis/test_plant_analysis.py b/erpnext/agriculture/doctype/plant_analysis/test_plant_analysis.py deleted file mode 100644 index cee241f53a..0000000000 --- a/erpnext/agriculture/doctype/plant_analysis/test_plant_analysis.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestPlantAnalysis(unittest.TestCase): - pass diff --git a/erpnext/agriculture/doctype/plant_analysis_criteria/__init__.py b/erpnext/agriculture/doctype/plant_analysis_criteria/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/plant_analysis_criteria/plant_analysis_criteria.json b/erpnext/agriculture/doctype/plant_analysis_criteria/plant_analysis_criteria.json deleted file mode 100644 index eefc5ee1ab..0000000000 --- a/erpnext/agriculture/doctype/plant_analysis_criteria/plant_analysis_criteria.json +++ /dev/null @@ -1,173 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-12-05 19:23:52.481348", - "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": "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": "Title", - "length": 0, - "no_copy": 0, - "options": "Agriculture Analysis 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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "value", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "minimum_permissible_value", - "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": "Minimum Permissible Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "maximum_permissible_value", - "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": "Maximum Permissible Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-11-04 03:25:43.714882", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Plant Analysis Criteria", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Agriculture", - "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/agriculture/doctype/plant_analysis_criteria/plant_analysis_criteria.py b/erpnext/agriculture/doctype/plant_analysis_criteria/plant_analysis_criteria.py deleted file mode 100644 index 7e6571c4a3..0000000000 --- a/erpnext/agriculture/doctype/plant_analysis_criteria/plant_analysis_criteria.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class PlantAnalysisCriteria(Document): - pass diff --git a/erpnext/agriculture/doctype/soil_analysis/__init__.py b/erpnext/agriculture/doctype/soil_analysis/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/soil_analysis/soil_analysis.js b/erpnext/agriculture/doctype/soil_analysis/soil_analysis.js deleted file mode 100644 index 12829beefb..0000000000 --- a/erpnext/agriculture/doctype/soil_analysis/soil_analysis.js +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Soil Analysis', { - onload: (frm) => { - if (frm.doc.soil_analysis_criteria == undefined) frm.call('load_contents'); - }, - refresh: (frm) => { - let map_tools = ["a.leaflet-draw-draw-polyline", - "a.leaflet-draw-draw-polygon", - "a.leaflet-draw-draw-rectangle", - "a.leaflet-draw-draw-circle", - "a.leaflet-draw-draw-circlemarker"]; - - map_tools.forEach((element) => $(element).hide()); - } -}); diff --git a/erpnext/agriculture/doctype/soil_analysis/soil_analysis.json b/erpnext/agriculture/doctype/soil_analysis/soil_analysis.json deleted file mode 100644 index 59680fab99..0000000000 --- a/erpnext/agriculture/doctype/soil_analysis/soil_analysis.json +++ /dev/null @@ -1,593 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "AG-ANA-.YY.-.MM.-.#####", - "beta": 0, - "creation": "2017-10-17 19:12:16.728395", - "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": "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, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "collection_datetime", - "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": "Collection Datetime", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "laboratory_testing_datetime", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Laboratory Testing Datetime", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "result_datetime", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Result Datetime", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_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, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "ca_per_k", - "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": "Ca/K", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "ca_per_mg", - "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": "Ca/Mg", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mg_per_k", - "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": "Mg/K", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_31", - "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": "ca_mg_per_k", - "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": "(Ca+Mg)/K", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "ca_per_k_ca_mg", - "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": "Ca/(K+Ca+Mg)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_28", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "invoice_number", - "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 Number", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "soil_analysis_criterias", - "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": "Soil Analysis Criterias", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "soil_analysis_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, - "length": 0, - "no_copy": 0, - "options": "Soil Analysis 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": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-11-04 03:28:58.403760", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Soil Analysis", - "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": "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": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Agriculture", - "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/agriculture/doctype/soil_analysis/soil_analysis.py b/erpnext/agriculture/doctype/soil_analysis/soil_analysis.py deleted file mode 100644 index 03667fbcae..0000000000 --- a/erpnext/agriculture/doctype/soil_analysis/soil_analysis.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe.model.document import Document - - -class SoilAnalysis(Document): - @frappe.whitelist() - def load_contents(self): - docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Soil Analysis'}) - for doc in docs: - self.append('soil_analysis_criteria', {'title': str(doc.name)}) diff --git a/erpnext/agriculture/doctype/soil_analysis/test_soil_analysis.py b/erpnext/agriculture/doctype/soil_analysis/test_soil_analysis.py deleted file mode 100644 index bb99363ffd..0000000000 --- a/erpnext/agriculture/doctype/soil_analysis/test_soil_analysis.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestSoilAnalysis(unittest.TestCase): - pass diff --git a/erpnext/agriculture/doctype/soil_analysis_criteria/__init__.py b/erpnext/agriculture/doctype/soil_analysis_criteria/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/soil_analysis_criteria/soil_analysis_criteria.json b/erpnext/agriculture/doctype/soil_analysis_criteria/soil_analysis_criteria.json deleted file mode 100644 index 860e48aa50..0000000000 --- a/erpnext/agriculture/doctype/soil_analysis_criteria/soil_analysis_criteria.json +++ /dev/null @@ -1,173 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-12-05 19:36:05.300770", - "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": "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": "Title", - "length": 0, - "no_copy": 0, - "options": "Agriculture Analysis 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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "value", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "minimum_permissible_value", - "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": "Minimum Permissible Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "maximum_permissible_value", - "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": "Maximum Permissible Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-11-04 03:25:54.359008", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Soil Analysis Criteria", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Agriculture", - "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/agriculture/doctype/soil_analysis_criteria/soil_analysis_criteria.py b/erpnext/agriculture/doctype/soil_analysis_criteria/soil_analysis_criteria.py deleted file mode 100644 index f501820328..0000000000 --- a/erpnext/agriculture/doctype/soil_analysis_criteria/soil_analysis_criteria.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class SoilAnalysisCriteria(Document): - pass diff --git a/erpnext/agriculture/doctype/soil_texture/__init__.py b/erpnext/agriculture/doctype/soil_texture/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/soil_texture/soil_texture.js b/erpnext/agriculture/doctype/soil_texture/soil_texture.js deleted file mode 100644 index 673284b246..0000000000 --- a/erpnext/agriculture/doctype/soil_texture/soil_texture.js +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.provide('agriculture'); - -frappe.ui.form.on('Soil Texture', { - refresh: (frm) => { - let map_tools = ["a.leaflet-draw-draw-polyline", - "a.leaflet-draw-draw-polygon", - "a.leaflet-draw-draw-rectangle", - "a.leaflet-draw-draw-circle", - "a.leaflet-draw-draw-circlemarker"]; - - map_tools.forEach((element) => $(element).hide()); - }, - onload: function(frm) { - if (frm.doc.soil_texture_criteria == undefined) frm.call('load_contents'); - if (frm.doc.ternary_plot) return; - frm.doc.ternary_plot = new agriculture.TernaryPlot({ - parent: frm.get_field("ternary_plot").$wrapper, - clay: frm.doc.clay_composition, - sand: frm.doc.sand_composition, - silt: frm.doc.silt_composition, - }); - }, - soil_type: (frm) => { - let composition_types = ['clay_composition', 'sand_composition', 'silt_composition']; - composition_types.forEach((composition_type) => { - frm.doc[composition_type] = 0; - frm.refresh_field(composition_type); - }); - }, - clay_composition: function(frm) { - frm.call("update_soil_edit", { - soil_type: 'clay_composition' - }, () => { - refresh_ternary_plot(frm, this); - }); - }, - sand_composition: function(frm) { - frm.call("update_soil_edit", { - soil_type: 'sand_composition' - }, () => { - refresh_ternary_plot(frm, this); - }); - }, - silt_composition: function(frm) { - frm.call("update_soil_edit", { - soil_type: 'silt_composition' - }, () => { - refresh_ternary_plot(frm, this); - }); - } -}); - -let refresh_ternary_plot = (frm, me) => { - me.ternary_plot.remove_blip(); - me.ternary_plot.mark_blip({clay: frm.doc.clay_composition, sand: frm.doc.sand_composition, silt: frm.doc.silt_composition}); -}; diff --git a/erpnext/agriculture/doctype/soil_texture/soil_texture.json b/erpnext/agriculture/doctype/soil_texture/soil_texture.json deleted file mode 100644 index f78c262be4..0000000000 --- a/erpnext/agriculture/doctype/soil_texture/soil_texture.json +++ /dev/null @@ -1,533 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "AG-TEX-.YYYY.-.#####", - "beta": 0, - "creation": "2017-10-18 13:06:47.506762", - "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": "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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "collection_datetime", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Collection Datetime", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "laboratory_testing_datetime", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Laboratory Testing Datetime", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "result_datetime", - "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": "Result Datetime", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_4", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "soil_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": "Soil Type", - "length": 0, - "no_copy": 0, - "options": "Select\nSand\nLoamy Sand\nSandy Loam\nLoam\nSilt Loam\nSilt\nSandy Clay Loam\nClay Loam\nSilty Clay Loam\nSandy Clay\nSilty Clay\nClay", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "clay_composition", - "fieldtype": "Percent", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Clay Composition (%)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "sand_composition", - "fieldtype": "Percent", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sand Composition (%)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "silt_composition", - "fieldtype": "Percent", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Silt Composition (%)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_6", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "ternary_plot", - "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": "Ternary Plot", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_15", - "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": "Soil Texture Criteria", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "soil_texture_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, - "length": 0, - "no_copy": 0, - "options": "Soil Texture 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": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-11-04 03:29:18.221173", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Soil Texture", - "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": "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": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Agriculture", - "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/agriculture/doctype/soil_texture/soil_texture.py b/erpnext/agriculture/doctype/soil_texture/soil_texture.py deleted file mode 100644 index b1fc9a063d..0000000000 --- a/erpnext/agriculture/doctype/soil_texture/soil_texture.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import cint, flt - - -class SoilTexture(Document): - soil_edit_order = [2, 1, 0] - soil_types = ['clay_composition', 'sand_composition', 'silt_composition'] - - @frappe.whitelist() - def load_contents(self): - docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Soil Texture'}) - for doc in docs: - self.append('soil_texture_criteria', {'title': str(doc.name)}) - - def validate(self): - self.update_soil_edit('sand_composition') - for soil_type in self.soil_types: - if self.get(soil_type) > 100 or self.get(soil_type) < 0: - frappe.throw(_("{0} should be a value between 0 and 100").format(soil_type)) - if sum(self.get(soil_type) for soil_type in self.soil_types) != 100: - frappe.throw(_('Soil compositions do not add up to 100')) - - @frappe.whitelist() - def update_soil_edit(self, soil_type): - self.soil_edit_order[self.soil_types.index(soil_type)] = max(self.soil_edit_order)+1 - self.soil_type = self.get_soil_type() - - def get_soil_type(self): - # update the last edited soil type - if sum(self.soil_edit_order) < 5: return - last_edit_index = self.soil_edit_order.index(min(self.soil_edit_order)) - - # set composition of the last edited soil - self.set(self.soil_types[last_edit_index], - 100 - sum(cint(self.get(soil_type)) for soil_type in self.soil_types) + cint(self.get(self.soil_types[last_edit_index]))) - - # calculate soil type - c, sa, si = flt(self.clay_composition), flt(self.sand_composition), flt(self.silt_composition) - - if si + (1.5 * c) < 15: - return 'Sand' - elif si + 1.5 * c >= 15 and si + 2 * c < 30: - return 'Loamy Sand' - elif ((c >= 7 and c < 20) or (sa > 52) and ((si + 2*c) >= 30) or (c < 7 and si < 50 and (si+2*c) >= 30)): - return 'Sandy Loam' - elif ((c >= 7 and c < 27) and (si >= 28 and si < 50) and (sa <= 52)): - return 'Loam' - elif ((si >= 50 and (c >= 12 and c < 27)) or ((si >= 50 and si < 80) and c < 12)): - return 'Silt Loam' - elif (si >= 80 and c < 12): - return 'Silt' - elif ((c >= 20 and c < 35) and (si < 28) and (sa > 45)): - return 'Sandy Clay Loam' - elif ((c >= 27 and c < 40) and (sa > 20 and sa <= 45)): - return 'Clay Loam' - elif ((c >= 27 and c < 40) and (sa <= 20)): - return 'Silty Clay Loam' - elif (c >= 35 and sa > 45): - return 'Sandy Clay' - elif (c >= 40 and si >= 40): - return 'Silty Clay' - elif (c >= 40 and sa <= 45 and si < 40): - return 'Clay' - else: - return 'Select' diff --git a/erpnext/agriculture/doctype/soil_texture/test_records.json b/erpnext/agriculture/doctype/soil_texture/test_records.json deleted file mode 100644 index dcac7ad8df..0000000000 --- a/erpnext/agriculture/doctype/soil_texture/test_records.json +++ /dev/null @@ -1,9 +0,0 @@ -[ - { - "doctype": "Soil Texture", - "location": "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"properties\":{},\"geometry\":{\"type\":\"Point\",\"coordinates\":[72.861242,19.079153]}}]}", - "collection_datetime": "2017-11-08", - "clay_composition": 20, - "sand_composition": 30 - } -] \ No newline at end of file diff --git a/erpnext/agriculture/doctype/soil_texture/test_soil_texture.py b/erpnext/agriculture/doctype/soil_texture/test_soil_texture.py deleted file mode 100644 index 45497675ce..0000000000 --- a/erpnext/agriculture/doctype/soil_texture/test_soil_texture.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - -import frappe - - -class TestSoilTexture(unittest.TestCase): - def test_texture_selection(self): - soil_tex = frappe.get_all('Soil Texture', fields=['name'], filters={'collection_datetime': '2017-11-08'}) - doc = frappe.get_doc('Soil Texture', soil_tex[0].name) - self.assertEqual(doc.silt_composition, 50) - self.assertEqual(doc.soil_type, 'Silt Loam') diff --git a/erpnext/agriculture/doctype/soil_texture_criteria/__init__.py b/erpnext/agriculture/doctype/soil_texture_criteria/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/soil_texture_criteria/soil_texture_criteria.json b/erpnext/agriculture/doctype/soil_texture_criteria/soil_texture_criteria.json deleted file mode 100644 index 0cd72b0f6e..0000000000 --- a/erpnext/agriculture/doctype/soil_texture_criteria/soil_texture_criteria.json +++ /dev/null @@ -1,173 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-12-05 23:45:17.419610", - "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": "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": "Title", - "length": 0, - "no_copy": 0, - "options": "Agriculture Analysis 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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "value", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "minimum_permissible_value", - "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": "Minimum Permissible Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "maximum_permissible_value", - "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": "Maximum Permissible Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-11-04 03:26:46.178377", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Soil Texture Criteria", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Agriculture", - "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/agriculture/doctype/soil_texture_criteria/soil_texture_criteria.py b/erpnext/agriculture/doctype/soil_texture_criteria/soil_texture_criteria.py deleted file mode 100644 index 92a0cf9aec..0000000000 --- a/erpnext/agriculture/doctype/soil_texture_criteria/soil_texture_criteria.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class SoilTextureCriteria(Document): - pass diff --git a/erpnext/agriculture/doctype/water_analysis/__init__.py b/erpnext/agriculture/doctype/water_analysis/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/water_analysis/test_water_analysis.py b/erpnext/agriculture/doctype/water_analysis/test_water_analysis.py deleted file mode 100644 index ae144ccb21..0000000000 --- a/erpnext/agriculture/doctype/water_analysis/test_water_analysis.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestWaterAnalysis(unittest.TestCase): - pass diff --git a/erpnext/agriculture/doctype/water_analysis/water_analysis.js b/erpnext/agriculture/doctype/water_analysis/water_analysis.js deleted file mode 100644 index 13fe3adde6..0000000000 --- a/erpnext/agriculture/doctype/water_analysis/water_analysis.js +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Water Analysis', { - onload: (frm) => { - if (frm.doc.water_analysis_criteria == undefined) frm.call('load_contents'); - }, - refresh: (frm) => { - let map_tools = ["a.leaflet-draw-draw-polyline", - "a.leaflet-draw-draw-polygon", - "a.leaflet-draw-draw-rectangle", - "a.leaflet-draw-draw-circle", - "a.leaflet-draw-draw-circlemarker"]; - - map_tools.forEach((element) => $(element).hide()); - }, - laboratory_testing_datetime: (frm) => frm.call("update_lab_result_date") -}); diff --git a/erpnext/agriculture/doctype/water_analysis/water_analysis.json b/erpnext/agriculture/doctype/water_analysis/water_analysis.json deleted file mode 100644 index f990fef997..0000000000 --- a/erpnext/agriculture/doctype/water_analysis/water_analysis.json +++ /dev/null @@ -1,594 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "HR-WAT-.YYYY.-.#####", - "beta": 0, - "creation": "2017-10-17 18:51:19.946950", - "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": "location", - "fieldtype": "Geolocation", - "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", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "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, - "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, - "fieldname": "collection_datetime", - "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": "Collection Datetime", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "laboratory_testing_datetime", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Laboratory Testing Datetime", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "result_datetime", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Result Datetime", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_4", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "type_of_sample", - "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": "Type of Sample", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "container", - "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": "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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "origin", - "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": "Origin", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_8", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "collection_temperature", - "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": "Collection Temperature ", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "storage_temperature", - "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": "Storage Temperature", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "appearance", - "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": "Appearance", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "person_responsible", - "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": "Person Responsible", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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_29", - "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": "Water Analysis Criteria", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "water_analysis_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, - "length": 0, - "no_copy": 0, - "options": "Water Analysis 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": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-11-04 03:29:08.325644", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Water Analysis", - "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": "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": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Agriculture", - "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/agriculture/doctype/water_analysis/water_analysis.py b/erpnext/agriculture/doctype/water_analysis/water_analysis.py deleted file mode 100644 index 434acecc6e..0000000000 --- a/erpnext/agriculture/doctype/water_analysis/water_analysis.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.model.document import Document - - -class WaterAnalysis(Document): - @frappe.whitelist() - def load_contents(self): - docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Water Analysis'}) - for doc in docs: - self.append('water_analysis_criteria', {'title': str(doc.name)}) - - @frappe.whitelist() - def update_lab_result_date(self): - if not self.result_datetime: - self.result_datetime = self.laboratory_testing_datetime - - def validate(self): - if self.collection_datetime > self.laboratory_testing_datetime: - frappe.throw(_('Lab testing datetime cannot be before collection datetime')) - if self.laboratory_testing_datetime > self.result_datetime: - frappe.throw(_('Lab result datetime cannot be before testing datetime')) diff --git a/erpnext/agriculture/doctype/water_analysis_criteria/__init__.py b/erpnext/agriculture/doctype/water_analysis_criteria/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/water_analysis_criteria/water_analysis_criteria.json b/erpnext/agriculture/doctype/water_analysis_criteria/water_analysis_criteria.json deleted file mode 100644 index be9f1beffe..0000000000 --- a/erpnext/agriculture/doctype/water_analysis_criteria/water_analysis_criteria.json +++ /dev/null @@ -1,173 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-12-05 23:36:22.723558", - "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": "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": "Title", - "length": 0, - "no_copy": 0, - "options": "Agriculture Analysis 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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "value", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "minimum_permissible_value", - "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": "Minimum Permissible Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "maximum_permissible_value", - "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": "Maximum Permissible Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-11-04 03:26:07.026834", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Water Analysis Criteria", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Agriculture", - "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/agriculture/doctype/water_analysis_criteria/water_analysis_criteria.py b/erpnext/agriculture/doctype/water_analysis_criteria/water_analysis_criteria.py deleted file mode 100644 index 225c4f6529..0000000000 --- a/erpnext/agriculture/doctype/water_analysis_criteria/water_analysis_criteria.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class WaterAnalysisCriteria(Document): - pass diff --git a/erpnext/agriculture/doctype/weather/__init__.py b/erpnext/agriculture/doctype/weather/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/weather/test_weather.py b/erpnext/agriculture/doctype/weather/test_weather.py deleted file mode 100644 index 345baa9e99..0000000000 --- a/erpnext/agriculture/doctype/weather/test_weather.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestWeather(unittest.TestCase): - pass diff --git a/erpnext/agriculture/doctype/weather/weather.js b/erpnext/agriculture/doctype/weather/weather.js deleted file mode 100644 index dadb1d8b13..0000000000 --- a/erpnext/agriculture/doctype/weather/weather.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Weather', { - onload: (frm) => { - if (frm.doc.weather_parameter == undefined) frm.call('load_contents'); - } -}); diff --git a/erpnext/agriculture/doctype/weather/weather.json b/erpnext/agriculture/doctype/weather/weather.json deleted file mode 100644 index ebab78ad72..0000000000 --- a/erpnext/agriculture/doctype/weather/weather.json +++ /dev/null @@ -1,307 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "format:WEA-{date}-{location}", - "beta": 0, - "creation": "2017-10-17 19:01:05.095598", - "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": "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": "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": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "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": "Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "source", - "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": "Source", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_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, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_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, - "label": "Weather Parameter", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "weather_parameter", - "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": "Weather Parameter", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-11-04 03:31:36.839743", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Weather", - "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": "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": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Agriculture", - "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/agriculture/doctype/weather/weather.py b/erpnext/agriculture/doctype/weather/weather.py deleted file mode 100644 index 8750709c56..0000000000 --- a/erpnext/agriculture/doctype/weather/weather.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe.model.document import Document - - -class Weather(Document): - @frappe.whitelist() - def load_contents(self): - docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Weather'}) - for doc in docs: - self.append('weather_parameter', {'title': str(doc.name)}) diff --git a/erpnext/agriculture/doctype/weather_parameter/__init__.py b/erpnext/agriculture/doctype/weather_parameter/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/agriculture/doctype/weather_parameter/weather_parameter.json b/erpnext/agriculture/doctype/weather_parameter/weather_parameter.json deleted file mode 100644 index 45c4cfc4f5..0000000000 --- a/erpnext/agriculture/doctype/weather_parameter/weather_parameter.json +++ /dev/null @@ -1,173 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-12-06 00:19:15.967334", - "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": "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": "Title", - "length": 0, - "no_copy": 0, - "options": "Agriculture Analysis 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 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "value", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "minimum_permissible_value", - "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": "Minimum Permissible Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "maximum_permissible_value", - "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": "Maximum Permissible Value", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-11-04 03:26:58.794373", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Weather Parameter", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Agriculture", - "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/agriculture/doctype/weather_parameter/weather_parameter.py b/erpnext/agriculture/doctype/weather_parameter/weather_parameter.py deleted file mode 100644 index 7f02ab39ae..0000000000 --- a/erpnext/agriculture/doctype/weather_parameter/weather_parameter.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -from frappe.model.document import Document - - -class WeatherParameter(Document): - pass diff --git a/erpnext/agriculture/setup.py b/erpnext/agriculture/setup.py deleted file mode 100644 index 70931b9ad5..0000000000 --- a/erpnext/agriculture/setup.py +++ /dev/null @@ -1,429 +0,0 @@ -import frappe -from frappe import _ -from erpnext.setup.utils import insert_record - -def setup_agriculture(): - if frappe.get_all('Agriculture Analysis Criteria'): - # already setup - return - create_agriculture_data() - -def create_agriculture_data(): - records = [ - dict( - doctype='Item Group', - item_group_name='Fertilizer', - is_group=0, - parent_item_group=_('All Item Groups')), - dict( - doctype='Item Group', - item_group_name='Seed', - is_group=0, - parent_item_group=_('All Item Groups')), - dict( - doctype='Item Group', - item_group_name='By-product', - is_group=0, - parent_item_group=_('All Item Groups')), - dict( - doctype='Item Group', - item_group_name='Produce', - is_group=0, - parent_item_group=_('All Item Groups')), - dict( - doctype='Agriculture Analysis Criteria', - title='Nitrogen Content', - standard=1, - linked_doctype='Fertilizer'), - dict( - doctype='Agriculture Analysis Criteria', - title='Phosphorous Content', - standard=1, - linked_doctype='Fertilizer'), - dict( - doctype='Agriculture Analysis Criteria', - title='Potassium Content', - standard=1, - linked_doctype='Fertilizer'), - dict( - doctype='Agriculture Analysis Criteria', - title='Calcium Content', - standard=1, - linked_doctype='Fertilizer'), - dict( - doctype='Agriculture Analysis Criteria', - title='Sulphur Content', - standard=1, - linked_doctype='Fertilizer'), - dict( - doctype='Agriculture Analysis Criteria', - title='Magnesium Content', - standard=1, - linked_doctype='Fertilizer'), - dict( - doctype='Agriculture Analysis Criteria', - title='Iron Content', - standard=1, - linked_doctype='Fertilizer'), - dict( - doctype='Agriculture Analysis Criteria', - title='Copper Content', - standard=1, - linked_doctype='Fertilizer'), - dict( - doctype='Agriculture Analysis Criteria', - title='Zinc Content', - standard=1, - linked_doctype='Fertilizer'), - dict( - doctype='Agriculture Analysis Criteria', - title='Boron Content', - standard=1, - linked_doctype='Fertilizer'), - dict( - doctype='Agriculture Analysis Criteria', - title='Manganese Content', - standard=1, - linked_doctype='Fertilizer'), - dict( - doctype='Agriculture Analysis Criteria', - title='Chlorine Content', - standard=1, - linked_doctype='Fertilizer'), - dict( - doctype='Agriculture Analysis Criteria', - title='Molybdenum Content', - standard=1, - linked_doctype='Fertilizer'), - dict( - doctype='Agriculture Analysis Criteria', - title='Sodium Content', - standard=1, - linked_doctype='Fertilizer'), - dict( - doctype='Agriculture Analysis Criteria', - title='Humic Acid', - standard=1, - linked_doctype='Fertilizer'), - dict( - doctype='Agriculture Analysis Criteria', - title='Fulvic Acid', - standard=1, - linked_doctype='Fertilizer'), - dict( - doctype='Agriculture Analysis Criteria', - title='Inert', - standard=1, - linked_doctype='Fertilizer'), - dict( - doctype='Agriculture Analysis Criteria', - title='Others', - standard=1, - linked_doctype='Fertilizer'), - dict( - doctype='Agriculture Analysis Criteria', - title='Nitrogen', - standard=1, - linked_doctype='Plant Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Phosphorous', - standard=1, - linked_doctype='Plant Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Potassium', - standard=1, - linked_doctype='Plant Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Calcium', - standard=1, - linked_doctype='Plant Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Magnesium', - standard=1, - linked_doctype='Plant Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Sulphur', - standard=1, - linked_doctype='Plant Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Boron', - standard=1, - linked_doctype='Plant Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Copper', - standard=1, - linked_doctype='Plant Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Iron', - standard=1, - linked_doctype='Plant Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Manganese', - standard=1, - linked_doctype='Plant Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Zinc', - standard=1, - linked_doctype='Plant Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Depth (in cm)', - standard=1, - linked_doctype='Soil Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Soil pH', - standard=1, - linked_doctype='Soil Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Salt Concentration (%)', - standard=1, - linked_doctype='Soil Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Organic Matter (%)', - standard=1, - linked_doctype='Soil Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='CEC (Cation Exchange Capacity) (MAQ/100mL)', - standard=1, - linked_doctype='Soil Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Potassium Saturation (%)', - standard=1, - linked_doctype='Soil Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Calcium Saturation (%)', - standard=1, - linked_doctype='Soil Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Manganese Saturation (%)', - standard=1, - linked_doctype='Soil Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Nirtogen (ppm)', - standard=1, - linked_doctype='Soil Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Phosphorous (ppm)', - standard=1, - linked_doctype='Soil Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Potassium (ppm)', - standard=1, - linked_doctype='Soil Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Calcium (ppm)', - standard=1, - linked_doctype='Soil Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Magnesium (ppm)', - standard=1, - linked_doctype='Soil Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Sulphur (ppm)', - standard=1, - linked_doctype='Soil Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Copper (ppm)', - standard=1, - linked_doctype='Soil Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Iron (ppm)', - standard=1, - linked_doctype='Soil Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Manganese (ppm)', - standard=1, - linked_doctype='Soil Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Zinc (ppm)', - standard=1, - linked_doctype='Soil Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Aluminium (ppm)', - standard=1, - linked_doctype='Soil Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Water pH', - standard=1, - linked_doctype='Water Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Conductivity (mS/cm)', - standard=1, - linked_doctype='Water Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Hardness (mg/CaCO3)', - standard=1, - linked_doctype='Water Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Turbidity (NTU)', - standard=1, - linked_doctype='Water Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Odor', - standard=1, - linked_doctype='Water Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Color', - standard=1, - linked_doctype='Water Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Nitrate (mg/L)', - standard=1, - linked_doctype='Water Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Nirtite (mg/L)', - standard=1, - linked_doctype='Water Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Calcium (mg/L)', - standard=1, - linked_doctype='Water Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Magnesium (mg/L)', - standard=1, - linked_doctype='Water Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Sulphate (mg/L)', - standard=1, - linked_doctype='Water Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Boron (mg/L)', - standard=1, - linked_doctype='Water Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Copper (mg/L)', - standard=1, - linked_doctype='Water Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Iron (mg/L)', - standard=1, - linked_doctype='Water Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Manganese (mg/L)', - standard=1, - linked_doctype='Water Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Zinc (mg/L)', - standard=1, - linked_doctype='Water Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Chlorine (mg/L)', - standard=1, - linked_doctype='Water Analysis'), - dict( - doctype='Agriculture Analysis Criteria', - title='Bulk Density', - standard=1, - linked_doctype='Soil Texture'), - dict( - doctype='Agriculture Analysis Criteria', - title='Field Capacity', - standard=1, - linked_doctype='Soil Texture'), - dict( - doctype='Agriculture Analysis Criteria', - title='Wilting Point', - standard=1, - linked_doctype='Soil Texture'), - dict( - doctype='Agriculture Analysis Criteria', - title='Hydraulic Conductivity', - standard=1, - linked_doctype='Soil Texture'), - dict( - doctype='Agriculture Analysis Criteria', - title='Organic Matter', - standard=1, - linked_doctype='Soil Texture'), - dict( - doctype='Agriculture Analysis Criteria', - title='Temperature High', - standard=1, - linked_doctype='Weather'), - dict( - doctype='Agriculture Analysis Criteria', - title='Temperature Low', - standard=1, - linked_doctype='Weather'), - dict( - doctype='Agriculture Analysis Criteria', - title='Temperature Average', - standard=1, - linked_doctype='Weather'), - dict( - doctype='Agriculture Analysis Criteria', - title='Dew Point', - standard=1, - linked_doctype='Weather'), - dict( - doctype='Agriculture Analysis Criteria', - title='Precipitation Received', - standard=1, - linked_doctype='Weather'), - dict( - doctype='Agriculture Analysis Criteria', - title='Humidity', - standard=1, - linked_doctype='Weather'), - dict( - doctype='Agriculture Analysis Criteria', - title='Pressure', - standard=1, - linked_doctype='Weather'), - dict( - doctype='Agriculture Analysis Criteria', - title='Insolation/ PAR (Photosynthetically Active Radiation)', - standard=1, - linked_doctype='Weather'), - dict( - doctype='Agriculture Analysis Criteria', - title='Degree Days', - standard=1, - linked_doctype='Weather') - ] - insert_record(records) diff --git a/erpnext/agriculture/workspace/agriculture/agriculture.json b/erpnext/agriculture/workspace/agriculture/agriculture.json deleted file mode 100644 index 6714de6d38..0000000000 --- a/erpnext/agriculture/workspace/agriculture/agriculture.json +++ /dev/null @@ -1,171 +0,0 @@ -{ - "charts": [], - "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Crops & Lands\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Analytics\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Diseases & Fertilizers\", \"col\": 4}}]", - "creation": "2020-03-02 17:23:34.339274", - "docstatus": 0, - "doctype": "Workspace", - "for_user": "", - "hide_custom": 0, - "icon": "agriculture", - "idx": 0, - "label": "Agriculture", - "links": [ - { - "hidden": 0, - "is_query_report": 0, - "label": "Crops & Lands", - "link_count": 0, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Crop", - "link_count": 0, - "link_to": "Crop", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Crop Cycle", - "link_count": 0, - "link_to": "Crop Cycle", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Location", - "link_count": 0, - "link_to": "Location", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Analytics", - "link_count": 0, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Plant Analysis", - "link_count": 0, - "link_to": "Plant Analysis", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Soil Analysis", - "link_count": 0, - "link_to": "Soil Analysis", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Water Analysis", - "link_count": 0, - "link_to": "Water Analysis", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Soil Texture", - "link_count": 0, - "link_to": "Soil Texture", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Weather", - "link_count": 0, - "link_to": "Weather", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Agriculture Analysis Criteria", - "link_count": 0, - "link_to": "Agriculture Analysis Criteria", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Diseases & Fertilizers", - "link_count": 0, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Disease", - "link_count": 0, - "link_to": "Disease", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Fertilizer", - "link_count": 0, - "link_to": "Fertilizer", - "link_type": "DocType", - "onboard": 1, - "type": "Link" - } - ], - "modified": "2021-08-05 12:15:54.595198", - "modified_by": "Administrator", - "module": "Agriculture", - "name": "Agriculture", - "owner": "Administrator", - "parent_page": "", - "public": 1, - "restrict_to_domain": "Agriculture", - "roles": [], - "sequence_id": 3, - "shortcuts": [], - "title": "Agriculture" -} \ No newline at end of file diff --git a/erpnext/demo/domains.py b/erpnext/demo/domains.py index 5fa181dfa4..346787e3c7 100644 --- a/erpnext/demo/domains.py +++ b/erpnext/demo/domains.py @@ -14,9 +14,6 @@ data = { 'Education': { 'company_name': 'Whitmore College' }, - 'Agriculture': { - 'company_name': 'Schrute Farms' - }, 'Non Profit': { 'company_name': 'Erpnext Foundation' } diff --git a/erpnext/domains/agriculture.py b/erpnext/domains/agriculture.py deleted file mode 100644 index e5414a9c09..0000000000 --- a/erpnext/domains/agriculture.py +++ /dev/null @@ -1,26 +0,0 @@ -data = { - 'desktop_icons': [ - 'Agriculture Task', - 'Crop', - 'Crop Cycle', - 'Fertilizer', - 'Item', - 'Location', - 'Disease', - 'Plant Analysis', - 'Soil Analysis', - 'Soil Texture', - 'Task', - 'Water Analysis', - 'Weather' - ], - 'restricted_roles': [ - 'Agriculture Manager', - 'Agriculture User' - ], - 'modules': [ - 'Agriculture' - ], - 'default_portal_role': 'System Manager', - 'on_setup': 'erpnext.agriculture.setup.setup_agriculture' -} diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 1d11f20ab7..f014b0e1e9 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -65,7 +65,6 @@ webform_list_context = "erpnext.controllers.website_list_for_contact.get_webform calendars = ["Task", "Work Order", "Leave Application", "Sales Order", "Holiday List", "Course Schedule"] domains = { - 'Agriculture': 'erpnext.domains.agriculture', 'Distribution': 'erpnext.domains.distribution', 'Education': 'erpnext.domains.education', 'Hospitality': 'erpnext.domains.hospitality', @@ -567,18 +566,6 @@ global_search_doctypes = { {'doctype': 'Assessment Code', 'index': 39}, {'doctype': 'Discussion', 'index': 40}, ], - "Agriculture": [ - {'doctype': 'Weather', 'index': 1}, - {'doctype': 'Soil Texture', 'index': 2}, - {'doctype': 'Water Analysis', 'index': 3}, - {'doctype': 'Soil Analysis', 'index': 4}, - {'doctype': 'Plant Analysis', 'index': 5}, - {'doctype': 'Agriculture Analysis Criteria', 'index': 6}, - {'doctype': 'Disease', 'index': 7}, - {'doctype': 'Crop', 'index': 8}, - {'doctype': 'Fertilizer', 'index': 9}, - {'doctype': 'Crop Cycle', 'index': 10} - ], "Non Profit": [ {'doctype': 'Certified Consultant', 'index': 1}, {'doctype': 'Certification Application', 'index': 2}, diff --git a/erpnext/modules.txt b/erpnext/modules.txt index 15a24a746f..ae0bb2d5c9 100644 --- a/erpnext/modules.txt +++ b/erpnext/modules.txt @@ -16,7 +16,6 @@ Maintenance Education Regional Restaurant -Agriculture ERPNext Integrations Non Profit Hotels diff --git a/erpnext/patches.txt b/erpnext/patches.txt index deeeeb7a1c..268db40a8e 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -320,4 +320,6 @@ erpnext.patches.v13_0.disable_ksa_print_format_for_others # 16-12-2021 erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template erpnext.patches.v13_0.update_tax_category_for_rcm execute:frappe.delete_doc_if_exists('Workspace', 'ERPNext Integrations Settings') -erpnext.patches.v14_0.set_payroll_cost_centers \ No newline at end of file +erpnext.patches.v14_0.set_payroll_cost_centers +erpnext.patches.v13_0.agriculture_deprecation_warning +erpnext.patches.v14_0.delete_agriculture_doctypes diff --git a/erpnext/patches/v13_0/agriculture_deprecation_warning.py b/erpnext/patches/v13_0/agriculture_deprecation_warning.py new file mode 100644 index 0000000000..512444ef65 --- /dev/null +++ b/erpnext/patches/v13_0/agriculture_deprecation_warning.py @@ -0,0 +1,10 @@ +import click + + +def execute(): + + click.secho( + "Agriculture Domain is moved to a separate app and will be removed from ERPNext in version-14.\n" + "Please install the app to continue using the Agriculture domain: https://github.com/frappe/agriculture", + fg="yellow", + ) diff --git a/erpnext/patches/v14_0/delete_agriculture_doctypes.py b/erpnext/patches/v14_0/delete_agriculture_doctypes.py new file mode 100644 index 0000000000..d7fe832f9a --- /dev/null +++ b/erpnext/patches/v14_0/delete_agriculture_doctypes.py @@ -0,0 +1,19 @@ +import frappe + + +def execute(): + frappe.delete_doc("Module Def", "Agriculture", ignore_missing=True, force=True) + + frappe.delete_doc("Workspace", "Agriculture", ignore_missing=True, force=True) + + reports = frappe.get_all("Report", {"module": "agriculture", "is_standard": "Yes"}, pluck='name') + for report in reports: + frappe.delete_doc("Report", report, ignore_missing=True, force=True) + + dashboards = frappe.get_all("Dashboard", {"module": "agriculture", "is_standard": 1}, pluck='name') + for dashboard in dashboards: + frappe.delete_doc("Dashboard", dashboard, ignore_missing=True, force=True) + + doctypes = frappe.get_all("DocType", {"module": "agriculture", "custom": 0}, pluck='name') + for doctype in doctypes: + frappe.delete_doc("DocType", doctype, ignore_missing=True) diff --git a/erpnext/public/js/setup_wizard.js b/erpnext/public/js/setup_wizard.js index 38e1eb5156..e746ce9ae0 100644 --- a/erpnext/public/js/setup_wizard.js +++ b/erpnext/public/js/setup_wizard.js @@ -27,7 +27,6 @@ erpnext.setup.slides_settings = [ { "label": __("Manufacturing"), "value": "Manufacturing" }, { "label": __("Retail"), "value": "Retail" }, { "label": __("Services"), "value": "Services" }, - { "label": __("Agriculture (beta)"), "value": "Agriculture" }, { "label": __("Healthcare (beta)"), "value": "Healthcare" }, { "label": __("Non Profit (beta)"), "value": "Non Profit" } ], reqd: 1 diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 97d850ba19..336b51c0ab 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -33,7 +33,6 @@ def install(country=None): { 'doctype': 'Domain', 'domain': 'Services'}, { 'doctype': 'Domain', 'domain': 'Education'}, { 'doctype': 'Domain', 'domain': 'Healthcare'}, - { 'doctype': 'Domain', 'domain': 'Agriculture'}, { 'doctype': 'Domain', 'domain': 'Non Profit'}, # ensure at least an empty Address Template exists for this Country From 733c9defdfe96c3fa1f75d29c9ec085cf69aced6 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 4 Jan 2022 18:39:30 +0530 Subject: [PATCH 146/193] fix: update idx after updating items in so/po (#29134) --- erpnext/controllers/accounts_controller.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index c5d8f09793..ce5d5dcb57 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2119,6 +2119,11 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil parent.update_status_updater() else: parent.check_credit_limit() + + # reset index of child table + for idx, row in enumerate(parent.get(child_docname), start=1): + row.idx = idx + parent.save() if parent_doctype == 'Purchase Order': From 08dcbd6fce188488af2a4545ffe6695967082315 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Tue, 4 Jan 2022 19:05:36 +0530 Subject: [PATCH 147/193] feat: allow user to change the parent company --- erpnext/setup/doctype/company/company.js | 11 ++-- erpnext/setup/doctype/company/company.py | 14 ++++- erpnext/setup/doctype/company/test_company.py | 55 +++++++++++++++++++ 3 files changed, 72 insertions(+), 8 deletions(-) diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 91f60fbd4e..45e8dccc31 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -79,14 +79,11 @@ frappe.ui.form.on("Company", { }, refresh: function(frm) { - if(!frm.doc.__islocal) { - frm.doc.abbr && frm.set_df_property("abbr", "read_only", 1); - frm.set_df_property("parent_company", "read_only", 1); - disbale_coa_fields(frm); - } + frm.toggle_display('address_html', !frm.is_new()); - frm.toggle_display('address_html', !frm.doc.__islocal); - if(!frm.doc.__islocal) { + if (!frm.is_new()) { + frm.doc.abbr && frm.set_df_property("abbr", "read_only", 1); + disbale_coa_fields(frm); frappe.contacts.render_address_and_contact(frm); frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Company'} diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index e739739458..0a02bcd6cd 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -47,6 +47,7 @@ class Company(NestedSet): self.validate_perpetual_inventory() self.validate_perpetual_inventory_for_non_stock_items() self.check_country_change() + self.check_parent_changed() self.set_chart_of_accounts() self.validate_parent_company() @@ -130,6 +131,10 @@ class Company(NestedSet): self.name in frappe.local.enable_perpetual_inventory: frappe.local.enable_perpetual_inventory[self.name] = self.enable_perpetual_inventory + if frappe.flags.parent_company_changed: + from frappe.utils.nestedset import rebuild_tree + rebuild_tree("Company", "parent_company") + frappe.clear_cache() def create_default_warehouses(self): @@ -191,7 +196,7 @@ class Company(NestedSet): def check_country_change(self): frappe.flags.country_change = False - if not self.get('__islocal') and \ + if not self.is_new() and \ self.country != frappe.get_cached_value('Company', self.name, 'country'): frappe.flags.country_change = True @@ -396,6 +401,13 @@ class Company(NestedSet): if not frappe.db.get_value('GL Entry', {'company': self.name}): frappe.db.sql("delete from `tabProcess Deferred Accounting` where company=%s", self.name) + def check_parent_changed(self): + frappe.flags.parent_company_changed = False + + if not self.is_new() and \ + self.parent_company != frappe.db.get_value("Company", self.name, "parent_company"): + frappe.flags.parent_company_changed = True + def get_name_with_abbr(name, company): company_abbr = frappe.get_cached_value('Company', company, "abbr") parts = name.split(" - ") diff --git a/erpnext/setup/doctype/company/test_company.py b/erpnext/setup/doctype/company/test_company.py index 4ee9492738..e175c5435a 100644 --- a/erpnext/setup/doctype/company/test_company.py +++ b/erpnext/setup/doctype/company/test_company.py @@ -93,6 +93,61 @@ class TestCompany(unittest.TestCase): frappe.db.sql(""" delete from `tabMode of Payment Account` where company =%s """, (company)) + def test_basic_tree(self, records=None): + min_lft = 1 + max_rgt = frappe.db.sql("select max(rgt) from `tabCompany`")[0][0] + + if not records: + records = test_records[2:] + + for company in records: + lft, rgt, parent_company = frappe.db.get_value("Company", company["company_name"], + ["lft", "rgt", "parent_company"]) + + if parent_company: + parent_lft, parent_rgt = frappe.db.get_value("Company", parent_company, + ["lft", "rgt"]) + else: + # root + parent_lft = min_lft - 1 + parent_rgt = max_rgt + 1 + + self.assertTrue(lft) + self.assertTrue(rgt) + self.assertTrue(lft < rgt) + self.assertTrue(parent_lft < parent_rgt) + self.assertTrue(lft > parent_lft) + self.assertTrue(rgt < parent_rgt) + self.assertTrue(lft >= min_lft) + self.assertTrue(rgt <= max_rgt) + + def get_no_of_children(self, company): + def get_no_of_children(companies, no_of_children): + children = [] + for company in companies: + children += frappe.db.sql_list("""select name from `tabCompany` + where ifnull(parent_company, '')=%s""", company or '') + + if len(children): + return get_no_of_children(children, no_of_children + len(children)) + else: + return no_of_children + + return get_no_of_children([company], 0) + + def test_change_parent_company(self): + child_company = frappe.get_doc("Company", "_Test Company 5") + + # changing parent of company + child_company.parent_company = "_Test Company 3" + child_company.save() + self.test_basic_tree() + + # move it back + child_company.parent_company = "_Test Company 4" + child_company.save() + self.test_basic_tree() + def create_company_communication(doctype, docname): comm = frappe.get_doc({ "doctype": "Communication", From f4db474be0f86128b88a25cffc4ba580d857b252 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 4 Jan 2022 19:12:19 +0530 Subject: [PATCH 148/193] fix: minor issues (#29130) --- erpnext/accounts/doctype/account/account.js | 26 ++++++++++--------- .../uae_vat_settings/uae_vat_settings.js | 12 ++++++--- .../page/point_of_sale/pos_item_selector.js | 2 +- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.js b/erpnext/accounts/doctype/account/account.js index 7a1d735948..320e1cab7c 100644 --- a/erpnext/accounts/doctype/account/account.js +++ b/erpnext/accounts/doctype/account/account.js @@ -43,12 +43,12 @@ frappe.ui.form.on('Account', { frm.trigger('add_toolbar_buttons'); } if (frm.has_perm('write')) { - frm.add_custom_button(__('Update Account Name / Number'), function () { - frm.trigger("update_account_number"); - }); frm.add_custom_button(__('Merge Account'), function () { frm.trigger("merge_account"); - }); + }, __('Actions')); + frm.add_custom_button(__('Update Account Name / Number'), function () { + frm.trigger("update_account_number"); + }, __('Actions')); } } }, @@ -59,11 +59,12 @@ frappe.ui.form.on('Account', { } }, add_toolbar_buttons: function(frm) { - frm.add_custom_button(__('Chart of Accounts'), - function () { frappe.set_route("Tree", "Account"); }); + frm.add_custom_button(__('Chart of Accounts'), () => { + frappe.set_route("Tree", "Account"); + }, __('View')); if (frm.doc.is_group == 1) { - frm.add_custom_button(__('Group to Non-Group'), function () { + frm.add_custom_button(__('Convert to Non-Group'), function () { return frappe.call({ doc: frm.doc, method: 'convert_group_to_ledger', @@ -71,10 +72,11 @@ frappe.ui.form.on('Account', { frm.refresh(); } }); - }); + }, __('Actions')); + } else if (cint(frm.doc.is_group) == 0 && frappe.boot.user.can_read.indexOf("GL Entry") !== -1) { - frm.add_custom_button(__('Ledger'), function () { + frm.add_custom_button(__('General Ledger'), function () { frappe.route_options = { "account": frm.doc.name, "from_date": frappe.sys_defaults.year_start_date, @@ -82,9 +84,9 @@ frappe.ui.form.on('Account', { "company": frm.doc.company }; frappe.set_route("query-report", "General Ledger"); - }); + }, __('View')); - frm.add_custom_button(__('Non-Group to Group'), function () { + frm.add_custom_button(__('Convert to Group'), function () { return frappe.call({ doc: frm.doc, method: 'convert_ledger_to_group', @@ -92,7 +94,7 @@ frappe.ui.form.on('Account', { frm.refresh(); } }); - }); + }, __('Actions')); } }, diff --git a/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.js b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.js index 07a93010b5..66531412fa 100644 --- a/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.js +++ b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.js @@ -2,7 +2,13 @@ // For license information, please see license.txt frappe.ui.form.on('UAE VAT Settings', { - // refresh: function(frm) { - - // } + onload: function(frm) { + frm.set_query('account', 'uae_vat_accounts', function() { + return { + filters: { + 'company': frm.doc.company + } + }; + }); + } }); diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js index 496385248c..a30bcd7cf6 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -113,7 +113,7 @@ erpnext.PointOfSale.ItemSelector = class { `
${get_item_image_html()} From 19bb01c973d26d42a96c293c77331924c794e834 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Tue, 4 Jan 2022 19:14:40 +0530 Subject: [PATCH 149/193] fix: POS items added to cart despite low qty (#29126) --- erpnext/selling/page/point_of_sale/pos_controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index e61a634aae..ce74f6d0a5 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -643,7 +643,7 @@ erpnext.PointOfSale.Controller = class { message: __('Item Code: {0} is not available under warehouse {1}.', [bold_item_code, bold_warehouse]) }) } else if (available_qty < qty_needed) { - frappe.show_alert({ + frappe.throw({ message: __('Stock quantity not enough for Item Code: {0} under warehouse {1}. Available quantity {2}.', [bold_item_code, bold_warehouse, bold_available_qty]), indicator: 'orange' }); From 5eeb94db313f805c5e740198bfa0d0fa008c3718 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 4 Jan 2022 19:21:16 +0530 Subject: [PATCH 150/193] fix: map Accounting Dimensions for Bank Entry against Payroll Entry --- erpnext/payroll/doctype/payroll_entry/payroll_entry.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py index 5bb32cf909..ed3fa5befc 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py @@ -398,23 +398,24 @@ class PayrollEntry(Document): currencies = [] multi_currency = 0 company_currency = erpnext.get_company_currency(self.company) + accounting_dimensions = get_accounting_dimensions() or [] exchange_rate, amount = self.get_amount_and_exchange_rate_for_journal_entry(self.payment_account, je_payment_amount, company_currency, currencies) - accounts.append({ + accounts.append(self.update_accounting_dimensions({ "account": self.payment_account, "bank_account": self.bank_account, "credit_in_account_currency": flt(amount, precision), "exchange_rate": flt(exchange_rate), - }) + }, accounting_dimensions)) exchange_rate, amount = self.get_amount_and_exchange_rate_for_journal_entry(payroll_payable_account, je_payment_amount, company_currency, currencies) - accounts.append({ + accounts.append(self.update_accounting_dimensions({ "account": payroll_payable_account, "debit_in_account_currency": flt(amount, precision), "exchange_rate": flt(exchange_rate), "reference_type": self.doctype, "reference_name": self.name - }) + }, accounting_dimensions)) if len(currencies) > 1: multi_currency = 1 From 3febca1a9afcbcda90070029adddcd214f8b78e7 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Tue, 4 Jan 2022 19:37:44 +0530 Subject: [PATCH 151/193] fix: Modifying Opening invoice creation tool timestamp (#29127) --- .../opening_invoice_creation_tool.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.json b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.json index bc9241802d..daee8f8c1a 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.json +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.json @@ -75,7 +75,7 @@ ], "hide_toolbar": 1, "issingle": 1, - "modified": "2019-07-25 14:57:33.187689", + "modified": "2022-01-04 15:25:06.053187", "modified_by": "Administrator", "module": "Accounts", "name": "Opening Invoice Creation Tool", From ac816f4fed0a557c821fabb1a0d9e803d7e367ae Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 4 Jan 2022 21:46:31 +0530 Subject: [PATCH 152/193] fix(UX): validate setup on clicking Mark Attendance button in Shift Type (#29146) --- erpnext/hr/doctype/shift_type/shift_type.js | 33 ++++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/erpnext/hr/doctype/shift_type/shift_type.js b/erpnext/hr/doctype/shift_type/shift_type.js index ba53312bce..7138e3bcf3 100644 --- a/erpnext/hr/doctype/shift_type/shift_type.js +++ b/erpnext/hr/doctype/shift_type/shift_type.js @@ -4,15 +4,32 @@ frappe.ui.form.on('Shift Type', { refresh: function(frm) { frm.add_custom_button( - 'Mark Attendance', - () => frm.call({ - doc: frm.doc, - method: 'process_auto_attendance', - freeze: true, - callback: () => { - frappe.msgprint(__("Attendance has been marked as per employee check-ins")); + __('Mark Attendance'), + () => { + if (!frm.doc.enable_auto_attendance) { + frm.scroll_to_field('enable_auto_attendance'); + frappe.throw(__('Please Enable Auto Attendance and complete the setup first.')); } - }) + + if (!frm.doc.process_attendance_after) { + frm.scroll_to_field('process_attendance_after'); + frappe.throw(__('Please set {0}.', [__('Process Attendance After').bold()])); + } + + if (!frm.doc.last_sync_of_checkin) { + frm.scroll_to_field('last_sync_of_checkin'); + frappe.throw(__('Please set {0}.', [__('Last Sync of Checkin').bold()])); + } + + frm.call({ + doc: frm.doc, + method: 'process_auto_attendance', + freeze: true, + callback: () => { + frappe.msgprint(__('Attendance has been marked as per employee check-ins')); + } + }); + } ); } }); From fbd706f232b6fa7fdf3cb7e560f07e9736228ce3 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 5 Jan 2022 17:55:08 +0530 Subject: [PATCH 153/193] fix: existing party link validation (#29159) --- erpnext/accounts/doctype/party_link/party_link.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/party_link/party_link.py b/erpnext/accounts/doctype/party_link/party_link.py index e9f813c17c..031a9fa4db 100644 --- a/erpnext/accounts/doctype/party_link/party_link.py +++ b/erpnext/accounts/doctype/party_link/party_link.py @@ -2,7 +2,7 @@ # For license information, please see license.txt import frappe -from frappe import _ +from frappe import _, bold from frappe.model.document import Document @@ -12,6 +12,17 @@ class PartyLink(Document): frappe.throw(_("Allowed primary roles are 'Customer' and 'Supplier'. Please select one of these roles only."), title=_("Invalid Primary Role")) + existing_party_link = frappe.get_all('Party Link', { + 'primary_party': self.primary_party, + 'secondary_party': self.secondary_party + }, pluck="primary_role") + if existing_party_link: + frappe.throw(_('{} {} is already linked with {} {}') + .format( + self.primary_role, bold(self.primary_party), + self.secondary_role, bold(self.secondary_party) + )) + existing_party_link = frappe.get_all('Party Link', { 'primary_party': self.secondary_party }, pluck="primary_role") From fcc5fa14c869d4b85221e3b9b694f695639dea3c Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 5 Jan 2022 20:44:06 +0530 Subject: [PATCH 154/193] fix: failing tests of first response time (#29153) --- erpnext/support/doctype/issue/issue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index d5e5b78288..e211e24c40 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -236,7 +236,7 @@ def is_first_response(issue): return False def calculate_first_response_time(issue, first_responded_on): - issue_creation_date = issue.creation + issue_creation_date = issue.service_level_agreement_creation or issue.creation issue_creation_time = get_time_in_seconds(issue_creation_date) first_responded_on_in_seconds = get_time_in_seconds(first_responded_on) support_hours = frappe.get_cached_doc("Service Level Agreement", issue.service_level_agreement).support_and_resolution From 26247be3b872a28f425ce51af663eeb0e0899799 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 6 Jan 2022 11:21:59 +0530 Subject: [PATCH 155/193] fix: Earned Leave allocation from Leave Policy Assignment (#29163) * fix: Earned Leave allocation from Leave Policy Assignment * test: Earned Leave Allocation from Leave Policy Assignment --- .../leave_policy_assignment.py | 2 + .../test_leave_policy_assignment.py | 61 +++++++++++++++++-- 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py index 655e3ac53e..355370f3a4 100644 --- a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py +++ b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.py @@ -128,6 +128,8 @@ class LeavePolicyAssignment(Document): monthly_earned_leave = get_monthly_earned_leave(new_leaves_allocated, leave_type_details.get(leave_type).earned_leave_frequency, leave_type_details.get(leave_type).rounding) new_leaves_allocated = monthly_earned_leave * months_passed + else: + new_leaves_allocated = 0 return new_leaves_allocated diff --git a/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py b/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py index b1861ad4d8..8953a51e8b 100644 --- a/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py +++ b/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py @@ -4,6 +4,7 @@ import unittest import frappe +from frappe.utils import add_months, get_first_day, getdate from erpnext.hr.doctype.leave_application.test_leave_application import ( get_employee, @@ -17,9 +18,8 @@ from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import ( test_dependencies = ["Employee"] class TestLeavePolicyAssignment(unittest.TestCase): - def setUp(self): - for doctype in ["Leave Application", "Leave Allocation", "Leave Policy Assignment", "Leave Ledger Entry"]: + for doctype in ["Leave Period", "Leave Application", "Leave Allocation", "Leave Policy Assignment", "Leave Ledger Entry"]: frappe.db.sql("delete from `tab{0}`".format(doctype)) #nosec def test_grant_leaves(self): @@ -54,8 +54,8 @@ class TestLeavePolicyAssignment(unittest.TestCase): self.assertEqual(leave_alloc_doc.new_leaves_allocated, 10) self.assertEqual(leave_alloc_doc.leave_type, "_Test Leave Type") - self.assertEqual(leave_alloc_doc.from_date, leave_period.from_date) - self.assertEqual(leave_alloc_doc.to_date, leave_period.to_date) + self.assertEqual(getdate(leave_alloc_doc.from_date), getdate(leave_period.from_date)) + self.assertEqual(getdate(leave_alloc_doc.to_date), getdate(leave_period.to_date)) self.assertEqual(leave_alloc_doc.leave_policy, leave_policy.name) self.assertEqual(leave_alloc_doc.leave_policy_assignment, leave_policy_assignments[0]) @@ -101,6 +101,55 @@ class TestLeavePolicyAssignment(unittest.TestCase): # User are now allowed to grant leave self.assertEqual(leave_policy_assignment_doc.leaves_allocated, 0) + def test_earned_leave_allocation(self): + leave_period = create_leave_period("Test Earned Leave Period") + employee = get_employee() + leave_type = create_earned_leave_type("Test Earned Leave") + + leave_policy = frappe.get_doc({ + "doctype": "Leave Policy", + "leave_policy_details": [{"leave_type": leave_type.name, "annual_allocation": 6}] + }).insert() + + data = { + "assignment_based_on": "Leave Period", + "leave_policy": leave_policy.name, + "leave_period": leave_period.name + } + leave_policy_assignments = create_assignment_for_multiple_employees([employee.name], frappe._dict(data)) + + # leaves allocated should be 0 since it is an earned leave and allocation happens via scheduler based on set frequency + leaves_allocated = frappe.db.get_value("Leave Allocation", { + "leave_policy_assignment": leave_policy_assignments[0] + }, "total_leaves_allocated") + self.assertEqual(leaves_allocated, 0) + def tearDown(self): - for doctype in ["Leave Application", "Leave Allocation", "Leave Policy Assignment", "Leave Ledger Entry"]: - frappe.db.sql("delete from `tab{0}`".format(doctype)) #nosec + frappe.db.rollback() + + +def create_earned_leave_type(leave_type): + frappe.delete_doc_if_exists("Leave Type", leave_type, force=1) + + return frappe.get_doc(dict( + leave_type_name=leave_type, + doctype="Leave Type", + is_earned_leave=1, + earned_leave_frequency="Monthly", + rounding=0.5, + max_leaves_allowed=6 + )).insert() + + +def create_leave_period(name): + frappe.delete_doc_if_exists("Leave Period", name, force=1) + start_date = get_first_day(getdate()) + + return frappe.get_doc(dict( + name=name, + doctype="Leave Period", + from_date=start_date, + to_date=add_months(start_date, 12), + company="_Test Company", + is_active=1 + )).insert() \ No newline at end of file From f316aaf41e40e41be52c7b10c4bf53651f42f063 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 6 Jan 2022 13:19:17 +0530 Subject: [PATCH 156/193] fix: GL Entries for loan repayment via Salary --- .../loan_repayment/loan_repayment.json | 23 +++- .../doctype/loan_repayment/loan_repayment.py | 116 +++++++++--------- .../doctype/salary_slip/salary_slip.py | 22 +++- .../doctype/salary_slip/test_salary_slip.py | 2 +- 4 files changed, 99 insertions(+), 64 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json index 6479853246..93ef217042 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json @@ -13,8 +13,10 @@ "column_break_3", "company", "posting_date", - "is_term_loan", "rate_of_interest", + "payroll_payable_account", + "is_term_loan", + "repay_from_salary", "payment_details_section", "due_date", "pending_principal_amount", @@ -243,15 +245,31 @@ "label": "Total Penalty Paid", "options": "Company:company:default_currency", "read_only": 1 + }, + { + "depends_on": "eval:doc.repay_from_salary", + "fieldname": "payroll_payable_account", + "fieldtype": "Link", + "label": "Payroll Payable Account", + "mandatory_depends_on": "eval:doc.repay_from_salary", + "options": "Account" + }, + { + "default": "0", + "fetch_from": "against_loan.repay_from_salary", + "fieldname": "repay_from_salary", + "fieldtype": "Check", + "label": "Repay From Salary" } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-04-19 18:10:00.935364", + "modified": "2022-01-06 01:51:06.707782", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Repayment", + "naming_rule": "Expression (old style)", "owner": "Administrator", "permissions": [ { @@ -287,5 +305,6 @@ ], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index 5922e4f902..8ffaf63237 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -241,74 +241,79 @@ class LoanRepayment(AccountsController): else: remarks = _("Repayment against Loan: ") + self.against_loan - if not loan_details.repay_from_salary: - if self.total_penalty_paid: - gle_map.append( - self.get_gl_dict({ - "account": loan_details.loan_account, - "against": loan_details.payment_account, - "debit": self.total_penalty_paid, - "debit_in_account_currency": self.total_penalty_paid, - "against_voucher_type": "Loan", - "against_voucher": self.against_loan, - "remarks": _("Penalty against loan:") + self.against_loan, - "cost_center": self.cost_center, - "party_type": self.applicant_type, - "party": self.applicant, - "posting_date": getdate(self.posting_date) - }) - ) - - gle_map.append( - self.get_gl_dict({ - "account": loan_details.penalty_income_account, - "against": loan_details.payment_account, - "credit": self.total_penalty_paid, - "credit_in_account_currency": self.total_penalty_paid, - "against_voucher_type": "Loan", - "against_voucher": self.against_loan, - "remarks": _("Penalty against loan:") + self.against_loan, - "cost_center": self.cost_center, - "posting_date": getdate(self.posting_date) - }) - ) - - gle_map.append( - self.get_gl_dict({ - "account": loan_details.payment_account, - "against": loan_details.loan_account + ", " + loan_details.interest_income_account - + ", " + loan_details.penalty_income_account, - "debit": self.amount_paid, - "debit_in_account_currency": self.amount_paid, - "against_voucher_type": "Loan", - "against_voucher": self.against_loan, - "remarks": remarks, - "cost_center": self.cost_center, - "posting_date": getdate(self.posting_date) - }) - ) + if self.repay_from_salary: + payment_account = self.payroll_payable_account + else: + payment_account = loan_details.payment_account + if self.total_penalty_paid: gle_map.append( self.get_gl_dict({ "account": loan_details.loan_account, - "party_type": loan_details.applicant_type, - "party": loan_details.applicant, "against": loan_details.payment_account, - "credit": self.amount_paid, - "credit_in_account_currency": self.amount_paid, + "debit": self.total_penalty_paid, + "debit_in_account_currency": self.total_penalty_paid, "against_voucher_type": "Loan", "against_voucher": self.against_loan, - "remarks": remarks, + "remarks": _("Penalty against loan:") + self.against_loan, + "cost_center": self.cost_center, + "party_type": self.applicant_type, + "party": self.applicant, + "posting_date": getdate(self.posting_date) + }) + ) + + gle_map.append( + self.get_gl_dict({ + "account": loan_details.penalty_income_account, + "against": payment_account, + "credit": self.total_penalty_paid, + "credit_in_account_currency": self.total_penalty_paid, + "against_voucher_type": "Loan", + "against_voucher": self.against_loan, + "remarks": _("Penalty against loan:") + self.against_loan, "cost_center": self.cost_center, "posting_date": getdate(self.posting_date) }) ) - if gle_map: - make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj, merge_entries=False) + gle_map.append( + self.get_gl_dict({ + "account": payment_account, + "against": loan_details.loan_account + ", " + loan_details.interest_income_account + + ", " + loan_details.penalty_income_account, + "debit": self.amount_paid, + "debit_in_account_currency": self.amount_paid, + "against_voucher_type": "Loan", + "against_voucher": self.against_loan, + "remarks": remarks, + "cost_center": self.cost_center, + "posting_date": getdate(self.posting_date) + }) + ) + + gle_map.append( + self.get_gl_dict({ + "account": loan_details.loan_account, + "party_type": loan_details.applicant_type, + "party": loan_details.applicant, + "against": payment_account, + "credit": self.amount_paid, + "credit_in_account_currency": self.amount_paid, + "against_voucher_type": "Loan", + "against_voucher": self.against_loan, + "remarks": remarks, + "cost_center": self.cost_center, + "posting_date": getdate(self.posting_date) + }) + ) + + if gle_map: + make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj, merge_entries=False) def create_repayment_entry(loan, applicant, company, posting_date, loan_type, - payment_type, interest_payable, payable_principal_amount, amount_paid, penalty_amount=None): + payment_type, interest_payable, payable_principal_amount, amount_paid, penalty_amount=None, + payroll_payable_account=None): lr = frappe.get_doc({ "doctype": "Loan Repayment", @@ -321,7 +326,8 @@ def create_repayment_entry(loan, applicant, company, posting_date, loan_type, "interest_payable": interest_payable, "payable_principal_amount": payable_principal_amount, "amount_paid": amount_paid, - "loan_type": loan_type + "loan_type": loan_type, + "payroll_payable_account": payroll_payable_account }).insert() return lr diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index b035292c0b..0ee3c7c113 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -1138,15 +1138,17 @@ class SalarySlip(TransactionBase): }) def make_loan_repayment_entry(self): + payroll_payable_account = get_payroll_payable_account(self.company, self.payroll_entry) for loan in self.loans: - repayment_entry = create_repayment_entry(loan.loan, self.employee, - self.company, self.posting_date, loan.loan_type, "Regular Payment", loan.interest_amount, - loan.principal_amount, loan.total_payment) + if loan.total_payment: + repayment_entry = create_repayment_entry(loan.loan, self.employee, + self.company, self.posting_date, loan.loan_type, "Regular Payment", loan.interest_amount, + loan.principal_amount, loan.total_payment, payroll_payable_account=payroll_payable_account) - repayment_entry.save() - repayment_entry.submit() + repayment_entry.save() + repayment_entry.submit() - frappe.db.set_value("Salary Slip Loan", loan.name, "loan_repayment_entry", repayment_entry.name) + frappe.db.set_value("Salary Slip Loan", loan.name, "loan_repayment_entry", repayment_entry.name) def cancel_loan_repayment_entry(self): for loan in self.loans: @@ -1380,3 +1382,11 @@ def get_salary_component_data(component): ], as_dict=1, ) + +def get_payroll_payable_account(company, payroll_entry): + if payroll_entry: + payroll_payable_account = frappe.db.get_value('Payroll Entry', payroll_entry, 'payroll_payable_account') + else: + payroll_payable_account = frappe.db.get_value('Company', company, 'default_payroll_payable_account') + + return payroll_payable_account \ No newline at end of file diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py index 6e8fae0c1a..c0e005ad99 100644 --- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py @@ -380,7 +380,7 @@ class TestSalarySlip(unittest.TestCase): make_salary_structure("Test Loan Repayment Salary Structure", "Monthly", employee=applicant, currency='INR', payroll_period=payroll_period) - frappe.db.sql("delete from tabLoan") + frappe.db.sql("delete from tabLoan where applicant = 'test_loan_repayment_salary_slip@salary.com'") loan = create_loan(applicant, "Car Loan", 11000, "Repay Over Number of Periods", 20, posting_date=add_months(nowdate(), -1)) loan.repay_from_salary = 1 loan.submit() From fe929304cfb6b09a6c17a5dfc412829055844aa7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 5 Jan 2022 15:08:29 +0530 Subject: [PATCH 157/193] fix: Currency in KSA VAT report (cherry picked from commit 1d87e9d8f67e3611993d638f2d422fe66acf2c00) --- erpnext/regional/report/ksa_vat/ksa_vat.py | 33 +++++++++++++++------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/erpnext/regional/report/ksa_vat/ksa_vat.py b/erpnext/regional/report/ksa_vat/ksa_vat.py index b41b2b0428..cc26bd7a57 100644 --- a/erpnext/regional/report/ksa_vat/ksa_vat.py +++ b/erpnext/regional/report/ksa_vat/ksa_vat.py @@ -20,25 +20,35 @@ def get_columns(): "fieldname": "title", "label": _("Title"), "fieldtype": "Data", - "width": 300 + "width": 300, }, { "fieldname": "amount", "label": _("Amount (SAR)"), "fieldtype": "Currency", + "options": "currency", "width": 150, }, { "fieldname": "adjustment_amount", "label": _("Adjustment (SAR)"), "fieldtype": "Currency", + "options": "currency", "width": 150, }, { "fieldname": "vat_amount", "label": _("VAT Amount (SAR)"), "fieldtype": "Currency", + "options": "currency", "width": 150, + }, + { + "fieldname": "currency", + "label": _("Currency"), + "fieldtype": "Currency", + "width": 150, + "hidden": 1 } ] @@ -47,6 +57,8 @@ def get_data(filters): # Validate if vat settings exist company = filters.get('company') + company_currency = frappe.get_cached_value('Company', company, "default_currency") + if frappe.db.exists('KSA VAT Setting', company) is None: url = get_url_to_list('KSA VAT Setting') frappe.msgprint(_('Create KSA VAT Setting for this company').format(url)) @@ -55,7 +67,7 @@ def get_data(filters): ksa_vat_setting = frappe.get_doc('KSA VAT Setting', company) # Sales Heading - append_data(data, 'VAT on Sales', '', '', '') + append_data(data, 'VAT on Sales', '', '', '', company_currency) grand_total_taxable_amount = 0 grand_total_taxable_adjustment_amount = 0 @@ -67,7 +79,7 @@ def get_data(filters): # Adding results to data append_data(data, vat_setting.title, total_taxable_amount, - total_taxable_adjustment_amount, total_tax) + total_taxable_adjustment_amount, total_tax, company_currency) grand_total_taxable_amount += total_taxable_amount grand_total_taxable_adjustment_amount += total_taxable_adjustment_amount @@ -75,13 +87,13 @@ def get_data(filters): # Sales Grand Total append_data(data, 'Grand Total', grand_total_taxable_amount, - grand_total_taxable_adjustment_amount, grand_total_tax) + grand_total_taxable_adjustment_amount, grand_total_tax, company_currency) # Blank Line - append_data(data, '', '', '', '') + append_data(data, '', '', '', '', company_currency) # Purchase Heading - append_data(data, 'VAT on Purchases', '', '', '') + append_data(data, 'VAT on Purchases', '', '', '', company_currency) grand_total_taxable_amount = 0 grand_total_taxable_adjustment_amount = 0 @@ -93,7 +105,7 @@ def get_data(filters): # Adding results to data append_data(data, vat_setting.title, total_taxable_amount, - total_taxable_adjustment_amount, total_tax) + total_taxable_adjustment_amount, total_tax, company_currency) grand_total_taxable_amount += total_taxable_amount grand_total_taxable_adjustment_amount += total_taxable_adjustment_amount @@ -101,7 +113,7 @@ def get_data(filters): # Purchase Grand Total append_data(data, 'Grand Total', grand_total_taxable_amount, - grand_total_taxable_adjustment_amount, grand_total_tax) + grand_total_taxable_adjustment_amount, grand_total_tax, company_currency) return data @@ -147,9 +159,10 @@ def get_tax_data_for_each_vat_setting(vat_setting, filters, doctype): -def append_data(data, title, amount, adjustment_amount, vat_amount): +def append_data(data, title, amount, adjustment_amount, vat_amount, company_currency): """Returns data with appended value.""" - data.append({"title": _(title), "amount": amount, "adjustment_amount": adjustment_amount, "vat_amount": vat_amount}) + data.append({"title": _(title), "amount": amount, "adjustment_amount": adjustment_amount, "vat_amount": vat_amount, + "currency": company_currency}) def get_tax_amount(item_code, account_head, doctype, parent): if doctype == 'Sales Invoice': From 24fc487dd8c2516908d658a52153328175ec8918 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 6 Jan 2022 17:03:34 +0530 Subject: [PATCH 158/193] fix: incorrect serial no valuation report showing cancelled entries (#29172) --- .../incorrect_serial_no_valuation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py index d452ffd913..be8597dfed 100644 --- a/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py +++ b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py @@ -73,7 +73,7 @@ def get_stock_ledger_entries(report_filters): fields = ['name', 'voucher_type', 'voucher_no', 'item_code', 'serial_no as serial_nos', 'actual_qty', 'posting_date', 'posting_time', 'company', 'warehouse', '(stock_value_difference / actual_qty) as valuation_rate'] - filters = {'serial_no': ("is", "set")} + filters = {'serial_no': ("is", "set"), "is_cancelled": 0} if report_filters.get('item_code'): filters['item_code'] = report_filters.get('item_code') From 9d3a5c3184ccab1500bdbb31bb14cc5b4f56dd30 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 6 Jan 2022 18:58:49 +0530 Subject: [PATCH 159/193] fix: Inconsistency in calculating outstanding amount --- .../doctype/purchase_invoice/purchase_invoice.py | 8 ++++---- .../accounts/doctype/sales_invoice/sales_invoice.py | 10 +++++----- erpnext/controllers/taxes_and_totals.py | 7 ++++--- erpnext/public/js/controllers/taxes_and_totals.js | 10 ++++++---- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index df957d261b..b3642181ac 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -505,11 +505,11 @@ class PurchaseInvoice(BuyingController): # Checked both rounding_adjustment and rounded_total # because rounded_total had value even before introcution of posting GLE based on rounded total grand_total = self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total + base_grand_total = flt(self.base_rounded_total if (self.base_rounding_adjustment and self.base_rounded_total) + else self.base_grand_total, self.precision("base_grand_total")) if grand_total and not self.is_internal_transfer(): # Did not use base_grand_total to book rounding loss gle - grand_total_in_company_currency = flt(grand_total * self.conversion_rate, - self.precision("grand_total")) gl_entries.append( self.get_gl_dict({ "account": self.credit_to, @@ -517,8 +517,8 @@ class PurchaseInvoice(BuyingController): "party": self.supplier, "due_date": self.due_date, "against": self.against_expense_account, - "credit": grand_total_in_company_currency, - "credit_in_account_currency": grand_total_in_company_currency \ + "credit": base_grand_total, + "credit_in_account_currency": base_grand_total \ if self.party_account_currency==self.company_currency else grand_total, "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 321b45323f..98bc9539c2 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -862,11 +862,11 @@ class SalesInvoice(SellingController): # Checked both rounding_adjustment and rounded_total # because rounded_total had value even before introcution of posting GLE based on rounded total grand_total = self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total + base_grand_total = flt(self.base_rounded_total if (self.base_rounding_adjustment and self.base_rounded_total) + else self.base_grand_total, self.precision("base_grand_total")) + if grand_total and not self.is_internal_transfer(): # Didnot use base_grand_total to book rounding loss gle - grand_total_in_company_currency = flt(grand_total * self.conversion_rate, - self.precision("grand_total")) - gl_entries.append( self.get_gl_dict({ "account": self.debit_to, @@ -874,8 +874,8 @@ class SalesInvoice(SellingController): "party": self.customer, "due_date": self.due_date, "against": self.against_income_account, - "debit": grand_total_in_company_currency, - "debit_in_account_currency": grand_total_in_company_currency \ + "debit": base_grand_total, + "debit_in_account_currency": base_grand_total \ if self.party_account_currency==self.company_currency else grand_total, "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 746c6fd9a4..987fd31260 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -594,13 +594,14 @@ class calculate_taxes_and_totals(object): if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]: grand_total = self.doc.rounded_total or self.doc.grand_total + base_grand_total = self.doc.base_rounded_total or self.doc.base_grand_total + if self.doc.party_account_currency == self.doc.currency: total_amount_to_pay = flt(grand_total - self.doc.total_advance - flt(self.doc.write_off_amount), self.doc.precision("grand_total")) else: - total_amount_to_pay = flt(flt(grand_total * - self.doc.conversion_rate, self.doc.precision("grand_total")) - self.doc.total_advance - - flt(self.doc.base_write_off_amount), self.doc.precision("grand_total")) + total_amount_to_pay = flt(flt(base_grand_total, self.doc.precision("base_grand_total")) - self.doc.total_advance + - flt(self.doc.base_write_off_amount), self.doc.precision("base_grand_total")) self.doc.round_floats_in(self.doc, ["paid_amount"]) change_amount = 0 diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 7c1c8c7e46..ff56be9a73 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -710,14 +710,15 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { frappe.model.round_floats_in(this.frm.doc, ["grand_total", "total_advance", "write_off_amount"]); if(in_list(["Sales Invoice", "POS Invoice", "Purchase Invoice"], this.frm.doc.doctype)) { - var grand_total = this.frm.doc.rounded_total || this.frm.doc.grand_total; + let grand_total = this.frm.doc.rounded_total || this.frm.doc.grand_total; + let base_grand_total = this.frm.doc.base_rounded_total || this.frm.doc.base_grand_total; if(this.frm.doc.party_account_currency == this.frm.doc.currency) { var total_amount_to_pay = flt((grand_total - this.frm.doc.total_advance - this.frm.doc.write_off_amount), precision("grand_total")); } else { var total_amount_to_pay = flt( - (flt(grand_total*this.frm.doc.conversion_rate, precision("grand_total")) + (flt(base_grand_total, precision("base_grand_total")) - this.frm.doc.total_advance - this.frm.doc.base_write_off_amount), precision("base_grand_total") ); @@ -748,14 +749,15 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { } set_total_amount_to_default_mop() { - var grand_total = this.frm.doc.rounded_total || this.frm.doc.grand_total; + let grand_total = this.frm.doc.rounded_total || this.frm.doc.grand_total; + let base_grand_total = this.frm.doc.base_rounded_total || this.frm.doc.base_grand_total; if(this.frm.doc.party_account_currency == this.frm.doc.currency) { var total_amount_to_pay = flt((grand_total - this.frm.doc.total_advance - this.frm.doc.write_off_amount), precision("grand_total")); } else { var total_amount_to_pay = flt( - (flt(grand_total*this.frm.doc.conversion_rate, precision("grand_total")) + (flt(base_grand_total, precision("base_grand_total")) - this.frm.doc.total_advance - this.frm.doc.base_write_off_amount), precision("base_grand_total") ); From bf5930c4bb617f1c518efec1cbc7410956125129 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 4 Jan 2022 17:53:08 +0530 Subject: [PATCH 160/193] fix: Show GL balance in Accounts Receivable and payable summary (cherry picked from commit 1d270dca05501b5a15d19e3270188029b48eafde) # Conflicts: # erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py --- .../accounts_receivable_summary.js | 5 ++++ .../accounts_receivable_summary.py | 24 +++++++++++++++++++ 2 files changed, 29 insertions(+) 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 305cddb102..715cd6476e 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js @@ -117,6 +117,11 @@ frappe.query_reports["Accounts Receivable Summary"] = { "label": __("Show Future Payments"), "fieldtype": "Check", }, + { + "fieldname":"show_gl_balance", + "label": __("Show GL Balance"), + "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 3c94629203..7b69d3e958 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py @@ -4,7 +4,12 @@ import frappe from frappe import _, scrub +<<<<<<< HEAD from frappe.utils import cint +======= +from frappe.utils import cint, flt +from six import iteritems +>>>>>>> 1d270dca05 (fix: Show GL balance in Accounts Receivable and payable summary) from erpnext.accounts.party import get_partywise_advanced_payment_amount from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport @@ -36,7 +41,14 @@ class AccountsReceivableSummary(ReceivablePayableReport): party_advance_amount = get_partywise_advanced_payment_amount(self.party_type, self.filters.report_date, self.filters.show_future_payments, self.filters.company) or {} +<<<<<<< HEAD for party, party_dict in self.party_total.items(): +======= + if self.filters.show_gl_balance: + gl_balance_map = get_gl_balance(self.filters.report_date) + + for party, party_dict in iteritems(self.party_total): +>>>>>>> 1d270dca05 (fix: Show GL balance in Accounts Receivable and payable summary) if party_dict.outstanding == 0: continue @@ -55,6 +67,10 @@ class AccountsReceivableSummary(ReceivablePayableReport): # but in summary report advance shown in separate column row.paid -= row.advance + if self.filters.show_gl_balance: + row.gl_balance = gl_balance_map.get(party) + row.diff = flt(row.outstanding) - flt(row.gl_balance) + self.data.append(row) def get_party_total(self, args): @@ -114,6 +130,10 @@ class AccountsReceivableSummary(ReceivablePayableReport): self.add_column(_(credit_debit_label), fieldname='credit_note') self.add_column(_('Outstanding Amount'), fieldname='outstanding') + if self.filters.show_gl_balance: + self.add_column(_('GL Balance'), fieldname='gl_balance') + self.add_column(_('Difference'), fieldname='diff') + self.setup_ageing_columns() if self.party_type == "Customer": @@ -140,3 +160,7 @@ class AccountsReceivableSummary(ReceivablePayableReport): # Add column for total due amount self.add_column(label="Total Amount Due", fieldname='total_due') + +def get_gl_balance(report_date): + return frappe._dict(frappe.db.get_all("GL Entry", fields=['party', 'sum(debit - credit)'], + filters={'posting_date': ("<=", report_date), 'is_cancelled': 0}, group_by='party', as_list=1)) From fe4d7f86ee7384e794c54b32e5b5f4415654f02a Mon Sep 17 00:00:00 2001 From: ruthra Date: Fri, 7 Jan 2022 16:30:56 +0530 Subject: [PATCH 161/193] fix: deferred report division by zero exception --- .../deferred_revenue_and_expense.py | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py index a4842c1844..3a51db8a97 100644 --- a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py +++ b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py @@ -121,20 +121,21 @@ class Deferred_Item(object): """ simulate future posting by creating dummy gl entries. starts from the last posting date. """ - if add_days(self.last_entry_date, 1) < self.period_list[-1].to_date: - self.estimate_for_period_list = get_period_list( - self.filters.from_fiscal_year, - self.filters.to_fiscal_year, - add_days(self.last_entry_date, 1), - self.period_list[-1].to_date, - "Date Range", - "Monthly", - company=self.filters.company, - ) - for period in self.estimate_for_period_list: - amount = self.calculate_amount(period.from_date, period.to_date) - gle = self.make_dummy_gle(period.key, period.to_date, amount) - self.gle_entries.append(gle) + if self.service_start_date != self.service_end_date: + if add_days(self.last_entry_date, 1) < self.period_list[-1].to_date: + self.estimate_for_period_list = get_period_list( + self.filters.from_fiscal_year, + self.filters.to_fiscal_year, + add_days(self.last_entry_date, 1), + self.period_list[-1].to_date, + "Date Range", + "Monthly", + company=self.filters.company, + ) + for period in self.estimate_for_period_list: + amount = self.calculate_amount(period.from_date, period.to_date) + gle = self.make_dummy_gle(period.key, period.to_date, amount) + self.gle_entries.append(gle) def calculate_item_revenue_expense_for_period(self): """ From 00055bb1596a3de699981c3f93fc0e88b950688b Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 7 Jan 2022 18:11:38 +0530 Subject: [PATCH 162/193] fix: Conflicts --- .../accounts_receivable_summary.py | 9 --------- 1 file changed, 9 deletions(-) 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 7b69d3e958..14322db074 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py @@ -4,12 +4,7 @@ import frappe from frappe import _, scrub -<<<<<<< HEAD -from frappe.utils import cint -======= from frappe.utils import cint, flt -from six import iteritems ->>>>>>> 1d270dca05 (fix: Show GL balance in Accounts Receivable and payable summary) from erpnext.accounts.party import get_partywise_advanced_payment_amount from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport @@ -41,14 +36,10 @@ class AccountsReceivableSummary(ReceivablePayableReport): party_advance_amount = get_partywise_advanced_payment_amount(self.party_type, self.filters.report_date, self.filters.show_future_payments, self.filters.company) or {} -<<<<<<< HEAD - for party, party_dict in self.party_total.items(): -======= if self.filters.show_gl_balance: gl_balance_map = get_gl_balance(self.filters.report_date) for party, party_dict in iteritems(self.party_total): ->>>>>>> 1d270dca05 (fix: Show GL balance in Accounts Receivable and payable summary) if party_dict.outstanding == 0: continue From 2de51222623f85395e57966f655d1b76d5503257 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 7 Jan 2022 18:30:19 +0530 Subject: [PATCH 163/193] fix: Add iteritems back --- .../accounts_receivable_summary/accounts_receivable_summary.py | 1 + 1 file changed, 1 insertion(+) 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 14322db074..4559fa94a4 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py @@ -5,6 +5,7 @@ import frappe from frappe import _, scrub from frappe.utils import cint, flt +from six import iteritems from erpnext.accounts.party import get_partywise_advanced_payment_amount from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport From 25c875e4476e01faa80f9f8195db5138e18b664c Mon Sep 17 00:00:00 2001 From: Shadrak Gurupnor <30501401+shadrak98@users.noreply.github.com> Date: Fri, 7 Jan 2022 18:54:50 +0530 Subject: [PATCH 164/193] chore: remove framework patch for custom fields (#29117) * chore: patches were breaking during migration * fix: rewrote the query in query builder * chore: removed patch from patches.txt --- erpnext/patches.txt | 1 - .../v13_0/validate_options_for_data_field.py | 26 ------------------- 2 files changed, 27 deletions(-) delete mode 100644 erpnext/patches/v13_0/validate_options_for_data_field.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 268db40a8e..99741eb078 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -165,7 +165,6 @@ erpnext.patches.v12_0.set_updated_purpose_in_pick_list erpnext.patches.v12_0.set_default_payroll_based_on 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.validate_options_for_data_field erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123 erpnext.patches.v12_0.fix_quotation_expired_status erpnext.patches.v12_0.rename_pos_closing_doctype diff --git a/erpnext/patches/v13_0/validate_options_for_data_field.py b/erpnext/patches/v13_0/validate_options_for_data_field.py deleted file mode 100644 index ad777b8586..0000000000 --- a/erpnext/patches/v13_0/validate_options_for_data_field.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 2021, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - - -import frappe -from frappe.model import data_field_options - - -def execute(): - - for field in frappe.get_all('Custom Field', - fields = ['name'], - filters = { - 'fieldtype': 'Data', - 'options': ['!=', None] - }): - - if field not in data_field_options: - frappe.db.sql(""" - UPDATE - `tabCustom Field` - SET - options=NULL - WHERE - name=%s - """, (field)) From 2fe919e746cd7b1294f305da83aef34672656e08 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 9 Jan 2022 20:04:21 +0530 Subject: [PATCH 165/193] chore: update CODEOWNERS --- CODEOWNERS | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index a4a14de1b8..bfc2601088 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -23,13 +23,13 @@ erpnext/stock/ @marination @rohitwaghchaure @ankush erpnext/crm/ @ruchamahabal @pateljannat erpnext/education/ @ruchamahabal @pateljannat -erpnext/healthcare/ @ruchamahabal @pateljannat @chillaranand erpnext/hr/ @ruchamahabal @pateljannat -erpnext/non_profit/ @ruchamahabal erpnext/payroll @ruchamahabal @pateljannat erpnext/projects/ @ruchamahabal @pateljannat -erpnext/controllers @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure @marination +erpnext/controllers/ @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure @marination @ankush +erpnext/patches/ @deepeshgarg007 @nextchamp-saqib @marination @ankush +erpnext/public/ @nextchamp-saqib @marination -.github/ @surajshetty3416 @ankush +.github/ @ankush requirements.txt @gavindsouza From 23b0a3f54643c77be52d81b9d1cd0760550849f2 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 9 Jan 2022 20:04:35 +0530 Subject: [PATCH 166/193] ci: add auto labeller workflow --- .github/labeler.yml | 55 ++++++++++++++++++++++++++++++++++ .github/workflows/labeller.yml | 12 ++++++++ 2 files changed, 67 insertions(+) create mode 100644 .github/labeler.yml create mode 100644 .github/workflows/labeller.yml diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000000..19d08aeb8b --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,55 @@ +accounts: +- 'erpnext/accounts/*' +- 'erpnext/controllers/accounts_controller.py' +- 'erpnext/controllers/taxes_and_totals.py' + +stock: +- 'erpnext/stock/*' +- 'erpnext/controllers/stock_controller.py' +- 'erpnext/controllers/item_variant.py' + +assets: +- 'erpnext/assets/*' + +regional: +- 'erpnext/regional/*' + +selling: +- 'erpnext/selling/*' +- 'erpnext/controllers/selling_controller.py' + +buying: +- 'erpnext/buying/*' +- 'erpnext/controllers/buying_controller.py' + +support: +- 'erpnext/support/*' + +POS: +- 'pos*' + +ecommerce: +- 'erpnext/e_commerce/*' + +maintenance: +- 'erpnext/maintenance/*' + +manufacturing: +- 'erpnext/manufacturing/*' + +crm: +- 'erpnext/crm/*' + +HR: +- 'erpnext/hr/*' + +payroll: +- 'erpnext/payroll*' + +projects: +- 'erpnext/projects/*' + +# Any python files modifed but no test files modified +needs-tests: +- any: ['**/*.py'] +- all: ['!**/test*.py'] diff --git a/.github/workflows/labeller.yml b/.github/workflows/labeller.yml new file mode 100644 index 0000000000..0e112f97ac --- /dev/null +++ b/.github/workflows/labeller.yml @@ -0,0 +1,12 @@ +name: "Pull Request Labeler" +on: +- pull_request_target + +jobs: + triage: + runs-on: ubuntu-latest + steps: + - uses: actions/labeler@v3 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" + sync-labels: true From 55d00a3cf6d9cb0239176bb7ca9c4cc890dd61f2 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 9 Jan 2022 20:18:42 +0530 Subject: [PATCH 167/193] ci: fix auto label behaviour --- .github/labeler.yml | 4 ++-- .github/workflows/labeller.yml | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 19d08aeb8b..fc3f06da92 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -51,5 +51,5 @@ projects: # Any python files modifed but no test files modified needs-tests: -- any: ['**/*.py'] -- all: ['!**/test*.py'] +- any: ['erpnext/**/*.py'] + all: ['!erpnext/**/test*.py'] diff --git a/.github/workflows/labeller.yml b/.github/workflows/labeller.yml index 0e112f97ac..331ddc9b80 100644 --- a/.github/workflows/labeller.yml +++ b/.github/workflows/labeller.yml @@ -9,4 +9,3 @@ jobs: - uses: actions/labeler@v3 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" - sync-labels: true From 5b67dbf8d026a95b5a049d5bc97e11bd2c31ff66 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 9 Jan 2022 20:27:18 +0530 Subject: [PATCH 168/193] ci: only run label action once --- .github/workflows/labeller.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/labeller.yml b/.github/workflows/labeller.yml index 331ddc9b80..a774400611 100644 --- a/.github/workflows/labeller.yml +++ b/.github/workflows/labeller.yml @@ -1,6 +1,7 @@ name: "Pull Request Labeler" on: -- pull_request_target + pull_request_target: + types: [opened, reopened] jobs: triage: From 31123436fbe223b0116922097108fa444e5092d3 Mon Sep 17 00:00:00 2001 From: Conn Campbell Date: Sun, 9 Jan 2022 21:58:10 -0700 Subject: [PATCH 169/193] fix: Task Depends on not removed from Gantt chart (#28309) Co-authored-by: conncampbell --- erpnext/projects/doctype/task/task.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index 9b1ea043be..8fa0538f36 100755 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -102,7 +102,7 @@ class Task(NestedSet): frappe.throw(_("Completed On cannot be greater than Today")) def update_depends_on(self): - depends_on_tasks = self.depends_on_tasks or "" + depends_on_tasks = "" for d in self.depends_on: if d.task and d.task not in depends_on_tasks: depends_on_tasks += d.task + "," From 0faa116f9799f6d921ce8868a8f8eac1756ae008 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 4 Jan 2022 16:36:08 +0530 Subject: [PATCH 170/193] fix(patch): serial no whitespace trimming old data can contain trailing/leading whitespace which doesn't work well with code to find last SLE for serial no. --- erpnext/patches.txt | 1 + .../v13_0/trim_whitespace_from_serial_nos.py | 61 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 erpnext/patches/v13_0/trim_whitespace_from_serial_nos.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 99741eb078..c5e4f7e8a9 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -279,6 +279,7 @@ erpnext.patches.v13_0.add_custom_field_for_south_africa #2 erpnext.patches.v13_0.update_recipient_email_digest erpnext.patches.v13_0.shopify_deprecation_warning erpnext.patches.v13_0.remove_bad_selling_defaults +erpnext.patches.v13_0.trim_whitespace_from_serial_nos erpnext.patches.v13_0.migrate_stripe_api erpnext.patches.v13_0.reset_clearance_date_for_intracompany_payment_entries erpnext.patches.v13_0.einvoicing_deprecation_warning diff --git a/erpnext/patches/v13_0/trim_whitespace_from_serial_nos.py b/erpnext/patches/v13_0/trim_whitespace_from_serial_nos.py new file mode 100644 index 0000000000..4f112550c5 --- /dev/null +++ b/erpnext/patches/v13_0/trim_whitespace_from_serial_nos.py @@ -0,0 +1,61 @@ +import frappe + +from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos + + +def execute(): + broken_sles = frappe.db.sql(""" + select name, serial_no + from `tabStock Ledger Entry` + where + is_cancelled = 0 + and (serial_no like %s or serial_no like %s or serial_no like %s or serial_no like %s) + """, + ( + " %", # leading whitespace + "% ", # trailing whitespace + "%\n %", # leading whitespace on newline + "% \n%", # trailing whitespace on newline + ), + as_dict=True, + ) + + frappe.db.MAX_WRITES_PER_TRANSACTION += len(broken_sles) + + if not broken_sles: + return + + broken_serial_nos = set() + + for sle in broken_sles: + serial_no_list = get_serial_nos(sle.serial_no) + correct_sr_no = "\n".join(serial_no_list) + + if correct_sr_no == sle.serial_no: + continue + + frappe.db.set_value("Stock Ledger Entry", sle.name, "serial_no", correct_sr_no, update_modified=False) + broken_serial_nos.update(serial_no_list) + + if not broken_serial_nos: + return + + broken_sr_no_records = [sr[0] for sr in frappe.db.sql(""" + select name + from `tabSerial No` + where status='Active' + and coalesce(purchase_document_type, '') = '' + and name in %s """, (list(broken_serial_nos),) + )] + + frappe.db.MAX_WRITES_PER_TRANSACTION += len(broken_sr_no_records) + + patch_savepoint = "serial_no_patch" + for serial_no in broken_sr_no_records: + try: + frappe.db.savepoint(patch_savepoint) + sn = frappe.get_doc("Serial No", serial_no) + sn.update_serial_no_reference() + sn.db_update() + except Exception: + frappe.db.rollback(save_point=patch_savepoint) From cbaa8fdade4aad306887b23cef9bfeaa17ff07c0 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 10 Jan 2022 16:09:43 +0530 Subject: [PATCH 171/193] refactor: convert query to ORM --- .../v13_0/trim_whitespace_from_serial_nos.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/erpnext/patches/v13_0/trim_whitespace_from_serial_nos.py b/erpnext/patches/v13_0/trim_whitespace_from_serial_nos.py index 4f112550c5..8a9633d896 100644 --- a/erpnext/patches/v13_0/trim_whitespace_from_serial_nos.py +++ b/erpnext/patches/v13_0/trim_whitespace_from_serial_nos.py @@ -27,6 +27,7 @@ def execute(): broken_serial_nos = set() + # patch SLEs for sle in broken_sles: serial_no_list = get_serial_nos(sle.serial_no) correct_sr_no = "\n".join(serial_no_list) @@ -40,13 +41,16 @@ def execute(): if not broken_serial_nos: return - broken_sr_no_records = [sr[0] for sr in frappe.db.sql(""" - select name - from `tabSerial No` - where status='Active' - and coalesce(purchase_document_type, '') = '' - and name in %s """, (list(broken_serial_nos),) - )] + # Patch serial No documents if they don't have purchase info + # Purchase info is used for fetching incoming rate + broken_sr_no_records = frappe.get_list("Serial No", + filters={ + "status":"Active", + "name": ("in", broken_serial_nos), + "purchase_document_type": ("is", "not set") + }, + pluck="name", + ) frappe.db.MAX_WRITES_PER_TRANSACTION += len(broken_sr_no_records) From e2dab6f421c55f1a8aea1439a83c1fadedae8369 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 10 Jan 2022 17:31:38 +0530 Subject: [PATCH 172/193] fix: Cleanup and fixes --- .../currency_exchange_settings.js | 7 +-- .../currency_exchange_settings.json | 27 ++++++++- .../currency_exchange_settings.py | 59 ++++++++++++++----- erpnext/patches.txt | 3 +- .../v13_0/update_exchange_rate_settings.py | 5 ++ erpnext/setup/install.py | 17 ++++-- erpnext/setup/utils.py | 2 +- 7 files changed, 92 insertions(+), 28 deletions(-) create mode 100644 erpnext/patches/v13_0/update_exchange_rate_settings.py diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js index a3a6561d35..6c40f2bec0 100644 --- a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js +++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js @@ -1,9 +1,9 @@ -// Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and contributors +// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt frappe.ui.form.on('Currency Exchange Settings', { service_provider: function(frm) { - if (frm.doc.service_provider == "Exchangerate.host") { + if (frm.doc.service_provider == "exchangerate.host") { let result = ['result']; let params = { date: '{transaction_date}', @@ -11,7 +11,7 @@ frappe.ui.form.on('Currency Exchange Settings', { to: '{to_currency}' }; add_param(frm, "https://api.exchangerate.host/convert", params, result); - } else if (frm.doc.service_provider == "Frankfurter.app") { + } else if (frm.doc.service_provider == "frankfurter.app") { let result = ['rates', '{to_currency}']; let params = { base: '{from_currency}', @@ -42,5 +42,4 @@ function add_param(frm, api, params, result) { }); frm.refresh_fields(); - frm.save(); } diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json index 091102ce47..7921fcc2b9 100644 --- a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json +++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json @@ -1,6 +1,6 @@ { "actions": [], - "creation": "2021-09-02 14:53:50.923529", + "creation": "2022-01-10 13:03:26.237081", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", @@ -75,14 +75,14 @@ "fieldname": "service_provider", "fieldtype": "Select", "label": "Service Provider", - "options": "Exchangerate.host\nFrankfurter.app\nCustom", + "options": "frankfurter.app\nexchangerate.host\nCustom", "reqd": 1 } ], "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-11-04 10:27:09.332768", + "modified": "2022-01-10 15:51:14.521174", "modified_by": "Administrator", "module": "Accounts", "name": "Currency Exchange Settings", @@ -97,9 +97,30 @@ "role": "System Manager", "share": 1, "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "Accounts Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "Accounts User", + "share": 1, + "write": 1 } ], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py index e515542f1d..c55e28b06c 100644 --- a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py +++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py @@ -1,7 +1,8 @@ -# Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and contributors +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt import frappe +import requests from frappe import _ from frappe.model.document import Document from frappe.utils import nowdate @@ -9,38 +10,68 @@ from frappe.utils import nowdate class CurrencyExchangeSettings(Document): def validate(self): - transaction_date = nowdate() - from_currency = 'USD' - to_currency = 'INR' + self.set_parameters_and_result() + response, value = self.validate_parameters() + self.validate_result(response, value) + + def set_parameters_and_result(self): + if self.service_provider == 'exchangerate.host': + self.set('result_key', []) + self.set('req_params', []) + + self.api_endpoint = "https://api.exchangerate.host/convert" + self.append('result_key', {'key': 'result'}) + self.append('req_params', {'key': 'date', 'value': '{transaction_date}'}) + self.append('req_params', {'key': 'from', 'value': '{from_currency}'}) + self.append('req_params', {'key': 'to', 'value': '{to_currency}'}) + elif self.service_provider == 'frankfurter.app': + self.set('result_key', []) + self.set('req_params', []) + + self.api_endpoint = "https://frankfurter.app/{transaction_date}" + self.append('result_key', {'key': 'rates'}) + self.append('result_key', {'key': '{to_currency}'}) + self.append('req_params', {'key': 'base', 'value': '{from_currency}'}) + self.append('req_params', {'key': 'symbols', 'value': '{to_currency}'}) + + def validate_parameters(self): params = {} + for row in self.req_params: params[row.key] = row.value.format( - transaction_date=transaction_date, - to_currency=to_currency, - from_currency=from_currency + transaction_date=nowdate(), + to_currency='INR', + from_currency='USD' ) - import requests + api_url = self.api_endpoint.format( - transaction_date=transaction_date, - to_currency=to_currency, - from_currency=from_currency + transaction_date=nowdate(), + to_currency='INR', + from_currency='USD' ) + try: response = requests.get(api_url, params=params) except requests.exceptions.RequestException as e: frappe.throw("Error: " + str(e)) + response.raise_for_status() value = response.json() + + return response, value + + def validate_result(self, response, value): try: for key in self.result_key: value = value[str(key.key).format( - transaction_date=transaction_date, - to_currency=to_currency, - from_currency=from_currency + transaction_date=nowdate(), + to_currency='INR', + from_currency='USD' )] except Exception: frappe.throw("Invalid result key. Response: " + response.text) if not isinstance(value, (int, float)): frappe.throw(_("Returned exchange rate is neither integer not float.")) + self.url = response.url frappe.msgprint("Exchange rate of USD to INR is " + str(value)) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d9cedab52a..7f24273a6c 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -317,4 +317,5 @@ erpnext.patches.v14_0.rename_ongoing_status_in_sla_documents erpnext.patches.v14_0.migrate_crm_settings erpnext.patches.v13_0.rename_ksa_qr_field erpnext.patches.v13_0.disable_ksa_print_format_for_others # 16-12-2021 -erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template \ No newline at end of file +erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template +erpnext.patches.v13_0.update_exchange_rate_settings \ No newline at end of file diff --git a/erpnext/patches/v13_0/update_exchange_rate_settings.py b/erpnext/patches/v13_0/update_exchange_rate_settings.py new file mode 100644 index 0000000000..6af93dcba1 --- /dev/null +++ b/erpnext/patches/v13_0/update_exchange_rate_settings.py @@ -0,0 +1,5 @@ +from erpnext.setup.install import setup_currency_exchange + + +def execute(): + setup_currency_exchange() \ No newline at end of file diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index da39776333..bafaab814b 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -59,13 +59,20 @@ def set_single_defaults(): pass frappe.db.set_default("date_format", "dd-mm-yyyy") + + setup_currency_exchange() + +def setup_currency_exchange(): ces = frappe.get_single('Currency Exchange Settings') try: - ces.api_endpoint = "https://api.exchangerate.host/convert" - ces.append('result_key', {'key': 'result'}) - ces.append('req_params', {'key': 'date', 'value': '{transaction_date}'}) - ces.append('req_params', {'key': 'from', 'value': '{from_currency}'}) - ces.append('req_params', {'key': 'to', 'value': '{to_currency}'}) + ces.set('result_key', []) + ces.set('req_params', []) + + ces.api_endpoint = "https://frankfurter.app/{transaction_date}" + ces.append('result_key', {'key': 'rates'}) + ces.append('result_key', {'key': '{to_currency}'}) + ces.append('req_params', {'key': 'base', 'value': '{from_currency}'}) + ces.append('req_params', {'key': 'symbols', 'value': '{to_currency}'}) ces.save() except frappe.ValidationError: pass diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index bfa26f29d9..4441bb9562 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -100,7 +100,7 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No if not value: import requests - settings = frappe.get_single('Currency Exchange Settings') + settings = frappe.get_cached_doc('Currency Exchange Settings') req_params = { "transaction_date": transaction_date, "from_currency": from_currency, From e5b649b588edaf177f7b4ca280e65137a4d45172 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 10 Jan 2022 18:24:08 +0530 Subject: [PATCH 173/193] fix: pos invoices consolidation case with permlevel (#29011) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 545abf77e6..5062c1c807 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -651,7 +651,7 @@ "hide_seconds": 1, "label": "Ignore Pricing Rule", "no_copy": 1, - "permlevel": 1, + "permlevel": 0, "print_hide": 1 }, { @@ -2038,7 +2038,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2021-10-21 20:19:38.667508", + "modified": "2021-12-23 20:19:38.667508", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", From 2854f63cafe87bdb87f3d85c2f032ee8fdec0983 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 10 Jan 2022 13:51:06 +0530 Subject: [PATCH 174/193] fix!: drop python 3.7 support --- .github/workflows/docs-checker.yml | 2 +- .github/workflows/patch.yml | 2 +- .github/workflows/server-tests-mariadb.yml | 2 +- .github/workflows/server-tests-postgres.yml | 2 +- .github/workflows/ui-tests.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docs-checker.yml b/.github/workflows/docs-checker.yml index db46c5621b..b644568d5e 100644 --- a/.github/workflows/docs-checker.yml +++ b/.github/workflows/docs-checker.yml @@ -12,7 +12,7 @@ jobs: - name: 'Setup Environment' uses: actions/setup-python@v2 with: - python-version: 3.6 + python-version: 3.8 - name: 'Clone repo' uses: actions/checkout@v2 diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml index 33a28ac9bb..d05bbbec50 100644 --- a/.github/workflows/patch.yml +++ b/.github/workflows/patch.yml @@ -34,7 +34,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v2 with: - python-version: 3.7 + python-version: 3.8 - name: Setup Node uses: actions/setup-node@v2 diff --git a/.github/workflows/server-tests-mariadb.yml b/.github/workflows/server-tests-mariadb.yml index 186f95e6ec..7347a5856a 100644 --- a/.github/workflows/server-tests-mariadb.yml +++ b/.github/workflows/server-tests-mariadb.yml @@ -46,7 +46,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v2 with: - python-version: 3.7 + python-version: 3.8 - name: Setup Node uses: actions/setup-node@v2 diff --git a/.github/workflows/server-tests-postgres.yml b/.github/workflows/server-tests-postgres.yml index 3bbf6a91f5..77d3c1ae61 100644 --- a/.github/workflows/server-tests-postgres.yml +++ b/.github/workflows/server-tests-postgres.yml @@ -46,7 +46,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v2 with: - python-version: 3.7 + python-version: 3.8 - name: Setup Node uses: actions/setup-node@v2 diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index d765f0482c..ab6a53b5d9 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -36,7 +36,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v2 with: - python-version: 3.7 + python-version: 3.8 - uses: actions/setup-node@v2 with: From fab7a580569496ed88d74218acaf54d4fcd5e966 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 10 Jan 2022 14:19:29 +0530 Subject: [PATCH 175/193] chore: remove misleading comment --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index faefb77a9c..f447fac736 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ # frappe # https://github.com/frappe/frappe is installed during bench-init gocardless-pro~=1.22.0 -googlemaps # used in ERPNext, but dependency is defined in Frappe +googlemaps pandas~=1.1.5 plaid-python~=7.2.1 pycountry~=20.7.3 From 2d76c05175bf324da4ea24e1542a7dcb7d52c178 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 9 Jan 2022 18:41:33 +0530 Subject: [PATCH 176/193] refactor: unnecessary joining of lists --- erpnext/accounts/report/general_ledger/general_ledger.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 385c8b2b6e..7303bf5ef8 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -547,10 +547,7 @@ def get_columns(filters): "fieldname": "balance", "fieldtype": "Float", "width": 130 - } - ] - - columns.extend([ + }, { "label": _("Voucher Type"), "fieldname": "voucher_type", @@ -584,7 +581,7 @@ def get_columns(filters): "fieldname": "project", "width": 100 } - ]) + ] if filters.get("include_dimensions"): for dim in get_accounting_dimensions(as_list = False): From f195f803ff3226450cd1c0b739e4ef34114a6ffb Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 9 Jan 2022 19:23:27 +0530 Subject: [PATCH 177/193] test: account reports --- erpnext/accounts/test/test_reports.py | 48 +++++++++++++++++++++++++++ erpnext/tests/utils.py | 14 +++++--- 2 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 erpnext/accounts/test/test_reports.py diff --git a/erpnext/accounts/test/test_reports.py b/erpnext/accounts/test/test_reports.py new file mode 100644 index 0000000000..78c109ab94 --- /dev/null +++ b/erpnext/accounts/test/test_reports.py @@ -0,0 +1,48 @@ +import unittest +from typing import List, Tuple + +from erpnext.tests.utils import ReportFilters, ReportName, execute_script_report + +DEFAULT_FILTERS = { + "company": "_Test Company", + "from_date": "2010-01-01", + "to_date": "2030-01-01", + "period_start_date": "2010-01-01", + "period_end_date": "2030-01-01" +} + + +REPORT_FILTER_TEST_CASES: List[Tuple[ReportName, ReportFilters]] = [ + ("General Ledger", {"group_by": "Group by Voucher (Consolidated)"} ), + ("General Ledger", {"group_by": "Group by Voucher (Consolidated)", "include_dimensions": 1} ), + ("Accounts Payable", {"range1": 30, "range2": 60, "range3": 90, "range4": 120}), + ("Accounts Receivable", {"range1": 30, "range2": 60, "range3": 90, "range4": 120}), + ("Consolidated Financial Statement", {"report": "Balance Sheet"} ), + ("Consolidated Financial Statement", {"report": "Profit and Loss Statement"} ), + ("Consolidated Financial Statement", {"report": "Cash Flow"} ), + ("Gross Profit", {"group_by": "Invoice"}), + ("Gross Profit", {"group_by": "Item Code"}), + ("Gross Profit", {"group_by": "Item Group"}), + ("Gross Profit", {"group_by": "Customer"}), + ("Gross Profit", {"group_by": "Customer Group"}), + ("Item-wise Sales Register", {}), + ("Item-wise Purchase Register", {}), + ("Sales Register", {}), + ("Purchase Register", {}), + ("Tax Detail", {"mode": "run", "report_name": "Tax Detail"},), +] + +OPTIONAL_FILTERS = {} + + +class TestReports(unittest.TestCase): + def test_execute_all_accounts_reports(self): + """Test that all script report in stock modules are executable with supported filters""" + for report, filter in REPORT_FILTER_TEST_CASES: + execute_script_report( + report_name=report, + module="Accounts", + filters=filter, + default_filters=DEFAULT_FILTERS, + optional_filters=OPTIONAL_FILTERS if filter.get("_optional") else None, + ) diff --git a/erpnext/tests/utils.py b/erpnext/tests/utils.py index fbf25948a7..bc9f04e089 100644 --- a/erpnext/tests/utils.py +++ b/erpnext/tests/utils.py @@ -125,17 +125,23 @@ def execute_script_report( if default_filters is None: default_filters = {} + test_filters = [] report_execute_fn = frappe.get_attr(get_report_module_dotted_path(module, report_name) + ".execute") report_filters = frappe._dict(default_filters).copy().update(filters) - report_data = report_execute_fn(report_filters) + test_filters.append(report_filters) if optional_filters: for key, value in optional_filters.items(): - filter_with_optional_param = report_filters.copy().update({key: value}) - report_execute_fn(filter_with_optional_param) + test_filters.append(report_filters.copy().update({key: value})) + + for test_filter in test_filters: + try: + report_execute_fn(test_filter) + except Exception: + print(f"Report failed to execute with filters: {test_filter}") + raise - return report_data def timeout(seconds=30, error_message="Test timed out."): From ca5ea5f55faec323f3d0612de6fe25fe98965921 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 10 Jan 2022 21:28:29 +0530 Subject: [PATCH 178/193] fix: test cases --- .../currency_exchange_settings.py | 7 +++- .../v13_0/update_exchange_rate_settings.py | 5 +++ .../test_currency_exchange.py | 42 ++++++++++++++++++- 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py index c55e28b06c..e16ff3aa7e 100644 --- a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py +++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py @@ -35,8 +35,10 @@ class CurrencyExchangeSettings(Document): self.append('req_params', {'key': 'symbols', 'value': '{to_currency}'}) def validate_parameters(self): - params = {} + if frappe.flags.in_test: + return None, None + params = {} for row in self.req_params: params[row.key] = row.value.format( transaction_date=nowdate(), @@ -61,6 +63,9 @@ class CurrencyExchangeSettings(Document): return response, value def validate_result(self, response, value): + if frappe.flags.in_test: + return + try: for key in self.result_key: value = value[str(key.key).format( diff --git a/erpnext/patches/v13_0/update_exchange_rate_settings.py b/erpnext/patches/v13_0/update_exchange_rate_settings.py index 6af93dcba1..b7ec232bba 100644 --- a/erpnext/patches/v13_0/update_exchange_rate_settings.py +++ b/erpnext/patches/v13_0/update_exchange_rate_settings.py @@ -1,5 +1,10 @@ +import frappe + from erpnext.setup.install import setup_currency_exchange def execute(): + frappe.reload_doc("accounts", "doctype", "currency_exchange_settings") + frappe.reload_doc("accounts", "doctype", "currency_exchange_settings_result") + frappe.reload_doc("accounts", "doctype", "currency_exchange_settings_details") setup_currency_exchange() \ No newline at end of file diff --git a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py index 2b007e9efd..06a79b4102 100644 --- a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py +++ b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py @@ -62,8 +62,13 @@ def patched_requests_get(*args, **kwargs): if kwargs['params'].get('date') and kwargs['params'].get('from') and kwargs['params'].get('to'): if test_exchange_values.get(kwargs['params']['date']): return PatchResponse({'result': test_exchange_values[kwargs['params']['date']]}, 200) + elif args[0].startswith("https://frankfurter.app") and kwargs.get('params'): + if kwargs['params'].get('base') and kwargs['params'].get('symbols'): + date = args[0].replace("https://frankfurter.app/", "") + if test_exchange_values.get(date): + return PatchResponse({'rates': {kwargs['params'].get('symbols'): test_exchange_values.get(date)}}, 200) - return PatchResponse({'result': None}, 404) + return PatchResponse({'rates': None}, 404) @mock.patch('requests.get', side_effect=patched_requests_get) class TestCurrencyExchange(unittest.TestCase): @@ -102,6 +107,41 @@ class TestCurrencyExchange(unittest.TestCase): self.assertFalse(exchange_rate == 60) self.assertEqual(flt(exchange_rate, 3), 65.1) + def test_exchange_rate_via_exchangerate_host(self, mock_get): + save_new_records(test_records) + + # Update Currency Exchange Rate + settings = frappe.get_single("Currency Exchange Settings") + settings.service_provider = 'exchangerate.host' + settings.save() + + # Update exchange + frappe.db.set_value("Accounts Settings", None, "allow_stale", 1) + + # Start with allow_stale is True + exchange_rate = get_exchange_rate("USD", "INR", "2016-01-01", "for_buying") + self.assertEqual(flt(exchange_rate, 3), 60.0) + + exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15", "for_buying") + self.assertEqual(exchange_rate, 65.1) + + exchange_rate = get_exchange_rate("USD", "INR", "2016-01-30", "for_selling") + self.assertEqual(exchange_rate, 62.9) + + # Exchange rate as on 15th Dec, 2015 + self.clear_cache() + exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15", "for_selling") + self.assertFalse(exchange_rate == 60) + self.assertEqual(flt(exchange_rate, 3), 66.999) + + exchange_rate = get_exchange_rate("USD", "INR", "2016-01-20", "for_buying") + self.assertFalse(exchange_rate == 60) + self.assertEqual(flt(exchange_rate, 3), 65.1) + + settings = frappe.get_single("Currency Exchange Settings") + settings.service_provider = 'frankfurter.app' + settings.save() + def test_exchange_rate_strict(self, mock_get): # strict currency settings frappe.db.set_value("Accounts Settings", None, "allow_stale", 0) From c62083653b528fa5d3b93fbbe35d4a6a2c475bec Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 11 Jan 2022 12:45:40 +0530 Subject: [PATCH 179/193] fix(gl-report): group by cost center only if include_dimensions is checked (#28883) --- .../report/general_ledger/general_ledger.js | 2 +- .../report/general_ledger/general_ledger.py | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index b2968761c6..010284c2ea 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -167,7 +167,7 @@ frappe.query_reports["General Ledger"] = { "fieldname": "include_dimensions", "label": __("Consider Accounting Dimensions"), "fieldtype": "Check", - "default": 0 + "default": 1 }, { "fieldname": "show_opening_entries", diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 7303bf5ef8..7f27920547 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -448,9 +448,11 @@ def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map): elif group_by_voucher_consolidated: keylist = [gle.get("voucher_type"), gle.get("voucher_no"), gle.get("account")] - for dim in accounting_dimensions: - keylist.append(gle.get(dim)) - keylist.append(gle.get("cost_center")) + if filters.get("include_dimensions"): + for dim in accounting_dimensions: + keylist.append(gle.get(dim)) + keylist.append(gle.get("cost_center")) + key = tuple(keylist) if key not in consolidated_gle: consolidated_gle.setdefault(key, gle) @@ -591,14 +593,14 @@ def get_columns(filters): "fieldname": dim.fieldname, "width": 100 }) - - columns.extend([ - { + columns.append({ "label": _("Cost Center"), "options": "Cost Center", "fieldname": "cost_center", "width": 100 - }, + }) + + columns.extend([ { "label": _("Against Voucher Type"), "fieldname": "against_voucher_type", From 75c8b2556e2282f15bf0bc44f9bec3afad146215 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 11 Jan 2022 13:25:42 +0530 Subject: [PATCH 180/193] fix(pos): cannot ignore pricing rule for one particular invoice (#29222) --- .../doctype/pos_invoice/pos_invoice.py | 1 - .../doctype/pos_invoice/test_pos_invoice.py | 31 +++++++++++++++++++ .../doctype/pricing_rule/test_pricing_rule.py | 4 ++- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 11d59bcf70..134bccf3d1 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -353,7 +353,6 @@ class POSInvoice(SalesInvoice): if not for_validate and not self.customer: self.customer = profile.customer - self.ignore_pricing_rule = profile.ignore_pricing_rule self.account_for_change_amount = profile.get('account_for_change_amount') or self.account_for_change_amount self.set_warehouse = profile.get('warehouse') or self.set_warehouse diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py index 7d31e0aa19..56479a0b77 100644 --- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py @@ -556,6 +556,37 @@ class TestPOSInvoice(unittest.TestCase): batch.cancel() batch.delete() + def test_ignore_pricing_rule(self): + from erpnext.accounts.doctype.pricing_rule.test_pricing_rule import make_pricing_rule + + item_price = frappe.get_doc({ + 'doctype': 'Item Price', + 'item_code': '_Test Item', + 'price_list': '_Test Price List', + 'price_list_rate': '450', + }) + item_price.insert() + pr = make_pricing_rule(selling=1, priority=5, discount_percentage=10) + pr.save() + pos_inv = create_pos_invoice(qty=1, do_not_submit=1) + pos_inv.items[0].rate = 300 + pos_inv.save() + self.assertEquals(pos_inv.items[0].discount_percentage, 10) + # rate shouldn't change + self.assertEquals(pos_inv.items[0].rate, 405) + + pos_inv.ignore_pricing_rule = 1 + pos_inv.items[0].rate = 300 + pos_inv.save() + self.assertEquals(pos_inv.ignore_pricing_rule, 1) + # rate should change since pricing rules are ignored + self.assertEquals(pos_inv.items[0].rate, 300) + + item_price.delete() + pos_inv.delete() + pr.delete() + + def create_pos_invoice(**args): args = frappe._dict(args) pos_profile = None diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 314c89424b..5746a840f3 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -650,7 +650,7 @@ def make_pricing_rule(**args): "rate": args.rate or 0.0, "margin_rate_or_amount": args.margin_rate_or_amount or 0.0, "condition": args.condition or '', - "priority": 1, + "priority": args.priority or 1, "discount_amount": args.discount_amount or 0.0, "apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0 }) @@ -676,6 +676,8 @@ def make_pricing_rule(**args): if args.get(applicable_for): doc.db_set(applicable_for, args.get(applicable_for)) + return doc + def setup_pricing_rule_data(): if not frappe.db.exists('Campaign', '_Test Campaign'): frappe.get_doc({ From 754596dfc139a9890b1e446a4d2b2abfeab68449 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 11 Jan 2022 14:20:44 +0530 Subject: [PATCH 181/193] fix: "update cost" should ignore overridden routing times #29154 fix: "update cost" should ignore overridden routing times --- erpnext/manufacturing/doctype/bom/bom.py | 10 ---------- erpnext/manufacturing/doctype/routing/test_routing.py | 5 +++-- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index f82d9a0d55..5a60fb751d 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -530,16 +530,6 @@ class BOM(WebsiteGenerator): row.hour_rate = (hour_rate / flt(self.conversion_rate) if self.conversion_rate and hour_rate else hour_rate) - if self.routing: - time_in_mins = flt(frappe.db.get_value("BOM Operation", { - "workstation": row.workstation, - "operation": row.operation, - "parent": self.routing - }, ["time_in_mins"])) - - if time_in_mins: - row.time_in_mins = time_in_mins - if row.hour_rate and row.time_in_mins: row.base_hour_rate = flt(row.hour_rate) * flt(self.conversion_rate) row.operating_cost = flt(row.hour_rate) * flt(row.time_in_mins) / 60.0 diff --git a/erpnext/manufacturing/doctype/routing/test_routing.py b/erpnext/manufacturing/doctype/routing/test_routing.py index e90b0a7d6d..8bd60ea4ac 100644 --- a/erpnext/manufacturing/doctype/routing/test_routing.py +++ b/erpnext/manufacturing/doctype/routing/test_routing.py @@ -46,6 +46,7 @@ class TestRouting(ERPNextTestCase): wo_doc.delete() def test_update_bom_operation_time(self): + """Update cost shouldn't update routing times.""" operations = [ { "operation": "Test Operation A", @@ -85,8 +86,8 @@ class TestRouting(ERPNextTestCase): routing_doc.save() bom_doc.update_cost() bom_doc.reload() - self.assertEqual(bom_doc.operations[0].time_in_mins, 90) - self.assertEqual(bom_doc.operations[1].time_in_mins, 42.2) + self.assertEqual(bom_doc.operations[0].time_in_mins, 30) + self.assertEqual(bom_doc.operations[1].time_in_mins, 20) def setup_operations(rows): From 2b33e63d2ca0f9872215453419c349493946c64d Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 11 Jan 2022 14:40:08 +0530 Subject: [PATCH 182/193] fix: use `get_all` instead of `get_list` while fetching SLA doctypes (#29216) Co-authored-by: Ankush Menat --- .../doctype/service_level_agreement/service_level_agreement.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index b3348f1e1e..ea617fd1eb 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -853,7 +853,7 @@ def get_user_time(user, to_string=False): @frappe.whitelist() def get_sla_doctypes(): doctypes = [] - data = frappe.get_list('Service Level Agreement', + data = frappe.get_all('Service Level Agreement', {'enabled': 1}, ['document_type'], distinct=1 From c45042c00edcfe385810d22b1564a6c0b971121c Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 11 Jan 2022 15:03:07 +0530 Subject: [PATCH 183/193] feat: early payment discount on sales & purchase orders (#29101) --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index c1b056b9c7..0e07abd725 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1708,7 +1708,10 @@ def set_paid_amount_and_received_amount(dt, party_account_currency, bank, outsta def apply_early_payment_discount(paid_amount, received_amount, doc): total_discount = 0 - if doc.doctype in ['Sales Invoice', 'Purchase Invoice'] and doc.payment_schedule: + eligible_for_payments = ['Sales Order', 'Sales Invoice', 'Purchase Order', 'Purchase Invoice'] + has_payment_schedule = hasattr(doc, 'payment_schedule') and doc.payment_schedule + + if doc.doctype in eligible_for_payments and has_payment_schedule: for term in doc.payment_schedule: if not term.discounted_amount and term.discount and getdate(nowdate()) <= term.discount_date: if term.discount_type == 'Percentage': From 33aad4b950045d90eb4505274fd8d504776608a8 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 11 Jan 2022 17:06:31 +0530 Subject: [PATCH 184/193] fix: UOM autocomplete broken All new recent sites seem to have all UOMs as disabled by default. The desired behaviour is exact opposite of this. --- erpnext/controllers/tests/test_queries.py | 5 +++++ erpnext/setup/setup_wizard/operations/install_fixtures.py | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/tests/test_queries.py b/erpnext/controllers/tests/test_queries.py index 05541d1688..908d78c15b 100644 --- a/erpnext/controllers/tests/test_queries.py +++ b/erpnext/controllers/tests/test_queries.py @@ -1,6 +1,8 @@ import unittest from functools import partial +import frappe + from erpnext.controllers import queries @@ -85,3 +87,6 @@ class TestQueries(unittest.TestCase): wh = query(filters=[["Bin", "item_code", "=", "_Test Item"]]) self.assertGreaterEqual(len(wh), 1) + + def test_default_uoms(self): + self.assertGreaterEqual(frappe.db.count("UOM", {"enabled": 1}), 10) diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 336b51c0ab..9dbf49eae7 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -353,7 +353,8 @@ def add_uom_data(): "doctype": "UOM", "uom_name": _(d.get("uom_name")), "name": _(d.get("uom_name")), - "must_be_whole_number": d.get("must_be_whole_number") + "must_be_whole_number": d.get("must_be_whole_number"), + "enabled": 1, }).db_insert() # bootstrap uom conversion factors From f8119563ca51f46917cc521429d61a374b6cbc8a Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 11 Jan 2022 17:22:40 +0530 Subject: [PATCH 185/193] fix(patch): enable all uoms on recently created sites --- erpnext/patches.txt | 1 + erpnext/patches/v13_0/enable_uoms.py | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 erpnext/patches/v13_0/enable_uoms.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 821a49329e..5190f9f8c6 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -305,6 +305,7 @@ erpnext.patches.v13_0.add_default_interview_notification_templates erpnext.patches.v13_0.enable_scheduler_job_for_item_reposting erpnext.patches.v13_0.requeue_failed_reposts erpnext.patches.v13_0.update_job_card_status +erpnext.patches.v13_0.enable_uoms erpnext.patches.v12_0.update_production_plan_status erpnext.patches.v13_0.healthcare_deprecation_warning erpnext.patches.v13_0.item_naming_series_not_mandatory diff --git a/erpnext/patches/v13_0/enable_uoms.py b/erpnext/patches/v13_0/enable_uoms.py new file mode 100644 index 0000000000..4d3f637630 --- /dev/null +++ b/erpnext/patches/v13_0/enable_uoms.py @@ -0,0 +1,13 @@ +import frappe + + +def execute(): + frappe.reload_doc('setup', 'doctype', 'uom') + + uom = frappe.qb.DocType("UOM") + + (frappe.qb + .update(uom) + .set(uom.enabled, 1) + .where(uom.creation >= "2021-10-18") # date when this field was released + ).run() From 8f0d10a1b791fb6c9471fe8aec785e5bd6ac5321 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 11 Jan 2022 18:45:53 +0530 Subject: [PATCH 186/193] chore: add crm module in issue template [skip ci] --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 8f938112a7..4d61f1fb94 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -40,6 +40,7 @@ body: - HR - projects - support + - CRM - assets - integrations - quality @@ -48,6 +49,7 @@ body: - agriculture - education - non-profit + - other validations: required: true From 8f0b2fa90ed395a0fcbefb5e099ef60fc79bf6aa Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 6 Jan 2022 22:12:31 +0530 Subject: [PATCH 187/193] fix: incorrect scrap item qty --- .../doctype/work_order/test_work_order.py | 38 +++++++++++++++++- .../stock/doctype/stock_entry/stock_entry.py | 39 ++++++++++++------- 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 9926b15894..e7eb9c6149 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -2,7 +2,7 @@ # License: GNU General Public License v3. See license.txt import frappe -from frappe.utils import add_months, cint, flt, now, today +from frappe.utils import add_days, add_months, cint, flt, now, today from erpnext.manufacturing.doctype.job_card.job_card import JobCardCancelError from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom @@ -12,6 +12,7 @@ from erpnext.manufacturing.doctype.work_order.work_order import ( OverProductionError, StockOverProductionError, close_work_order, + make_job_card, make_stock_entry, stop_unstop, ) @@ -804,6 +805,34 @@ class TestWorkOrder(ERPNextTestCase): if row.is_scrap_item: self.assertEqual(row.qty, 1) + # Partial Job Card 1 with qty 10 + wo_order = make_wo_order_test_record(item=item, company=company, planned_start_date=add_days(now(), 60), qty=20, skip_transfer=1) + job_card = frappe.db.get_value('Job Card', {'work_order': wo_order.name}, 'name') + update_job_card(job_card, 10) + + stock_entry = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 10)) + for row in stock_entry.items: + if row.is_scrap_item: + self.assertEqual(row.qty, 2) + + # Partial Job Card 2 with qty 10 + operations = [] + wo_order.load_from_db() + for row in wo_order.operations: + n_dict = row.as_dict() + n_dict['qty'] = 10 + n_dict['pending_qty'] = 10 + operations.append(n_dict) + + make_job_card(wo_order.name, operations) + job_card = frappe.db.get_value('Job Card', {'work_order': wo_order.name, 'docstatus': 0}, 'name') + update_job_card(job_card, 10) + + stock_entry = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 10)) + for row in stock_entry.items: + if row.is_scrap_item: + self.assertEqual(row.qty, 2) + def test_close_work_order(self): items = ['Test FG Item for Closed WO', 'Test RM Item 1 for Closed WO', 'Test RM Item 2 for Closed WO'] @@ -883,7 +912,8 @@ class TestWorkOrder(ERPNextTestCase): self.assertEqual(wo1.operations[0].time_in_mins, wo2.operations[0].time_in_mins) -def update_job_card(job_card): +def update_job_card(job_card, jc_qty=None): + employee = frappe.db.get_value('Employee', {'status': 'Active'}, 'name') job_card_doc = frappe.get_doc('Job Card', job_card) job_card_doc.set('scrap_items', [ { @@ -896,8 +926,12 @@ def update_job_card(job_card): }, ]) + if jc_qty: + job_card_doc.for_quantity = jc_qty + job_card_doc.append('time_logs', { 'from_time': now(), + 'employee': employee, 'time_in_mins': 60, 'completed_qty': job_card_doc.for_quantity }) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 93e303c9a7..8dcf48aa24 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -8,6 +8,7 @@ from collections import defaultdict import frappe from frappe import _ from frappe.model.mapper import get_mapped_doc +from frappe.query_builder.functions import Sum from frappe.utils import cint, comma_or, cstr, flt, format_time, formatdate, getdate, nowdate import erpnext @@ -1275,22 +1276,29 @@ class StockEntry(StockController): if not self.pro_doc: self.set_work_order_details() - scrap_items = frappe.db.sql(''' - SELECT - JCSI.item_code, JCSI.item_name, SUM(JCSI.stock_qty) as stock_qty, JCSI.stock_uom, JCSI.description - FROM - `tabJob Card` JC, `tabJob Card Scrap Item` JCSI - WHERE - JCSI.parent = JC.name AND JC.docstatus = 1 - AND JCSI.item_code IS NOT NULL AND JC.work_order = %s - GROUP BY - JCSI.item_code - ''', self.work_order, as_dict=1) - - pending_qty = flt(self.pro_doc.qty) - flt(self.pro_doc.produced_qty) - if pending_qty <=0: + if not self.pro_doc.operations: return [] + job_card = frappe.qb.DocType('Job Card') + job_card_scrap_item = frappe.qb.DocType('Job Card Scrap Item') + + scrap_items = ( + frappe.qb.from_(job_card) + .select( + Sum(job_card_scrap_item.stock_qty).as_('stock_qty'), + job_card_scrap_item.item_code, job_card_scrap_item.item_name, + job_card_scrap_item.description, job_card_scrap_item.stock_uom) + .join(job_card_scrap_item) + .on(job_card_scrap_item.parent == job_card.name) + .where( + (job_card_scrap_item.item_code.isnotnull()) + & (job_card.work_order == self.work_order) + & (job_card.docstatus == 1)) + .groupby(job_card_scrap_item.item_code) + ).run(as_dict=1) + + pending_qty = flt(self.get_completed_job_card_qty()) - flt(self.pro_doc.produced_qty) + used_scrap_items = self.get_used_scrap_items() for row in scrap_items: row.stock_qty -= flt(used_scrap_items.get(row.item_code)) @@ -1304,6 +1312,9 @@ class StockEntry(StockController): return scrap_items + def get_completed_job_card_qty(self): + return flt(min([d.completed_qty for d in self.pro_doc.operations])) + def get_used_scrap_items(self): used_scrap_items = defaultdict(float) data = frappe.get_all( From aef432ee2f8f5e043a226c3c5ab1b2c9d267b67a Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 12 Jan 2022 13:11:21 +0530 Subject: [PATCH 188/193] fix: description not fetched in sales order analytics report (#29230) --- .../report/sales_order_analysis/sales_order_analysis.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py index 0c0acc76e3..b2bf5464b5 100644 --- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py +++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py @@ -68,7 +68,8 @@ def get_data(conditions, filters): (soi.billed_amt * IFNULL(so.conversion_rate, 1)) as billed_amount, (soi.base_amount - (soi.billed_amt * IFNULL(so.conversion_rate, 1))) as pending_amount, soi.warehouse as warehouse, - so.company, soi.name + so.company, soi.name, + soi.description as description FROM `tabSales Order` so, (`tabSales Order Item` soi @@ -184,6 +185,12 @@ def get_columns(filters): "options": "Item", "width": 100 }) + columns.append({ + "label":_("Description"), + "fieldname": "description", + "fieldtype": "Small Text", + "width": 100 + }) columns.extend([ { From 11fae15becc67a266065be2372c2cd3bc717acdd Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 12 Jan 2022 13:23:18 +0530 Subject: [PATCH 189/193] fix(asset): check if fixed asset account is set against company (#29189) --- erpnext/assets/doctype/asset/asset.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index a18b03a888..ee3ec8e63a 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -608,7 +608,17 @@ class Asset(AccountsController): return purchase_document def get_fixed_asset_account(self): - return get_asset_category_account('fixed_asset_account', None, self.name, None, self.asset_category, self.company) + fixed_asset_account = get_asset_category_account('fixed_asset_account', None, self.name, None, self.asset_category, self.company) + if not fixed_asset_account: + frappe.throw( + _("Set {0} in asset category {1} for company {2}").format( + frappe.bold("Fixed Asset Account"), + frappe.bold(self.asset_category), + frappe.bold(self.company), + ), + title=_("Account not Found"), + ) + return fixed_asset_account def get_cwip_account(self, cwip_enabled=False): cwip_account = None From efcfb825d7a72a403f06f62e322cedd7fbbffd29 Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 12 Jan 2022 16:44:33 +0530 Subject: [PATCH 190/193] Merge pull request #29257 from marination/fix-reset-wh-defaults fix: Avoid resetting Default wh fields for Manufacture Entry --- .../tests/test_transaction_base.py | 79 ++++++++++++++++--- .../stock/doctype/stock_entry/stock_entry.py | 8 +- erpnext/utilities/transaction_base.py | 2 - 3 files changed, 72 insertions(+), 17 deletions(-) diff --git a/erpnext/controllers/tests/test_transaction_base.py b/erpnext/controllers/tests/test_transaction_base.py index 13aa697610..f4d3f97ef0 100644 --- a/erpnext/controllers/tests/test_transaction_base.py +++ b/erpnext/controllers/tests/test_transaction_base.py @@ -4,19 +4,72 @@ import frappe class TestUtils(unittest.TestCase): - def test_reset_default_field_value(self): - doc = frappe.get_doc({ - "doctype": "Purchase Receipt", - "set_warehouse": "Warehouse 1", - }) + def test_reset_default_field_value(self): + doc = frappe.get_doc({ + "doctype": "Purchase Receipt", + "set_warehouse": "Warehouse 1", + }) - # Same values - doc.items = [{"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 1"}] - doc.reset_default_field_value("set_warehouse", "items", "warehouse") - self.assertEqual(doc.set_warehouse, "Warehouse 1") + # Same values + doc.items = [{"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 1"}] + doc.reset_default_field_value("set_warehouse", "items", "warehouse") + self.assertEqual(doc.set_warehouse, "Warehouse 1") - # Mixed values - doc.items = [{"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 2"}, {"warehouse": "Warehouse 1"}] - doc.reset_default_field_value("set_warehouse", "items", "warehouse") - self.assertEqual(doc.set_warehouse, None) + # Mixed values + doc.items = [{"warehouse": "Warehouse 1"}, {"warehouse": "Warehouse 2"}, {"warehouse": "Warehouse 1"}] + doc.reset_default_field_value("set_warehouse", "items", "warehouse") + self.assertEqual(doc.set_warehouse, None) + def test_reset_default_field_value_in_mfg_stock_entry(self): + # manufacture stock entry with rows having blank source/target wh + se = frappe.get_doc( + doctype="Stock Entry", + purpose="Manufacture", + stock_entry_type="Manufacture", + company="_Test Company", + from_warehouse="_Test Warehouse - _TC", + to_warehouse="_Test Warehouse 1 - _TC", + items=[ + frappe._dict(item_code="_Test Item", qty=1, basic_rate=200, s_warehouse="_Test Warehouse - _TC"), + frappe._dict(item_code="_Test FG Item", qty=4, t_warehouse="_Test Warehouse 1 - _TC", is_finished_item=1) + ] + ) + se.save() + + # default fields must be untouched + self.assertEqual(se.from_warehouse, "_Test Warehouse - _TC") + self.assertEqual(se.to_warehouse, "_Test Warehouse 1 - _TC") + + se.delete() + + def test_reset_default_field_value_in_transfer_stock_entry(self): + doc = frappe.get_doc({ + "doctype": "Stock Entry", + "purpose": "Material Receipt", + "from_warehouse": "Warehouse 1", + "to_warehouse": "Warehouse 2", + }) + + # Same values + doc.items = [ + {"s_warehouse": "Warehouse 1", "t_warehouse": "Warehouse 2"}, + {"s_warehouse": "Warehouse 1", "t_warehouse": "Warehouse 2"}, + {"s_warehouse": "Warehouse 1", "t_warehouse": "Warehouse 2"} + ] + + doc.reset_default_field_value("from_warehouse", "items", "s_warehouse") + doc.reset_default_field_value("to_warehouse", "items", "t_warehouse") + self.assertEqual(doc.from_warehouse, "Warehouse 1") + self.assertEqual(doc.to_warehouse, "Warehouse 2") + + # Mixed values in source wh + doc.items = [ + {"s_warehouse": "Warehouse 1", "t_warehouse": "Warehouse 2"}, + {"s_warehouse": "Warehouse 3", "t_warehouse": "Warehouse 2"}, + {"s_warehouse": "Warehouse 1", "t_warehouse": "Warehouse 2"} + ] + + doc.reset_default_field_value("from_warehouse", "items", "s_warehouse") + doc.reset_default_field_value("to_warehouse", "items", "t_warehouse") + self.assertEqual(doc.from_warehouse, None) + self.assertEqual(doc.to_warehouse, "Warehouse 2") \ No newline at end of file diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 8dcf48aa24..d1149ea6a2 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -110,8 +110,12 @@ class StockEntry(StockController): self.set_actual_qty() self.calculate_rate_and_amount() self.validate_putaway_capacity() - self.reset_default_field_value("from_warehouse", "items", "s_warehouse") - self.reset_default_field_value("to_warehouse", "items", "t_warehouse") + + if not self.get("purpose") == "Manufacture": + # ignore scrap item wh difference and empty source/target wh + # in Manufacture Entry + self.reset_default_field_value("from_warehouse", "items", "s_warehouse") + self.reset_default_field_value("to_warehouse", "items", "t_warehouse") def on_submit(self): self.update_stock_ledger() diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py index 1d8b3a8db6..feea2284b7 100644 --- a/erpnext/utilities/transaction_base.py +++ b/erpnext/utilities/transaction_base.py @@ -181,8 +181,6 @@ class TransactionBase(StatusUpdater): if len(child_table_values) > 1: self.set(default_field, None) - else: - self.set(default_field, list(child_table_values)[0]) def delete_events(ref_type, ref_name): events = frappe.db.sql_list(""" SELECT From 8b5827ed6db1041526b6440ca8e4fde19c646e1e Mon Sep 17 00:00:00 2001 From: Mohammed Yusuf Shaikh <49878143+mohammedyusufshaikh@users.noreply.github.com> Date: Wed, 12 Jan 2022 18:49:33 +0530 Subject: [PATCH 191/193] fix: from time and to time not updated in drag and drop action #29114 fix: from time and to time not updated in drag and drop action --- erpnext/education/api.py | 4 ++-- .../doctype/course_schedule/course_schedule.py | 12 +++++++++++- .../course_schedule/course_schedule_calendar.js | 7 +++---- .../doctype/course_schedule/test_course_schedule.py | 6 ++++++ 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/erpnext/education/api.py b/erpnext/education/api.py index d9013b0816..636b948a1c 100644 --- a/erpnext/education/api.py +++ b/erpnext/education/api.py @@ -201,8 +201,8 @@ def get_course_schedule_events(start, end, filters=None): conditions = get_event_conditions("Course Schedule", filters) data = frappe.db.sql("""select name, course, color, - timestamp(schedule_date, from_time) as from_datetime, - timestamp(schedule_date, to_time) as to_datetime, + timestamp(schedule_date, from_time) as from_time, + timestamp(schedule_date, to_time) as to_time, room, student_group, 0 as 'allDay' from `tabCourse Schedule` where ( schedule_date between %(start)s and %(end)s ) diff --git a/erpnext/education/doctype/course_schedule/course_schedule.py b/erpnext/education/doctype/course_schedule/course_schedule.py index ffd323d3d2..615d2c4709 100644 --- a/erpnext/education/doctype/course_schedule/course_schedule.py +++ b/erpnext/education/doctype/course_schedule/course_schedule.py @@ -3,6 +3,8 @@ # For license information, please see license.txt +from datetime import datetime + import frappe from frappe import _ from frappe.model.document import Document @@ -30,6 +32,14 @@ class CourseSchedule(Document): if self.from_time > self.to_time: frappe.throw(_("From Time cannot be greater than To Time.")) + """Handles specicfic case to update schedule date in calendar """ + if isinstance(self.from_time, str): + try: + datetime_obj = datetime.strptime(self.from_time, '%Y-%m-%d %H:%M:%S') + self.schedule_date = datetime_obj + except ValueError: + pass + def validate_overlap(self): """Validates overlap for Student Group, Instructor, Room""" @@ -47,4 +57,4 @@ class CourseSchedule(Document): validate_overlap_for(self, "Assessment Plan", "student_group") validate_overlap_for(self, "Assessment Plan", "room") - validate_overlap_for(self, "Assessment Plan", "supervisor", self.instructor) + validate_overlap_for(self, "Assessment Plan", "supervisor", self.instructor) \ No newline at end of file diff --git a/erpnext/education/doctype/course_schedule/course_schedule_calendar.js b/erpnext/education/doctype/course_schedule/course_schedule_calendar.js index 803527e548..cacd539b22 100644 --- a/erpnext/education/doctype/course_schedule/course_schedule_calendar.js +++ b/erpnext/education/doctype/course_schedule/course_schedule_calendar.js @@ -1,11 +1,10 @@ frappe.views.calendar["Course Schedule"] = { field_map: { - // from_datetime and to_datetime don't exist as docfields but are used in onload - "start": "from_datetime", - "end": "to_datetime", + "start": "from_time", + "end": "to_time", "id": "name", "title": "course", - "allDay": "allDay" + "allDay": "allDay", }, gantt: false, order_by: "schedule_date", diff --git a/erpnext/education/doctype/course_schedule/test_course_schedule.py b/erpnext/education/doctype/course_schedule/test_course_schedule.py index a732419555..56149affce 100644 --- a/erpnext/education/doctype/course_schedule/test_course_schedule.py +++ b/erpnext/education/doctype/course_schedule/test_course_schedule.py @@ -6,6 +6,7 @@ import unittest import frappe from frappe.utils import to_timedelta, today +from frappe.utils.data import add_to_date from erpnext.education.utils import OverlapError @@ -39,6 +40,11 @@ class TestCourseSchedule(unittest.TestCase): make_course_schedule_test_record(from_time= cs1.from_time, to_time= cs1.to_time, student_group="Course-TC102-2014-2015 (_Test Academic Term)", instructor="_Test Instructor 2", room=frappe.get_all("Room")[1].name) + def test_update_schedule_date(self): + doc = make_course_schedule_test_record(schedule_date= add_to_date(today(), days=1)) + doc.schedule_date = add_to_date(doc.schedule_date, days=1) + doc.save() + def make_course_schedule_test_record(**args): args = frappe._dict(args) From 5cda4ea39f85456e6d68df0c6a6498f98a052734 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 12 Jan 2022 19:03:36 +0530 Subject: [PATCH 192/193] fix: autoname generated for Job Applicant is too long (#29260) * fix: autoname generated for Job Applicant is too long - autoname based on email and append number if exists instead of concatenating name, email, title - add more search fields for context during selection * test: Job applicant naming and fix related tests --- .../test_employee_onboarding.py | 8 +++---- .../doctype/job_applicant/job_applicant.json | 6 ++++-- .../hr/doctype/job_applicant/job_applicant.py | 10 +++++---- .../job_applicant/test_job_applicant.py | 21 ++++++++++++++++++- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py index cb1b56048b..2d129c8acf 100644 --- a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py +++ b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py @@ -19,7 +19,7 @@ class TestEmployeeOnboarding(unittest.TestCase): if frappe.db.exists('Employee Onboarding', {'employee_name': 'Test Researcher'}): frappe.delete_doc('Employee Onboarding', {'employee_name': 'Test Researcher'}) - project = "Employee Onboarding : Test Researcher - test@researcher.com" + project = "Employee Onboarding : test@researcher.com" frappe.db.sql("delete from tabProject where name=%s", project) frappe.db.sql("delete from tabTask where project=%s", project) @@ -27,7 +27,7 @@ class TestEmployeeOnboarding(unittest.TestCase): onboarding = create_employee_onboarding() project_name = frappe.db.get_value('Project', onboarding.project, 'project_name') - self.assertEqual(project_name, 'Employee Onboarding : Test Researcher - test@researcher.com') + self.assertEqual(project_name, 'Employee Onboarding : test@researcher.com') # don't allow making employee if onboarding is not complete self.assertRaises(IncompleteTaskError, make_employee, onboarding.name) @@ -64,8 +64,8 @@ class TestEmployeeOnboarding(unittest.TestCase): def get_job_applicant(): - if frappe.db.exists('Job Applicant', 'Test Researcher - test@researcher.com'): - return frappe.get_doc('Job Applicant', 'Test Researcher - test@researcher.com') + if frappe.db.exists('Job Applicant', 'test@researcher.com'): + return frappe.get_doc('Job Applicant', 'test@researcher.com') applicant = frappe.new_doc('Job Applicant') applicant.applicant_name = 'Test Researcher' applicant.email_id = 'test@researcher.com' diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.json b/erpnext/hr/doctype/job_applicant/job_applicant.json index 200f675221..66b609cf99 100644 --- a/erpnext/hr/doctype/job_applicant/job_applicant.json +++ b/erpnext/hr/doctype/job_applicant/job_applicant.json @@ -192,10 +192,11 @@ "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2021-09-29 23:06:10.904260", + "modified": "2022-01-12 16:28:53.196881", "modified_by": "Administrator", "module": "HR", "name": "Job Applicant", + "naming_rule": "Expression (old style)", "owner": "Administrator", "permissions": [ { @@ -210,10 +211,11 @@ "write": 1 } ], - "search_fields": "applicant_name", + "search_fields": "applicant_name, email_id, job_title, phone_number", "sender_field": "email_id", "sort_field": "modified", "sort_order": "ASC", + "states": [], "subject_field": "notes", "title_field": "applicant_name" } \ No newline at end of file diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.py b/erpnext/hr/doctype/job_applicant/job_applicant.py index abaa50c84c..5b3d9bfb4f 100644 --- a/erpnext/hr/doctype/job_applicant/job_applicant.py +++ b/erpnext/hr/doctype/job_applicant/job_applicant.py @@ -7,6 +7,7 @@ import frappe from frappe import _ from frappe.model.document import Document +from frappe.model.naming import append_number_if_name_exists from frappe.utils import validate_email_address from erpnext.hr.doctype.interview.interview import get_interviewers @@ -21,10 +22,11 @@ class JobApplicant(Document): self.get("__onload").job_offer = job_offer[0].name def autoname(self): - keys = filter(None, (self.applicant_name, self.email_id, self.job_title)) - if not keys: - frappe.throw(_("Name or Email is mandatory"), frappe.NameError) - self.name = " - ".join(keys) + self.name = self.email_id + + # applicant can apply more than once for a different job title or reapply + if frappe.db.exists("Job Applicant", self.name): + self.name = append_number_if_name_exists("Job Applicant", self.name) def validate(self): if self.email_id: diff --git a/erpnext/hr/doctype/job_applicant/test_job_applicant.py b/erpnext/hr/doctype/job_applicant/test_job_applicant.py index 36dcf6b074..bf1622028d 100644 --- a/erpnext/hr/doctype/job_applicant/test_job_applicant.py +++ b/erpnext/hr/doctype/job_applicant/test_job_applicant.py @@ -9,7 +9,26 @@ from erpnext.hr.doctype.designation.test_designation import create_designation class TestJobApplicant(unittest.TestCase): - pass + def test_job_applicant_naming(self): + applicant = frappe.get_doc({ + "doctype": "Job Applicant", + "status": "Open", + "applicant_name": "_Test Applicant", + "email_id": "job_applicant_naming@example.com" + }).insert() + self.assertEqual(applicant.name, 'job_applicant_naming@example.com') + + applicant = frappe.get_doc({ + "doctype": "Job Applicant", + "status": "Open", + "applicant_name": "_Test Applicant", + "email_id": "job_applicant_naming@example.com" + }).insert() + self.assertEqual(applicant.name, 'job_applicant_naming@example.com-1') + + def tearDown(self): + frappe.db.rollback() + def create_job_applicant(**args): args = frappe._dict(args) From eb8d08b411b66797d9f3892e079c49828335d34a Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 12 Jan 2022 19:23:22 +0530 Subject: [PATCH 193/193] fix: remove all stock UOM conversion when changing stock UOM --- erpnext/stock/doctype/item/item.py | 22 ++++++++++++---------- erpnext/stock/doctype/item/test_item.py | 10 ++++++++++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index decf522d2f..281e881875 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -492,18 +492,20 @@ class Item(WebsiteGenerator): 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")] - if self.stock_uom not in uom_conv_list: - ch = self.append('uoms', {}) - ch.uom = self.stock_uom - ch.conversion_factor = 1 + if not self.is_new() and self.has_value_changed("stock_uom"): + self.uoms = [] + frappe.msgprint( + _("Successfully changed Stock UOM, please redefine conversion factors for new UOM."), + alert=True, + ) - to_remove = [] - for d in self.get("uoms"): - if d.conversion_factor == 1 and d.uom != self.stock_uom: - to_remove.append(d) + uoms_list = [d.uom for d in self.get("uoms")] - [self.remove(d) for d in to_remove] + if self.stock_uom not in uoms_list: + self.append("uoms", { + "uom": self.stock_uom, + "conversion_factor": 1 + }) def update_show_in_website(self): if self.disabled: diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 4028d93334..0957ce0615 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -584,6 +584,16 @@ class TestItem(ERPNextTestCase): except frappe.ValidationError as e: self.fail(f"UoM change not allowed even though no SLE / BIN with positive qty exists: {e}") + def test_erasure_of_old_conversions(self): + item = create_item("_item change uom") + item.stock_uom = "Gram" + item.append("uoms", frappe._dict(uom="Box", conversion_factor=2)) + item.save() + item.reload() + item.stock_uom = "Nos" + item.save() + self.assertEqual(len(item.uoms), 1) + def test_validate_stock_item(self): self.assertRaises(frappe.ValidationError, validate_is_stock_item, "_Test Non Stock Item")