From 613d82e12f734aa8f06d3a9093dad9f15cc77301 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 19 Apr 2019 16:28:19 +0530 Subject: [PATCH 01/16] fix: received qty in the purchase order item showing incorrect if user has returned the rejected quantity --- erpnext/stock/doctype/purchase_receipt/purchase_receipt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index eb821023568..7f03fdca36f 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -31,7 +31,7 @@ class PurchaseReceipt(BuyingController): 'target_parent_dt': 'Purchase Order', 'target_parent_field': 'per_received', 'target_ref_field': 'qty', - 'source_field': 'qty', + 'source_field': 'received_qty', 'percent_join_field': 'purchase_order', 'overflow_type': 'receipt' }, From 4a9127f9a66aa5b3245b8262ae7f3c7d53a608cb Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Mon, 22 Apr 2019 03:46:25 +0200 Subject: [PATCH 02/16] feat(accounts): validate IBAN --- .../doctype/bank_account/bank_account.py | 24 ++++++++++++++ .../doctype/bank_account/test_bank_account.py | 33 ++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_account/bank_account.py b/erpnext/accounts/doctype/bank_account/bank_account.py index 3c679fa215a..06393e7a54e 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.py +++ b/erpnext/accounts/doctype/bank_account/bank_account.py @@ -21,11 +21,35 @@ class BankAccount(Document): def validate(self): self.validate_company() + self.validate_iban() def validate_company(self): if self.is_company_account and not self.company: frappe.throw(_("Company is manadatory for company account")) + def validate_iban(self): + ''' + Algorithm: https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN + ''' + def encode_char(c): + # Position in the alphabet (A=1, B=2, ...) plus nine + return str(9 + ord(c) - 64) + + # remove whitespaces, upper case to get the right number from ord() + iban = ''.join(self.iban.split(' ')).upper() + + # Move country code and checksum from the start to the end + flipped = iban[4:] + iban[:4] + + # Encode characters as numbers + encoded = [encode_char(c) if ord(c) >= 65 and ord(c) <= 90 else c for c in flipped] + + to_check = int(''.join(encoded)) + + if to_check % 97 != 1: + frappe.throw(_('IBAN is not valid')) + + @frappe.whitelist() def make_bank_account(doctype, docname): doc = frappe.new_doc("Bank Account") diff --git a/erpnext/accounts/doctype/bank_account/test_bank_account.py b/erpnext/accounts/doctype/bank_account/test_bank_account.py index 43a3298ec4a..8e1a91bc45f 100644 --- a/erpnext/accounts/doctype/bank_account/test_bank_account.py +++ b/erpnext/accounts/doctype/bank_account/test_bank_account.py @@ -4,9 +4,40 @@ from __future__ import unicode_literals import frappe +from frappe import _ +from frappe import ValidationError import unittest # test_records = frappe.get_test_records('Bank Account') class TestBankAccount(unittest.TestCase): - pass + + def test_validate_iban(self): + valid_ibans = [ + 'GB82 WEST 1234 5698 7654 32', + 'DE91 1000 0000 0123 4567 89', + 'FR76 3000 6000 0112 3456 7890 189' + ] + + invalid_ibans = [ + # wrong checksum (3rd place) + 'GB72 WEST 1234 5698 7654 32', + 'DE81 1000 0000 0123 4567 89', + 'FR66 3000 6000 0112 3456 7890 189' + ] + + bank_account = frappe.get_doc({'doctype':'Bank Account'}) + + for iban in valid_ibans: + bank_account.iban = iban + try: + bank_account.validate_iban() + except ValidationError: + msg = _('BankAccount.validate_iban() failed for valid IBAN {}'.format(iban)) + self.fail(msg=msg) + + for not_iban in invalid_ibans: + bank_account.iban = not_iban + msg = _('BankAccount.validate_iban() accepted invalid IBAN {}'.format(iban)) + with self.assertRaises(ValidationError, msg=msg): + bank_account.validate_iban() From 49f919a4fcf7b4d7b372416fd48d1892e8812203 Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Mon, 22 Apr 2019 05:32:35 +0200 Subject: [PATCH 03/16] fix test's error message --- erpnext/accounts/doctype/bank_account/test_bank_account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_account/test_bank_account.py b/erpnext/accounts/doctype/bank_account/test_bank_account.py index 8e1a91bc45f..bd148df33ba 100644 --- a/erpnext/accounts/doctype/bank_account/test_bank_account.py +++ b/erpnext/accounts/doctype/bank_account/test_bank_account.py @@ -38,6 +38,6 @@ class TestBankAccount(unittest.TestCase): for not_iban in invalid_ibans: bank_account.iban = not_iban - msg = _('BankAccount.validate_iban() accepted invalid IBAN {}'.format(iban)) + msg = _('BankAccount.validate_iban() accepted invalid IBAN {}'.format(not_iban)) with self.assertRaises(ValidationError, msg=msg): bank_account.validate_iban() From 70f89462a86e78e33389f668d41e68d35326c6a7 Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Mon, 22 Apr 2019 12:27:12 +0200 Subject: [PATCH 04/16] fix: consider empty iban --- erpnext/accounts/doctype/bank_account/bank_account.py | 3 ++- erpnext/accounts/doctype/bank_account/test_bank_account.py | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_account/bank_account.py b/erpnext/accounts/doctype/bank_account/bank_account.py index 06393e7a54e..aa18b966707 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.py +++ b/erpnext/accounts/doctype/bank_account/bank_account.py @@ -21,7 +21,8 @@ class BankAccount(Document): def validate(self): self.validate_company() - self.validate_iban() + if self.validate_iban: + self.validate_iban() def validate_company(self): if self.is_company_account and not self.company: diff --git a/erpnext/accounts/doctype/bank_account/test_bank_account.py b/erpnext/accounts/doctype/bank_account/test_bank_account.py index bd148df33ba..f3bb086fa96 100644 --- a/erpnext/accounts/doctype/bank_account/test_bank_account.py +++ b/erpnext/accounts/doctype/bank_account/test_bank_account.py @@ -28,6 +28,12 @@ class TestBankAccount(unittest.TestCase): bank_account = frappe.get_doc({'doctype':'Bank Account'}) + try: + bank_account.validate_iban() + except AttributeError: + msg = _('BankAccount.validate_iban() failed for empty IBAN') + self.fail(msg=msg) + for iban in valid_ibans: bank_account.iban = iban try: From f3b07495f63784020d3916aeb7e628f2b4a83905 Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Mon, 22 Apr 2019 12:38:22 +0200 Subject: [PATCH 05/16] fix typo --- erpnext/accounts/doctype/bank_account/bank_account.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_account/bank_account.py b/erpnext/accounts/doctype/bank_account/bank_account.py index aa18b966707..e71fca256ea 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.py +++ b/erpnext/accounts/doctype/bank_account/bank_account.py @@ -21,7 +21,8 @@ class BankAccount(Document): def validate(self): self.validate_company() - if self.validate_iban: + + if self.iban: self.validate_iban() def validate_company(self): From 5a6fc77751c17a3c381688603ffedd334799744e Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 23 Apr 2019 17:26:01 +0530 Subject: [PATCH 06/16] fix: Straight line asset depreciation not working if Expected Value After Useful Life is defined --- erpnext/assets/doctype/asset/asset.js | 6 ++++++ erpnext/assets/doctype/asset/asset.py | 15 +++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 18ba8f9b5cb..3b41006f2b3 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -296,6 +296,12 @@ frappe.ui.form.on('Asset', { frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation); }, + gross_purchase_amount: function(frm) { + frm.doc.finance_books.forEach(d => { + frm.events.set_depreciation_rate(frm, d); + }) + }, + set_depreciation_rate: function(frm, row) { if (row.total_number_of_depreciations && row.frequency_of_depreciation) { frappe.call({ diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 8011038b1b1..72f5c627a71 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -101,7 +101,7 @@ class Asset(AccountsController): def set_depreciation_rate(self): for d in self.get("finance_books"): - d.rate_of_depreciation = self.get_depreciation_rate(d) + d.rate_of_depreciation = self.get_depreciation_rate(d, on_validate=True) def make_depreciation_schedule(self): depreciation_method = [d.depreciation_method for d in self.finance_books] @@ -125,7 +125,7 @@ class Asset(AccountsController): no_of_depreciations * cint(d.frequency_of_depreciation)) total_days = date_diff(end_date, self.available_for_use_date) - rate_per_day = value_after_depreciation / total_days + rate_per_day = (value_after_depreciation - d.get("expected_value_after_useful_life")) / total_days number_of_pending_depreciations = cint(d.total_number_of_depreciations) - \ cint(self.number_of_depreciations_booked) @@ -291,8 +291,8 @@ class Asset(AccountsController): def validate_expected_value_after_useful_life(self): for row in self.get('finance_books'): - accumulated_depreciation_after_full_schedule = \ - max([d.accumulated_depreciation_amount for d in self.get("schedules") if d.finance_book_id == row.idx]) + accumulated_depreciation_after_full_schedule = max([d.accumulated_depreciation_amount + for d in self.get("schedules") if cint(d.finance_book_id) == row.idx]) asset_value_after_full_schedule = flt(flt(self.gross_purchase_amount) - flt(accumulated_depreciation_after_full_schedule), @@ -403,7 +403,7 @@ class Asset(AccountsController): make_gl_entries(gl_entries) self.db_set('booked_fixed_asset', 1) - def get_depreciation_rate(self, args): + def get_depreciation_rate(self, args, on_validate=False): if isinstance(args, string_types): args = json.loads(args) @@ -420,7 +420,10 @@ class Asset(AccountsController): if args.get("depreciation_method") == 'Double Declining Balance': return 200.0 / args.get("total_number_of_depreciations") - if args.get("depreciation_method") == "Written Down Value" and not args.get("rate_of_depreciation"): + if args.get("depreciation_method") == "Written Down Value": + if args.get("rate_of_depreciation") and on_validate: + return args.get("rate_of_depreciation") + no_of_years = flt(args.get("total_number_of_depreciations") * flt(args.get("frequency_of_depreciation"))) / 12 value = flt(args.get("expected_value_after_useful_life")) / flt(self.gross_purchase_amount) From 62d51d82ea62b7022bc502a5e1349583bcfa39db Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Tue, 16 Apr 2019 17:07:13 +0530 Subject: [PATCH 07/16] feat: Added filters and columns for inactive items report --- .../stock/report/inactive_items/__init__.py | 0 .../report/inactive_items/inactive_items.js | 39 ++++++++++++ .../report/inactive_items/inactive_items.json | 31 ++++++++++ .../report/inactive_items/inactive_items.py | 60 +++++++++++++++++++ 4 files changed, 130 insertions(+) create mode 100644 erpnext/stock/report/inactive_items/__init__.py create mode 100644 erpnext/stock/report/inactive_items/inactive_items.js create mode 100644 erpnext/stock/report/inactive_items/inactive_items.json create mode 100644 erpnext/stock/report/inactive_items/inactive_items.py diff --git a/erpnext/stock/report/inactive_items/__init__.py b/erpnext/stock/report/inactive_items/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/stock/report/inactive_items/inactive_items.js b/erpnext/stock/report/inactive_items/inactive_items.js new file mode 100644 index 00000000000..73ce59b977d --- /dev/null +++ b/erpnext/stock/report/inactive_items/inactive_items.js @@ -0,0 +1,39 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Inactive Items"] = { + "filters": [ + { + fieldname: "territory", + label: __("Territory"), + fieldtype: "Link", + options: "Territory" + }, + { + fieldname: "customer", + label: __("Customer"), + fieldtype: "Link", + options: "Customer" + }, + { + fieldname: "item", + label: __("Item"), + fieldtype: "Link", + options: "Item" + }, + { + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + options: "Item Group" + }, + { + fieldname: "days", + label: __("Days Since Last order"), + fieldtype: "Select", + options: [30, 60, 90], + default: 30 + }, + ] +} diff --git a/erpnext/stock/report/inactive_items/inactive_items.json b/erpnext/stock/report/inactive_items/inactive_items.json new file mode 100644 index 00000000000..b9eb05ec050 --- /dev/null +++ b/erpnext/stock/report/inactive_items/inactive_items.json @@ -0,0 +1,31 @@ +{ + "add_total_row": 0, + "creation": "2019-04-16 16:05:00.647308", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "letter_head": "Test Letter Head 1", + "modified": "2019-04-16 16:06:33.630043", + "modified_by": "Administrator", + "module": "Stock", + "name": "Inactive Items", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Sales Invoice", + "report_name": "Inactive Items", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Accounts Manager" + }, + { + "role": "Auditor" + } + ] +} \ No newline at end of file diff --git a/erpnext/stock/report/inactive_items/inactive_items.py b/erpnext/stock/report/inactive_items/inactive_items.py new file mode 100644 index 00000000000..8aeb24349a7 --- /dev/null +++ b/erpnext/stock/report/inactive_items/inactive_items.py @@ -0,0 +1,60 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe + +def execute(filters=None): + columns, data = [], [] + return columns, data + +def get_columns(): + + columns = [ + { + "fieldname": "territory", + "fieldtype": "Link", + "label": _("Territory"), + "options": "Territory", + "width": 100 + }, + { + "fieldname": "item_group", + "fieldtype": "Link", + "label": _("Item Group"), + "options": "Item Group", + "width": 100 + }, + { + "fieldname": "item", + "fieldtype": "Link", + "label": _("Item"), + "options": "Item", + "width": 100 + }, + { + "fieldname": "customer", + "fieldtype": "Link", + "label": _("Customer"), + "options": "Customer", + "width": 100 + }, + { + "fieldname": "last_order_date", + "fieldtype": "Date", + "label": _("Last Order Date"), + "width": 100 + }, + { + "fieldname": "qty", + "fieldtype": "Float", + "label": _("Quantity"), + "width": 100 + }, + { + "fieldname": "days_since_last_order", + "fieldtype": "Int", + "label": _("Days Since Last Order"), + "width": 100 + }, + ] From 1bd69b449086b795f1c8d92d0d6db4d743d0a3f9 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Thu, 18 Apr 2019 08:29:21 +0530 Subject: [PATCH 08/16] fix: Reordered and deleted unnecessary filters --- .../report/inactive_items/inactive_items.js | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/erpnext/stock/report/inactive_items/inactive_items.js b/erpnext/stock/report/inactive_items/inactive_items.js index 73ce59b977d..39dfd5c8c36 100644 --- a/erpnext/stock/report/inactive_items/inactive_items.js +++ b/erpnext/stock/report/inactive_items/inactive_items.js @@ -4,18 +4,6 @@ frappe.query_reports["Inactive Items"] = { "filters": [ - { - fieldname: "territory", - label: __("Territory"), - fieldtype: "Link", - options: "Territory" - }, - { - fieldname: "customer", - label: __("Customer"), - fieldtype: "Link", - options: "Customer" - }, { fieldname: "item", label: __("Item"), @@ -28,6 +16,13 @@ frappe.query_reports["Inactive Items"] = { fieldtype: "Link", options: "Item Group" }, + { + fieldname: "based_on", + label: __("Based On"), + fieldtype: "Select", + options: "Sales Order\nSales Invoice", + default: "Sales Order" + }, { fieldname: "days", label: __("Days Since Last order"), From da64113b9adff34beba7d212934f3ddffc8de708 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Thu, 18 Apr 2019 08:45:10 +0530 Subject: [PATCH 09/16] feat: Logic for query and report creation for inactive items --- .../report/inactive_items/inactive_items.py | 95 ++++++++++++++++++- 1 file changed, 92 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/report/inactive_items/inactive_items.py b/erpnext/stock/report/inactive_items/inactive_items.py index 8aeb24349a7..95cec7bb348 100644 --- a/erpnext/stock/report/inactive_items/inactive_items.py +++ b/erpnext/stock/report/inactive_items/inactive_items.py @@ -3,9 +3,13 @@ from __future__ import unicode_literals import frappe +from frappe.utils import getdate, add_days, today, cint +from frappe import _ def execute(filters=None): - columns, data = [], [] + + columns = get_columns() + data = get_data(filters) return columns, data def get_columns(): @@ -26,12 +30,20 @@ def get_columns(): "width": 100 }, { - "fieldname": "item", + "fieldname": "item_name", "fieldtype": "Link", - "label": _("Item"), + "options": "Item", + "label": "Item", + "width": 100 + }, + { + "fieldname": "Item Name", + "fieldtype": "Link", + "label": _("Item Name"), "options": "Item", "width": 100 }, + { "fieldname": "customer", "fieldtype": "Link", @@ -58,3 +70,80 @@ def get_columns(): "width": 100 }, ] + + return columns + + +def get_data(filters): + + data = [] + items = get_items(filters) + sales_invoice_data = get_sales_details(filters) + + for item in items: + if sales_invoice_data.get(item.name): + item_obj = sales_invoice_data[item.name] + if item_obj.days_since_last_order > cint(filters['days']): + row = { + "territory": item_obj.territory, + "item_group": item_obj.item_group, + "item": item_obj.name, + "item_name": item_obj.item_name, + "customer": item_obj.customer, + "last_order_date": item_obj.last_order_date, + "qty": item_obj.qty, + "days_since_last_order": item_obj.days_since_last_order + } + data.append(row) + else: + row = { + "item_group": item.item_group, + "item": item.name, + "item_name": item.item_name + } + data.append(row) + + return data + + +def get_sales_details(filters): + + data = [] + item_details_map = {} + + date_field = "s.transaction_date" if filters["based_on"] == "Sales Order" else "s.posting_date" + + sales_data = frappe.db.sql(""" + select s.territory, s.customer, si.item_group, si.item_name, si.qty, {date_field} as last_order_date, + DATEDIFF(CURDATE(), {date_field}) as days_since_last_order + from `tab{doctype}` s, `tab{doctype} Item` si + where s.name = si.parent and s.docstatus = 1 + group by si.name order by days_since_last_order """ + .format(date_field = date_field, doctype = filters['based_on']), as_dict=1) + + for d in sales_data: + item_details_map.setdefault(d.item_name, d) + + return item_details_map + +def get_items(filters): + + filters_dict = { + "disabled": 0, + "is_stock_item": 1 + } + + if filters.get("item_group"): + filters_dict.update({ + "item_group": filters["item_group"] + }) + + if filters.get("item"): + filters_dict.update({ + "name": filtesr["item"] + }) + + items = frappe.get_all("Item", fields=["name", "item_group", "item_name"], filters=filters_dict) + + return items + From fe7baae9f747fc73aa0f1f42e55fc103912e05a9 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 22 Apr 2019 17:15:23 +0530 Subject: [PATCH 10/16] fix: Ordering and datatype fixes in inactive items report --- .../stock/report/inactive_items/inactive_items.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/erpnext/stock/report/inactive_items/inactive_items.py b/erpnext/stock/report/inactive_items/inactive_items.py index 95cec7bb348..3aaf1ce7090 100644 --- a/erpnext/stock/report/inactive_items/inactive_items.py +++ b/erpnext/stock/report/inactive_items/inactive_items.py @@ -27,21 +27,20 @@ def get_columns(): "fieldtype": "Link", "label": _("Item Group"), "options": "Item Group", - "width": 100 + "width": 150 }, { "fieldname": "item_name", "fieldtype": "Link", "options": "Item", "label": "Item", - "width": 100 + "width": 150 }, { - "fieldname": "Item Name", - "fieldtype": "Link", + "fieldname": "item_name", + "fieldtype": "Data", "label": _("Item Name"), - "options": "Item", - "width": 100 + "width": 150 }, { @@ -143,7 +142,7 @@ def get_items(filters): "name": filtesr["item"] }) - items = frappe.get_all("Item", fields=["name", "item_group", "item_name"], filters=filters_dict) + items = frappe.get_all("Item", fields=["name", "item_group", "item_name"], filters=filters_dict, order_by="name") return items From 11e1c60cd3b235f3274fcc8359758b384ebe73dc Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 22 Apr 2019 17:54:38 +0530 Subject: [PATCH 11/16] fix: Typo fixes --- erpnext/stock/report/inactive_items/inactive_items.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/inactive_items/inactive_items.py b/erpnext/stock/report/inactive_items/inactive_items.py index 3aaf1ce7090..188f59bf09e 100644 --- a/erpnext/stock/report/inactive_items/inactive_items.py +++ b/erpnext/stock/report/inactive_items/inactive_items.py @@ -139,7 +139,7 @@ def get_items(filters): if filters.get("item"): filters_dict.update({ - "name": filtesr["item"] + "name": filters["item"] }) items = frappe.get_all("Item", fields=["name", "item_group", "item_name"], filters=filters_dict, order_by="name") From a981a8a1536d053dd644534e7fc865bc68d5c8b1 Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 22 Apr 2019 21:08:29 +0530 Subject: [PATCH 12/16] fix: Ignore sql injections --- erpnext/stock/report/inactive_items/inactive_items.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/inactive_items/inactive_items.py b/erpnext/stock/report/inactive_items/inactive_items.py index 188f59bf09e..8d879126da6 100644 --- a/erpnext/stock/report/inactive_items/inactive_items.py +++ b/erpnext/stock/report/inactive_items/inactive_items.py @@ -117,7 +117,7 @@ def get_sales_details(filters): DATEDIFF(CURDATE(), {date_field}) as days_since_last_order from `tab{doctype}` s, `tab{doctype} Item` si where s.name = si.parent and s.docstatus = 1 - group by si.name order by days_since_last_order """ + group by si.name order by days_since_last_order """ #nosec .format(date_field = date_field, doctype = filters['based_on']), as_dict=1) for d in sales_data: From e534221245a01aff8613e89b3c2556bc6c87e34c Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Wed, 24 Apr 2019 16:22:34 +0200 Subject: [PATCH 13/16] fix: validate IBAN only if it exists --- erpnext/accounts/doctype/bank_account/bank_account.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/bank_account/bank_account.py b/erpnext/accounts/doctype/bank_account/bank_account.py index e71fca256ea..20ce7ca9a4c 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.py +++ b/erpnext/accounts/doctype/bank_account/bank_account.py @@ -21,9 +21,7 @@ class BankAccount(Document): def validate(self): self.validate_company() - - if self.iban: - self.validate_iban() + self.validate_iban() def validate_company(self): if self.is_company_account and not self.company: @@ -33,6 +31,10 @@ class BankAccount(Document): ''' Algorithm: https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN ''' + # IBAN field is optional + if not self.iban: + return + def encode_char(c): # Position in the alphabet (A=1, B=2, ...) plus nine return str(9 + ord(c) - 64) From d14799a9fa9034c581017f46a5e7f310f0f9b9a4 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Wed, 24 Apr 2019 18:06:14 +0200 Subject: [PATCH 14/16] fix: verbose error message when api keys are not setup --- .../doctype/plaid_settings/plaid_settings.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js index 76205e6c823..1f6499ec98a 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js @@ -22,6 +22,10 @@ erpnext.integrations.plaidLink = class plaidLink { frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.plaid_configuration') .then(result => { if (result !== "disabled") { + console.log(result) + if (result.plaid_env == undefined || result.plaid_public_key == undefined) { + frappe.throw(__("Please add valid Plaid api keys in site_config.json first")); + } me.plaid_env = result.plaid_env; me.plaid_public_key = result.plaid_public_key; me.client_name = result.client_name; From 97842ca8048d0a357824b429b537f62b25df54c8 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Wed, 24 Apr 2019 18:15:46 +0200 Subject: [PATCH 15/16] fix: cleanup development --- .../doctype/plaid_settings/plaid_settings.js | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js index 1f6499ec98a..ace4fbf9e30 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js @@ -22,7 +22,6 @@ erpnext.integrations.plaidLink = class plaidLink { frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.plaid_configuration') .then(result => { if (result !== "disabled") { - console.log(result) if (result.plaid_env == undefined || result.plaid_public_key == undefined) { frappe.throw(__("Please add valid Plaid api keys in site_config.json first")); } From 7d0bc2bd5ac907780c9a21407c6068581621e860 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 25 Apr 2019 01:26:27 +0530 Subject: [PATCH 16/16] fixed test cases --- erpnext/assets/doctype/asset/test_asset.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 985097b447d..ef85ffa1cb8 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -102,9 +102,9 @@ class TestAsset(unittest.TestCase): asset.save() self.assertEqual(asset.status, "Draft") expected_schedules = [ - ["2020-06-06", 163.93, 163.93], - ["2021-04-06", 49836.07, 50000.0], - ["2022-02-06", 40000.0, 90000.00] + ["2020-06-06", 147.54, 147.54], + ["2021-04-06", 44852.46, 45000.0], + ["2022-02-06", 45000.0, 90000.00] ] schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount] @@ -130,8 +130,8 @@ class TestAsset(unittest.TestCase): self.assertEqual(asset.status, "Draft") asset.save() expected_schedules = [ - ["2020-06-06", 197.37, 40197.37], - ["2021-04-06", 49802.63, 90000.00] + ["2020-06-06", 164.47, 40164.47], + ["2021-04-06", 49835.53, 90000.00] ] schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount] for d in asset.get("schedules")] @@ -266,8 +266,8 @@ class TestAsset(unittest.TestCase): self.assertEqual(asset.get("schedules")[0].journal_entry[:4], "DEPR") expected_gle = ( - ("_Test Accumulated Depreciations - _TC", 0.0, 35699.15), - ("_Test Depreciations - _TC", 35699.15, 0.0) + ("_Test Accumulated Depreciations - _TC", 0.0, 32129.24), + ("_Test Depreciations - _TC", 32129.24, 0.0) ) gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`