From 164a2ad28db541e1f68b0e9f4913991b067c721a Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 20 Sep 2021 04:33:30 +0530 Subject: [PATCH 001/105] fix: Calculate depreciation_left accurately --- erpnext/assets/doctype/asset/asset.py | 20 +++++++++++++++++--- erpnext/regional/india/utils.py | 5 +++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 8ff4f9790aa..7861c7e371f 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -221,7 +221,7 @@ class Asset(AccountsController): # If depreciation is already completed (for double declining balance) if skip_row: continue - depreciation_amount = get_depreciation_amount(self, value_after_depreciation, d) + depreciation_amount = get_depreciation_amount(self, value_after_depreciation, d, date_of_sale) if not has_pro_rata or n < cint(number_of_pending_depreciations) - 1: schedule_date = add_months(d.depreciation_start_date, @@ -835,8 +835,8 @@ def get_total_days(date, frequency): return date_diff(date, period_start_date) @erpnext.allow_regional -def get_depreciation_amount(asset, depreciable_value, row): - depreciation_left = flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked) +def get_depreciation_amount(asset, depreciable_value, row, date_of_sale=None): + depreciation_left = get_depreciation_left(asset, row, date_of_sale) if row.depreciation_method in ("Straight Line", "Manual"): # if the Depreciation Schedule is being prepared for the first time @@ -852,3 +852,17 @@ def get_depreciation_amount(asset, depreciable_value, row): depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100)) return depreciation_amount + +def get_depreciation_left(asset, row, date_of_sale): + if not date_of_sale: + return flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked) + else: + if len(asset.finance_books) == 1: + return (flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked)) - len(asset.schedules) + else: + depreciation_left = flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked) + for schedule in asset.get('schedules'): + if schedule.finance_book == row.finance_book: + depreciation_left -= 1 + + return depreciation_left diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 903168d009f..b1cbe70d14b 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -14,6 +14,7 @@ from erpnext.controllers.taxes_and_totals import get_itemised_tax, get_itemised_ from erpnext.hr.utils import get_salary_assignment from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip from erpnext.regional.india import number_state_mapping, state_numbers, states +from erpnext.assets.doctype.asset.asset import get_depreciation_left GST_INVOICE_NUMBER_FORMAT = re.compile(r"^[a-zA-Z0-9\-/]+$") #alphanumeric and - / GSTIN_FORMAT = re.compile("^[0-9]{2}[A-Z]{4}[0-9A-Z]{1}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}[1-9A-Z]{1}[0-9A-Z]{1}$") @@ -842,8 +843,8 @@ def update_taxable_values(doc, method): diff = additional_taxes - total_charges doc.get('items')[item_count - 1].taxable_value += diff -def get_depreciation_amount(asset, depreciable_value, row): - depreciation_left = flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked) +def get_depreciation_amount(asset, depreciable_value, row, date_of_sale=None): + depreciation_left = get_depreciation_left(asset, row, date_of_sale) if row.depreciation_method in ("Straight Line", "Manual"): # if the Depreciation Schedule is being prepared for the first time From 8396f24e70520eed6c6b73a90a7b92349cbb2139 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 20 Sep 2021 19:01:46 +0530 Subject: [PATCH 002/105] fix: Unlink PO on cancelling SO --- erpnext/controllers/accounts_controller.py | 49 ++++++++++++++++++- .../doctype/sales_order/sales_order.py | 6 +++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index b90db054b57..4da7a72b45e 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -19,7 +19,8 @@ from frappe.utils import ( get_link_to_form, getdate, nowdate, - today, + now, + today ) from six import text_type @@ -811,6 +812,52 @@ class AccountsController(TransactionBase): if frappe.db.get_single_value('Accounts Settings', 'unlink_advance_payment_on_cancelation_of_order'): unlink_ref_doc_from_payment_entries(self) + if self.doctype == "Sales Order": + self.unlink_ref_doc_from_po() + + def unlink_ref_doc_from_po(self): + print("\n"*5, "*"*50, "\n"*5) + + so_items = [] + for item in self.items: + so_items.append(item.name) + + print("SO Items: ", so_items) + + linked_po = frappe.get_all( + 'Purchase Order Item', + filters = { + 'sales_order': self.name, + 'sales_order_item': ['in', so_items], + 'docstatus': ['<', 2] + }, + pluck='parent' + ) + + print("Before unlinking: ", linked_po) + + if linked_po: + frappe.db.sql("""update `tabPurchase Order Item` + set sales_order = null, sales_order_item = null, + modified = %s, modified_by = %s + where sales_order = %s and sales_order_item in %s + and docstatus < 2""", (now(), frappe.session.user, self.name, so_items)) + + frappe.msgprint(_("Purchase Orders {0} are un-linked").format("\n".join(linked_po))) + + linked_po = frappe.get_all( + 'Purchase Order Item', + filters = { + 'sales_order': self.name, + 'sales_order_item': ['in', so_items], + 'docstatus': ['<', 2] + }, + pluck='parent' + ) + print("After unlinking: ", linked_po) + + print("\n"*5, "*"*50, "\n"*5) + def get_tax_map(self): tax_map = {} for tax in self.get('taxes'): diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 93676094218..4397d19dca1 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -209,6 +209,12 @@ class SalesOrder(SellingController): from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count update_coupon_code_count(self.coupon_code,'cancelled') + def cancel(self): + import pdb + + pdb.set_trace() + super(SalesOrder, self).cancel() + def update_project(self): if frappe.db.get_single_value('Selling Settings', 'sales_update_frequency') != "Each Transaction": return From e77534fe13a8384a066352f028eb26fb3520f26f Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 20 Sep 2021 19:01:46 +0530 Subject: [PATCH 003/105] fix: Unlink PO on cancelling SO --- erpnext/controllers/accounts_controller.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 8289b8a0807..075069c6e84 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -837,11 +837,16 @@ class AccountsController(TransactionBase): print("Before unlinking: ", linked_po) if linked_po: - frappe.db.sql("""update `tabPurchase Order Item` - set sales_order = null, sales_order_item = null, - modified = %s, modified_by = %s - where sales_order = %s and sales_order_item in %s - and docstatus < 2""", (now(), frappe.session.user, self.name, so_items)) + frappe.db.set_value( + 'Purchase Order Item', { + 'sales_order': self.name, + 'sales_order_item': ['in', so_items], + 'docstatus': ['<', 2] + },{ + 'sales_order': None, + 'sales_order_item': None + } + ) frappe.msgprint(_("Purchase Orders {0} are un-linked").format("\n".join(linked_po))) From 244d9dee044091ad3f27c6ddec32d2e45165363f Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 21 Sep 2021 06:06:21 +0530 Subject: [PATCH 004/105] fix: Correct expected_values --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 3720ac33bbd..e9eb6ca36a6 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2214,7 +2214,7 @@ class TestSalesInvoice(unittest.TestCase): expected_values = [ ["2020-06-30", 1311.48, 1311.48], ["2021-06-30", 20000.0, 21311.48], - ["2021-09-30", 3966.76, 25278.24] + ["2021-09-30", 5041.1, 26352.58] ] for i, schedule in enumerate(asset.schedules): From 3c8879e777eccd5b4a0c61f9d932e421662848eb Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 21 Sep 2021 06:07:06 +0530 Subject: [PATCH 005/105] fix: Calculate depreciation_amount accurately --- erpnext/assets/doctype/asset/asset.py | 22 ++++------------------ erpnext/regional/india/utils.py | 7 +++---- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 7861c7e371f..01f4935feec 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -221,7 +221,7 @@ class Asset(AccountsController): # If depreciation is already completed (for double declining balance) if skip_row: continue - depreciation_amount = get_depreciation_amount(self, value_after_depreciation, d, date_of_sale) + depreciation_amount = get_depreciation_amount(self, value_after_depreciation, d) if not has_pro_rata or n < cint(number_of_pending_depreciations) - 1: schedule_date = add_months(d.depreciation_start_date, @@ -835,13 +835,13 @@ def get_total_days(date, frequency): return date_diff(date, period_start_date) @erpnext.allow_regional -def get_depreciation_amount(asset, depreciable_value, row, date_of_sale=None): - depreciation_left = get_depreciation_left(asset, row, date_of_sale) +def get_depreciation_amount(asset, depreciable_value, row): + depreciation_left = flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked) if row.depreciation_method in ("Straight Line", "Manual"): # if the Depreciation Schedule is being prepared for the first time if not asset.flags.increase_in_asset_life: - depreciation_amount = (flt(row.value_after_depreciation) - + depreciation_amount = ((flt(asset.gross_purchase_amount) - flt(asset.opening_accumulated_depreciation)) - flt(row.expected_value_after_useful_life)) / depreciation_left # if the Depreciation Schedule is being modified after Asset Repair @@ -852,17 +852,3 @@ def get_depreciation_amount(asset, depreciable_value, row, date_of_sale=None): depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100)) return depreciation_amount - -def get_depreciation_left(asset, row, date_of_sale): - if not date_of_sale: - return flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked) - else: - if len(asset.finance_books) == 1: - return (flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked)) - len(asset.schedules) - else: - depreciation_left = flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked) - for schedule in asset.get('schedules'): - if schedule.finance_book == row.finance_book: - depreciation_left -= 1 - - return depreciation_left diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index b1cbe70d14b..bfccb48c5a3 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -14,7 +14,6 @@ from erpnext.controllers.taxes_and_totals import get_itemised_tax, get_itemised_ from erpnext.hr.utils import get_salary_assignment from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip from erpnext.regional.india import number_state_mapping, state_numbers, states -from erpnext.assets.doctype.asset.asset import get_depreciation_left GST_INVOICE_NUMBER_FORMAT = re.compile(r"^[a-zA-Z0-9\-/]+$") #alphanumeric and - / GSTIN_FORMAT = re.compile("^[0-9]{2}[A-Z]{4}[0-9A-Z]{1}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}[1-9A-Z]{1}[0-9A-Z]{1}$") @@ -843,13 +842,13 @@ def update_taxable_values(doc, method): diff = additional_taxes - total_charges doc.get('items')[item_count - 1].taxable_value += diff -def get_depreciation_amount(asset, depreciable_value, row, date_of_sale=None): - depreciation_left = get_depreciation_left(asset, row, date_of_sale) +def get_depreciation_amount(asset, depreciable_value, row): + depreciation_left = flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked) if row.depreciation_method in ("Straight Line", "Manual"): # if the Depreciation Schedule is being prepared for the first time if not asset.flags.increase_in_asset_life: - depreciation_amount = (flt(row.value_after_depreciation) - + depreciation_amount = ((flt(asset.gross_purchase_amount) - flt(asset.opening_accumulated_depreciation)) - flt(row.expected_value_after_useful_life)) / depreciation_left # if the Depreciation Schedule is being modified after Asset Repair From 700e78d69b2e4e8f12dafc20e536b08c99cd2852 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 21 Sep 2021 07:03:12 +0530 Subject: [PATCH 006/105] fix: Remove extra brackets --- erpnext/assets/doctype/asset/asset.py | 2 +- erpnext/regional/india/utils.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 01f4935feec..5c552f9850e 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -841,7 +841,7 @@ def get_depreciation_amount(asset, depreciable_value, row): if row.depreciation_method in ("Straight Line", "Manual"): # if the Depreciation Schedule is being prepared for the first time if not asset.flags.increase_in_asset_life: - depreciation_amount = ((flt(asset.gross_purchase_amount) - flt(asset.opening_accumulated_depreciation)) - + depreciation_amount = (flt(asset.gross_purchase_amount) - flt(asset.opening_accumulated_depreciation) - flt(row.expected_value_after_useful_life)) / depreciation_left # if the Depreciation Schedule is being modified after Asset Repair diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 01742d11c3d..5c3a48e77bd 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -845,7 +845,7 @@ def get_depreciation_amount(asset, depreciable_value, row): if row.depreciation_method in ("Straight Line", "Manual"): # if the Depreciation Schedule is being prepared for the first time if not asset.flags.increase_in_asset_life: - depreciation_amount = ((flt(asset.gross_purchase_amount) - flt(asset.opening_accumulated_depreciation)) - + depreciation_amount = (flt(asset.gross_purchase_amount) - flt(asset.opening_accumulated_depreciation) - flt(row.expected_value_after_useful_life)) / depreciation_left # if the Depreciation Schedule is being modified after Asset Repair From 249672c35db9278e1c4027b2331e1b6b5ef8f7d2 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 21 Sep 2021 07:04:39 +0530 Subject: [PATCH 007/105] fix: Add depreciation_schedule details in create_asset() --- erpnext/assets/doctype/asset/test_asset.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 4cc9be5b05d..b4e891e06c1 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -704,9 +704,8 @@ def create_asset(**args): "calculate_depreciation": args.calculate_depreciation or 0, "gross_purchase_amount": 100000, "purchase_receipt_amount": 100000, - "expected_value_after_useful_life": 10000, "warehouse": args.warehouse or "_Test Warehouse - _TC", - "available_for_use_date": "2020-06-06", + "available_for_use_date": args.available_for_use_date or "2020-06-06", "location": "Test Location", "asset_owner": "Company", "is_existing_asset": 1 @@ -715,8 +714,10 @@ def create_asset(**args): if asset.calculate_depreciation: asset.append("finance_books", { "depreciation_method": "Straight Line", - "frequency_of_depreciation": 12, - "total_number_of_depreciations": 5 + "frequency_of_depreciation": args.frequency_of_depreciation or 12, + "total_number_of_depreciations": args.total_number_of_depreciations or 5, + "expected_value_after_useful_life": args.expected_value_after_useful_life or 0, + "depreciation_start_date": args.depreciation_start_date }) try: From 7ab3b9dd5a8877b1c1dff6eb1d5ab453b0a4c8f5 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 21 Sep 2021 07:07:00 +0530 Subject: [PATCH 008/105] fix: Add test for depreciation on sale of a depreciated Asset --- .../sales_invoice/test_sales_invoice.py | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index e9eb6ca36a6..1c75f5cc51d 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2201,7 +2201,7 @@ class TestSalesInvoice(unittest.TestCase): def test_asset_depreciation_on_sale(self): """ - Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on Sept 30. + Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on its date of sale. """ create_asset_data() @@ -2223,6 +2223,32 @@ class TestSalesInvoice(unittest.TestCase): self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount) self.assertTrue(schedule.journal_entry) + def test_depreciation_on_sale_for_depreciated_asset(self): + """ + Tests if an Asset set to depreciate yearly on Dec 31, that gets sold on Dec 31 after two years, created an additional depreciation entry on its date of sale. + """ + + create_asset_data() + asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, + available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3, + expected_value_after_useful_life=10000, depreciation_start_date=getdate("2020-12-31"), submit=1) + + post_depreciation_entries(getdate("2021-09-30")) + + create_sales_invoice(item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000, posting_date=getdate("2021-12-31")) + asset.load_from_db() + + expected_values = [ + ["2020-12-31", 30000, 30000], + ["2021-12-31", 30000, 60000] + ] + + for i, schedule in enumerate(asset.schedules): + self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date) + self.assertEqual(expected_values[i][1], schedule.depreciation_amount) + self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount) + self.assertTrue(schedule.journal_entry) + def test_sales_invoice_against_supplier(self): from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import ( make_customer, From b9fb59da58aa4ac5f0e287fcdce7d2993d9af9c7 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 25 Sep 2021 19:04:16 +0530 Subject: [PATCH 009/105] fix: Reset depreciation schedule on returning asset --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 4 ++-- erpnext/assets/doctype/asset/asset.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index ca6a77a2af7..64cbd24e9f0 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1015,7 +1015,7 @@ class SalesInvoice(SellingController): def depreciate_asset(self, asset): asset.flags.ignore_validate_update_after_submit = True - asset.prepare_depreciation_data(self.posting_date) + asset.prepare_depreciation_data(date_of_sale=self.posting_date) asset.save() post_depreciation_entries(self.posting_date) @@ -1024,7 +1024,7 @@ class SalesInvoice(SellingController): asset.flags.ignore_validate_update_after_submit = True # recreate original depreciation schedule of the asset - asset.prepare_depreciation_data() + asset.prepare_depreciation_data(date_of_return=self.posting_date) self.modify_depreciation_schedule_for_asset_repairs(asset) asset.save() diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 5c552f9850e..653fa662fd0 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -75,12 +75,12 @@ class Asset(AccountsController): if self.is_existing_asset and self.purchase_invoice: frappe.throw(_("Purchase Invoice cannot be made against an existing asset {0}").format(self.name)) - def prepare_depreciation_data(self, date_of_sale=None): + def prepare_depreciation_data(self, date_of_sale=None, date_of_return=None): if self.calculate_depreciation: self.value_after_depreciation = 0 self.set_depreciation_rate() self.make_depreciation_schedule(date_of_sale) - self.set_accumulated_depreciation(date_of_sale) + self.set_accumulated_depreciation(date_of_sale, date_of_return) else: self.finance_books = [] self.value_after_depreciation = (flt(self.gross_purchase_amount) - @@ -406,7 +406,7 @@ class Asset(AccountsController): frappe.throw(_("Depreciation Row {0}: Next Depreciation Date cannot be before Available-for-use Date") .format(row.idx)) - def set_accumulated_depreciation(self, date_of_sale=None, ignore_booked_entry = False): + def set_accumulated_depreciation(self, date_of_sale=None, date_of_return=None, ignore_booked_entry = False): straight_line_idx = [d.idx for d in self.get("schedules") if d.depreciation_method == 'Straight Line'] finance_books = [] @@ -423,7 +423,7 @@ class Asset(AccountsController): value_after_depreciation -= flt(depreciation_amount) # for the last row, if depreciation method = Straight Line - if straight_line_idx and i == max(straight_line_idx) - 1 and not date_of_sale: + if straight_line_idx and i == max(straight_line_idx) - 1 and not date_of_sale and not date_of_return: book = self.get('finance_books')[cint(d.finance_book_id) - 1] depreciation_amount += flt(value_after_depreciation - flt(book.expected_value_after_useful_life), d.precision("depreciation_amount")) From 796ed947ce2217496706bf00e7c3ddf8c4fc2e3a Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 25 Sep 2021 19:11:29 +0530 Subject: [PATCH 010/105] fix: Reverse depreciation entry made on sale if asset that was set to be sold in the future gets returned --- .../doctype/sales_invoice/sales_invoice.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 64cbd24e9f0..e969aa6cc4d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -952,6 +952,7 @@ class SalesInvoice(SellingController): asset.db_set("disposal_date", None) if asset.calculate_depreciation: + self.reverse_depreciation_entry_made_after_sale(asset) self.reset_depreciation_schedule(asset) else: @@ -1029,8 +1030,6 @@ class SalesInvoice(SellingController): self.modify_depreciation_schedule_for_asset_repairs(asset) asset.save() - self.delete_depreciation_entry_made_after_sale(asset) - def modify_depreciation_schedule_for_asset_repairs(self, asset): asset_repairs = frappe.get_all( 'Asset Repair', @@ -1044,7 +1043,7 @@ class SalesInvoice(SellingController): asset_repair.modify_depreciation_schedule() asset.prepare_depreciation_data() - def delete_depreciation_entry_made_after_sale(self, asset): + def reverse_depreciation_entry_made_after_sale(self, asset): from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry posting_date_of_original_invoice = self.get_posting_date_of_sales_invoice() @@ -1059,7 +1058,8 @@ class SalesInvoice(SellingController): row += 1 if schedule.schedule_date == posting_date_of_original_invoice: - if not self.sale_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_original_invoice): + if not self.sale_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_original_invoice) \ + or self.sale_happens_in_the_future(posting_date_of_original_invoice): reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry) reverse_journal_entry.posting_date = nowdate() reverse_journal_entry.submit() @@ -1078,6 +1078,12 @@ class SalesInvoice(SellingController): return True return False + def sale_happens_in_the_future(self, posting_date_of_original_invoice): + if posting_date_of_original_invoice > getdate(): + return True + + return False + @property def enable_discount_accounting(self): if not hasattr(self, "_enable_discount_accounting"): From fdd9e6cc3c62a85573b7f02ff4940046d572e92b Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 27 Sep 2021 22:14:16 +0530 Subject: [PATCH 011/105] fix: Replace asset.schedules with asset.get('schedules') --- erpnext/assets/doctype/asset/asset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 653fa662fd0..0138a1282a8 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -187,7 +187,7 @@ class Asset(AccountsController): d.precision("rate_of_depreciation")) def make_depreciation_schedule(self, date_of_sale): - if 'Manual' not in [d.depreciation_method for d in self.finance_books] and not self.schedules: + if 'Manual' not in [d.depreciation_method for d in self.finance_books] and not self.get('schedules'): self.schedules = [] if not self.available_for_use_date: From 40ec2d622baeb43f45086fcb7298457100a94f40 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 27 Sep 2021 22:14:42 +0530 Subject: [PATCH 012/105] fix: Add tests for depreciation --- erpnext/assets/doctype/asset/test_asset.py | 256 ++++++++++++++++++++- 1 file changed, 252 insertions(+), 4 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index b4e891e06c1..e747204a696 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -276,6 +276,7 @@ class TestAsset(unittest.TestCase): self.assertEqual(gle, expected_gle) self.assertEqual(asset.get("value_after_depreciation"), 0) + # WDV: Written Down Value def test_depreciation_entry_for_wdv_without_pro_rata(self): pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=8000.0, location="Test Location") @@ -479,6 +480,7 @@ class TestAsset(unittest.TestCase): self.assertTrue(asset.finance_books[0].expected_value_after_useful_life >= asset_value_after_full_schedule) + # CWIP: Capital Work In Progress def test_cwip_accounting(self): pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=5000, do_not_submit=True, location="Test Location") @@ -676,6 +678,249 @@ class TestAsset(unittest.TestCase): # reset indian company frappe.flags.company = company_flag + def test_depreciation_without_pro_rata(self): + asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, + available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3, + expected_value_after_useful_life=10000, depreciation_start_date=getdate("2020-12-31"), submit=1) + + expected_values = [ + ["2020-12-31", 30000, 30000], + ["2021-12-31", 30000, 60000], + ["2022-12-31", 30000, 90000] + ] + + for i, schedule in enumerate(asset.schedules): + self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date) + self.assertEqual(expected_values[i][1], schedule.depreciation_amount) + self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount) + + def test_depreciation_with_pro_rata(self): + asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, + available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3, + expected_value_after_useful_life=10000, depreciation_start_date=getdate("2020-07-01"), submit=1) + + expected_values = [ + ["2020-07-01", 15000, 15000], + ["2021-07-01", 30000, 45000], + ["2022-07-01", 30000, 75000], + ["2022-12-31", 15000, 90000] + ] + + for i, schedule in enumerate(asset.schedules): + self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date) + self.assertEqual(expected_values[i][1], schedule.depreciation_amount) + self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount) + + def test_get_depreciation_amount(self): + """Tests if get_depreciation_amount() returns the right value.""" + + from erpnext.assets.doctype.asset.asset import get_depreciation_amount + + asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, + available_for_use_date=getdate("2019-12-31")) + + asset.finance_books = [] + asset.append("finance_books", { + "depreciation_method": "Straight Line", + "frequency_of_depreciation": 12, + "total_number_of_depreciations": 3, + "expected_value_after_useful_life": 10000, + "depreciation_start_date": getdate("2020-12-31") + }) + + depreciation_amount = get_depreciation_amount(asset, 100000, asset.finance_books[0]) + self.assertEqual(depreciation_amount, 30000) + + def test_make_depreciation_schedule(self): + """Tests if make_depreciation_schedule() returns the right values.""" + + asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, + available_for_use_date=getdate("2019-12-31"), do_not_save=1) + + asset.finance_books = [] + asset.append("finance_books", { + "depreciation_method": "Straight Line", + "frequency_of_depreciation": 12, + "total_number_of_depreciations": 3, + "expected_value_after_useful_life": 10000, + "depreciation_start_date": getdate("2020-12-31") + }) + + asset.make_depreciation_schedule(date_of_sale=None) + + expected_values = [ + ['2020-12-31', 30000.0], + ['2021-12-31', 30000.0], + ['2022-12-31', 30000.0] + ] + + for i, schedule in enumerate(asset.schedules): + self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date) + self.assertEqual(expected_values[i][1], schedule.depreciation_amount) + + def test_set_accumulated_depreciation(self): + """Tests if set_accumulated_depreciation() returns the right values.""" + + asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, + available_for_use_date=getdate("2019-12-31"), do_not_save=1) + + asset.finance_books = [] + asset.append("finance_books", { + "depreciation_method": "Straight Line", + "frequency_of_depreciation": 12, + "total_number_of_depreciations": 3, + "expected_value_after_useful_life": 10000, + "depreciation_start_date": getdate("2020-12-31") + }) + + asset.make_depreciation_schedule(date_of_sale=None) + asset.set_accumulated_depreciation() + + expected_values = [30000.0, 60000.0, 90000.0] + + for i, schedule in enumerate(asset.schedules): + self.assertEqual(expected_values[i], schedule.accumulated_depreciation_amount) + + def test_check_is_pro_rata(self): + """Tests if check_is_pro_rata() returns the right value(i.e. checks if has_pro_rata is accurate).""" + + asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, + available_for_use_date=getdate("2019-12-31"), do_not_save=1) + + asset.finance_books = [] + asset.append("finance_books", { + "depreciation_method": "Straight Line", + "frequency_of_depreciation": 12, + "total_number_of_depreciations": 3, + "expected_value_after_useful_life": 10000, + "depreciation_start_date": getdate("2020-12-31") + }) + + has_pro_rata = asset.check_is_pro_rata(asset.finance_books[0]) + self.assertFalse(has_pro_rata) + + asset.finance_books = [] + asset.append("finance_books", { + "depreciation_method": "Straight Line", + "frequency_of_depreciation": 12, + "total_number_of_depreciations": 3, + "expected_value_after_useful_life": 10000, + "depreciation_start_date": getdate("2020-07-01") + }) + + has_pro_rata = asset.check_is_pro_rata(asset.finance_books[0]) + self.assertTrue(has_pro_rata) + + def test_expected_value_after_useful_life(self): + """Tests if an error is raised when expected_value_after_useful_life(110,000) > gross_purchase_amount(100,000).""" + + asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, + available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3, + expected_value_after_useful_life=110000, depreciation_start_date=getdate("2020-07-01"), do_not_save=1) + + self.assertRaises(frappe.ValidationError, asset.save) + + def test_depreciation_start_date(self): + """Tests if an error is raised when neither depreciation_start_date nor available_for_use_date are specified.""" + + asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, + total_number_of_depreciations=3, expected_value_after_useful_life=110000, do_not_save=1) + + self.assertRaises(frappe.ValidationError, asset.save) + + def test_opening_accumulated_depreciation(self): + """Tests if an error is raised when opening_accumulated_depreciation > (gross_purchase_amount - expected_value_after_useful_life).""" + + asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, + available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3, + expected_value_after_useful_life=10000, depreciation_start_date=getdate("2020-07-01"), + opening_accumulated_depreciation=100000, do_not_save=1) + + self.assertRaises(frappe.ValidationError, asset.save) + + def test_number_of_depreciations_booked(self): + """Tests if an error is raised when number_of_depreciations_booked is not specified when opening_accumulated_depreciation is.""" + + asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, + available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3, + expected_value_after_useful_life=10000, depreciation_start_date=getdate("2020-07-01"), + opening_accumulated_depreciation=10000, do_not_save=1) + + self.assertRaises(frappe.ValidationError, asset.save) + + def test_number_of_depreciations(self): + """Tests if an error is raised when number_of_depreciations_booked > total_number_of_depreciations.""" + + asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, + available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3, + expected_value_after_useful_life=10000, depreciation_start_date=getdate("2020-07-01"), + opening_accumulated_depreciation=10000, number_of_depreciations_booked=5, + do_not_save=1) + + self.assertRaises(frappe.ValidationError, asset.save) + + def test_depreciation_start_date_is_before_purchase_date(self): + asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, + available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3, + expected_value_after_useful_life=10000, depreciation_start_date=getdate("2014-07-01"), + do_not_save=1) + + self.assertRaises(frappe.ValidationError, asset.save) + + def test_depreciation_start_date_is_before_available_for_use_date(self): + asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, + available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3, + expected_value_after_useful_life=10000, depreciation_start_date=getdate("2018-07-01"), + do_not_save=1) + + self.assertRaises(frappe.ValidationError, asset.save) + + def test_post_depreciation_entries(self): + """Tests if post_depreciation_entries() works as expected.""" + + asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, + available_for_use_date=getdate("2019-12-31"), do_not_save=1) + + asset.finance_books = [] + asset.append("finance_books", { + "depreciation_method": "Straight Line", + "frequency_of_depreciation": 12, + "total_number_of_depreciations": 3, + "expected_value_after_useful_life": 10000, + "depreciation_start_date": getdate("2020-12-31") + }) + asset.submit() + + post_depreciation_entries(date="2021-06-01") + asset.load_from_db() + + self.assertTrue(asset.schedules[0].journal_entry) + self.assertFalse(asset.schedules[1].journal_entry) + self.assertFalse(asset.schedules[2].journal_entry) + + def test_clear_depreciation_schedule(self): + """Tests if clear_depreciation_schedule() works as expected.""" + + asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, + available_for_use_date=getdate("2019-12-31"), do_not_save=1) + + asset.finance_books = [] + asset.append("finance_books", { + "depreciation_method": "Straight Line", + "frequency_of_depreciation": 12, + "total_number_of_depreciations": 3, + "expected_value_after_useful_life": 10000, + "depreciation_start_date": getdate("2020-12-31") + }) + asset.submit() + + post_depreciation_entries(date="2021-06-01") + asset.load_from_db() + + asset.clear_depreciation_schedule() + + self.assertEqual(len(asset.schedules), 1) + def create_asset_data(): if not frappe.db.exists("Asset Category", "Computers"): create_asset_category() @@ -702,6 +947,8 @@ def create_asset(**args): "company": args.company or"_Test Company", "purchase_date": "2015-01-01", "calculate_depreciation": args.calculate_depreciation or 0, + "opening_accumulated_depreciation": args.opening_accumulated_depreciation or 0, + "number_of_depreciations_booked": args.number_of_depreciations_booked or 0, "gross_purchase_amount": 100000, "purchase_receipt_amount": 100000, "warehouse": args.warehouse or "_Test Warehouse - _TC", @@ -720,10 +967,11 @@ def create_asset(**args): "depreciation_start_date": args.depreciation_start_date }) - try: - asset.save() - except frappe.DuplicateEntryError: - pass + if not args.do_not_save: + try: + asset.save() + except frappe.DuplicateEntryError: + pass if args.submit: asset.submit() From c84c983073943ae711100b265a981dfdc73c5fd6 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 28 Sep 2021 01:22:38 +0530 Subject: [PATCH 013/105] fix: Categorize into test suites --- erpnext/assets/doctype/asset/test_asset.py | 621 +++++++++++---------- 1 file changed, 312 insertions(+), 309 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index e747204a696..b5488750eba 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -20,13 +20,13 @@ from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( ) from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt - -class TestAsset(unittest.TestCase): +class AssetSetup(unittest.TestCase): def setUp(self): set_depreciation_settings_in_company() create_asset_data() frappe.db.sql("delete from `tabTax Rule`") +class TestAsset(AssetSetup): def test_purchase_asset(self): pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location") @@ -89,287 +89,6 @@ class TestAsset(unittest.TestCase): doc.set_missing_values() self.assertEqual(doc.items[0].is_fixed_asset, 1) - def test_schedule_for_straight_line_method(self): - pr = make_purchase_receipt(item_code="Macbook Pro", - qty=1, rate=100000.0, location="Test Location") - - asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') - asset = frappe.get_doc('Asset', asset_name) - asset.calculate_depreciation = 1 - asset.available_for_use_date = '2030-01-01' - asset.purchase_date = '2030-01-01' - - asset.append("finance_books", { - "expected_value_after_useful_life": 10000, - "depreciation_method": "Straight Line", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 12, - "depreciation_start_date": "2030-12-31" - }) - asset.save() - - self.assertEqual(asset.status, "Draft") - expected_schedules = [ - ["2030-12-31", 30000.00, 30000.00], - ["2031-12-31", 30000.00, 60000.00], - ["2032-12-31", 30000.00, 90000.00] - ] - - schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount] - for d in asset.get("schedules")] - - self.assertEqual(schedules, expected_schedules) - - def test_schedule_for_straight_line_method_for_existing_asset(self): - create_asset(is_existing_asset=1) - asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"}) - asset.calculate_depreciation = 1 - asset.number_of_depreciations_booked = 1 - asset.opening_accumulated_depreciation = 40000 - asset.available_for_use_date = "2030-06-06" - asset.append("finance_books", { - "expected_value_after_useful_life": 10000, - "depreciation_method": "Straight Line", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 12, - "depreciation_start_date": "2030-12-31" - }) - self.assertEqual(asset.status, "Draft") - asset.save() - expected_schedules = [ - ["2030-12-31", 14246.58, 54246.58], - ["2031-12-31", 25000.00, 79246.58], - ["2032-06-06", 10753.42, 90000.00] - ] - schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount] - for d in asset.get("schedules")] - - self.assertEqual(schedules, expected_schedules) - - def test_schedule_for_double_declining_method(self): - pr = make_purchase_receipt(item_code="Macbook Pro", - qty=1, rate=100000.0, location="Test Location") - - asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') - asset = frappe.get_doc('Asset', asset_name) - asset.calculate_depreciation = 1 - asset.available_for_use_date = '2030-01-01' - asset.purchase_date = '2030-01-01' - asset.append("finance_books", { - "expected_value_after_useful_life": 10000, - "depreciation_method": "Double Declining Balance", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 12, - "depreciation_start_date": '2030-12-31' - }) - asset.save() - self.assertEqual(asset.status, "Draft") - - expected_schedules = [ - ['2030-12-31', 66667.00, 66667.00], - ['2031-12-31', 22222.11, 88889.11], - ['2032-12-31', 1110.89, 90000.0] - ] - - schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount] - for d in asset.get("schedules")] - - self.assertEqual(schedules, expected_schedules) - - def test_schedule_for_double_declining_method_for_existing_asset(self): - create_asset(is_existing_asset = 1) - asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"}) - asset.calculate_depreciation = 1 - asset.is_existing_asset = 1 - asset.number_of_depreciations_booked = 1 - asset.opening_accumulated_depreciation = 50000 - asset.available_for_use_date = '2030-01-01' - asset.purchase_date = '2029-11-30' - asset.append("finance_books", { - "expected_value_after_useful_life": 10000, - "depreciation_method": "Double Declining Balance", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 12, - "depreciation_start_date": "2030-12-31" - }) - asset.save() - self.assertEqual(asset.status, "Draft") - - expected_schedules = [ - ["2030-12-31", 33333.50, 83333.50], - ["2031-12-31", 6666.50, 90000.0] - ] - - schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount] - for d in asset.get("schedules")] - - self.assertEqual(schedules, expected_schedules) - - def test_schedule_for_prorated_straight_line_method(self): - pr = make_purchase_receipt(item_code="Macbook Pro", - qty=1, rate=100000.0, location="Test Location") - - asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') - asset = frappe.get_doc('Asset', asset_name) - asset.calculate_depreciation = 1 - asset.purchase_date = '2030-01-30' - asset.is_existing_asset = 0 - asset.available_for_use_date = "2030-01-30" - asset.append("finance_books", { - "expected_value_after_useful_life": 10000, - "depreciation_method": "Straight Line", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 12, - "depreciation_start_date": "2030-12-31" - }) - - asset.save() - - expected_schedules = [ - ["2030-12-31", 27534.25, 27534.25], - ["2031-12-31", 30000.0, 57534.25], - ["2032-12-31", 30000.0, 87534.25], - ["2033-01-30", 2465.75, 90000.0] - ] - - schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)] - for d in asset.get("schedules")] - - self.assertEqual(schedules, expected_schedules) - - def test_depreciation(self): - pr = make_purchase_receipt(item_code="Macbook Pro", - qty=1, rate=100000.0, location="Test Location") - - asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') - asset = frappe.get_doc('Asset', asset_name) - asset.calculate_depreciation = 1 - asset.purchase_date = '2020-01-30' - asset.available_for_use_date = "2020-01-30" - asset.append("finance_books", { - "expected_value_after_useful_life": 10000, - "depreciation_method": "Straight Line", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 10, - "depreciation_start_date": "2020-12-31" - }) - asset.submit() - asset.load_from_db() - self.assertEqual(asset.status, "Submitted") - - frappe.db.set_value("Company", "_Test Company", "series_for_depreciation_entry", "DEPR-") - post_depreciation_entries(date="2021-01-01") - asset.load_from_db() - - # check depreciation entry series - self.assertEqual(asset.get("schedules")[0].journal_entry[:4], "DEPR") - - expected_gle = ( - ("_Test Accumulated Depreciations - _TC", 0.0, 30000.0), - ("_Test Depreciations - _TC", 30000.0, 0.0) - ) - - gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry` - where against_voucher_type='Asset' and against_voucher = %s - order by account""", asset.name) - - self.assertEqual(gle, expected_gle) - self.assertEqual(asset.get("value_after_depreciation"), 0) - - # WDV: Written Down Value - def test_depreciation_entry_for_wdv_without_pro_rata(self): - pr = make_purchase_receipt(item_code="Macbook Pro", - qty=1, rate=8000.0, location="Test Location") - - asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') - asset = frappe.get_doc('Asset', asset_name) - asset.calculate_depreciation = 1 - asset.available_for_use_date = '2030-01-01' - asset.purchase_date = '2030-01-01' - asset.append("finance_books", { - "expected_value_after_useful_life": 1000, - "depreciation_method": "Written Down Value", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 12, - "depreciation_start_date": "2030-12-31" - }) - asset.save(ignore_permissions=True) - - self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0) - - expected_schedules = [ - ["2030-12-31", 4000.00, 4000.00], - ["2031-12-31", 2000.00, 6000.00], - ["2032-12-31", 1000.00, 7000.0], - ] - - schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)] - for d in asset.get("schedules")] - - self.assertEqual(schedules, expected_schedules) - - def test_pro_rata_depreciation_entry_for_wdv(self): - pr = make_purchase_receipt(item_code="Macbook Pro", - qty=1, rate=8000.0, location="Test Location") - - asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') - asset = frappe.get_doc('Asset', asset_name) - asset.calculate_depreciation = 1 - asset.available_for_use_date = '2030-06-06' - asset.purchase_date = '2030-01-01' - asset.append("finance_books", { - "expected_value_after_useful_life": 1000, - "depreciation_method": "Written Down Value", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 12, - "depreciation_start_date": "2030-12-31" - }) - asset.save(ignore_permissions=True) - - self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0) - - expected_schedules = [ - ["2030-12-31", 2279.45, 2279.45], - ["2031-12-31", 2860.28, 5139.73], - ["2032-12-31", 1430.14, 6569.87], - ["2033-06-06", 430.13, 7000.0], - ] - - schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)] - for d in asset.get("schedules")] - - self.assertEqual(schedules, expected_schedules) - - def test_depreciation_entry_cancellation(self): - pr = make_purchase_receipt(item_code="Macbook Pro", - qty=1, rate=100000.0, location="Test Location") - - asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') - asset = frappe.get_doc('Asset', asset_name) - asset.calculate_depreciation = 1 - asset.available_for_use_date = '2020-06-06' - asset.purchase_date = '2020-06-06' - asset.append("finance_books", { - "expected_value_after_useful_life": 10000, - "depreciation_method": "Straight Line", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 10, - "depreciation_start_date": "2020-12-31" - }) - asset.submit() - post_depreciation_entries(date="2021-01-01") - - asset.load_from_db() - - # cancel depreciation entry - depr_entry = asset.get("schedules")[0].journal_entry - self.assertTrue(depr_entry) - frappe.get_doc("Journal Entry", depr_entry).cancel() - - asset.load_from_db() - depr_entry = asset.get("schedules")[0].journal_entry - self.assertFalse(depr_entry) - def test_scrap_asset(self): pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location") @@ -412,7 +131,7 @@ class TestAsset(unittest.TestCase): self.assertFalse(asset.journal_entry_for_scrap) self.assertEqual(asset.status, "Partially Depreciated") - def test_asset_sale(self): + def test_gle_made_by_asset_sale(self): pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location") @@ -456,30 +175,6 @@ class TestAsset(unittest.TestCase): si.cancel() self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Partially Depreciated") - def test_asset_expected_value_after_useful_life(self): - pr = make_purchase_receipt(item_code="Macbook Pro", - qty=1, rate=100000.0, location="Test Location") - - asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') - asset = frappe.get_doc('Asset', asset_name) - asset.calculate_depreciation = 1 - asset.available_for_use_date = '2020-06-06' - asset.purchase_date = '2020-06-06' - asset.append("finance_books", { - "expected_value_after_useful_life": 10000, - "depreciation_method": "Straight Line", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 10 - }) - asset.save() - accumulated_depreciation_after_full_schedule = \ - max(d.accumulated_depreciation_amount for d in asset.get("schedules")) - - asset_value_after_full_schedule = (flt(asset.gross_purchase_amount) - - flt(accumulated_depreciation_after_full_schedule)) - - self.assertTrue(asset.finance_books[0].expected_value_after_useful_life >= asset_value_after_full_schedule) - # CWIP: Capital Work In Progress def test_cwip_accounting(self): pr = make_purchase_receipt(item_code="Macbook Pro", @@ -639,6 +334,220 @@ class TestAsset(unittest.TestCase): frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", cwip_acc) frappe.db.get_value("Company", "_Test Company", "capital_work_in_progress_account", cwip_acc) +class TestDepreciationMethods(AssetSetup): + def test_schedule_for_straight_line_method(self): + pr = make_purchase_receipt(item_code="Macbook Pro", + qty=1, rate=100000.0, location="Test Location") + + asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') + asset = frappe.get_doc('Asset', asset_name) + asset.calculate_depreciation = 1 + asset.available_for_use_date = '2030-01-01' + asset.purchase_date = '2030-01-01' + + asset.append("finance_books", { + "expected_value_after_useful_life": 10000, + "depreciation_method": "Straight Line", + "total_number_of_depreciations": 3, + "frequency_of_depreciation": 12, + "depreciation_start_date": "2030-12-31" + }) + asset.save() + + self.assertEqual(asset.status, "Draft") + expected_schedules = [ + ["2030-12-31", 30000.00, 30000.00], + ["2031-12-31", 30000.00, 60000.00], + ["2032-12-31", 30000.00, 90000.00] + ] + + schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount] + for d in asset.get("schedules")] + + self.assertEqual(schedules, expected_schedules) + + def test_schedule_for_straight_line_method_for_existing_asset(self): + create_asset(is_existing_asset=1) + asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"}) + asset.calculate_depreciation = 1 + asset.number_of_depreciations_booked = 1 + asset.opening_accumulated_depreciation = 40000 + asset.available_for_use_date = "2030-06-06" + asset.append("finance_books", { + "expected_value_after_useful_life": 10000, + "depreciation_method": "Straight Line", + "total_number_of_depreciations": 3, + "frequency_of_depreciation": 12, + "depreciation_start_date": "2030-12-31" + }) + self.assertEqual(asset.status, "Draft") + asset.save() + expected_schedules = [ + ["2030-12-31", 14246.58, 54246.58], + ["2031-12-31", 25000.00, 79246.58], + ["2032-06-06", 10753.42, 90000.00] + ] + schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount] + for d in asset.get("schedules")] + + self.assertEqual(schedules, expected_schedules) + + def test_schedule_for_double_declining_method(self): + pr = make_purchase_receipt(item_code="Macbook Pro", + qty=1, rate=100000.0, location="Test Location") + + asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') + asset = frappe.get_doc('Asset', asset_name) + asset.calculate_depreciation = 1 + asset.available_for_use_date = '2030-01-01' + asset.purchase_date = '2030-01-01' + asset.append("finance_books", { + "expected_value_after_useful_life": 10000, + "depreciation_method": "Double Declining Balance", + "total_number_of_depreciations": 3, + "frequency_of_depreciation": 12, + "depreciation_start_date": '2030-12-31' + }) + asset.save() + self.assertEqual(asset.status, "Draft") + + expected_schedules = [ + ['2030-12-31', 66667.00, 66667.00], + ['2031-12-31', 22222.11, 88889.11], + ['2032-12-31', 1110.89, 90000.0] + ] + + schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount] + for d in asset.get("schedules")] + + self.assertEqual(schedules, expected_schedules) + + def test_schedule_for_double_declining_method_for_existing_asset(self): + create_asset(is_existing_asset = 1) + asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"}) + asset.calculate_depreciation = 1 + asset.is_existing_asset = 1 + asset.number_of_depreciations_booked = 1 + asset.opening_accumulated_depreciation = 50000 + asset.available_for_use_date = '2030-01-01' + asset.purchase_date = '2029-11-30' + asset.append("finance_books", { + "expected_value_after_useful_life": 10000, + "depreciation_method": "Double Declining Balance", + "total_number_of_depreciations": 3, + "frequency_of_depreciation": 12, + "depreciation_start_date": "2030-12-31" + }) + asset.save() + self.assertEqual(asset.status, "Draft") + + expected_schedules = [ + ["2030-12-31", 33333.50, 83333.50], + ["2031-12-31", 6666.50, 90000.0] + ] + + schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount] + for d in asset.get("schedules")] + + self.assertEqual(schedules, expected_schedules) + + def test_schedule_for_prorated_straight_line_method(self): + pr = make_purchase_receipt(item_code="Macbook Pro", + qty=1, rate=100000.0, location="Test Location") + + asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') + asset = frappe.get_doc('Asset', asset_name) + asset.calculate_depreciation = 1 + asset.purchase_date = '2030-01-30' + asset.is_existing_asset = 0 + asset.available_for_use_date = "2030-01-30" + asset.append("finance_books", { + "expected_value_after_useful_life": 10000, + "depreciation_method": "Straight Line", + "total_number_of_depreciations": 3, + "frequency_of_depreciation": 12, + "depreciation_start_date": "2030-12-31" + }) + + asset.save() + + expected_schedules = [ + ["2030-12-31", 27534.25, 27534.25], + ["2031-12-31", 30000.0, 57534.25], + ["2032-12-31", 30000.0, 87534.25], + ["2033-01-30", 2465.75, 90000.0] + ] + + schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)] + for d in asset.get("schedules")] + + self.assertEqual(schedules, expected_schedules) + + # WDV: Written Down Value method + def test_depreciation_entry_for_wdv_without_pro_rata(self): + pr = make_purchase_receipt(item_code="Macbook Pro", + qty=1, rate=8000.0, location="Test Location") + + asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') + asset = frappe.get_doc('Asset', asset_name) + asset.calculate_depreciation = 1 + asset.available_for_use_date = '2030-01-01' + asset.purchase_date = '2030-01-01' + asset.append("finance_books", { + "expected_value_after_useful_life": 1000, + "depreciation_method": "Written Down Value", + "total_number_of_depreciations": 3, + "frequency_of_depreciation": 12, + "depreciation_start_date": "2030-12-31" + }) + asset.save(ignore_permissions=True) + + self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0) + + expected_schedules = [ + ["2030-12-31", 4000.00, 4000.00], + ["2031-12-31", 2000.00, 6000.00], + ["2032-12-31", 1000.00, 7000.0], + ] + + schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)] + for d in asset.get("schedules")] + + self.assertEqual(schedules, expected_schedules) + + # WDV: Written Down Value method + def test_pro_rata_depreciation_entry_for_wdv(self): + pr = make_purchase_receipt(item_code="Macbook Pro", + qty=1, rate=8000.0, location="Test Location") + + asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') + asset = frappe.get_doc('Asset', asset_name) + asset.calculate_depreciation = 1 + asset.available_for_use_date = '2030-06-06' + asset.purchase_date = '2030-01-01' + asset.append("finance_books", { + "expected_value_after_useful_life": 1000, + "depreciation_method": "Written Down Value", + "total_number_of_depreciations": 3, + "frequency_of_depreciation": 12, + "depreciation_start_date": "2030-12-31" + }) + asset.save(ignore_permissions=True) + + self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0) + + expected_schedules = [ + ["2030-12-31", 2279.45, 2279.45], + ["2031-12-31", 2860.28, 5139.73], + ["2032-12-31", 1430.14, 6569.87], + ["2033-06-06", 430.13, 7000.0], + ] + + schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)] + for d in asset.get("schedules")] + + self.assertEqual(schedules, expected_schedules) + def test_discounted_wdv_depreciation_rate_for_indian_region(self): # set indian company company_flag = frappe.flags.company @@ -678,6 +587,7 @@ class TestAsset(unittest.TestCase): # reset indian company frappe.flags.company = company_flag +class TestDepreciationBasics(AssetSetup): def test_depreciation_without_pro_rata(self): asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3, @@ -811,7 +721,7 @@ class TestAsset(unittest.TestCase): has_pro_rata = asset.check_is_pro_rata(asset.finance_books[0]) self.assertTrue(has_pro_rata) - def test_expected_value_after_useful_life(self): + def test_expected_value_after_useful_life_greater_than_purchase_amount(self): """Tests if an error is raised when expected_value_after_useful_life(110,000) > gross_purchase_amount(100,000).""" asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, @@ -921,6 +831,99 @@ class TestAsset(unittest.TestCase): self.assertEqual(len(asset.schedules), 1) + def test_depreciation_entry_cancellation(self): + pr = make_purchase_receipt(item_code="Macbook Pro", + qty=1, rate=100000.0, location="Test Location") + + asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') + asset = frappe.get_doc('Asset', asset_name) + asset.calculate_depreciation = 1 + asset.available_for_use_date = '2020-06-06' + asset.purchase_date = '2020-06-06' + asset.append("finance_books", { + "expected_value_after_useful_life": 10000, + "depreciation_method": "Straight Line", + "total_number_of_depreciations": 3, + "frequency_of_depreciation": 10, + "depreciation_start_date": "2020-12-31" + }) + asset.submit() + post_depreciation_entries(date="2021-01-01") + + asset.load_from_db() + + # cancel depreciation entry + depr_entry = asset.get("schedules")[0].journal_entry + self.assertTrue(depr_entry) + frappe.get_doc("Journal Entry", depr_entry).cancel() + + asset.load_from_db() + depr_entry = asset.get("schedules")[0].journal_entry + self.assertFalse(depr_entry) + + def test_asset_expected_value_after_useful_life(self): + pr = make_purchase_receipt(item_code="Macbook Pro", + qty=1, rate=100000.0, location="Test Location") + + asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') + asset = frappe.get_doc('Asset', asset_name) + asset.calculate_depreciation = 1 + asset.available_for_use_date = '2020-06-06' + asset.purchase_date = '2020-06-06' + asset.append("finance_books", { + "expected_value_after_useful_life": 10000, + "depreciation_method": "Straight Line", + "total_number_of_depreciations": 3, + "frequency_of_depreciation": 10 + }) + asset.save() + accumulated_depreciation_after_full_schedule = \ + max(d.accumulated_depreciation_amount for d in asset.get("schedules")) + + asset_value_after_full_schedule = (flt(asset.gross_purchase_amount) - + flt(accumulated_depreciation_after_full_schedule)) + + self.assertTrue(asset.finance_books[0].expected_value_after_useful_life >= asset_value_after_full_schedule) + + def test_gle_made_by_depreciation_entries(self): + pr = make_purchase_receipt(item_code="Macbook Pro", + qty=1, rate=100000.0, location="Test Location") + + asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') + asset = frappe.get_doc('Asset', asset_name) + asset.calculate_depreciation = 1 + asset.purchase_date = '2020-01-30' + asset.available_for_use_date = "2020-01-30" + asset.append("finance_books", { + "expected_value_after_useful_life": 10000, + "depreciation_method": "Straight Line", + "total_number_of_depreciations": 3, + "frequency_of_depreciation": 10, + "depreciation_start_date": "2020-12-31" + }) + asset.submit() + asset.load_from_db() + self.assertEqual(asset.status, "Submitted") + + frappe.db.set_value("Company", "_Test Company", "series_for_depreciation_entry", "DEPR-") + post_depreciation_entries(date="2021-01-01") + asset.load_from_db() + + # check depreciation entry series + self.assertEqual(asset.get("schedules")[0].journal_entry[:4], "DEPR") + + expected_gle = ( + ("_Test Accumulated Depreciations - _TC", 0.0, 30000.0), + ("_Test Depreciations - _TC", 30000.0, 0.0) + ) + + gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry` + where against_voucher_type='Asset' and against_voucher = %s + order by account""", asset.name) + + self.assertEqual(gle, expected_gle) + self.assertEqual(asset.get("value_after_depreciation"), 0) + def create_asset_data(): if not frappe.db.exists("Asset Category", "Computers"): create_asset_category() From 62fea8a5aa02b14e05eeb7aa5eb6496d65ceef2d Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 5 Oct 2021 21:38:39 +0530 Subject: [PATCH 014/105] fix: Rename tests --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 31f911203d0..79d46c5e566 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2202,7 +2202,7 @@ class TestSalesInvoice(unittest.TestCase): check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1)) enable_discount_accounting(enable=0) - def test_asset_depreciation_on_sale(self): + def test_asset_depreciation_on_sale_with_pro_rata(self): """ Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on its date of sale. """ @@ -2226,7 +2226,7 @@ class TestSalesInvoice(unittest.TestCase): self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount) self.assertTrue(schedule.journal_entry) - def test_depreciation_on_sale_for_depreciated_asset(self): + def test_asset_depreciation_on_sale_without_pro_rata(self): """ Tests if an Asset set to depreciate yearly on Dec 31, that gets sold on Dec 31 after two years, created an additional depreciation entry on its date of sale. """ From f51bd44929275949652dfc97f1b5a8107f64e6cf Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 6 Oct 2021 01:18:05 +0530 Subject: [PATCH 015/105] fix: Unlink Depreciation Entry made on sale if the Asset is returned --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index e135490cad3..44a5fae6fb1 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1060,10 +1060,15 @@ class SalesInvoice(SellingController): if schedule.schedule_date == posting_date_of_original_invoice: if not self.sale_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_original_invoice) \ or self.sale_happens_in_the_future(posting_date_of_original_invoice): + reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry) reverse_journal_entry.posting_date = nowdate() reverse_journal_entry.submit() + asset.flags.ignore_validate_update_after_submit = True + schedule.journal_entry = None + asset.save() + def get_posting_date_of_sales_invoice(self): return frappe.db.get_value('Sales Invoice', self.return_against, 'posting_date') From adebf2d71b2336037e45e85b214920e1f91deaae Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 6 Oct 2021 02:04:05 +0530 Subject: [PATCH 016/105] fix: Adjust depreciation_amount in final row --- erpnext/assets/doctype/asset/asset.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 0138a1282a8..2a1d51beb45 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -262,11 +262,15 @@ class Asset(AccountsController): self.to_date = add_months(self.available_for_use_date, n * cint(d.frequency_of_depreciation)) + depreciation_amount_without_pro_rata = depreciation_amount + depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount, schedule_date, self.to_date) - monthly_schedule_date = add_months(schedule_date, 1) + depreciation_amount = self.get_adjusted_depreciation_amount(depreciation_amount_without_pro_rata, + depreciation_amount, d.finance_book) + monthly_schedule_date = add_months(schedule_date, 1) schedule_date = add_days(schedule_date, days) last_schedule_date = schedule_date @@ -406,6 +410,27 @@ class Asset(AccountsController): frappe.throw(_("Depreciation Row {0}: Next Depreciation Date cannot be before Available-for-use Date") .format(row.idx)) + # to ensure that final accumulated depreciation amount is accurate + def get_adjusted_depreciation_amount(self, depreciation_amount_without_pro_rata, depreciation_amount_for_last_row, finance_book): + depreciation_amount_for_first_row = self.get_depreciation_amount_for_first_row(finance_book) + + if depreciation_amount_for_first_row + depreciation_amount_for_last_row != depreciation_amount_without_pro_rata: + depreciation_amount_for_last_row = depreciation_amount_without_pro_rata - depreciation_amount_for_first_row + + return depreciation_amount_for_last_row + + def get_depreciation_amount_for_first_row(self, finance_book): + if self.has_only_one_finance_book(): + return self.schedules[0].depreciation_amount + else: + for schedule in self.schedules: + if schedule.finance_book == finance_book: + return schedule.depreciation_amount + + def has_only_one_finance_book(self): + if len(self.finance_books) == 1: + return True + def set_accumulated_depreciation(self, date_of_sale=None, date_of_return=None, ignore_booked_entry = False): straight_line_idx = [d.idx for d in self.get("schedules") if d.depreciation_method == 'Straight Line'] finance_books = [] From 273fccf0ddfd5e6d79150b0a2c7e010b12753e21 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 6 Oct 2021 02:08:28 +0530 Subject: [PATCH 017/105] fix: Add test for depreciation on return of sold Asset --- .../sales_invoice/test_sales_invoice.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 79d46c5e566..0e9ceead7f5 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2252,6 +2252,33 @@ class TestSalesInvoice(unittest.TestCase): self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount) self.assertTrue(schedule.journal_entry) + def test_depreciation_on_return_of_sold_asset(self): + from erpnext.controllers.sales_and_purchase_return import make_return_doc + + create_asset_data() + asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, submit=1) + post_depreciation_entries(getdate("2021-09-30")) + + si = create_sales_invoice(item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000, posting_date=getdate("2021-09-30")) + return_si = make_return_doc("Sales Invoice", si.name) + return_si.submit() + asset.load_from_db() + + expected_values = [ + ["2020-06-30", 1311.48, 1311.48, True], + ["2021-06-30", 20000.0, 21311.48, True], + ["2022-06-30", 20000.0, 41311.48, False], + ["2023-06-30", 20000.0, 61311.48, False], + ["2024-06-30", 20000.0, 81311.48, False], + ["2025-06-06", 18688.52, 100000.0, False] + ] + + for i, schedule in enumerate(asset.schedules): + self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date) + self.assertEqual(expected_values[i][1], schedule.depreciation_amount) + self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount) + self.assertEqual(schedule.journal_entry, schedule.journal_entry) + def test_sales_invoice_against_supplier(self): from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import ( make_customer, From ef3f2fcb3a9ccd4be37b9359b31458ba48e91865 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sun, 10 Oct 2021 03:50:45 +0530 Subject: [PATCH 018/105] fix: Replace setUp() with setUpClass() --- erpnext/assets/doctype/asset/test_asset.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index b5488750eba..461e110a999 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -21,7 +21,8 @@ from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt class AssetSetup(unittest.TestCase): - def setUp(self): + @classmethod + def setUpClass(cls): set_depreciation_settings_in_company() create_asset_data() frappe.db.sql("delete from `tabTax Rule`") From 4918e9533b10bbc56461a5514d9e8556352d7dd2 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sun, 10 Oct 2021 03:51:35 +0530 Subject: [PATCH 019/105] fix: Add tearDownClass() --- erpnext/assets/doctype/asset/test_asset.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 461e110a999..45a1ae4a7c5 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -27,6 +27,10 @@ class AssetSetup(unittest.TestCase): create_asset_data() frappe.db.sql("delete from `tabTax Rule`") + @classmethod + def tearDownClass(cls): + frappe.db.rollback() + class TestAsset(AssetSetup): def test_purchase_asset(self): pr = make_purchase_receipt(item_code="Macbook Pro", From d8aaf3d389366544b39c05d26e0bc29312f0e797 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 12 Oct 2021 01:07:11 +0530 Subject: [PATCH 020/105] fix: Add tests to validate Asset values --- erpnext/assets/doctype/asset/test_asset.py | 62 +++++++++++++++++++--- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 45a1ae4a7c5..6a283df9f44 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -32,6 +32,49 @@ class AssetSetup(unittest.TestCase): frappe.db.rollback() class TestAsset(AssetSetup): + def test_asset_category_is_fetched(self): + """Tests if the Item's Asset Category value is assigned to the Asset, if the field is empty.""" + + asset = create_asset(item_code="Macbook Pro", do_not_save=1) + asset.asset_category = None + asset.save() + + self.assertEqual(asset.asset_category, "Computers") + + def test_gross_purchase_amount_is_mandatory(self): + asset = create_asset(item_code="Macbook Pro", do_not_save=1) + asset.gross_purchase_amount = 0 + + self.assertRaises(frappe.MandatoryError, asset.save) + + def test_pr_or_pi_mandatory_if_not_existing_asset(self): + """Tests if either PI or PR is present if CWIP is enabled and is_existing_asset=0.""" + + asset = create_asset(item_code="Macbook Pro", do_not_save=1) + enable_cwip_accounting(asset.asset_category) + asset.is_existing_asset=0 + + self.assertRaises(frappe.ValidationError, asset.save) + + enable_cwip_accounting(asset.asset_category, enable=0) + + def test_finance_books_are_present_if_calculate_depreciation_is_enabled(self): + asset = create_asset(item_code="Macbook Pro", do_not_save=1) + asset.calculate_depreciation = 1 + + self.assertRaises(frappe.ValidationError, asset.save) + + # def test_available_for_use_date_is_after_purchase_date(self): + # pr = make_purchase_receipt(item_code="Macbook Pro", + # qty=1, rate=100000.0, location="Test Location", posting_date=add_days(nowdate(), -15)) + + # asset = create_asset(item_code="Macbook Pro", do_not_save=1) + # asset.is_existing_asset = 0 + # asset.purchase_receipt = pr + # asset.available_for_use_date = nowdate() + + # self.assertRaises(frappe.ValidationError, asset.save) + def test_purchase_asset(self): pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location") @@ -950,20 +993,20 @@ def create_asset(**args): asset = frappe.get_doc({ "doctype": "Asset", "asset_name": args.asset_name or "Macbook Pro 1", - "asset_category": "Computers", + "asset_category": args.asset_category or "Computers", "item_code": args.item_code or "Macbook Pro", - "company": args.company or"_Test Company", - "purchase_date": "2015-01-01", + "company": args.company or "_Test Company", + "purchase_date": args.purchase_date or "2015-01-01", "calculate_depreciation": args.calculate_depreciation or 0, "opening_accumulated_depreciation": args.opening_accumulated_depreciation or 0, "number_of_depreciations_booked": args.number_of_depreciations_booked or 0, - "gross_purchase_amount": 100000, - "purchase_receipt_amount": 100000, + "gross_purchase_amount": args.gross_purchase_amount or 100000, + "purchase_receipt_amount": args.purchase_receipt_amount or 100000, "warehouse": args.warehouse or "_Test Warehouse - _TC", "available_for_use_date": args.available_for_use_date or "2020-06-06", - "location": "Test Location", - "asset_owner": "Company", - "is_existing_asset": 1 + "location": args.location or "Test Location", + "asset_owner": args.asset_owner or "Company", + "is_existing_asset": args.is_existing_asset or 1 }) if asset.calculate_depreciation: @@ -1030,3 +1073,6 @@ def set_depreciation_settings_in_company(): # Enable booking asset depreciation entry automatically frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1) + +def enable_cwip_accounting(asset_category, enable=1): + frappe.db.set_value("Asset Category", asset_category, "enable_cwip_accounting", enable) From a7ec007dcff77f99d1af9e8f8c473cba629f6efe Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 12 Oct 2021 01:33:46 +0530 Subject: [PATCH 021/105] fix: Add tests to validate item --- erpnext/assets/doctype/asset/test_asset.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 6a283df9f44..ce1d8d51944 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -75,6 +75,27 @@ class TestAsset(AssetSetup): # self.assertRaises(frappe.ValidationError, asset.save) + def test_item_exists(self): + asset = create_asset(item_code="MacBook", do_not_save=1) + + self.assertRaises(frappe.DoesNotExistError, asset.save) + + def test_validate_item(self): + asset = create_asset(item_code="MacBook Pro", do_not_save=1) + item = frappe.get_doc("Item", "MacBook Pro") + + item.disabled = 1 + item.save() + self.assertRaises(frappe.ValidationError, asset.save) + item.disabled = 0 + + item.is_fixed_asset = 0 + self.assertRaises(frappe.ValidationError, asset.save) + item.is_fixed_asset = 1 + + item.is_stock_item = 1 + self.assertRaises(frappe.ValidationError, asset.save) + def test_purchase_asset(self): pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location") From a5002525704d5e9d4a33913143f49a355ac20f16 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 12 Oct 2021 13:25:55 +0530 Subject: [PATCH 022/105] perf: skip get_pricing_rules if no pricing rule exists --- erpnext/accounts/doctype/pricing_rule/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 0637fdaef02..ef44b414761 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -29,6 +29,9 @@ def get_pricing_rules(args, doc=None): pricing_rules = [] values = {} + if not frappe.db.exists('Pricing Rule', {'disable': 0, args.transaction_type: 1}): + return + for apply_on in ['Item Code', 'Item Group', 'Brand']: pricing_rules.extend(_get_pricing_rules(apply_on, args, values)) if pricing_rules and not apply_multiple_pricing_rules(pricing_rules): From 7b691beabb40cdf3156a5cf81355d8d4c6b8bd7b Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 12 Oct 2021 13:28:31 +0530 Subject: [PATCH 023/105] perf: fetch mode of payments data in single query --- .../doctype/sales_invoice/sales_invoice.py | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index d9098149210..0c3eff2a030 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1951,22 +1951,23 @@ def update_multi_mode_option(doc, pos_profile): def append_payment(payment_mode): payment = doc.append('payments', {}) payment.default = payment_mode.default - payment.mode_of_payment = payment_mode.parent + payment.mode_of_payment = payment_mode.mop payment.account = payment_mode.default_account payment.type = payment_mode.type doc.set('payments', []) invalid_modes = [] - for pos_payment_method in pos_profile.get('payments'): - pos_payment_method = pos_payment_method.as_dict() + mode_of_payments = [d.mode_of_payment for d in pos_profile.get('payments')] + mode_of_payments_info = get_mode_of_payments_info(mode_of_payments, doc.company) - payment_mode = get_mode_of_payment_info(pos_payment_method.mode_of_payment, doc.company) + for row in pos_profile.get('payments'): + payment_mode = mode_of_payments_info.get(row.mode_of_payment) if not payment_mode: - invalid_modes.append(get_link_to_form("Mode of Payment", pos_payment_method.mode_of_payment)) + invalid_modes.append(get_link_to_form("Mode of Payment", row.mode_of_payment)) continue - payment_mode[0].default = pos_payment_method.default - append_payment(payment_mode[0]) + payment_mode.default = row.default + append_payment(payment_mode) if invalid_modes: if invalid_modes == 1: @@ -1982,6 +1983,25 @@ def get_all_mode_of_payments(doc): where mpa.parent = mp.name and mpa.company = %(company)s and mp.enabled = 1""", {'company': doc.company}, as_dict=1) +def get_mode_of_payments_info(mode_of_payments, company): + data = frappe.db.sql( + """ + select + mpa.default_account, mpa.parent as mop, mp.type as type + from + `tabMode of Payment Account` mpa,`tabMode of Payment` mp + where + mpa.parent = mp.name and + mpa.company = %s and + mp.enabled = 1 and + mp.name in (%s) + group by + mp.name + """, + (company, mode_of_payments), as_dict=1) + + return {row.get('mop'): row for row in data} + def get_mode_of_payment_info(mode_of_payment, company): return frappe.db.sql(""" select mpa.default_account, mpa.parent, mp.type as type From eb3aae870f92e73b2b7babdb1bba01aaebf6e854 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 12 Oct 2021 13:29:32 +0530 Subject: [PATCH 024/105] perf: get total company stock only for purchase order --- erpnext/stock/get_item_details.py | 36 ++++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index cbff2149d64..e0190b64a75 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -89,7 +89,13 @@ def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=Tru out.update(get_bin_details(args.item_code, args.get("from_warehouse"))) elif out.get("warehouse"): - out.update(get_bin_details(args.item_code, out.warehouse, args.company)) + if doc and doc.get('doctype') == 'Purchase Order': + # calculate company_total_stock only for po + bin_details = get_bin_details(args.item_code, out.warehouse, args.company) + else: + bin_details = get_bin_details(args.item_code, out.warehouse) + + out.update(bin_details) # update args with out, if key or value not exists for key, value in iteritems(out): @@ -485,8 +491,9 @@ def get_item_tax_template(args, item, out): "item_tax_template": None } """ - item_tax_template = args.get("item_tax_template") - item_tax_template = _get_item_tax_template(args, item.taxes, out) + item_tax_template = None + if item.taxes: + item_tax_template = _get_item_tax_template(args, item.taxes, out) if not item_tax_template: item_group = item.item_group @@ -502,17 +509,17 @@ def _get_item_tax_template(args, taxes, out=None, for_validate=False): taxes_with_no_validity = [] for tax in taxes: - tax_company = frappe.get_value("Item Tax Template", tax.item_tax_template, 'company') - if (tax.valid_from or tax.maximum_net_rate) and tax_company == args['company']: - # In purchase Invoice first preference will be given to supplier invoice date - # if supplier date is not present then posting date - validation_date = args.get('transaction_date') or args.get('bill_date') or args.get('posting_date') + tax_company = frappe.get_cached_value("Item Tax Template", tax.item_tax_template, 'company') + if tax_company == args['company']: + if (tax.valid_from or tax.maximum_net_rate): + # In purchase Invoice first preference will be given to supplier invoice date + # if supplier date is not present then posting date + validation_date = args.get('transaction_date') or args.get('bill_date') or args.get('posting_date') - if getdate(tax.valid_from) <= getdate(validation_date) \ - and is_within_valid_range(args, tax): - taxes_with_validity.append(tax) - else: - if tax_company == args['company']: + if getdate(tax.valid_from) <= getdate(validation_date) \ + and is_within_valid_range(args, tax): + taxes_with_validity.append(tax) + else: taxes_with_no_validity.append(tax) if taxes_with_validity: @@ -890,8 +897,7 @@ def get_pos_profile_item_details(company, args, pos_profile=None, update_data=Fa res[fieldname] = pos_profile.get(fieldname) if res.get("warehouse"): - res.actual_qty = get_bin_details(args.item_code, - res.warehouse).get("actual_qty") + res.actual_qty = get_bin_details(args.item_code, res.warehouse).get("actual_qty") return res From c7fc609236aeaada0375e90b664c041bdbc52570 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 12 Oct 2021 13:30:40 +0530 Subject: [PATCH 025/105] perf: skip insertion of stock ledger entry --- erpnext/stock/stock_ledger.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 1b5b792f946..bff7880fd6f 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -123,12 +123,11 @@ def set_as_cancel(voucher_type, voucher_no): (now(), frappe.session.user, voucher_type, voucher_no)) def make_entry(args, allow_negative_stock=False, via_landed_cost_voucher=False): - args.update({"doctype": "Stock Ledger Entry"}) + args["doctype"] = "Stock Ledger Entry" sle = frappe.get_doc(args) sle.flags.ignore_permissions = 1 sle.allow_negative_stock=allow_negative_stock sle.via_landed_cost_voucher = via_landed_cost_voucher - sle.insert() sle.submit() return sle @@ -240,8 +239,7 @@ class update_entries_after(object): self.verbose = verbose self.allow_zero_rate = allow_zero_rate self.via_landed_cost_voucher = via_landed_cost_voucher - self.allow_negative_stock = allow_negative_stock \ - or cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock")) + self.allow_negative_stock = True self.args = frappe._dict(args) self.item_code = args.get("item_code") From 7bafa11d5758c40496898cd0151efde4ff358946 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 12 Oct 2021 20:39:10 +0530 Subject: [PATCH 026/105] fix: undo changes to allow negative stock flag --- erpnext/stock/stock_ledger.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index bff7880fd6f..2b6eefb5cc6 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -239,7 +239,8 @@ class update_entries_after(object): self.verbose = verbose self.allow_zero_rate = allow_zero_rate self.via_landed_cost_voucher = via_landed_cost_voucher - self.allow_negative_stock = True + self.allow_negative_stock = allow_negative_stock \ + or cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock")) self.args = frappe._dict(args) self.item_code = args.get("item_code") From 8ea1ad9232ceac388c3fc4117752af3fa6880276 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 13 Oct 2021 20:47:39 +0530 Subject: [PATCH 027/105] fix: Only validate against JV if it's not a reverse depreciation entry --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 6 ++++-- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index e568a827617..d04e4a5d8bd 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -58,7 +58,10 @@ class JournalEntry(AccountsController): if not frappe.flags.in_import: self.validate_total_debit_and_credit() - self.validate_against_jv() + if not self.flags.is_reverse_depr_entry: + self.validate_against_jv() + self.validate_stock_accounts() + self.validate_reference_doc() if self.docstatus == 0: self.set_against_account() @@ -69,7 +72,6 @@ class JournalEntry(AccountsController): self.validate_empty_accounts_table() self.set_account_and_party_balance() self.validate_inter_company_accounts() - self.validate_stock_accounts() if self.docstatus == 0: self.apply_tax_withholding() diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 44a5fae6fb1..2744e0d310e 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1063,6 +1063,7 @@ class SalesInvoice(SellingController): reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry) reverse_journal_entry.posting_date = nowdate() + reverse_journal_entry.flags.is_reverse_depr_entry = True reverse_journal_entry.submit() asset.flags.ignore_validate_update_after_submit = True From 749d1b6ee68603984c99045c3d4115678523893e Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 13 Oct 2021 20:49:07 +0530 Subject: [PATCH 028/105] fix: Enable cwip accounting --- erpnext/assets/doctype/asset/test_asset.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index ce1d8d51944..878a31ef930 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -25,6 +25,7 @@ class AssetSetup(unittest.TestCase): def setUpClass(cls): set_depreciation_settings_in_company() create_asset_data() + enable_cwip_accounting("Computers") frappe.db.sql("delete from `tabTax Rule`") @classmethod @@ -51,13 +52,10 @@ class TestAsset(AssetSetup): """Tests if either PI or PR is present if CWIP is enabled and is_existing_asset=0.""" asset = create_asset(item_code="Macbook Pro", do_not_save=1) - enable_cwip_accounting(asset.asset_category) asset.is_existing_asset=0 self.assertRaises(frappe.ValidationError, asset.save) - enable_cwip_accounting(asset.asset_category, enable=0) - def test_finance_books_are_present_if_calculate_depreciation_is_enabled(self): asset = create_asset(item_code="Macbook Pro", do_not_save=1) asset.calculate_depreciation = 1 @@ -330,7 +328,6 @@ class TestAsset(AssetSetup): def test_expense_head(self): pr = make_purchase_receipt(item_code="Macbook Pro", qty=2, rate=200000.0, location="Test Location") - doc = make_invoice(pr.name) self.assertEqual('Asset Received But Not Billed - _TC', doc.items[0].expense_account) From 83ec9879ee963c3e8b3b7551df93679eb3643689 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 13 Oct 2021 21:21:50 +0530 Subject: [PATCH 029/105] fix: Add test to validate available_for_use_date --- erpnext/assets/doctype/asset/test_asset.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 878a31ef930..964451dc2f4 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -62,16 +62,17 @@ class TestAsset(AssetSetup): self.assertRaises(frappe.ValidationError, asset.save) - # def test_available_for_use_date_is_after_purchase_date(self): - # pr = make_purchase_receipt(item_code="Macbook Pro", - # qty=1, rate=100000.0, location="Test Location", posting_date=add_days(nowdate(), -15)) + def test_available_for_use_date_is_after_purchase_date(self): + pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=100000.0, + location="Test Location", posting_date=getdate("2021-10-10")) - # asset = create_asset(item_code="Macbook Pro", do_not_save=1) - # asset.is_existing_asset = 0 - # asset.purchase_receipt = pr - # asset.available_for_use_date = nowdate() + asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, do_not_save=1) + asset.is_existing_asset = 0 + asset.purchase_receipt = pr.name + asset.purchase_date = getdate("2021-10-10") + asset.available_for_use_date = getdate("2021-10-1") - # self.assertRaises(frappe.ValidationError, asset.save) + self.assertRaises(frappe.ValidationError, asset.save) def test_item_exists(self): asset = create_asset(item_code="MacBook", do_not_save=1) From e8986df3cafa49d1b317429036f4c145ca4f8d86 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 13 Oct 2021 21:36:10 +0530 Subject: [PATCH 030/105] fix: Move test for Finance Books to Depreciation test suite --- erpnext/assets/doctype/asset/test_asset.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 964451dc2f4..cb984a70bc0 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -56,12 +56,6 @@ class TestAsset(AssetSetup): self.assertRaises(frappe.ValidationError, asset.save) - def test_finance_books_are_present_if_calculate_depreciation_is_enabled(self): - asset = create_asset(item_code="Macbook Pro", do_not_save=1) - asset.calculate_depreciation = 1 - - self.assertRaises(frappe.ValidationError, asset.save) - def test_available_for_use_date_is_after_purchase_date(self): pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location", posting_date=getdate("2021-10-10")) @@ -852,6 +846,12 @@ class TestDepreciationBasics(AssetSetup): self.assertRaises(frappe.ValidationError, asset.save) + def test_finance_books_are_present_if_calculate_depreciation_is_enabled(self): + asset = create_asset(item_code="Macbook Pro", do_not_save=1) + asset.calculate_depreciation = 1 + + self.assertRaises(frappe.ValidationError, asset.save) + def test_post_depreciation_entries(self): """Tests if post_depreciation_entries() works as expected.""" From 4bf01bb4b7b6770b5f957824d912397b1556f168 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 14 Oct 2021 20:28:57 +0530 Subject: [PATCH 031/105] fix: Move Purchase Receipt creation to setUpClass --- erpnext/assets/doctype/asset/test_asset.py | 56 ++++++---------------- 1 file changed, 15 insertions(+), 41 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 773ecc78414..a94f84f9691 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -26,6 +26,7 @@ class AssetSetup(unittest.TestCase): set_depreciation_settings_in_company() create_asset_data() enable_cwip_accounting("Computers") + make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location") frappe.db.sql("delete from `tabTax Rule`") @classmethod @@ -57,12 +58,8 @@ class TestAsset(AssetSetup): self.assertRaises(frappe.ValidationError, asset.save) def test_available_for_use_date_is_after_purchase_date(self): - pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=100000.0, - location="Test Location", posting_date=getdate("2021-10-10")) - asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, do_not_save=1) asset.is_existing_asset = 0 - asset.purchase_receipt = pr.name asset.purchase_date = getdate("2021-10-10") asset.available_for_use_date = getdate("2021-10-1") @@ -152,21 +149,9 @@ class TestAsset(AssetSetup): self.assertEqual(doc.items[0].is_fixed_asset, 1) def test_scrap_asset(self): - pr = make_purchase_receipt(item_code="Macbook Pro", - qty=1, rate=100000.0, location="Test Location") - - asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') - asset = frappe.get_doc('Asset', asset_name) - asset.calculate_depreciation = 1 - asset.available_for_use_date = '2020-01-01' - asset.purchase_date = '2020-01-01' - asset.append("finance_books", { - "expected_value_after_useful_life": 10000, - "depreciation_method": "Straight Line", - "total_number_of_depreciations": 10, - "frequency_of_depreciation": 1 - }) - asset.submit() + asset = create_asset(calculate_depreciation=1, available_for_use_date='2020-01-01', + purchase_date = '2020-01-01', expected_value_after_useful_life=10000, + total_number_of_depreciations=10, frequency_of_depreciation=1, submit=1) post_depreciation_entries(date=add_months('2020-01-01', 4)) @@ -194,22 +179,11 @@ class TestAsset(AssetSetup): self.assertEqual(asset.status, "Partially Depreciated") def test_gle_made_by_asset_sale(self): - pr = make_purchase_receipt(item_code="Macbook Pro", - qty=1, rate=100000.0, location="Test Location") + asset = create_asset(calculate_depreciation=1, available_for_use_date='2020-06-06', + purchase_date = '2020-01-01', expected_value_after_useful_life=10000, + total_number_of_depreciations=3, frequency_of_depreciation=10, + depreciation_start_date='2020-12-31', submit=1) - asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') - asset = frappe.get_doc('Asset', asset_name) - asset.calculate_depreciation = 1 - asset.available_for_use_date = '2020-06-06' - asset.purchase_date = '2020-06-06' - asset.append("finance_books", { - "expected_value_after_useful_life": 10000, - "depreciation_method": "Straight Line", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 10, - "depreciation_start_date": "2020-12-31" - }) - asset.submit() post_depreciation_entries(date="2021-01-01") si = make_sales_invoice(asset=asset.name, item_code="Macbook Pro", company="_Test Company") @@ -237,6 +211,13 @@ class TestAsset(AssetSetup): si.cancel() self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Partially Depreciated") + def test_expense_head(self): + pr = make_purchase_receipt(item_code="Macbook Pro", + qty=2, rate=200000.0, location="Test Location") + doc = make_invoice(pr.name) + + self.assertEqual('Asset Received But Not Billed - _TC', doc.items[0].expense_account) + # CWIP: Capital Work In Progress def test_cwip_accounting(self): pr = make_purchase_receipt(item_code="Macbook Pro", @@ -320,13 +301,6 @@ class TestAsset(AssetSetup): self.assertEqual(gle, expected_gle) - def test_expense_head(self): - pr = make_purchase_receipt(item_code="Macbook Pro", - qty=2, rate=200000.0, location="Test Location") - doc = make_invoice(pr.name) - - self.assertEqual('Asset Received But Not Billed - _TC', doc.items[0].expense_account) - def test_asset_cwip_toggling_cases(self): cwip = frappe.db.get_value("Asset Category", "Computers", "enable_cwip_accounting") name = frappe.db.get_value("Asset Category Account", filters={"parent": "Computers"}, fieldname=["name"]) From 09215a9781379e78b78b3d7e5a5c6546558256a6 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 14 Oct 2021 22:15:10 +0530 Subject: [PATCH 032/105] fix: Remove PR creation from all tests for Depreciation Methods --- erpnext/assets/doctype/asset/test_asset.py | 194 ++++++--------------- 1 file changed, 54 insertions(+), 140 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index a94f84f9691..8795b0223ee 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -371,23 +371,10 @@ class TestAsset(AssetSetup): class TestDepreciationMethods(AssetSetup): def test_schedule_for_straight_line_method(self): - pr = make_purchase_receipt(item_code="Macbook Pro", - qty=1, rate=100000.0, location="Test Location") - - asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') - asset = frappe.get_doc('Asset', asset_name) - asset.calculate_depreciation = 1 - asset.available_for_use_date = '2030-01-01' - asset.purchase_date = '2030-01-01' - - asset.append("finance_books", { - "expected_value_after_useful_life": 10000, - "depreciation_method": "Straight Line", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 12, - "depreciation_start_date": "2030-12-31" - }) - asset.save() + asset = create_asset(calculate_depreciation=1, + available_for_use_date="2030-01-01", purchase_date="2030-01-01", + expected_value_after_useful_life=10000, depreciation_start_date="2030-12-31", + total_number_of_depreciations=3, frequency_of_depreciation=12) self.assertEqual(asset.status, "Draft") expected_schedules = [ @@ -402,21 +389,13 @@ class TestDepreciationMethods(AssetSetup): self.assertEqual(schedules, expected_schedules) def test_schedule_for_straight_line_method_for_existing_asset(self): - create_asset(is_existing_asset=1) - asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"}) - asset.calculate_depreciation = 1 - asset.number_of_depreciations_booked = 1 - asset.opening_accumulated_depreciation = 40000 - asset.available_for_use_date = "2030-06-06" - asset.append("finance_books", { - "expected_value_after_useful_life": 10000, - "depreciation_method": "Straight Line", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 12, - "depreciation_start_date": "2030-12-31" - }) + asset = create_asset(calculate_depreciation=1, + available_for_use_date="2030-06-06", is_existing_asset=1, + number_of_depreciations_booked = 1, opening_accumulated_depreciation=40000, + expected_value_after_useful_life=10000, depreciation_start_date="2030-12-31", + total_number_of_depreciations=3, frequency_of_depreciation=12) + self.assertEqual(asset.status, "Draft") - asset.save() expected_schedules = [ ["2030-12-31", 14246.58, 54246.58], ["2031-12-31", 25000.00, 79246.58], @@ -428,22 +407,12 @@ class TestDepreciationMethods(AssetSetup): self.assertEqual(schedules, expected_schedules) def test_schedule_for_double_declining_method(self): - pr = make_purchase_receipt(item_code="Macbook Pro", - qty=1, rate=100000.0, location="Test Location") + asset = create_asset(calculate_depreciation=1, + available_for_use_date="2030-01-01", purchase_date="2030-01-01", + depreciation_method="Double Declining Balance", + expected_value_after_useful_life=10000, depreciation_start_date="2030-12-31", + total_number_of_depreciations=3, frequency_of_depreciation=12) - asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') - asset = frappe.get_doc('Asset', asset_name) - asset.calculate_depreciation = 1 - asset.available_for_use_date = '2030-01-01' - asset.purchase_date = '2030-01-01' - asset.append("finance_books", { - "expected_value_after_useful_life": 10000, - "depreciation_method": "Double Declining Balance", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 12, - "depreciation_start_date": '2030-12-31' - }) - asset.save() self.assertEqual(asset.status, "Draft") expected_schedules = [ @@ -458,22 +427,13 @@ class TestDepreciationMethods(AssetSetup): self.assertEqual(schedules, expected_schedules) def test_schedule_for_double_declining_method_for_existing_asset(self): - create_asset(is_existing_asset = 1) - asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"}) - asset.calculate_depreciation = 1 - asset.is_existing_asset = 1 - asset.number_of_depreciations_booked = 1 - asset.opening_accumulated_depreciation = 50000 - asset.available_for_use_date = '2030-01-01' - asset.purchase_date = '2029-11-30' - asset.append("finance_books", { - "expected_value_after_useful_life": 10000, - "depreciation_method": "Double Declining Balance", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 12, - "depreciation_start_date": "2030-12-31" - }) - asset.save() + asset = create_asset(calculate_depreciation=1, + available_for_use_date="2030-01-01", is_existing_asset=1, + depreciation_method="Double Declining Balance", + number_of_depreciations_booked = 1, opening_accumulated_depreciation=50000, + expected_value_after_useful_life=10000, depreciation_start_date="2030-12-31", + total_number_of_depreciations=3, frequency_of_depreciation=12) + self.assertEqual(asset.status, "Draft") expected_schedules = [ @@ -487,24 +447,11 @@ class TestDepreciationMethods(AssetSetup): self.assertEqual(schedules, expected_schedules) def test_schedule_for_prorated_straight_line_method(self): - pr = make_purchase_receipt(item_code="Macbook Pro", - qty=1, rate=100000.0, location="Test Location") - - asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') - asset = frappe.get_doc('Asset', asset_name) - asset.calculate_depreciation = 1 - asset.purchase_date = '2030-01-30' - asset.is_existing_asset = 0 - asset.available_for_use_date = "2030-01-30" - asset.append("finance_books", { - "expected_value_after_useful_life": 10000, - "depreciation_method": "Straight Line", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 12, - "depreciation_start_date": "2030-12-31" - }) - - asset.save() + asset = create_asset(calculate_depreciation=1, + available_for_use_date="2030-01-30", purchase_date="2030-01-30", + depreciation_method="Straight Line", + expected_value_after_useful_life=10000, depreciation_start_date="2030-12-31", + total_number_of_depreciations=3, frequency_of_depreciation=12) expected_schedules = [ ["2030-12-31", 27534.25, 27534.25], @@ -520,29 +467,18 @@ class TestDepreciationMethods(AssetSetup): # WDV: Written Down Value method def test_depreciation_entry_for_wdv_without_pro_rata(self): - pr = make_purchase_receipt(item_code="Macbook Pro", - qty=1, rate=8000.0, location="Test Location") - - asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') - asset = frappe.get_doc('Asset', asset_name) - asset.calculate_depreciation = 1 - asset.available_for_use_date = '2030-01-01' - asset.purchase_date = '2030-01-01' - asset.append("finance_books", { - "expected_value_after_useful_life": 1000, - "depreciation_method": "Written Down Value", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 12, - "depreciation_start_date": "2030-12-31" - }) - asset.save(ignore_permissions=True) + asset = create_asset(calculate_depreciation=1, + available_for_use_date="2030-01-01", purchase_date="2030-01-01", + depreciation_method="Written Down Value", + expected_value_after_useful_life=12500, depreciation_start_date="2030-12-31", + total_number_of_depreciations=3, frequency_of_depreciation=12) self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0) expected_schedules = [ - ["2030-12-31", 4000.00, 4000.00], - ["2031-12-31", 2000.00, 6000.00], - ["2032-12-31", 1000.00, 7000.0], + ["2030-12-31", 50000.0, 50000.0], + ["2031-12-31", 25000.0, 75000.0], + ["2032-12-31", 12500.0, 87500.0], ] schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)] @@ -552,30 +488,19 @@ class TestDepreciationMethods(AssetSetup): # WDV: Written Down Value method def test_pro_rata_depreciation_entry_for_wdv(self): - pr = make_purchase_receipt(item_code="Macbook Pro", - qty=1, rate=8000.0, location="Test Location") - - asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') - asset = frappe.get_doc('Asset', asset_name) - asset.calculate_depreciation = 1 - asset.available_for_use_date = '2030-06-06' - asset.purchase_date = '2030-01-01' - asset.append("finance_books", { - "expected_value_after_useful_life": 1000, - "depreciation_method": "Written Down Value", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 12, - "depreciation_start_date": "2030-12-31" - }) - asset.save(ignore_permissions=True) + asset = create_asset(calculate_depreciation=1, + available_for_use_date="2030-06-06", purchase_date="2030-01-01", + depreciation_method="Written Down Value", + expected_value_after_useful_life=12500, depreciation_start_date="2030-12-31", + total_number_of_depreciations=3, frequency_of_depreciation=12) self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0) expected_schedules = [ - ["2030-12-31", 2279.45, 2279.45], - ["2031-12-31", 2860.28, 5139.73], - ["2032-12-31", 1430.14, 6569.87], - ["2033-06-06", 430.13, 7000.0], + ["2030-12-31", 28493.15, 28493.15], + ["2031-12-31", 35753.43, 64246.58], + ["2032-12-31", 17876.71, 82123.29], + ["2033-06-06", 5376.71, 87500.0] ] schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)] @@ -588,30 +513,19 @@ class TestDepreciationMethods(AssetSetup): company_flag = frappe.flags.company frappe.flags.company = "_Test Company" - pr = make_purchase_receipt(item_code="Macbook Pro", - qty=1, rate=8000.0, location="Test Location") - - asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') - asset = frappe.get_doc('Asset', asset_name) - asset.calculate_depreciation = 1 - asset.available_for_use_date = '2030-07-12' - asset.purchase_date = '2030-01-01' - asset.append("finance_books", { - "expected_value_after_useful_life": 1000, - "depreciation_method": "Written Down Value", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 12, - "depreciation_start_date": "2030-12-31" - }) - asset.save(ignore_permissions=True) + asset = create_asset(calculate_depreciation=1, + available_for_use_date="2030-07-12", purchase_date="2030-01-01", + depreciation_method="Written Down Value", + expected_value_after_useful_life=12500, depreciation_start_date="2030-12-31", + total_number_of_depreciations=3, frequency_of_depreciation=12) self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0) expected_schedules = [ - ["2030-12-31", 942.47, 942.47], - ["2031-12-31", 3528.77, 4471.24], - ["2032-12-31", 1764.38, 6235.62], - ["2033-07-12", 764.38, 7000.00] + ["2030-12-31", 11780.82, 11780.82], + ["2031-12-31", 44109.59, 55890.41], + ["2032-12-31", 22054.8, 77945.21], + ["2033-07-12", 9554.79, 87500.0] ] schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)] @@ -1009,7 +923,7 @@ def create_asset(**args): if asset.calculate_depreciation: asset.append("finance_books", { - "depreciation_method": "Straight Line", + "depreciation_method": args.depreciation_method or "Straight Line", "frequency_of_depreciation": args.frequency_of_depreciation or 12, "total_number_of_depreciations": args.total_number_of_depreciations or 5, "expected_value_after_useful_life": args.expected_value_after_useful_life or 0, From 968be70bd1c0011792ea2c3887dc2f794b15d300 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 14 Oct 2021 22:32:27 +0530 Subject: [PATCH 033/105] fix: Remove PR creation from all tests in TestDepreciationBasics --- erpnext/assets/doctype/asset/test_asset.py | 92 +++++----------------- 1 file changed, 20 insertions(+), 72 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 8795b0223ee..cbfadc04706 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -744,17 +744,9 @@ class TestDepreciationBasics(AssetSetup): """Tests if post_depreciation_entries() works as expected.""" asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, - available_for_use_date=getdate("2019-12-31"), do_not_save=1) - - asset.finance_books = [] - asset.append("finance_books", { - "depreciation_method": "Straight Line", - "frequency_of_depreciation": 12, - "total_number_of_depreciations": 3, - "expected_value_after_useful_life": 10000, - "depreciation_start_date": getdate("2020-12-31") - }) - asset.submit() + available_for_use_date="2019-12-31", depreciation_start_date="2020-12-31", + frequency_of_depreciation=12, total_number_of_depreciations=3, + expected_value_after_useful_life=10000, submit=1) post_depreciation_entries(date="2021-06-01") asset.load_from_db() @@ -767,17 +759,9 @@ class TestDepreciationBasics(AssetSetup): """Tests if clear_depreciation_schedule() works as expected.""" asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, - available_for_use_date=getdate("2019-12-31"), do_not_save=1) - - asset.finance_books = [] - asset.append("finance_books", { - "depreciation_method": "Straight Line", - "frequency_of_depreciation": 12, - "total_number_of_depreciations": 3, - "expected_value_after_useful_life": 10000, - "depreciation_start_date": getdate("2020-12-31") - }) - asset.submit() + available_for_use_date="2019-12-31", depreciation_start_date="2020-12-31", + frequency_of_depreciation=12, total_number_of_depreciations=3, + expected_value_after_useful_life=10000, submit=1) post_depreciation_entries(date="2021-06-01") asset.load_from_db() @@ -787,22 +771,12 @@ class TestDepreciationBasics(AssetSetup): self.assertEqual(len(asset.schedules), 1) def test_depreciation_entry_cancellation(self): - pr = make_purchase_receipt(item_code="Macbook Pro", - qty=1, rate=100000.0, location="Test Location") + asset = create_asset(item_code="Macbook Pro", + calculate_depreciation=1, purchase_date="2020-06-06", + available_for_use_date="2020-06-06", depreciation_start_date="2020-12-31", + frequency_of_depreciation=10, total_number_of_depreciations=3, + expected_value_after_useful_life=10000, submit=1) - asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') - asset = frappe.get_doc('Asset', asset_name) - asset.calculate_depreciation = 1 - asset.available_for_use_date = '2020-06-06' - asset.purchase_date = '2020-06-06' - asset.append("finance_books", { - "expected_value_after_useful_life": 10000, - "depreciation_method": "Straight Line", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 10, - "depreciation_start_date": "2020-12-31" - }) - asset.submit() post_depreciation_entries(date="2021-01-01") asset.load_from_db() @@ -817,21 +791,11 @@ class TestDepreciationBasics(AssetSetup): self.assertFalse(depr_entry) def test_asset_expected_value_after_useful_life(self): - pr = make_purchase_receipt(item_code="Macbook Pro", - qty=1, rate=100000.0, location="Test Location") + asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, + available_for_use_date="2020-06-06", purchase_date="2020-06-06", + frequency_of_depreciation=10, total_number_of_depreciations=3, + expected_value_after_useful_life=10000) - asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') - asset = frappe.get_doc('Asset', asset_name) - asset.calculate_depreciation = 1 - asset.available_for_use_date = '2020-06-06' - asset.purchase_date = '2020-06-06' - asset.append("finance_books", { - "expected_value_after_useful_life": 10000, - "depreciation_method": "Straight Line", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 10 - }) - asset.save() accumulated_depreciation_after_full_schedule = \ max(d.accumulated_depreciation_amount for d in asset.get("schedules")) @@ -841,28 +805,12 @@ class TestDepreciationBasics(AssetSetup): self.assertTrue(asset.finance_books[0].expected_value_after_useful_life >= asset_value_after_full_schedule) def test_gle_made_by_depreciation_entries(self): - pr = make_purchase_receipt(item_code="Macbook Pro", - qty=1, rate=100000.0, location="Test Location") + asset = create_asset(item_code="Macbook Pro", + calculate_depreciation=1, purchase_date="2020-01-30", + available_for_use_date="2020-01-30", depreciation_start_date="2020-12-31", + frequency_of_depreciation=10, total_number_of_depreciations=3, + expected_value_after_useful_life=10000, submit=1) - finance_book = frappe.new_doc('Finance Book') - finance_book.finance_book_name = 'Income Tax' - finance_book.for_income_tax = 1 - finance_book.insert(ignore_if_duplicate=1) - - asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') - asset = frappe.get_doc('Asset', asset_name) - asset.calculate_depreciation = 1 - asset.purchase_date = '2020-01-30' - asset.available_for_use_date = "2020-01-30" - asset.append("finance_books", { - "expected_value_after_useful_life": 10000, - "depreciation_method": "Straight Line", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 10, - "depreciation_start_date": "2020-12-31" - }) - asset.submit() - asset.load_from_db() self.assertEqual(asset.status, "Submitted") frappe.db.set_value("Company", "_Test Company", "series_for_depreciation_entry", "DEPR-") From e9d310a13e902d7208a925bb577faba2604cd684 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 15 Oct 2021 01:45:53 +0530 Subject: [PATCH 034/105] fix: Format tests --- erpnext/assets/doctype/asset/test_asset.py | 406 ++++++++++++++------- 1 file changed, 269 insertions(+), 137 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 070a3e9f55a..523c8195ae2 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -149,9 +149,15 @@ class TestAsset(AssetSetup): self.assertEqual(doc.items[0].is_fixed_asset, 1) def test_scrap_asset(self): - asset = create_asset(calculate_depreciation=1, available_for_use_date='2020-01-01', - purchase_date = '2020-01-01', expected_value_after_useful_life=10000, - total_number_of_depreciations=10, frequency_of_depreciation=1, submit=1) + asset = create_asset( + calculate_depreciation = 1, + available_for_use_date = '2020-01-01', + purchase_date = '2020-01-01', + expected_value_after_useful_life = 10000, + total_number_of_depreciations = 10, + frequency_of_depreciation = 1, + submit = 1 + ) post_depreciation_entries(date=add_months('2020-01-01', 4)) @@ -179,10 +185,16 @@ class TestAsset(AssetSetup): self.assertEqual(asset.status, "Partially Depreciated") def test_gle_made_by_asset_sale(self): - asset = create_asset(calculate_depreciation=1, available_for_use_date='2020-06-06', - purchase_date = '2020-01-01', expected_value_after_useful_life=10000, - total_number_of_depreciations=3, frequency_of_depreciation=10, - depreciation_start_date='2020-12-31', submit=1) + asset = create_asset( + calculate_depreciation = 1, + available_for_use_date = '2020-06-06', + purchase_date = '2020-01-01', + expected_value_after_useful_life = 10000, + total_number_of_depreciations = 3, + frequency_of_depreciation = 10, + depreciation_start_date = '2020-12-31', + submit = 1 + ) post_depreciation_entries(date="2021-01-01") @@ -371,10 +383,15 @@ class TestAsset(AssetSetup): class TestDepreciationMethods(AssetSetup): def test_schedule_for_straight_line_method(self): - asset = create_asset(calculate_depreciation=1, - available_for_use_date="2030-01-01", purchase_date="2030-01-01", - expected_value_after_useful_life=10000, depreciation_start_date="2030-12-31", - total_number_of_depreciations=3, frequency_of_depreciation=12) + asset = create_asset( + calculate_depreciation = 1, + available_for_use_date = "2030-01-01", + purchase_date = "2030-01-01", + expected_value_after_useful_life = 10000, + depreciation_start_date = "2030-12-31", + total_number_of_depreciations = 3, + frequency_of_depreciation = 12 + ) self.assertEqual(asset.status, "Draft") expected_schedules = [ @@ -389,11 +406,17 @@ class TestDepreciationMethods(AssetSetup): self.assertEqual(schedules, expected_schedules) def test_schedule_for_straight_line_method_for_existing_asset(self): - asset = create_asset(calculate_depreciation=1, - available_for_use_date="2030-06-06", is_existing_asset=1, - number_of_depreciations_booked = 1, opening_accumulated_depreciation=40000, - expected_value_after_useful_life=10000, depreciation_start_date="2030-12-31", - total_number_of_depreciations=3, frequency_of_depreciation=12) + asset = create_asset( + calculate_depreciation = 1, + available_for_use_date = "2030-06-06", + is_existing_asset = 1, + number_of_depreciations_booked = 1, + opening_accumulated_depreciation = 40000, + expected_value_after_useful_life = 10000, + depreciation_start_date = "2030-12-31", + total_number_of_depreciations = 3, + frequency_of_depreciation = 12 + ) self.assertEqual(asset.status, "Draft") expected_schedules = [ @@ -407,11 +430,16 @@ class TestDepreciationMethods(AssetSetup): self.assertEqual(schedules, expected_schedules) def test_schedule_for_double_declining_method(self): - asset = create_asset(calculate_depreciation=1, - available_for_use_date="2030-01-01", purchase_date="2030-01-01", - depreciation_method="Double Declining Balance", - expected_value_after_useful_life=10000, depreciation_start_date="2030-12-31", - total_number_of_depreciations=3, frequency_of_depreciation=12) + asset = create_asset( + calculate_depreciation = 1, + available_for_use_date = "2030-01-01", + purchase_date = "2030-01-01", + depreciation_method = "Double Declining Balance", + expected_value_after_useful_life = 10000, + depreciation_start_date = "2030-12-31", + total_number_of_depreciations = 3, + frequency_of_depreciation = 12 + ) self.assertEqual(asset.status, "Draft") @@ -427,12 +455,18 @@ class TestDepreciationMethods(AssetSetup): self.assertEqual(schedules, expected_schedules) def test_schedule_for_double_declining_method_for_existing_asset(self): - asset = create_asset(calculate_depreciation=1, - available_for_use_date="2030-01-01", is_existing_asset=1, - depreciation_method="Double Declining Balance", - number_of_depreciations_booked = 1, opening_accumulated_depreciation=50000, - expected_value_after_useful_life=10000, depreciation_start_date="2030-12-31", - total_number_of_depreciations=3, frequency_of_depreciation=12) + asset = create_asset( + calculate_depreciation = 1, + available_for_use_date = "2030-01-01", + is_existing_asset = 1, + depreciation_method = "Double Declining Balance", + number_of_depreciations_booked = 1, + opening_accumulated_depreciation = 50000, + expected_value_after_useful_life = 10000, + depreciation_start_date = "2030-12-31", + total_number_of_depreciations = 3, + frequency_of_depreciation = 12 + ) self.assertEqual(asset.status, "Draft") @@ -447,11 +481,16 @@ class TestDepreciationMethods(AssetSetup): self.assertEqual(schedules, expected_schedules) def test_schedule_for_prorated_straight_line_method(self): - asset = create_asset(calculate_depreciation=1, - available_for_use_date="2030-01-30", purchase_date="2030-01-30", - depreciation_method="Straight Line", - expected_value_after_useful_life=10000, depreciation_start_date="2030-12-31", - total_number_of_depreciations=3, frequency_of_depreciation=12) + asset = create_asset( + calculate_depreciation = 1, + available_for_use_date = "2030-01-30", + purchase_date = "2030-01-30", + depreciation_method = "Straight Line", + expected_value_after_useful_life = 10000, + depreciation_start_date = "2030-12-31", + total_number_of_depreciations = 3, + frequency_of_depreciation = 12 + ) expected_schedules = [ ["2030-12-31", 27534.25, 27534.25], @@ -467,11 +506,16 @@ class TestDepreciationMethods(AssetSetup): # WDV: Written Down Value method def test_depreciation_entry_for_wdv_without_pro_rata(self): - asset = create_asset(calculate_depreciation=1, - available_for_use_date="2030-01-01", purchase_date="2030-01-01", - depreciation_method="Written Down Value", - expected_value_after_useful_life=12500, depreciation_start_date="2030-12-31", - total_number_of_depreciations=3, frequency_of_depreciation=12) + asset = create_asset( + calculate_depreciation = 1, + available_for_use_date = "2030-01-01", + purchase_date = "2030-01-01", + depreciation_method = "Written Down Value", + expected_value_after_useful_life = 12500, + depreciation_start_date = "2030-12-31", + total_number_of_depreciations = 3, + frequency_of_depreciation = 12 + ) self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0) @@ -488,11 +532,16 @@ class TestDepreciationMethods(AssetSetup): # WDV: Written Down Value method def test_pro_rata_depreciation_entry_for_wdv(self): - asset = create_asset(calculate_depreciation=1, - available_for_use_date="2030-06-06", purchase_date="2030-01-01", - depreciation_method="Written Down Value", - expected_value_after_useful_life=12500, depreciation_start_date="2030-12-31", - total_number_of_depreciations=3, frequency_of_depreciation=12) + asset = create_asset( + calculate_depreciation = 1, + available_for_use_date = "2030-06-06", + purchase_date = "2030-01-01", + depreciation_method = "Written Down Value", + expected_value_after_useful_life = 12500, + depreciation_start_date = "2030-12-31", + total_number_of_depreciations = 3, + frequency_of_depreciation = 12 + ) self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0) @@ -513,11 +562,16 @@ class TestDepreciationMethods(AssetSetup): company_flag = frappe.flags.company frappe.flags.company = "_Test Company" - asset = create_asset(calculate_depreciation=1, - available_for_use_date="2030-07-12", purchase_date="2030-01-01", - depreciation_method="Written Down Value", - expected_value_after_useful_life=12500, depreciation_start_date="2030-12-31", - total_number_of_depreciations=3, frequency_of_depreciation=12) + asset = create_asset( + calculate_depreciation = 1, + available_for_use_date = "2030-07-12", + purchase_date = "2030-01-01", + depreciation_method = "Written Down Value", + expected_value_after_useful_life = 12500, + depreciation_start_date = "2030-12-31", + total_number_of_depreciations = 3, + frequency_of_depreciation = 12 + ) self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0) @@ -538,9 +592,15 @@ class TestDepreciationMethods(AssetSetup): class TestDepreciationBasics(AssetSetup): def test_depreciation_without_pro_rata(self): - asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, - available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3, - expected_value_after_useful_life=10000, depreciation_start_date=getdate("2020-12-31"), submit=1) + asset = create_asset( + item_code = "Macbook Pro", + calculate_depreciation = 1, + available_for_use_date = getdate("2019-12-31"), + total_number_of_depreciations = 3, + expected_value_after_useful_life = 10000, + depreciation_start_date = getdate("2020-12-31"), + submit = 1 + ) expected_values = [ ["2020-12-31", 30000, 30000], @@ -554,9 +614,15 @@ class TestDepreciationBasics(AssetSetup): self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount) def test_depreciation_with_pro_rata(self): - asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, - available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3, - expected_value_after_useful_life=10000, depreciation_start_date=getdate("2020-07-01"), submit=1) + asset = create_asset( + item_code = "Macbook Pro", + calculate_depreciation = 1, + available_for_use_date = getdate("2019-12-31"), + total_number_of_depreciations = 3, + expected_value_after_useful_life = 10000, + depreciation_start_date = getdate("2020-07-01"), + submit = 1 + ) expected_values = [ ["2020-07-01", 15000, 15000], @@ -575,16 +641,18 @@ class TestDepreciationBasics(AssetSetup): from erpnext.assets.doctype.asset.asset import get_depreciation_amount - asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, - available_for_use_date=getdate("2019-12-31")) + asset = create_asset( + item_code = "Macbook Pro", + available_for_use_date = "2019-12-31" + ) - asset.finance_books = [] + asset.calculate_depreciation = 1 asset.append("finance_books", { "depreciation_method": "Straight Line", "frequency_of_depreciation": 12, "total_number_of_depreciations": 3, "expected_value_after_useful_life": 10000, - "depreciation_start_date": getdate("2020-12-31") + "depreciation_start_date": "2020-12-31" }) depreciation_amount = get_depreciation_amount(asset, 100000, asset.finance_books[0]) @@ -593,19 +661,16 @@ class TestDepreciationBasics(AssetSetup): def test_make_depreciation_schedule(self): """Tests if make_depreciation_schedule() returns the right values.""" - asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, - available_for_use_date=getdate("2019-12-31"), do_not_save=1) - - asset.finance_books = [] - asset.append("finance_books", { - "depreciation_method": "Straight Line", - "frequency_of_depreciation": 12, - "total_number_of_depreciations": 3, - "expected_value_after_useful_life": 10000, - "depreciation_start_date": getdate("2020-12-31") - }) - - asset.make_depreciation_schedule(date_of_sale=None) + asset = create_asset( + item_code = "Macbook Pro", + calculate_depreciation = 1, + available_for_use_date = "2019-12-31", + depreciation_method = "Straight Line", + frequency_of_depreciation = 12, + total_number_of_depreciations = 3, + expected_value_after_useful_life = 1000, + depreciation_start_date = "2020-12-31" + ) expected_values = [ ['2020-12-31', 30000.0], @@ -620,20 +685,16 @@ class TestDepreciationBasics(AssetSetup): def test_set_accumulated_depreciation(self): """Tests if set_accumulated_depreciation() returns the right values.""" - asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, - available_for_use_date=getdate("2019-12-31"), do_not_save=1) - - asset.finance_books = [] - asset.append("finance_books", { - "depreciation_method": "Straight Line", - "frequency_of_depreciation": 12, - "total_number_of_depreciations": 3, - "expected_value_after_useful_life": 10000, - "depreciation_start_date": getdate("2020-12-31") - }) - - asset.make_depreciation_schedule(date_of_sale=None) - asset.set_accumulated_depreciation() + asset = create_asset( + item_code = "Macbook Pro", + calculate_depreciation = 1, + available_for_use_date = "2019-12-31", + depreciation_method = "Straight Line", + frequency_of_depreciation = 12, + total_number_of_depreciations = 3, + expected_value_after_useful_life = 1000, + depreciation_start_date = "2020-12-31" + ) expected_values = [30000.0, 60000.0, 90000.0] @@ -643,16 +704,19 @@ class TestDepreciationBasics(AssetSetup): def test_check_is_pro_rata(self): """Tests if check_is_pro_rata() returns the right value(i.e. checks if has_pro_rata is accurate).""" - asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, - available_for_use_date=getdate("2019-12-31"), do_not_save=1) + asset = create_asset( + item_code = "Macbook Pro", + available_for_use_date = "2019-12-31", + do_not_save = 1 + ) - asset.finance_books = [] + asset.calculate_depreciation = 1 asset.append("finance_books", { "depreciation_method": "Straight Line", "frequency_of_depreciation": 12, "total_number_of_depreciations": 3, "expected_value_after_useful_life": 10000, - "depreciation_start_date": getdate("2020-12-31") + "depreciation_start_date": "2020-12-31" }) has_pro_rata = asset.check_is_pro_rata(asset.finance_books[0]) @@ -664,7 +728,7 @@ class TestDepreciationBasics(AssetSetup): "frequency_of_depreciation": 12, "total_number_of_depreciations": 3, "expected_value_after_useful_life": 10000, - "depreciation_start_date": getdate("2020-07-01") + "depreciation_start_date": "2020-07-01" }) has_pro_rata = asset.check_is_pro_rata(asset.finance_books[0]) @@ -673,64 +737,103 @@ class TestDepreciationBasics(AssetSetup): def test_expected_value_after_useful_life_greater_than_purchase_amount(self): """Tests if an error is raised when expected_value_after_useful_life(110,000) > gross_purchase_amount(100,000).""" - asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, - available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3, - expected_value_after_useful_life=110000, depreciation_start_date=getdate("2020-07-01"), do_not_save=1) + asset = create_asset( + item_code = "Macbook Pro", + calculate_depreciation = 1, + available_for_use_date = "2019-12-31", + total_number_of_depreciations = 3, + expected_value_after_useful_life = 110000, + depreciation_start_date = "2020-07-01", + do_not_save = 1 + ) self.assertRaises(frappe.ValidationError, asset.save) def test_depreciation_start_date(self): """Tests if an error is raised when neither depreciation_start_date nor available_for_use_date are specified.""" - asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, - total_number_of_depreciations=3, expected_value_after_useful_life=110000, do_not_save=1) + asset = create_asset( + item_code = "Macbook Pro", + calculate_depreciation = 1, + total_number_of_depreciations = 3, + expected_value_after_useful_life = 110000, + do_not_save = 1 + ) self.assertRaises(frappe.ValidationError, asset.save) def test_opening_accumulated_depreciation(self): """Tests if an error is raised when opening_accumulated_depreciation > (gross_purchase_amount - expected_value_after_useful_life).""" - asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, - available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3, - expected_value_after_useful_life=10000, depreciation_start_date=getdate("2020-07-01"), - opening_accumulated_depreciation=100000, do_not_save=1) + asset = create_asset( + item_code = "Macbook Pro", + calculate_depreciation = 1, + available_for_use_date = "2019-12-31", + total_number_of_depreciations = 3, + expected_value_after_useful_life = 10000, + depreciation_start_date = "2020-07-01", + opening_accumulated_depreciation = 100000, + do_not_save = 1 + ) self.assertRaises(frappe.ValidationError, asset.save) def test_number_of_depreciations_booked(self): """Tests if an error is raised when number_of_depreciations_booked is not specified when opening_accumulated_depreciation is.""" - asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, - available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3, - expected_value_after_useful_life=10000, depreciation_start_date=getdate("2020-07-01"), - opening_accumulated_depreciation=10000, do_not_save=1) + asset = create_asset( + item_code = "Macbook Pro", + calculate_depreciation = 1, + available_for_use_date = "2019-12-31", + total_number_of_depreciations = 3, + expected_value_after_useful_life = 10000, + depreciation_start_date = "2020-07-01", + opening_accumulated_depreciation = 10000, + do_not_save = 1 + ) self.assertRaises(frappe.ValidationError, asset.save) def test_number_of_depreciations(self): """Tests if an error is raised when number_of_depreciations_booked > total_number_of_depreciations.""" - asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, - available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3, - expected_value_after_useful_life=10000, depreciation_start_date=getdate("2020-07-01"), - opening_accumulated_depreciation=10000, number_of_depreciations_booked=5, - do_not_save=1) + asset = create_asset( + item_code = "Macbook Pro", + calculate_depreciation = 1, + available_for_use_date = "2019-12-31", + total_number_of_depreciations = 3, + expected_value_after_useful_life = 10000, + depreciation_start_date = "2020-07-01", + opening_accumulated_depreciation = 10000, + number_of_depreciations_booked = 5, + do_not_save = 1 + ) self.assertRaises(frappe.ValidationError, asset.save) def test_depreciation_start_date_is_before_purchase_date(self): - asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, - available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3, - expected_value_after_useful_life=10000, depreciation_start_date=getdate("2014-07-01"), - do_not_save=1) + asset = create_asset( + item_code = "Macbook Pro", + calculate_depreciation = 1, + available_for_use_date = "2019-12-31", + total_number_of_depreciations = 3, + expected_value_after_useful_life = 10000, + depreciation_start_date = "2014-07-01", + do_not_save = 1 + ) self.assertRaises(frappe.ValidationError, asset.save) def test_depreciation_start_date_is_before_available_for_use_date(self): - asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, - available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3, - expected_value_after_useful_life=10000, depreciation_start_date=getdate("2018-07-01"), - do_not_save=1) + asset = create_asset( + item_code = "Macbook Pro", + calculate_depreciation = 1, + available_for_use_date = "2019-12-31", + total_number_of_depreciations = 3, + expected_value_after_useful_life = 10000, + depreciation_start_date = "2018-07-01", + do_not_save = 1 + ) self.assertRaises(frappe.ValidationError, asset.save) @@ -743,10 +846,16 @@ class TestDepreciationBasics(AssetSetup): def test_post_depreciation_entries(self): """Tests if post_depreciation_entries() works as expected.""" - asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, - available_for_use_date="2019-12-31", depreciation_start_date="2020-12-31", - frequency_of_depreciation=12, total_number_of_depreciations=3, - expected_value_after_useful_life=10000, submit=1) + asset = create_asset( + item_code = "Macbook Pro", + calculate_depreciation = 1, + available_for_use_date = "2019-12-31", + depreciation_start_date = "2020-12-31", + frequency_of_depreciation = 12, + total_number_of_depreciations = 3, + expected_value_after_useful_life = 10000, + submit = 1 + ) post_depreciation_entries(date="2021-06-01") asset.load_from_db() @@ -758,10 +867,16 @@ class TestDepreciationBasics(AssetSetup): def test_clear_depreciation_schedule(self): """Tests if clear_depreciation_schedule() works as expected.""" - asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, - available_for_use_date="2019-12-31", depreciation_start_date="2020-12-31", - frequency_of_depreciation=12, total_number_of_depreciations=3, - expected_value_after_useful_life=10000, submit=1) + asset = create_asset( + item_code = "Macbook Pro", + calculate_depreciation = 1, + available_for_use_date = "2019-12-31", + depreciation_start_date = "2020-12-31", + frequency_of_depreciation = 12, + total_number_of_depreciations = 3, + expected_value_after_useful_life = 10000, + submit = 1 + ) post_depreciation_entries(date="2021-06-01") asset.load_from_db() @@ -771,11 +886,17 @@ class TestDepreciationBasics(AssetSetup): self.assertEqual(len(asset.schedules), 1) def test_depreciation_entry_cancellation(self): - asset = create_asset(item_code="Macbook Pro", - calculate_depreciation=1, purchase_date="2020-06-06", - available_for_use_date="2020-06-06", depreciation_start_date="2020-12-31", - frequency_of_depreciation=10, total_number_of_depreciations=3, - expected_value_after_useful_life=10000, submit=1) + asset = create_asset( + item_code = "Macbook Pro", + calculate_depreciation = 1, + purchase_date = "2020-06-06", + available_for_use_date = "2020-06-06", + depreciation_start_date = "2020-12-31", + frequency_of_depreciation = 10, + total_number_of_depreciations = 3, + expected_value_after_useful_life = 10000, + submit = 1 + ) post_depreciation_entries(date="2021-01-01") @@ -791,10 +912,15 @@ class TestDepreciationBasics(AssetSetup): self.assertFalse(depr_entry) def test_asset_expected_value_after_useful_life(self): - asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, - available_for_use_date="2020-06-06", purchase_date="2020-06-06", - frequency_of_depreciation=10, total_number_of_depreciations=3, - expected_value_after_useful_life=10000) + asset = create_asset( + item_code = "Macbook Pro", + calculate_depreciation = 1, + available_for_use_date = "2020-06-06", + purchase_date = "2020-06-06", + frequency_of_depreciation = 10, + total_number_of_depreciations = 3, + expected_value_after_useful_life = 10000 + ) accumulated_depreciation_after_full_schedule = \ max(d.accumulated_depreciation_amount for d in asset.get("schedules")) @@ -805,11 +931,17 @@ class TestDepreciationBasics(AssetSetup): self.assertTrue(asset.finance_books[0].expected_value_after_useful_life >= asset_value_after_full_schedule) def test_gle_made_by_depreciation_entries(self): - asset = create_asset(item_code="Macbook Pro", - calculate_depreciation=1, purchase_date="2020-01-30", - available_for_use_date="2020-01-30", depreciation_start_date="2020-12-31", - frequency_of_depreciation=10, total_number_of_depreciations=3, - expected_value_after_useful_life=10000, submit=1) + asset = create_asset( + item_code = "Macbook Pro", + calculate_depreciation = 1, + purchase_date = "2020-01-30", + available_for_use_date = "2020-01-30", + depreciation_start_date = "2020-12-31", + frequency_of_depreciation = 10, + total_number_of_depreciations = 3, + expected_value_after_useful_life = 10000, + submit = 1 + ) self.assertEqual(asset.status, "Submitted") From 371b62136454f720a02b12644d6e57e7d9840a34 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 15 Oct 2021 04:02:27 +0530 Subject: [PATCH 035/105] fix: Compare date strings --- erpnext/assets/doctype/asset/test_asset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 523c8195ae2..c694677d8d7 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -679,7 +679,7 @@ class TestDepreciationBasics(AssetSetup): ] for i, schedule in enumerate(asset.schedules): - self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date) + self.assertEqual(expected_values[i][0], schedule.schedule_date) self.assertEqual(expected_values[i][1], schedule.depreciation_amount) def test_set_accumulated_depreciation(self): From 60aae4423dcf9e26557f0a44c2f7c206d54eb07b Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 15 Oct 2021 04:03:30 +0530 Subject: [PATCH 036/105] fix: Add missing digit --- erpnext/assets/doctype/asset/test_asset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index c694677d8d7..c28add80653 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -692,7 +692,7 @@ class TestDepreciationBasics(AssetSetup): depreciation_method = "Straight Line", frequency_of_depreciation = 12, total_number_of_depreciations = 3, - expected_value_after_useful_life = 1000, + expected_value_after_useful_life = 10000, depreciation_start_date = "2020-12-31" ) From fdeb273fa06670d802847199d57ecd40dbecd476 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 15 Oct 2021 04:53:23 +0530 Subject: [PATCH 037/105] fix: Sider issues --- erpnext/assets/doctype/asset/test_asset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index c28add80653..6dd7a5be71f 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -410,7 +410,7 @@ class TestDepreciationMethods(AssetSetup): calculate_depreciation = 1, available_for_use_date = "2030-06-06", is_existing_asset = 1, - number_of_depreciations_booked = 1, + number_of_depreciations_booked = 1, opening_accumulated_depreciation = 40000, expected_value_after_useful_life = 10000, depreciation_start_date = "2030-12-31", @@ -460,7 +460,7 @@ class TestDepreciationMethods(AssetSetup): available_for_use_date = "2030-01-01", is_existing_asset = 1, depreciation_method = "Double Declining Balance", - number_of_depreciations_booked = 1, + number_of_depreciations_booked = 1, opening_accumulated_depreciation = 50000, expected_value_after_useful_life = 10000, depreciation_start_date = "2030-12-31", From 0b8cb5dd47816b87cfea2176ffa76c889bb9d383 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 15 Oct 2021 04:56:00 +0530 Subject: [PATCH 038/105] fix: Add missing digit --- erpnext/assets/doctype/asset/test_asset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 6dd7a5be71f..c44c63ae9e4 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -668,7 +668,7 @@ class TestDepreciationBasics(AssetSetup): depreciation_method = "Straight Line", frequency_of_depreciation = 12, total_number_of_depreciations = 3, - expected_value_after_useful_life = 1000, + expected_value_after_useful_life = 10000, depreciation_start_date = "2020-12-31" ) From ac381d21fe94b99406b4d7d332a6356a41bd8e26 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 18 Oct 2021 10:43:15 +0530 Subject: [PATCH 039/105] fix: sider --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 0c3eff2a030..290c7fe60b2 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1997,8 +1997,7 @@ def get_mode_of_payments_info(mode_of_payments, company): mp.name in (%s) group by mp.name - """, - (company, mode_of_payments), as_dict=1) + """, (company, mode_of_payments), as_dict=1) return {row.get('mop'): row for row in data} From 1aa34d178051c9b1ec20bf45171bd12d6df4d151 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Mon, 18 Oct 2021 18:46:45 +0530 Subject: [PATCH 040/105] fix: incorrect VAT Amount in UAT VAT 201 report --- erpnext/regional/report/uae_vat_201/uae_vat_201.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py index f4c049d1623..8507b0effb2 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -122,7 +122,7 @@ def get_total_emiratewise(filters): try: return frappe.db.sql(""" select - s.vat_emirate as emirate, sum(i.base_amount) as total, sum(s.total_taxes_and_charges) + s.vat_emirate as emirate, sum(i.base_amount) as total, s.total_taxes_and_charges from `tabSales Invoice Item` i inner join `tabSales Invoice` s on From 4c499e804a29ac6639de6df9cfd6704f338a58c3 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Tue, 19 Oct 2021 14:12:59 +0530 Subject: [PATCH 041/105] fix: wrong vat amount --- erpnext/regional/report/uae_vat_201/uae_vat_201.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.py b/erpnext/regional/report/uae_vat_201/uae_vat_201.py index 8507b0effb2..2b5ecc3b18c 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.py +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.py @@ -122,7 +122,7 @@ def get_total_emiratewise(filters): try: return frappe.db.sql(""" select - s.vat_emirate as emirate, sum(i.base_amount) as total, s.total_taxes_and_charges + s.vat_emirate as emirate, sum(i.base_amount) as total, sum(i.tax_amount) from `tabSales Invoice Item` i inner join `tabSales Invoice` s on From 8b547e39a872f3ce5c785d1e905bff58496db8d6 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 21 Oct 2021 12:06:02 +0530 Subject: [PATCH 042/105] feat: Taxes template for selling/buying doctypes Depends on the new Print Format Builder frappe/frappe#14134 --- .../print_format_field_template/__init__.py | 0 .../purchase_invoice_taxes/__init__.py | 0 .../purchase_invoice_taxes.json | 16 +++++++++ .../sales_invoice_taxes/__init__.py | 0 .../sales_invoice_taxes.json | 16 +++++++++ .../print_format_field_template/__init__.py | 0 .../purchase_order_taxes/__init__.py | 0 .../purchase_order_taxes.json | 16 +++++++++ .../supplier_quotation_taxes/__init__.py | 0 .../supplier_quotation_taxes.json | 16 +++++++++ .../print_format_field_template/__init__.py | 0 .../quotation_taxes/__init__.py | 0 .../quotation_taxes/quotation_taxes.json | 16 +++++++++ .../sales_order_taxes/__init__.py | 0 .../sales_order_taxes/sales_order_taxes.json | 16 +++++++++ .../includes/taxes_and_charges.html | 34 +++++++++++++++++++ 16 files changed, 130 insertions(+) create mode 100644 erpnext/accounts/print_format_field_template/__init__.py create mode 100644 erpnext/accounts/print_format_field_template/purchase_invoice_taxes/__init__.py create mode 100644 erpnext/accounts/print_format_field_template/purchase_invoice_taxes/purchase_invoice_taxes.json create mode 100644 erpnext/accounts/print_format_field_template/sales_invoice_taxes/__init__.py create mode 100644 erpnext/accounts/print_format_field_template/sales_invoice_taxes/sales_invoice_taxes.json create mode 100644 erpnext/buying/print_format_field_template/__init__.py create mode 100644 erpnext/buying/print_format_field_template/purchase_order_taxes/__init__.py create mode 100644 erpnext/buying/print_format_field_template/purchase_order_taxes/purchase_order_taxes.json create mode 100644 erpnext/buying/print_format_field_template/supplier_quotation_taxes/__init__.py create mode 100644 erpnext/buying/print_format_field_template/supplier_quotation_taxes/supplier_quotation_taxes.json create mode 100644 erpnext/selling/print_format_field_template/__init__.py create mode 100644 erpnext/selling/print_format_field_template/quotation_taxes/__init__.py create mode 100644 erpnext/selling/print_format_field_template/quotation_taxes/quotation_taxes.json create mode 100644 erpnext/selling/print_format_field_template/sales_order_taxes/__init__.py create mode 100644 erpnext/selling/print_format_field_template/sales_order_taxes/sales_order_taxes.json create mode 100644 erpnext/templates/print_formats/includes/taxes_and_charges.html diff --git a/erpnext/accounts/print_format_field_template/__init__.py b/erpnext/accounts/print_format_field_template/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/print_format_field_template/purchase_invoice_taxes/__init__.py b/erpnext/accounts/print_format_field_template/purchase_invoice_taxes/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/print_format_field_template/purchase_invoice_taxes/purchase_invoice_taxes.json b/erpnext/accounts/print_format_field_template/purchase_invoice_taxes/purchase_invoice_taxes.json new file mode 100644 index 00000000000..f525f7b8d42 --- /dev/null +++ b/erpnext/accounts/print_format_field_template/purchase_invoice_taxes/purchase_invoice_taxes.json @@ -0,0 +1,16 @@ +{ + "creation": "2021-10-19 18:06:53.083133", + "docstatus": 0, + "doctype": "Print Format Field Template", + "document_type": "Purchase Invoice", + "field": "taxes", + "idx": 0, + "modified": "2021-10-19 18:06:53.083133", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Purchase Invoice Taxes", + "owner": "Administrator", + "standard": 1, + "template": "", + "template_file": "templates/print_formats/includes/taxes_and_charges.html" +} \ No newline at end of file diff --git a/erpnext/accounts/print_format_field_template/sales_invoice_taxes/__init__.py b/erpnext/accounts/print_format_field_template/sales_invoice_taxes/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/print_format_field_template/sales_invoice_taxes/sales_invoice_taxes.json b/erpnext/accounts/print_format_field_template/sales_invoice_taxes/sales_invoice_taxes.json new file mode 100644 index 00000000000..8ce62a8b26f --- /dev/null +++ b/erpnext/accounts/print_format_field_template/sales_invoice_taxes/sales_invoice_taxes.json @@ -0,0 +1,16 @@ +{ + "creation": "2021-10-19 17:50:00.152759", + "docstatus": 0, + "doctype": "Print Format Field Template", + "document_type": "Sales Invoice", + "field": "taxes", + "idx": 0, + "modified": "2021-10-19 18:13:20.894207", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Sales Invoice Taxes", + "owner": "Administrator", + "standard": 1, + "template": "", + "template_file": "templates/print_formats/includes/taxes_and_charges.html" +} \ No newline at end of file diff --git a/erpnext/buying/print_format_field_template/__init__.py b/erpnext/buying/print_format_field_template/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/buying/print_format_field_template/purchase_order_taxes/__init__.py b/erpnext/buying/print_format_field_template/purchase_order_taxes/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/buying/print_format_field_template/purchase_order_taxes/purchase_order_taxes.json b/erpnext/buying/print_format_field_template/purchase_order_taxes/purchase_order_taxes.json new file mode 100644 index 00000000000..73b77308949 --- /dev/null +++ b/erpnext/buying/print_format_field_template/purchase_order_taxes/purchase_order_taxes.json @@ -0,0 +1,16 @@ +{ + "creation": "2021-10-19 18:07:19.253457", + "docstatus": 0, + "doctype": "Print Format Field Template", + "document_type": "Purchase Order", + "field": "taxes", + "idx": 0, + "modified": "2021-10-19 18:07:19.253457", + "modified_by": "Administrator", + "module": "Buying", + "name": "Purchase Order Taxes", + "owner": "Administrator", + "standard": 1, + "template": "", + "template_file": "templates/print_formats/includes/taxes_and_charges.html" +} \ No newline at end of file diff --git a/erpnext/buying/print_format_field_template/supplier_quotation_taxes/__init__.py b/erpnext/buying/print_format_field_template/supplier_quotation_taxes/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/buying/print_format_field_template/supplier_quotation_taxes/supplier_quotation_taxes.json b/erpnext/buying/print_format_field_template/supplier_quotation_taxes/supplier_quotation_taxes.json new file mode 100644 index 00000000000..2be17a1b012 --- /dev/null +++ b/erpnext/buying/print_format_field_template/supplier_quotation_taxes/supplier_quotation_taxes.json @@ -0,0 +1,16 @@ +{ + "creation": "2021-10-19 18:09:08.103919", + "docstatus": 0, + "doctype": "Print Format Field Template", + "document_type": "Supplier Quotation", + "field": "taxes", + "idx": 0, + "modified": "2021-10-19 18:09:08.103919", + "modified_by": "Administrator", + "module": "Buying", + "name": "Supplier Quotation Taxes", + "owner": "Administrator", + "standard": 1, + "template": "", + "template_file": "templates/print_formats/includes/taxes_and_charges.html" +} \ No newline at end of file diff --git a/erpnext/selling/print_format_field_template/__init__.py b/erpnext/selling/print_format_field_template/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/selling/print_format_field_template/quotation_taxes/__init__.py b/erpnext/selling/print_format_field_template/quotation_taxes/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/selling/print_format_field_template/quotation_taxes/quotation_taxes.json b/erpnext/selling/print_format_field_template/quotation_taxes/quotation_taxes.json new file mode 100644 index 00000000000..eaa17cedf05 --- /dev/null +++ b/erpnext/selling/print_format_field_template/quotation_taxes/quotation_taxes.json @@ -0,0 +1,16 @@ +{ + "creation": "2021-10-19 15:48:56.416449", + "docstatus": 0, + "doctype": "Print Format Field Template", + "document_type": "Quotation", + "field": "taxes", + "idx": 0, + "modified": "2021-10-19 18:11:33.553722", + "modified_by": "Administrator", + "module": "Selling", + "name": "Quotation Taxes", + "owner": "Administrator", + "standard": 1, + "template": "", + "template_file": "templates/print_formats/includes/taxes_and_charges.html" +} \ No newline at end of file diff --git a/erpnext/selling/print_format_field_template/sales_order_taxes/__init__.py b/erpnext/selling/print_format_field_template/sales_order_taxes/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/selling/print_format_field_template/sales_order_taxes/sales_order_taxes.json b/erpnext/selling/print_format_field_template/sales_order_taxes/sales_order_taxes.json new file mode 100644 index 00000000000..2aacb440ff8 --- /dev/null +++ b/erpnext/selling/print_format_field_template/sales_order_taxes/sales_order_taxes.json @@ -0,0 +1,16 @@ +{ + "creation": "2021-10-19 18:04:24.443076", + "docstatus": 0, + "doctype": "Print Format Field Template", + "document_type": "Sales Order", + "field": "taxes", + "idx": 0, + "modified": "2021-10-19 18:04:24.443076", + "modified_by": "Administrator", + "module": "Selling", + "name": "Sales Order Taxes", + "owner": "Administrator", + "standard": 1, + "template": "", + "template_file": "templates/print_formats/includes/taxes_and_charges.html" +} \ No newline at end of file diff --git a/erpnext/templates/print_formats/includes/taxes_and_charges.html b/erpnext/templates/print_formats/includes/taxes_and_charges.html new file mode 100644 index 00000000000..0d8e3834d81 --- /dev/null +++ b/erpnext/templates/print_formats/includes/taxes_and_charges.html @@ -0,0 +1,34 @@ +{% macro render_row(label, value) %} +
+
+
{{ label }}
+
+
+ {{ value }} +
+
+{% endmacro %} + +{%- macro render_discount_amount(doc) -%} + {%- if doc.discount_amount -%} + {{ render_row(_(doc.meta.get_label('discount_amount')), '- ' + doc.get_formatted("discount_amount", doc)) }} + {%- endif -%} +{%- endmacro -%} + +
+
+
+ {%- if doc.apply_discount_on == "Net Total" -%} + {{ render_discount_amount(doc) }} + {%- endif -%} + {%- for charge in doc.taxes -%} + {%- if (charge.tax_amount or print_settings.print_taxes_with_zero_amount) and (not charge.included_in_print_rate or doc.flags.show_inclusive_tax_in_print) -%} + {{ render_row(charge.get_formatted("description"), charge.get_formatted('tax_amount', doc)) }} + {%- endif -%} + {%- endfor -%} + {%- if doc.apply_discount_on == "Grand Total" -%} + {{ render_discount_amount(doc) }} + {%- endif -%} +
+
+ From e6e804e7d783a43d13dafe7b16f6e5d3b750f1d3 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 23 Oct 2021 18:58:41 +0530 Subject: [PATCH 043/105] fix: Get LTDS based on tax withholding category --- .../doctype/tax_withholding_category/tax_withholding_category.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index c3cb8396d0d..54042fbe748 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -165,6 +165,7 @@ def get_lower_deduction_certificate(tax_details, pan_no): ldc_name = frappe.db.get_value('Lower Deduction Certificate', { 'pan_no': pan_no, + 'tax_withholding_category': tax_details.tax_withholding_category, 'valid_from': ('>=', tax_details.from_date), 'valid_upto': ('<=', tax_details.to_date) }, 'name') From bf13d183d8066219379a5e00c4ace4ca2f6c155b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 23 Oct 2021 19:00:03 +0530 Subject: [PATCH 044/105] fix: Replace section code with tax withholding category in LDC --- erpnext/patches.txt | 1 + .../update_category_in_ltds_certificate.py | 10 ++++++++++ .../lower_deduction_certificate.json | 20 ++++++++++--------- .../lower_deduction_certificate.py | 12 ++++++----- 4 files changed, 29 insertions(+), 14 deletions(-) create mode 100644 erpnext/patches/v13_0/update_category_in_ltds_certificate.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e446d6be42f..daea70cf62b 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -310,3 +310,4 @@ erpnext.patches.v13_0.enable_scheduler_job_for_item_reposting erpnext.patches.v13_0.requeue_failed_reposts erpnext.patches.v13_0.healthcare_deprecation_warning erpnext.patches.v14_0.delete_healthcare_doctypes +erpnext.patches.v13_0.update_category_in_ltds_certificate diff --git a/erpnext/patches/v13_0/update_category_in_ltds_certificate.py b/erpnext/patches/v13_0/update_category_in_ltds_certificate.py new file mode 100644 index 00000000000..f8a0646fcbb --- /dev/null +++ b/erpnext/patches/v13_0/update_category_in_ltds_certificate.py @@ -0,0 +1,10 @@ +import frappe + + +def execute(): + + frappe.db.sql(""" + UPDATE `tabLower Deduction Certificate` l, `tabSupplier` s + SET l.tax_withholding_category = s.tax_withholding_category + WHERE l.supplier = s.name + """) \ No newline at end of file diff --git a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json index f48fe6f4763..c32ab6bec24 100644 --- a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json +++ b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json @@ -7,7 +7,7 @@ "engine": "InnoDB", "field_order": [ "certificate_details_section", - "section_code", + "tax_withholding_category", "fiscal_year", "column_break_3", "certificate_no", @@ -33,13 +33,6 @@ "reqd": 1, "unique": 1 }, - { - "fieldname": "section_code", - "fieldtype": "Select", - "label": "Section Code", - "options": "192\n193\n194\n194A\n194C\n194D\n194H\n194I\n194J\n194LA\n194LBB\n194LBC\n195", - "reqd": 1 - }, { "fieldname": "section_break_3", "fieldtype": "Section Break", @@ -123,13 +116,22 @@ "label": "Fiscal Year", "options": "Fiscal Year", "reqd": 1 + }, + { + "fieldname": "tax_withholding_category", + "fieldtype": "Link", + "label": "Tax Withholding Category", + "options": "Tax Withholding Category", + "reqd": 1 } ], + "index_web_pages_for_search": 1, "links": [], - "modified": "2020-04-23 23:04:41.203721", + "modified": "2021-10-23 18:33:38.962622", "modified_by": "Administrator", "module": "Regional", "name": "Lower Deduction Certificate", + "naming_rule": "By fieldname", "owner": "Administrator", "permissions": [], "sort_field": "modified", diff --git a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py index d8553f1d913..7afbc00980c 100644 --- a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py +++ b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py @@ -15,7 +15,7 @@ from erpnext.accounts.utils import get_fiscal_year class LowerDeductionCertificate(Document): def validate(self): self.validate_dates() - self.validate_supplier_against_section_code() + self.validate_supplier_against_tax_category() def validate_dates(self): if getdate(self.valid_upto) < getdate(self.valid_from): @@ -31,12 +31,14 @@ class LowerDeductionCertificate(Document): <= fiscal_year.year_end_date): frappe.throw(_("Valid Upto date not in Fiscal Year {0}").format(frappe.bold(self.fiscal_year))) - def validate_supplier_against_section_code(self): - duplicate_certificate = frappe.db.get_value('Lower Deduction Certificate', {'supplier': self.supplier, 'section_code': self.section_code}, ['name', 'valid_from', 'valid_upto'], as_dict=True) + def tax_withholding_category(self): + duplicate_certificate = frappe.db.get_value('Lower Deduction Certificate', + {'supplier': self.supplier, 'tax_withholding_category': self.tax_withholding_category, 'name': ("!=", self.name)}, + ['name', 'valid_from', 'valid_upto'], as_dict=True) if duplicate_certificate and self.are_dates_overlapping(duplicate_certificate): certificate_link = get_link_to_form('Lower Deduction Certificate', duplicate_certificate.name) - frappe.throw(_("There is already a valid Lower Deduction Certificate {0} for Supplier {1} against Section Code {2} for this time period.") - .format(certificate_link, frappe.bold(self.supplier), frappe.bold(self.section_code))) + frappe.throw(_("There is already a valid Lower Deduction Certificate {0} for Supplier {1} against category {2} for this time period.") + .format(certificate_link, frappe.bold(self.supplier), frappe.bold(self.tax_withholding_category))) def are_dates_overlapping(self,duplicate_certificate): valid_from = duplicate_certificate.valid_from From 9e7022830e058c3ba95f034942cad054e045e8f6 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 25 Oct 2021 01:34:42 +0530 Subject: [PATCH 045/105] fix: Only add additional depreciation schedule row on sale if depreciation_amount > 0 --- erpnext/assets/doctype/asset/asset.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index f6c2f4caa2d..cf62f496ea6 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -232,13 +232,15 @@ class Asset(AccountsController): depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount, from_date, date_of_sale) - self.append("schedules", { - "schedule_date": date_of_sale, - "depreciation_amount": depreciation_amount, - "depreciation_method": d.depreciation_method, - "finance_book": d.finance_book, - "finance_book_id": d.idx - }) + if depreciation_amount > 0: + self.append("schedules", { + "schedule_date": date_of_sale, + "depreciation_amount": depreciation_amount, + "depreciation_method": d.depreciation_method, + "finance_book": d.finance_book, + "finance_book_id": d.idx + }) + break # For first row From 9c1705205f4597639c800b04eaf421bb7e987844 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 25 Oct 2021 20:06:24 +0530 Subject: [PATCH 046/105] fix: Payment Terms validation precision --- erpnext/controllers/accounts_controller.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 88c439b4f6c..b05d1b6f6d4 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1354,8 +1354,8 @@ class AccountsController(TransactionBase): total = 0 base_total = 0 for d in self.get("payment_schedule"): - total += flt(d.payment_amount) - base_total += flt(d.base_payment_amount) + total += flt(d.payment_amount, d.precision("payment_amount")) + base_total += flt(d.base_payment_amount, d.precision("base_payment_amount")) base_grand_total = self.get("base_rounded_total") or self.base_grand_total grand_total = self.get("rounded_total") or self.grand_total @@ -1371,8 +1371,9 @@ class AccountsController(TransactionBase): else: grand_total -= self.get("total_advance") base_grand_total = flt(grand_total * self.get("conversion_rate"), self.precision("base_grand_total")) - if total != flt(grand_total, self.precision("grand_total")) or \ - base_total != flt(base_grand_total, self.precision("base_grand_total")): + + if flt(total, self.precision("grand_total")) != flt(grand_total, self.precision("grand_total")) or \ + flt(base_total, self.precision("base_grand_total")) != flt(base_grand_total, self.precision("base_grand_total")): frappe.throw(_("Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total")) def is_rounded_total_disabled(self): From 82bf5e55393520f7681615e1addf4ceee4319ee5 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 26 Oct 2021 20:53:47 +0530 Subject: [PATCH 047/105] fix: Replace post_depreciation_entries() with make_depreciation_entry() --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 379bbbe9114..1f0b3c244b6 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -37,7 +37,7 @@ from erpnext.assets.doctype.asset.depreciation import ( get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal, get_gl_entries_on_asset_regain, - post_depreciation_entries, + make_depreciation_entry ) from erpnext.controllers.selling_controller import SellingController from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data @@ -1001,7 +1001,7 @@ class SalesInvoice(SellingController): asset.prepare_depreciation_data(date_of_sale=self.posting_date) asset.save() - post_depreciation_entries(self.posting_date) + make_depreciation_entry(asset.name, self.posting_date) def reset_depreciation_schedule(self, asset): asset.flags.ignore_validate_update_after_submit = True From cde0dae98733563c02c7e364de2b97d9831a3e97 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 26 Oct 2021 21:04:06 +0530 Subject: [PATCH 048/105] fix: Add flag for reverse depreciation entries --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 3 ++- erpnext/accounts/doctype/journal_entry/journal_entry.py | 2 +- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 1e983b1d429..60015f6ec88 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -58,7 +58,8 @@ class GLEntry(Document): # Update outstanding amt on against voucher if (self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees'] - and self.against_voucher and self.flags.update_outstanding == 'Yes'): + and self.against_voucher and self.flags.update_outstanding == 'Yes' + and not frappe.flags.is_reverse_depr_entry): update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type, self.against_voucher) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index d04e4a5d8bd..f3a0bdbec49 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -58,7 +58,7 @@ class JournalEntry(AccountsController): if not frappe.flags.in_import: self.validate_total_debit_and_credit() - if not self.flags.is_reverse_depr_entry: + if not frappe.flags.is_reverse_depr_entry: self.validate_against_jv() self.validate_stock_accounts() diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 1f0b3c244b6..ef72decaa2b 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1045,7 +1045,7 @@ class SalesInvoice(SellingController): reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry) reverse_journal_entry.posting_date = nowdate() - reverse_journal_entry.flags.is_reverse_depr_entry = True + frappe.flags.is_reverse_depr_entry = True reverse_journal_entry.submit() asset.flags.ignore_validate_update_after_submit = True From 06c505ddc2c3529703a35a986896112de7107ae3 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 27 Oct 2021 05:23:01 +0530 Subject: [PATCH 049/105] fix: Linters --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 2 +- erpnext/assets/doctype/asset/test_asset.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index ef72decaa2b..1c287d3f8cd 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -37,7 +37,7 @@ from erpnext.assets.doctype.asset.depreciation import ( get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal, get_gl_entries_on_asset_regain, - make_depreciation_entry + make_depreciation_entry, ) from erpnext.controllers.selling_controller import SellingController from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index c44c63ae9e4..81c679f851b 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -20,6 +20,7 @@ from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( ) from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt + class AssetSetup(unittest.TestCase): @classmethod def setUpClass(cls): From f2d136e57475421694c12c97b9eba6ccb8e45f76 Mon Sep 17 00:00:00 2001 From: Diksha Jadhav Date: Tue, 5 Oct 2021 14:50:29 +0530 Subject: [PATCH 050/105] feat(pick list): group items based on item code and warehouse before printing picklist --- .../stock/doctype/pick_list/pick_list.json | 22 +++++++++++++--- erpnext/stock/doctype/pick_list/pick_list.py | 26 +++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json index 21467935370..c604c711ef5 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -18,7 +18,9 @@ "get_item_locations", "section_break_6", "locations", - "amended_from" + "amended_from", + "print_settings_section", + "group_same_items" ], "fields": [ { @@ -110,14 +112,28 @@ "options": "STO-PICK-.YYYY.-", "reqd": 1, "set_only_once": 1 + }, + { + "fieldname": "print_settings_section", + "fieldtype": "Section Break", + "label": "Print Settings" + }, + { + "allow_on_submit": 1, + "default": "0", + "fieldname": "group_same_items", + "fieldtype": "Check", + "label": "Group Same Items", + "print_hide": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-03-17 11:38:41.932875", + "modified": "2021-10-05 15:08:40.369957", "modified_by": "Administrator", "module": "Stock", "name": "Pick List", + "naming_rule": "By \"Naming Series\" field", "owner": "Administrator", "permissions": [ { @@ -184,4 +200,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} +} \ No newline at end of file diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index dffbe80fa39..7b2f44b0843 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -121,6 +121,32 @@ class PickList(Document): and (self.for_qty is None or self.for_qty == 0): frappe.throw(_("Qty of Finished Goods Item should be greater than 0.")) + def before_print(self, settings=None): + if self.get("group_same_items"): + self.group_similar_items() + + def group_similar_items(self): + group_item_qty = {} + group_picked_qty = {} + count = 0 + + for item in self.locations: + group_item_qty[(item.item_code, item.warehouse)] = group_item_qty.get((item.item_code,item.warehouse), 0) + item.qty + group_picked_qty[(item.item_code, item.warehouse)] = group_picked_qty.get((item.item_code,item.warehouse), 0) + item.picked_qty + + duplicate_list = [] + for item in self.locations: + if (item.item_code, item.warehouse) in group_item_qty: + count += 1 + item.qty = group_item_qty[(item.item_code, item.warehouse)] + item.picked_qty = group_picked_qty[(item.item_code, item.warehouse)] + item.stock_qty = group_item_qty[(item.item_code, item.warehouse)] + item.idx = count + del group_item_qty[(item.item_code, item.warehouse)] + else: + duplicate_list.append(item) + for item in duplicate_list: + self.remove(item) def validate_item_locations(pick_list): if not pick_list.locations: From 69429005551161e63cda5c2b2fcda1a685280775 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 27 Oct 2021 10:36:06 +0530 Subject: [PATCH 051/105] refactor: use defaultdict and enumeration --- erpnext/stock/doctype/pick_list/pick_list.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 7b2f44b0843..4c02f3db43b 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -2,10 +2,8 @@ # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals - import json -from collections import OrderedDict +from collections import OrderedDict, defaultdict import frappe from frappe import _ @@ -126,28 +124,30 @@ class PickList(Document): self.group_similar_items() def group_similar_items(self): - group_item_qty = {} - group_picked_qty = {} - count = 0 + group_item_qty = defaultdict(float) + group_picked_qty = defaultdict(float) for item in self.locations: - group_item_qty[(item.item_code, item.warehouse)] = group_item_qty.get((item.item_code,item.warehouse), 0) + item.qty - group_picked_qty[(item.item_code, item.warehouse)] = group_picked_qty.get((item.item_code,item.warehouse), 0) + item.picked_qty + group_item_qty[(item.item_code, item.warehouse)] += item.qty + group_picked_qty[(item.item_code, item.warehouse)] += item.picked_qty duplicate_list = [] for item in self.locations: if (item.item_code, item.warehouse) in group_item_qty: - count += 1 item.qty = group_item_qty[(item.item_code, item.warehouse)] item.picked_qty = group_picked_qty[(item.item_code, item.warehouse)] item.stock_qty = group_item_qty[(item.item_code, item.warehouse)] - item.idx = count del group_item_qty[(item.item_code, item.warehouse)] else: duplicate_list.append(item) + for item in duplicate_list: self.remove(item) + for idx, item in enumerate(self.locations, start=1): + item.idx = idx + + def validate_item_locations(pick_list): if not pick_list.locations: frappe.throw(_("Add items in the Item Locations table")) From 479ecb8de0c77beafddd243c1b5fe38793ea32de Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 27 Oct 2021 10:51:47 +0530 Subject: [PATCH 052/105] test: picklist item grouping --- .../stock/doctype/pick_list/test_pick_list.py | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py index fd0b3680df2..58b46e1eefc 100644 --- a/erpnext/stock/doctype/pick_list/test_pick_list.py +++ b/erpnext/stock/doctype/pick_list/test_pick_list.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe +from frappe import _dict test_dependencies = ['Item', 'Sales Invoice', 'Stock Entry', 'Batch'] @@ -356,6 +357,39 @@ class TestPickList(ERPNextTestCase): sales_order.cancel() purchase_receipt.cancel() + def test_pick_list_grouping_before_print(self): + def _compare_dicts(a, b): + "compare dicts but ignore missing keys in `a`" + for key, value in a.items(): + self.assertEqual(b.get(key), value, msg=f"{key} doesn't match") + + # nothing should be grouped + pl = frappe.get_doc(doctype="Pick List", group_same_items=True, locations=[ + _dict(item_code="A", warehouse="X", qty=1, picked_qty=2), + _dict(item_code="B", warehouse="X", qty=1, picked_qty=2), + _dict(item_code="A", warehouse="Y", qty=1, picked_qty=2), + _dict(item_code="B", warehouse="Y", qty=1, picked_qty=2), + ]) + pl.before_print() + self.assertEqual(len(pl.locations), 4) + + # grouping should halve the number of items + pl = frappe.get_doc(doctype="Pick List", group_same_items=True, locations=[ + _dict(item_code="A", warehouse="X", qty=5, picked_qty=1), + _dict(item_code="B", warehouse="Y", qty=4, picked_qty=2), + _dict(item_code="A", warehouse="X", qty=3, picked_qty=2), + _dict(item_code="B", warehouse="Y", qty=2, picked_qty=2), + ]) + pl.before_print() + self.assertEqual(len(pl.locations), 2) + + expected_items = [ + _dict(item_code="A", warehouse="X", qty=8, picked_qty=3), + _dict(item_code="B", warehouse="Y", qty=6, picked_qty=4), + ] + for expected_item, created_item in zip(expected_items, pl.locations): + _compare_dicts(expected_item, created_item) + # def test_pick_list_skips_items_in_expired_batch(self): # pass From 2920f2f61479448e6467c97b7ba696bb90d9c092 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Wed, 27 Oct 2021 11:17:48 +0530 Subject: [PATCH 053/105] fix: change modified timestamp to apply changes on migrate #28095 fix: change modified timestamp to apply changes on migrate --- .../expense_taxes_and_charges/expense_taxes_and_charges.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json index 4a1064b66b7..2f7b8fcf679 100644 --- a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json +++ b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json @@ -100,7 +100,7 @@ ], "istable": 1, "links": [], - "modified": "2020-09-23 20:27:36.027728", + "modified": "2021-10-26 20:27:36.027728", "modified_by": "Administrator", "module": "HR", "name": "Expense Taxes and Charges", From 25d1c1ce86ec9d66b50bdf12d4954631d22d50c5 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 27 Oct 2021 11:21:26 +0530 Subject: [PATCH 054/105] fix: add filter to query to avoid send reminder for zero years (#28092) (#28096) Co-authored-by: Rucha Mahabal (cherry picked from commit 8cad23b8fb4d830a5703e107c371ae9319bc003d) Co-authored-by: gsi-maruiz <62341390+gsi-maruiz@users.noreply.github.com> --- erpnext/hr/doctype/employee/employee_reminders.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/hr/doctype/employee/employee_reminders.py b/erpnext/hr/doctype/employee/employee_reminders.py index 216d8f6bb3a..559bd393e62 100644 --- a/erpnext/hr/doctype/employee/employee_reminders.py +++ b/erpnext/hr/doctype/employee/employee_reminders.py @@ -156,6 +156,8 @@ def get_employees_having_an_event_today(event_type): DAY({condition_column}) = DAY(%(today)s) AND MONTH({condition_column}) = MONTH(%(today)s) + AND + YEAR({condition_column}) < YEAR(%(today)s) AND `status` = 'Active' """, @@ -166,6 +168,8 @@ def get_employees_having_an_event_today(event_type): DATE_PART('day', {condition_column}) = date_part('day', %(today)s) AND DATE_PART('month', {condition_column}) = date_part('month', %(today)s) + AND + DATE_PART('year', {condition_column}) < date_part('year', %(today)s) AND "status" = 'Active' """, From 05831b18ad08ffcbc8a332ed74697efae1bd870a Mon Sep 17 00:00:00 2001 From: Alan <2.alan.tom@gmail.com> Date: Wed, 27 Oct 2021 11:36:37 +0530 Subject: [PATCH 055/105] fix: update production plan status #27567 fix: update production plan status --- .../production_plan/production_plan.py | 11 ++++++- erpnext/patches.txt | 1 + .../v12_0/update_production_plan_status.py | 31 +++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 erpnext/patches/v12_0/update_production_plan_status.py diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 7e6fc3c4a64..2424ef9a71c 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -311,7 +311,7 @@ class ProductionPlan(Document): if self.total_produced_qty > 0: self.status = "In Process" - if self.total_produced_qty >= self.total_planned_qty: + if self.check_have_work_orders_completed(): self.status = "Completed" if self.status != 'Completed': @@ -575,6 +575,15 @@ class ProductionPlan(Document): self.append("sub_assembly_items", data) + def check_have_work_orders_completed(self): + wo_status = frappe.db.get_list( + "Work Order", + filters={"production_plan": self.name}, + fields="status", + pluck="status" + ) + return all(s == "Completed" for s in wo_status) + @frappe.whitelist() def download_raw_materials(doc, warehouses=None): if isinstance(doc, str): diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 20e54e08e6e..1dac50c6e19 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -308,6 +308,7 @@ erpnext.patches.v13_0.set_status_in_maintenance_schedule_table 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.v12_0.update_production_plan_status erpnext.patches.v13_0.healthcare_deprecation_warning erpnext.patches.v14_0.delete_healthcare_doctypes erpnext.patches.v13_0.create_pan_field_for_india #2 diff --git a/erpnext/patches/v12_0/update_production_plan_status.py b/erpnext/patches/v12_0/update_production_plan_status.py new file mode 100644 index 00000000000..06fc503a33f --- /dev/null +++ b/erpnext/patches/v12_0/update_production_plan_status.py @@ -0,0 +1,31 @@ +# Copyright (c) 2021, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe + + +def execute(): + frappe.reload_doc("manufacturing", "doctype", "production_plan") + frappe.db.sql(""" + UPDATE `tabProduction Plan` ppl + SET status = "Completed" + WHERE ppl.name IN ( + SELECT ss.name FROM ( + SELECT + ( + count(wo.status = "Completed") = + count(pp.name) + ) = + ( + pp.status != "Completed" + AND pp.total_produced_qty >= pp.total_planned_qty + ) AS should_set, + pp.name AS name + FROM + `tabWork Order` wo INNER JOIN`tabProduction Plan` pp + ON wo.production_plan = pp.name + GROUP BY pp.name + HAVING should_set = 1 + ) ss + ) + """) From 0806e32049fee67b731cb8c742fc6654faae6143 Mon Sep 17 00:00:00 2001 From: Summayya Hashmani <58825865+sumaiya2908@users.noreply.github.com> Date: Wed, 27 Oct 2021 11:46:10 +0530 Subject: [PATCH 056/105] fix(ux): add naming series to ERPNext setting workspace (#28090) * fix(ux): add naming series to setting workspace * fix: doctype link to naming series Co-authored-by: Ankush Menat Co-authored-by: Summayya Co-authored-by: Ankush Menat --- .../workspace/erpnext_settings/erpnext_settings.json | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json index 320cb7ba847..1412acfcead 100644 --- a/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json +++ b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json @@ -1,6 +1,6 @@ { "charts": [], - "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Projects Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Accounts Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Stock Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"HR Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Selling Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Buying Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Support Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Shopping Cart Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Portal Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Manufacturing Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Education Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Hotel Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Healthcare Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Domain Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Products Settings\", \"col\": 4}}]", + "content": "[{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t\",\"level\":4,\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Projects Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Stock Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"HR Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Selling Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Buying Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Support Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Shopping Cart Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Portal Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Domain Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Products Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Naming Series\",\"col\":4}}]", "creation": "2020-03-12 14:47:51.166455", "docstatus": 0, "doctype": "Workspace", @@ -10,7 +10,7 @@ "idx": 0, "label": "ERPNext Settings", "links": [], - "modified": "2021-08-05 12:15:59.052328", + "modified": "2021-10-26 21:32:55.323591", "modified_by": "Administrator", "module": "Setup", "name": "ERPNext Settings", @@ -27,6 +27,14 @@ "link_to": "Projects Settings", "type": "DocType" }, + { + "color": "Grey", + "doc_view": "", + "icon": "dot-horizontal", + "label": "Naming Series", + "link_to": "Naming Series", + "type": "DocType" + }, { "icon": "accounting", "label": "Accounts Settings", From f24ed6723ebc16282b3968e9ef5d58e629f7deb1 Mon Sep 17 00:00:00 2001 From: hendrik Date: Wed, 27 Oct 2021 16:08:20 +0700 Subject: [PATCH 057/105] fix(general_ledger): Order by in case Group by Account (#28093) * Update general_ledger.py Fix order_by_statement if filter group by: Group by Account * chore: whitespace Co-authored-by: Ankush Menat Co-authored-by: Afshan <33727827+AfshanKhan@users.noreply.github.com> --- erpnext/accounts/report/general_ledger/general_ledger.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 0094bc2eebe..31416da4ac4 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -155,6 +155,8 @@ def get_gl_entries(filters, accounting_dimensions): if filters.get("group_by") == "Group by Voucher": order_by_statement = "order by posting_date, voucher_type, voucher_no" + if filters.get("group_by") == "Group by Account": + order_by_statement = "order by account, posting_date, creation" if filters.get("include_default_book_entries"): filters['company_fb'] = frappe.db.get_value("Company", From c9f3ea5fea6ee1d07897d8ae6496d9a26289f900 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Wed, 27 Oct 2021 15:16:38 +0530 Subject: [PATCH 058/105] fix: Remove pointless buttons from Payment Order fix: Remove pointless buttons from Payment Order --- erpnext/accounts/doctype/payment_order/payment_order.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/doctype/payment_order/payment_order.js b/erpnext/accounts/doctype/payment_order/payment_order.js index aa373bc2fcc..9074defa577 100644 --- a/erpnext/accounts/doctype/payment_order/payment_order.js +++ b/erpnext/accounts/doctype/payment_order/payment_order.js @@ -10,6 +10,9 @@ frappe.ui.form.on('Payment Order', { } } }); + + frm.set_df_property('references', 'cannot_add_rows', true); + frm.set_df_property('references', 'cannot_delete_rows', true); }, refresh: function(frm) { if (frm.doc.docstatus == 0) { From e0cf45e7ec60649a35c591e3da44df4b47fb0cdd Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 27 Oct 2021 15:27:33 +0530 Subject: [PATCH 059/105] fix(ux): misleading label for image fields (#28107) --- erpnext/manufacturing/doctype/bom/bom.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.json b/erpnext/manufacturing/doctype/bom/bom.json index 7e539183b0c..62187077f3d 100644 --- a/erpnext/manufacturing/doctype/bom/bom.json +++ b/erpnext/manufacturing/doctype/bom/bom.json @@ -436,7 +436,7 @@ "description": "Item Image (if not slideshow)", "fieldname": "website_image", "fieldtype": "Attach Image", - "label": "Image" + "label": "Website Image" }, { "allow_on_submit": 1, @@ -539,7 +539,7 @@ "image_field": "image", "is_submittable": 1, "links": [], - "modified": "2021-05-16 12:25:09.081968", + "modified": "2021-10-27 14:52:04.500251", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM", From a261d08dd868ff6c306a2c8392dddd73d006ab85 Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 27 Oct 2021 18:30:37 +0530 Subject: [PATCH 060/105] fix: reset temporary flag after use --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 0ca11fd4511..cd204ba523b 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1048,6 +1048,7 @@ class SalesInvoice(SellingController): frappe.flags.is_reverse_depr_entry = True reverse_journal_entry.submit() + frappe.flags.is_reverse_depr_entry = False asset.flags.ignore_validate_update_after_submit = True schedule.journal_entry = None asset.save() From 3a6894fb9c2803cdd411a577f05f1cbf76ac2b06 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 27 Oct 2021 19:39:18 +0530 Subject: [PATCH 061/105] fix: Autoemail report not showing dynamic report filters --- erpnext/public/js/financial_statements.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 0d79b10c041..1a309ba0156 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -113,15 +113,15 @@ function get_filters() { "fieldname":"period_start_date", "label": __("Start Date"), "fieldtype": "Date", - "hidden": 1, - "reqd": 1 + "reqd": 1, + "depends_on": "eval:doc.filter_based_on == 'Date Range'" }, { "fieldname":"period_end_date", "label": __("End Date"), "fieldtype": "Date", - "hidden": 1, - "reqd": 1 + "reqd": 1, + "depends_on": "eval:doc.filter_based_on == 'Date Range'" }, { "fieldname":"from_fiscal_year", @@ -129,7 +129,8 @@ function get_filters() { "fieldtype": "Link", "options": "Fiscal Year", "default": frappe.defaults.get_user_default("fiscal_year"), - "reqd": 1 + "reqd": 1, + "depends_on": "eval:doc.filter_based_on == 'Fiscal Year'" }, { "fieldname":"to_fiscal_year", @@ -137,7 +138,8 @@ function get_filters() { "fieldtype": "Link", "options": "Fiscal Year", "default": frappe.defaults.get_user_default("fiscal_year"), - "reqd": 1 + "reqd": 1, + "depends_on": "eval:doc.filter_based_on == 'Fiscal Year'" }, { "fieldname": "periodicity", From 5f9bd9b8e9f0dca849f6ad78269cf4151181d5a5 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 27 Oct 2021 18:39:07 +0530 Subject: [PATCH 062/105] fix: remove bad hardcoded max value --- erpnext/manufacturing/doctype/work_order/work_order.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index e282dd3ecba..f881e1bf16a 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -685,9 +685,7 @@ class WorkOrder(Document): if not d.operation: d.operation = operation else: - # Attribute a big number (999) to idx for sorting putpose in case idx is NULL - # For instance in BOM Explosion Item child table, the items coming from sub assembly items - for item in sorted(item_dict.values(), key=lambda d: d['idx'] or 9999): + for item in sorted(item_dict.values(), key=lambda d: d['idx'] or float('inf')): self.append('required_items', { 'rate': item.rate, 'amount': item.rate * item.qty, From 5902762ec8e243c17116732d616426a17f49bc1c Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 27 Oct 2021 18:55:54 +0530 Subject: [PATCH 063/105] fix(ux): alternative item two way validation --- .../item_alternative/item_alternative.py | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/item_alternative/item_alternative.py b/erpnext/stock/doctype/item_alternative/item_alternative.py index 6080fb4a5fa..6f2a389b707 100644 --- a/erpnext/stock/doctype/item_alternative/item_alternative.py +++ b/erpnext/stock/doctype/item_alternative/item_alternative.py @@ -25,19 +25,29 @@ class ItemAlternative(Document): frappe.throw(_("Alternative item must not be same as item code")) item_meta = frappe.get_meta("Item") - fields = ["is_stock_item", "include_item_in_manufacturing","has_serial_no","has_batch_no"] - item_data = frappe.db.get_values("Item", self.item_code, fields, as_dict=1) - alternative_item_data = frappe.db.get_values("Item", self.alternative_item_code, fields, as_dict=1) + fields = ["is_stock_item", "include_item_in_manufacturing","has_serial_no", "has_batch_no", "allow_alternative_item"] + item_data = frappe.db.get_value("Item", self.item_code, fields, as_dict=1) + alternative_item_data = frappe.db.get_value("Item", self.alternative_item_code, fields, as_dict=1) for field in fields: - if item_data[0].get(field) != alternative_item_data[0].get(field): + if item_data.get(field) != alternative_item_data.get(field): raise_exception, alert = [1, False] if field == "is_stock_item" else [0, True] frappe.msgprint(_("The value of {0} differs between Items {1} and {2}") \ .format(frappe.bold(item_meta.get_label(field)), frappe.bold(self.alternative_item_code), frappe.bold(self.item_code)), - alert=alert, raise_exception=raise_exception) + alert=alert, raise_exception=raise_exception, indicator="Orange") + + alternate_item_check_msg = _("Allow Alternative Item must be checked on Item {}") + + if not item_data.allow_alternative_item: + frappe.throw(alternate_item_check_msg.format(self.item_code)) + if self.two_way and not alternative_item_data.allow_alternative_item: + frappe.throw(alternate_item_check_msg.format(self.item_code)) + + + def validate_duplicate(self): if frappe.db.get_value("Item Alternative", {'item_code': self.item_code, From 2221c9ed89467ee7db7289da7b15778aed0c68a0 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 27 Oct 2021 19:15:44 +0530 Subject: [PATCH 064/105] fix: don't show blocked supplier in autocomplete --- erpnext/controllers/queries.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 7b4566a2fa6..eeb659dee58 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -132,7 +132,8 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql("""select {field} from `tabSupplier` where docstatus < 2 and ({key} like %(txt)s - or supplier_name like %(txt)s) and disabled=0 + or supplier_name like %(txt)s) and disabled=0 + and (on_hold = 0 or (on_hold = 1 and CURDATE() > release_date)) {mcond} order by if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), From d81b87d9b3c83413adf4e1a87fccb95486b50931 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 27 Oct 2021 19:22:11 +0530 Subject: [PATCH 065/105] fix(ux): make qty 1 by default in WO --- erpnext/manufacturing/doctype/work_order/work_order.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json index 913fc85af61..7f8e816a22a 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.json +++ b/erpnext/manufacturing/doctype/work_order/work_order.json @@ -182,6 +182,7 @@ "reqd": 1 }, { + "default": "1.0", "fieldname": "qty", "fieldtype": "Float", "label": "Qty To Manufacture", @@ -572,10 +573,11 @@ "image_field": "image", "is_submittable": 1, "links": [], - "modified": "2021-08-24 15:14:03.844937", + "modified": "2021-10-27 19:21:35.139888", "modified_by": "Administrator", "module": "Manufacturing", "name": "Work Order", + "naming_rule": "By \"Naming Series\" field", "nsm_parent_field": "parent_work_order", "owner": "Administrator", "permissions": [ From 4787a7520847456a510577b708acfc22b8e4840b Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Thu, 28 Oct 2021 11:13:11 +0530 Subject: [PATCH 066/105] fix: opportunity link is missign from customer (#28110) --- erpnext/crm/doctype/opportunity/opportunity.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 55e0efaab15..0e469ac6421 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -314,6 +314,8 @@ def make_request_for_quotation(source_name, target_doc=None): @frappe.whitelist() def make_customer(source_name, target_doc=None): def set_missing_values(source, target): + target.opportunity_name = source.name + if source.opportunity_from == "Lead": target.lead_name = source.party_name From d786855d94a8d60be34ddd34e4b7a013bc7d4480 Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Thu, 28 Oct 2021 13:19:19 +0530 Subject: [PATCH 067/105] fix: Consolidated Financial Report throws error for empty equity data list --- .../consolidated_financial_statement.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 0de2a9854d6..0475231a934 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -114,8 +114,9 @@ def prepare_companywise_opening_balance(asset_data, liability_data, equity_data, # opening_value = Aseet - liability - equity for data in [asset_data, liability_data, equity_data]: - account_name = get_root_account_name(data[0].root_type, company) - opening_value += (get_opening_balance(account_name, data, company) or 0.0) + if data: + account_name = get_root_account_name(data[0].root_type, company) + opening_value += (get_opening_balance(account_name, data, company) or 0.0) opening_balance[company] = opening_value From b01635e1da0353d554b0de39d48a0fc275efc7d7 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 28 Oct 2021 14:07:15 +0530 Subject: [PATCH 068/105] refactor!: remove hub #28117 refactor!: remove hub --- .../doctype/pos_settings/pos_settings.js | 4 +- erpnext/demo/data/drug_list.json | 63 --- erpnext/hub/__init__.py | 0 erpnext/hub_node/__init__.py | 19 - erpnext/hub_node/api.py | 233 ---------- .../company_to_hub_company.json | 50 --- .../hub_message_to_lead.json | 31 -- .../item_to_hub_item/item_to_hub_item.json | 55 --- .../hub_sync/hub_sync.json | 19 - erpnext/hub_node/doctype/__init__.py | 0 .../doctype/hub_tracked_item/__init__.py | 0 .../hub_tracked_item/hub_tracked_item.js | 8 - .../hub_tracked_item/hub_tracked_item.json | 210 --------- .../hub_tracked_item/hub_tracked_item.py | 11 - .../hub_tracked_item/test_hub_tracked_item.py | 10 - erpnext/hub_node/doctype/hub_user/__init__.py | 0 .../hub_node/doctype/hub_user/hub_user.json | 140 ------ erpnext/hub_node/doctype/hub_user/hub_user.py | 11 - .../hub_node/doctype/hub_users/__init__.py | 0 .../hub_node/doctype/hub_users/hub_users.json | 72 --- .../hub_node/doctype/hub_users/hub_users.py | 11 - .../doctype/marketplace_settings/__init__.py | 0 .../marketplace_settings.js | 8 - .../marketplace_settings.json | 410 ------------------ .../marketplace_settings.py | 93 ---- .../test_marketplace_settings.py | 10 - erpnext/hub_node/legacy.py | 148 ------- erpnext/modules.txt | 3 +- erpnext/patches.txt | 6 +- erpnext/patches/v10_0/delete_hub_documents.py | 19 - .../reset_publish_in_hub_for_all_items.py | 8 - erpnext/patches/v11_0/update_hub_url.py | 8 - .../set_published_in_hub_tracked_item.py | 14 - erpnext/patches/v14_0/delete_hub_doctypes.py | 10 + erpnext/public/build.json | 8 - erpnext/public/images/hub_logo.svg | 112 ----- erpnext/public/js/erpnext.bundle.js | 1 - erpnext/public/js/hub/PageContainer.vue | 119 ----- erpnext/public/js/hub/Sidebar.vue | 110 ----- .../public/js/hub/components/CommentInput.vue | 39 -- .../js/hub/components/DetailHeaderItem.vue | 26 -- .../public/js/hub/components/DetailView.vue | 86 ---- .../public/js/hub/components/EmptyState.vue | 50 --- erpnext/public/js/hub/components/Image.vue | 40 -- erpnext/public/js/hub/components/ItemCard.vue | 142 ------ .../js/hub/components/ItemCardsContainer.vue | 62 --- .../public/js/hub/components/ItemListCard.vue | 21 - .../js/hub/components/NotificationMessage.vue | 38 -- erpnext/public/js/hub/components/Rating.vue | 16 - .../public/js/hub/components/ReviewArea.vue | 140 ------ .../js/hub/components/ReviewTimelineItem.vue | 53 --- .../public/js/hub/components/SearchInput.vue | 26 -- .../js/hub/components/SectionHeader.vue | 3 - .../public/js/hub/components/TimelineItem.vue | 9 - .../js/hub/components/edit_details_dialog.js | 41 -- .../js/hub/components/item_publish_dialog.js | 39 -- .../js/hub/components/profile_dialog.js | 56 --- erpnext/public/js/hub/components/reviews.js | 80 ---- erpnext/public/js/hub/hub_call.js | 68 --- erpnext/public/js/hub/hub_factory.js | 34 -- erpnext/public/js/hub/marketplace.bundle.js | 225 ---------- erpnext/public/js/hub/pages/Buying.vue | 56 --- erpnext/public/js/hub/pages/Category.vue | 76 ---- erpnext/public/js/hub/pages/FeaturedItems.vue | 116 ----- erpnext/public/js/hub/pages/Home.vue | 114 ----- erpnext/public/js/hub/pages/Item.vue | 356 --------------- erpnext/public/js/hub/pages/Messages.vue | 104 ----- erpnext/public/js/hub/pages/NotFound.vue | 36 -- erpnext/public/js/hub/pages/Publish.vue | 212 --------- .../public/js/hub/pages/PublishedItems.vue | 74 ---- erpnext/public/js/hub/pages/SavedItems.vue | 116 ----- erpnext/public/js/hub/pages/Search.vue | 81 ---- erpnext/public/js/hub/pages/Seller.vue | 201 --------- erpnext/public/js/hub/pages/SellerItems.vue | 57 --- erpnext/public/js/hub/pages/Selling.vue | 66 --- erpnext/public/js/hub/vue-plugins.js | 58 --- .../operations/install_fixtures.py | 1 - erpnext/stock/doctype/item/item.json | 54 +-- erpnext/stock/doctype/item/item.py | 1 - 79 files changed, 17 insertions(+), 5090 deletions(-) delete mode 100644 erpnext/hub/__init__.py delete mode 100644 erpnext/hub_node/__init__.py delete mode 100644 erpnext/hub_node/api.py delete mode 100644 erpnext/hub_node/data_migration_mapping/company_to_hub_company/company_to_hub_company.json delete mode 100644 erpnext/hub_node/data_migration_mapping/hub_message_to_lead/hub_message_to_lead.json delete mode 100644 erpnext/hub_node/data_migration_mapping/item_to_hub_item/item_to_hub_item.json delete mode 100644 erpnext/hub_node/data_migration_plan/hub_sync/hub_sync.json delete mode 100644 erpnext/hub_node/doctype/__init__.py delete mode 100644 erpnext/hub_node/doctype/hub_tracked_item/__init__.py delete mode 100644 erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.js delete mode 100644 erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.json delete mode 100644 erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.py delete mode 100644 erpnext/hub_node/doctype/hub_tracked_item/test_hub_tracked_item.py delete mode 100644 erpnext/hub_node/doctype/hub_user/__init__.py delete mode 100644 erpnext/hub_node/doctype/hub_user/hub_user.json delete mode 100644 erpnext/hub_node/doctype/hub_user/hub_user.py delete mode 100644 erpnext/hub_node/doctype/hub_users/__init__.py delete mode 100644 erpnext/hub_node/doctype/hub_users/hub_users.json delete mode 100644 erpnext/hub_node/doctype/hub_users/hub_users.py delete mode 100644 erpnext/hub_node/doctype/marketplace_settings/__init__.py delete mode 100644 erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.js delete mode 100644 erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json delete mode 100644 erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.py delete mode 100644 erpnext/hub_node/doctype/marketplace_settings/test_marketplace_settings.py delete mode 100644 erpnext/hub_node/legacy.py delete mode 100644 erpnext/patches/v10_0/delete_hub_documents.py delete mode 100644 erpnext/patches/v11_0/reset_publish_in_hub_for_all_items.py delete mode 100644 erpnext/patches/v11_0/update_hub_url.py delete mode 100644 erpnext/patches/v12_0/set_published_in_hub_tracked_item.py create mode 100644 erpnext/patches/v14_0/delete_hub_doctypes.py delete mode 100644 erpnext/public/images/hub_logo.svg delete mode 100644 erpnext/public/js/hub/PageContainer.vue delete mode 100644 erpnext/public/js/hub/Sidebar.vue delete mode 100644 erpnext/public/js/hub/components/CommentInput.vue delete mode 100644 erpnext/public/js/hub/components/DetailHeaderItem.vue delete mode 100644 erpnext/public/js/hub/components/DetailView.vue delete mode 100644 erpnext/public/js/hub/components/EmptyState.vue delete mode 100644 erpnext/public/js/hub/components/Image.vue delete mode 100644 erpnext/public/js/hub/components/ItemCard.vue delete mode 100644 erpnext/public/js/hub/components/ItemCardsContainer.vue delete mode 100644 erpnext/public/js/hub/components/ItemListCard.vue delete mode 100644 erpnext/public/js/hub/components/NotificationMessage.vue delete mode 100644 erpnext/public/js/hub/components/Rating.vue delete mode 100644 erpnext/public/js/hub/components/ReviewArea.vue delete mode 100644 erpnext/public/js/hub/components/ReviewTimelineItem.vue delete mode 100644 erpnext/public/js/hub/components/SearchInput.vue delete mode 100644 erpnext/public/js/hub/components/SectionHeader.vue delete mode 100644 erpnext/public/js/hub/components/TimelineItem.vue delete mode 100644 erpnext/public/js/hub/components/edit_details_dialog.js delete mode 100644 erpnext/public/js/hub/components/item_publish_dialog.js delete mode 100644 erpnext/public/js/hub/components/profile_dialog.js delete mode 100644 erpnext/public/js/hub/components/reviews.js delete mode 100644 erpnext/public/js/hub/hub_call.js delete mode 100644 erpnext/public/js/hub/hub_factory.js delete mode 100644 erpnext/public/js/hub/marketplace.bundle.js delete mode 100644 erpnext/public/js/hub/pages/Buying.vue delete mode 100644 erpnext/public/js/hub/pages/Category.vue delete mode 100644 erpnext/public/js/hub/pages/FeaturedItems.vue delete mode 100644 erpnext/public/js/hub/pages/Home.vue delete mode 100644 erpnext/public/js/hub/pages/Item.vue delete mode 100644 erpnext/public/js/hub/pages/Messages.vue delete mode 100644 erpnext/public/js/hub/pages/NotFound.vue delete mode 100644 erpnext/public/js/hub/pages/Publish.vue delete mode 100644 erpnext/public/js/hub/pages/PublishedItems.vue delete mode 100644 erpnext/public/js/hub/pages/SavedItems.vue delete mode 100644 erpnext/public/js/hub/pages/Search.vue delete mode 100644 erpnext/public/js/hub/pages/Seller.vue delete mode 100644 erpnext/public/js/hub/pages/SellerItems.vue delete mode 100644 erpnext/public/js/hub/pages/Selling.vue delete mode 100644 erpnext/public/js/hub/vue-plugins.js diff --git a/erpnext/accounts/doctype/pos_settings/pos_settings.js b/erpnext/accounts/doctype/pos_settings/pos_settings.js index 9003af56a5d..7d8f3562c8c 100644 --- a/erpnext/accounts/doctype/pos_settings/pos_settings.js +++ b/erpnext/accounts/doctype/pos_settings/pos_settings.js @@ -2,11 +2,11 @@ // For license information, please see license.txt let search_fields_datatypes = ['Data', 'Link', 'Dynamic Link', 'Long Text', 'Select', 'Small Text', 'Text', 'Text Editor']; -let do_not_include_fields = ["naming_series", "item_code", "item_name", "stock_uom", "hub_sync_id", "asset_naming_series", +let do_not_include_fields = ["naming_series", "item_code", "item_name", "stock_uom", "asset_naming_series", "default_material_request_type", "valuation_method", "warranty_period", "weight_uom", "batch_number_series", "serial_no_series", "purchase_uom", "customs_tariff_number", "sales_uom", "deferred_revenue_account", "deferred_expense_account", "quality_inspection_template", "route", "slideshow", "website_image_alt", "thumbnail", - "web_long_description", "hub_sync_id"] + "web_long_description"] frappe.ui.form.on('POS Settings', { onload: function(frm) { diff --git a/erpnext/demo/data/drug_list.json b/erpnext/demo/data/drug_list.json index e91c30d199a..3069042843a 100644 --- a/erpnext/demo/data/drug_list.json +++ b/erpnext/demo/data/drug_list.json @@ -60,7 +60,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -144,7 +143,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -226,7 +224,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -308,7 +305,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -390,7 +386,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -472,7 +467,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -554,7 +548,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -636,7 +629,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -718,7 +710,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -800,7 +791,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -882,7 +872,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -964,7 +953,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -1046,7 +1034,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -1128,7 +1115,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -1210,7 +1196,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -1292,7 +1277,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -1374,7 +1358,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -1456,7 +1439,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -1538,7 +1520,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -1620,7 +1601,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -1702,7 +1682,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -1784,7 +1763,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -1866,7 +1844,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -1948,7 +1925,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -2030,7 +2006,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -2112,7 +2087,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -2194,7 +2168,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -2276,7 +2249,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -2358,7 +2330,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -2440,7 +2411,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -2522,7 +2492,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -2604,7 +2573,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -2686,7 +2654,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -2768,7 +2735,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -2850,7 +2816,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -2932,7 +2897,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -3014,7 +2978,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -3098,7 +3061,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -3180,7 +3142,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -3262,7 +3223,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -3344,7 +3304,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -3426,7 +3385,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -3508,7 +3466,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -3590,7 +3547,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -3672,7 +3628,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -3754,7 +3709,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -3836,7 +3790,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -3918,7 +3871,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -4000,7 +3952,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -4082,7 +4033,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -4164,7 +4114,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -4246,7 +4195,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -4328,7 +4276,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -4410,7 +4357,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -4492,7 +4438,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -4574,7 +4519,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -4656,7 +4600,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -4738,7 +4681,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -4820,7 +4762,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -4902,7 +4843,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -4984,7 +4924,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -5066,7 +5005,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, @@ -5148,7 +5086,6 @@ "standard_rate": 0.0, "stock_uom": "Nos", "supplier_items": [], - "synced_with_hub": 0, "taxes": [], "thumbnail": null, "tolerance": 0.0, diff --git a/erpnext/hub/__init__.py b/erpnext/hub/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/hub_node/__init__.py b/erpnext/hub_node/__init__.py deleted file mode 100644 index 6ac3255c12a..00000000000 --- a/erpnext/hub_node/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals - -import frappe - - -@frappe.whitelist() -def enable_hub(): - hub_settings = frappe.get_doc('Marketplace Settings') - hub_settings.register() - frappe.db.commit() - return hub_settings - -@frappe.whitelist() -def sync(): - hub_settings = frappe.get_doc('Marketplace Settings') - hub_settings.sync() diff --git a/erpnext/hub_node/api.py b/erpnext/hub_node/api.py deleted file mode 100644 index 55304917759..00000000000 --- a/erpnext/hub_node/api.py +++ /dev/null @@ -1,233 +0,0 @@ -from __future__ import unicode_literals - -import json - -import frappe -from frappe import _ -from frappe.desk.form.load import get_attachments -from frappe.frappeclient import FrappeClient -from six import string_types - -current_user = frappe.session.user - - -@frappe.whitelist() -def register_marketplace(company, company_description): - validate_registerer() - - settings = frappe.get_single('Marketplace Settings') - message = settings.register_seller(company, company_description) - - if message.get('hub_seller_name'): - settings.registered = 1 - settings.hub_seller_name = message.get('hub_seller_name') - settings.save() - - settings.add_hub_user(frappe.session.user) - - return { 'ok': 1 } - - -@frappe.whitelist() -def register_users(user_list): - user_list = json.loads(user_list) - - settings = frappe.get_single('Marketplace Settings') - - for user in user_list: - settings.add_hub_user(user) - - return user_list - - -def validate_registerer(): - if current_user == 'Administrator': - frappe.throw(_('Please login as another user to register on Marketplace')) - - valid_roles = ['System Manager', 'Item Manager'] - - if not frappe.utils.is_subset(valid_roles, frappe.get_roles()): - frappe.throw(_('Only users with {0} role can register on Marketplace').format(', '.join(valid_roles)), - frappe.PermissionError) - - -@frappe.whitelist() -def call_hub_method(method, params=None): - connection = get_hub_connection() - - if isinstance(params, string_types): - params = json.loads(params) - - params.update({ - 'cmd': 'hub.hub.api.' + method - }) - - response = connection.post_request(params) - return response - - -def map_fields(items): - field_mappings = get_field_mappings() - table_fields = [d.fieldname for d in frappe.get_meta('Item').get_table_fields()] - - hub_seller_name = frappe.db.get_value('Marketplace Settings', 'Marketplace Settings', 'hub_seller_name') - - for item in items: - for fieldname in table_fields: - item.pop(fieldname, None) - - for mapping in field_mappings: - local_fieldname = mapping.get('local_fieldname') - remote_fieldname = mapping.get('remote_fieldname') - - value = item.get(local_fieldname) - item.pop(local_fieldname, None) - item[remote_fieldname] = value - - item['doctype'] = 'Hub Item' - item['hub_seller'] = hub_seller_name - item.pop('attachments', None) - - return items - - -@frappe.whitelist() -def get_valid_items(search_value=''): - items = frappe.get_list( - 'Item', - fields=["*"], - filters={ - 'disabled': 0, - 'item_name': ['like', '%' + search_value + '%'], - 'publish_in_hub': 0 - }, - order_by="modified desc" - ) - - valid_items = filter(lambda x: x.image and x.description, items) - - def prepare_item(item): - item.source_type = "local" - item.attachments = get_attachments('Item', item.item_code) - return item - - valid_items = map(prepare_item, valid_items) - - return valid_items - -@frappe.whitelist() -def update_item(ref_doc, data): - data = json.loads(data) - - data.update(dict(doctype='Hub Item', name=ref_doc)) - try: - connection = get_hub_connection() - connection.update(data) - except Exception as e: - frappe.log_error(message=e, title='Hub Sync Error') - -@frappe.whitelist() -def publish_selected_items(items_to_publish): - items_to_publish = json.loads(items_to_publish) - items_to_update = [] - if not len(items_to_publish): - frappe.throw(_('No items to publish')) - - for item in items_to_publish: - item_code = item.get('item_code') - frappe.db.set_value('Item', item_code, 'publish_in_hub', 1) - - hub_dict = { - 'doctype': 'Hub Tracked Item', - 'item_code': item_code, - 'published': 1, - 'hub_category': item.get('hub_category'), - 'image_list': item.get('image_list') - } - frappe.get_doc(hub_dict).insert(ignore_if_duplicate=True) - - items = map_fields(items_to_publish) - - try: - item_sync_preprocess(len(items)) - convert_relative_image_urls_to_absolute(items) - - # TODO: Publish Progress - connection = get_hub_connection() - connection.insert_many(items) - - item_sync_postprocess() - except Exception as e: - frappe.log_error(message=e, title='Hub Sync Error') - -@frappe.whitelist() -def unpublish_item(item_code, hub_item_name): - ''' Remove item listing from the marketplace ''' - - response = call_hub_method('unpublish_item', { - 'hub_item_name': hub_item_name - }) - - if response: - frappe.db.set_value('Item', item_code, 'publish_in_hub', 0) - frappe.delete_doc('Hub Tracked Item', item_code) - else: - frappe.throw(_('Unable to update remote activity')) - -@frappe.whitelist() -def get_unregistered_users(): - settings = frappe.get_single('Marketplace Settings') - registered_users = [user.user for user in settings.users] + ['Administrator', 'Guest'] - all_users = [user.name for user in frappe.db.get_all('User', filters={'enabled': 1})] - unregistered_users = [user for user in all_users if user not in registered_users] - return unregistered_users - - -def item_sync_preprocess(intended_item_publish_count): - response = call_hub_method('pre_items_publish', { - 'intended_item_publish_count': intended_item_publish_count - }) - - if response: - frappe.db.set_value("Marketplace Settings", "Marketplace Settings", "sync_in_progress", 1) - return response - else: - frappe.throw(_('Unable to update remote activity')) - - -def item_sync_postprocess(): - response = call_hub_method('post_items_publish', {}) - if response: - frappe.db.set_value('Marketplace Settings', 'Marketplace Settings', 'last_sync_datetime', frappe.utils.now()) - else: - frappe.throw(_('Unable to update remote activity')) - - frappe.db.set_value('Marketplace Settings', 'Marketplace Settings', 'sync_in_progress', 0) - - -def convert_relative_image_urls_to_absolute(items): - from six.moves.urllib.parse import urljoin - - for item in items: - file_path = item['image'] - - if file_path.startswith('/files/'): - item['image'] = urljoin(frappe.utils.get_url(), file_path) - - -def get_hub_connection(): - settings = frappe.get_single('Marketplace Settings') - marketplace_url = settings.marketplace_url - hub_user = settings.get_hub_user(frappe.session.user) - - if hub_user: - password = hub_user.get_password() - hub_connection = FrappeClient(marketplace_url, hub_user.user, password) - return hub_connection - else: - read_only_hub_connection = FrappeClient(marketplace_url) - return read_only_hub_connection - - -def get_field_mappings(): - return [] diff --git a/erpnext/hub_node/data_migration_mapping/company_to_hub_company/company_to_hub_company.json b/erpnext/hub_node/data_migration_mapping/company_to_hub_company/company_to_hub_company.json deleted file mode 100644 index b1e421dada8..00000000000 --- a/erpnext/hub_node/data_migration_mapping/company_to_hub_company/company_to_hub_company.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "condition": "{'name': ('=', frappe.db.get_single_value('Hub Settings', 'company'))}", - "creation": "2017-09-07 11:38:43.169065", - "docstatus": 0, - "doctype": "Data Migration Mapping", - "fields": [ - { - "is_child_table": 0, - "local_fieldname": "name", - "remote_fieldname": "company_name" - }, - { - "is_child_table": 0, - "local_fieldname": "country", - "remote_fieldname": "country" - }, - { - "is_child_table": 0, - "local_fieldname": "\"city\"", - "remote_fieldname": "seller_city" - }, - { - "is_child_table": 0, - "local_fieldname": "eval:frappe.local.site", - "remote_fieldname": "site_name" - }, - { - "is_child_table": 0, - "local_fieldname": "eval:frappe.session.user", - "remote_fieldname": "user" - }, - { - "is_child_table": 0, - "local_fieldname": "company_logo", - "remote_fieldname": "company_logo" - } - ], - "idx": 2, - "local_doctype": "Company", - "mapping_name": "Company to Hub Company", - "mapping_type": "Push", - "migration_id_field": "hub_sync_id", - "modified": "2020-09-18 17:26:09.703215", - "modified_by": "Administrator", - "name": "Company to Hub Company", - "owner": "Administrator", - "page_length": 10, - "remote_objectname": "Hub Company", - "remote_primary_key": "name" -} \ No newline at end of file diff --git a/erpnext/hub_node/data_migration_mapping/hub_message_to_lead/hub_message_to_lead.json b/erpnext/hub_node/data_migration_mapping/hub_message_to_lead/hub_message_to_lead.json deleted file mode 100644 index d11abeb4b38..00000000000 --- a/erpnext/hub_node/data_migration_mapping/hub_message_to_lead/hub_message_to_lead.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "condition": "{'reference_doctype': 'Lead', 'user': frappe.db.get_single_value('Hub Settings', 'user'), 'status': 'Pending'}", - "creation": "2017-09-20 15:06:40.279930", - "docstatus": 0, - "doctype": "Data Migration Mapping", - "fields": [ - { - "is_child_table": 0, - "local_fieldname": "email_id", - "remote_fieldname": "email_id" - }, - { - "is_child_table": 0, - "local_fieldname": "lead_name", - "remote_fieldname": "lead_name" - } - ], - "idx": 0, - "local_doctype": "Lead", - "local_primary_key": "email_id", - "mapping_name": "Hub Message to Lead", - "mapping_type": "Pull", - "migration_id_field": "hub_sync_id", - "modified": "2020-09-18 17:26:09.703215", - "modified_by": "Administrator", - "name": "Hub Message to Lead", - "owner": "Administrator", - "page_length": 10, - "remote_objectname": "Hub Message", - "remote_primary_key": "name" -} \ No newline at end of file diff --git a/erpnext/hub_node/data_migration_mapping/item_to_hub_item/item_to_hub_item.json b/erpnext/hub_node/data_migration_mapping/item_to_hub_item/item_to_hub_item.json deleted file mode 100644 index bcece69b38c..00000000000 --- a/erpnext/hub_node/data_migration_mapping/item_to_hub_item/item_to_hub_item.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "condition": "{\"publish_in_hub\": 1}", - "creation": "2017-09-07 13:27:52.726350", - "docstatus": 0, - "doctype": "Data Migration Mapping", - "fields": [ - { - "is_child_table": 0, - "local_fieldname": "item_code", - "remote_fieldname": "item_code" - }, - { - "is_child_table": 0, - "local_fieldname": "item_name", - "remote_fieldname": "item_name" - }, - { - "is_child_table": 0, - "local_fieldname": "eval:frappe.db.get_value('Hub Settings' , 'Hub Settings', 'company_email')", - "remote_fieldname": "hub_seller" - }, - { - "is_child_table": 0, - "local_fieldname": "image", - "remote_fieldname": "image" - }, - { - "is_child_table": 0, - "local_fieldname": "image_list", - "remote_fieldname": "image_list" - }, - { - "is_child_table": 0, - "local_fieldname": "item_group", - "remote_fieldname": "item_group" - }, - { - "is_child_table": 0, - "local_fieldname": "hub_category", - "remote_fieldname": "hub_category" - } - ], - "idx": 1, - "local_doctype": "Item", - "mapping_name": "Item to Hub Item", - "mapping_type": "Push", - "migration_id_field": "hub_sync_id", - "modified": "2018-08-19 22:20:25.727581", - "modified_by": "Administrator", - "name": "Item to Hub Item", - "owner": "Administrator", - "page_length": 10, - "remote_objectname": "Hub Item", - "remote_primary_key": "item_code" -} \ No newline at end of file diff --git a/erpnext/hub_node/data_migration_plan/hub_sync/hub_sync.json b/erpnext/hub_node/data_migration_plan/hub_sync/hub_sync.json deleted file mode 100644 index e90b1dd1e8d..00000000000 --- a/erpnext/hub_node/data_migration_plan/hub_sync/hub_sync.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "creation": "2017-09-07 11:39:38.445902", - "docstatus": 0, - "doctype": "Data Migration Plan", - "idx": 1, - "mappings": [ - { - "enabled": 1, - "mapping": "Item to Hub Item" - } - ], - "modified": "2018-08-19 22:20:25.644602", - "modified_by": "Administrator", - "module": "Hub Node", - "name": "Hub Sync", - "owner": "Administrator", - "plan_name": "Hub Sync", - "postprocess_method": "erpnext.hub_node.api.item_sync_postprocess" -} \ No newline at end of file diff --git a/erpnext/hub_node/doctype/__init__.py b/erpnext/hub_node/doctype/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/hub_node/doctype/hub_tracked_item/__init__.py b/erpnext/hub_node/doctype/hub_tracked_item/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.js b/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.js deleted file mode 100644 index 660532d13df..00000000000 --- a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Hub Tracked Item', { - refresh: function(frm) { - - } -}); diff --git a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.json b/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.json deleted file mode 100644 index 7d07ba40938..00000000000 --- a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.json +++ /dev/null @@ -1,210 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:item_code", - "beta": 0, - "creation": "2018-03-18 09:33:50.267762", - "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": "item_code", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Code", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 1 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "hub_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": "Hub 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": "published", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Published", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "image_list", - "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": "Image List", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-12-10 11:37:35.951019", - "modified_by": "Administrator", - "module": "Hub Node", - "name": "Hub Tracked Item", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Item Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 1, - "read_only": 1, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.py b/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.py deleted file mode 100644 index 823c79eb72b..00000000000 --- a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.py +++ /dev/null @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals - -from frappe.model.document import Document - - -class HubTrackedItem(Document): - pass diff --git a/erpnext/hub_node/doctype/hub_tracked_item/test_hub_tracked_item.py b/erpnext/hub_node/doctype/hub_tracked_item/test_hub_tracked_item.py deleted file mode 100644 index c403f902a2c..00000000000 --- a/erpnext/hub_node/doctype/hub_tracked_item/test_hub_tracked_item.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt -from __future__ import unicode_literals - -import unittest - - -class TestHubTrackedItem(unittest.TestCase): - pass diff --git a/erpnext/hub_node/doctype/hub_user/__init__.py b/erpnext/hub_node/doctype/hub_user/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/hub_node/doctype/hub_user/hub_user.json b/erpnext/hub_node/doctype/hub_user/hub_user.json deleted file mode 100644 index f51ffb4387d..00000000000 --- a/erpnext/hub_node/doctype/hub_user/hub_user.json +++ /dev/null @@ -1,140 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "", - "beta": 0, - "creation": "2018-08-31 12:36:45.627531", - "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": "user", - "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": "User", - "length": 0, - "no_copy": 0, - "options": "User", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 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": "hub_user_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": "Hub User", - "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": "password", - "fieldtype": "Password", - "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": "Hub Password", - "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 - } - ], - "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": "2020-09-18 17:26:09.703215", - "modified_by": "Administrator", - "module": "Hub Node", - "name": "Hub User", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hub_node/doctype/hub_user/hub_user.py b/erpnext/hub_node/doctype/hub_user/hub_user.py deleted file mode 100644 index 1f7c8fc3f20..00000000000 --- a/erpnext/hub_node/doctype/hub_user/hub_user.py +++ /dev/null @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals - -from frappe.model.document import Document - - -class HubUser(Document): - pass diff --git a/erpnext/hub_node/doctype/hub_users/__init__.py b/erpnext/hub_node/doctype/hub_users/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/hub_node/doctype/hub_users/hub_users.json b/erpnext/hub_node/doctype/hub_users/hub_users.json deleted file mode 100644 index d42f3fdf1b7..00000000000 --- a/erpnext/hub_node/doctype/hub_users/hub_users.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-03-06 04:38:49.891787", - "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": "user", - "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": "User", - "length": 0, - "no_copy": 0, - "options": "User", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "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": "2020-09-18 17:26:09.703215", - "modified_by": "Administrator", - "module": "Hub Node", - "name": "Hub Users", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hub_node/doctype/hub_users/hub_users.py b/erpnext/hub_node/doctype/hub_users/hub_users.py deleted file mode 100644 index e08ed68ed8f..00000000000 --- a/erpnext/hub_node/doctype/hub_users/hub_users.py +++ /dev/null @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals - -from frappe.model.document import Document - - -class HubUsers(Document): - pass diff --git a/erpnext/hub_node/doctype/marketplace_settings/__init__.py b/erpnext/hub_node/doctype/marketplace_settings/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.js b/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.js deleted file mode 100644 index 36da832c7c0..00000000000 --- a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Marketplace Settings', { - refresh: function(frm) { - $('#toolbar-user .marketplace-link').toggle(!frm.doc.disable_marketplace); - }, -}); diff --git a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json b/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json deleted file mode 100644 index e784f68fcf3..00000000000 --- a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json +++ /dev/null @@ -1,410 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 1, - "creation": "2018-08-31 15:54:38.795263", - "custom": 0, - "description": "", - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 0, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "disable_marketplace", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Disable Marketplace", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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.disable_marketplace", - "fieldname": "marketplace_settings_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Marketplace Settings", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "https://hubmarket.org", - "fieldname": "marketplace_url", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Marketplace URL (to hide and update 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": "registered", - "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": "Registered", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "sync_in_progress", - "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": "Sync in Progress", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "hub_seller_name", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Hub Seller 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": "users", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Users", - "length": 0, - "no_copy": 0, - "options": "Hub User", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "last_sync_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": "Last Sync On", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "", - "depends_on": "eval:1", - "fieldname": "custom_data", - "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Custom Data", - "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 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2020-09-18 17:26:09.703215", - "modified_by": "Administrator", - "module": "Hub Node", - "name": "Marketplace Settings", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "System Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "All", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.py b/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.py deleted file mode 100644 index 33d23f6eae4..00000000000 --- a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals - -import json - -import frappe -from frappe.frappeclient import FrappeClient -from frappe.model.document import Document -from frappe.utils import cint - - -class MarketplaceSettings(Document): - - def register_seller(self, company, company_description): - - country, currency, company_logo = frappe.db.get_value('Company', company, - ['country', 'default_currency', 'company_logo']) - - company_details = { - 'company': company, - 'country': country, - 'currency': currency, - 'company_description': company_description, - 'company_logo': company_logo, - 'site_name': frappe.utils.get_url() - } - - hub_connection = self.get_connection() - - response = hub_connection.post_request({ - 'cmd': 'hub.hub.api.add_hub_seller', - 'company_details': json.dumps(company_details) - }) - - return response - - - def add_hub_user(self, user_email): - '''Create a Hub User and User record on hub server - and if successfull append it to Hub User table - ''' - - if not self.registered: - return - - hub_connection = self.get_connection() - - first_name, last_name = frappe.db.get_value('User', user_email, ['first_name', 'last_name']) - - hub_user = hub_connection.post_request({ - 'cmd': 'hub.hub.api.add_hub_user', - 'user_email': user_email, - 'first_name': first_name, - 'last_name': last_name, - 'hub_seller': self.hub_seller_name - }) - - self.append('users', { - 'user': hub_user.get('user_email'), - 'hub_user_name': hub_user.get('hub_user_name'), - 'password': hub_user.get('password') - }) - - self.save() - - def get_hub_user(self, user): - '''Return the Hub User doc from the `users` table if password is set''' - - filtered_users = list(filter( - lambda x: x.user == user and x.password, - self.users - )) - - if filtered_users: - return filtered_users[0] - - - def get_connection(self): - return FrappeClient(self.marketplace_url) - - - def unregister(self): - """Disable the User on hubmarket.org""" - -@frappe.whitelist() -def is_marketplace_enabled(): - if not hasattr(frappe.local, 'is_marketplace_enabled'): - frappe.local.is_marketplace_enabled = cint(frappe.db.get_single_value('Marketplace Settings', - 'disable_marketplace')) - - return frappe.local.is_marketplace_enabled diff --git a/erpnext/hub_node/doctype/marketplace_settings/test_marketplace_settings.py b/erpnext/hub_node/doctype/marketplace_settings/test_marketplace_settings.py deleted file mode 100644 index 7922f45ab59..00000000000 --- a/erpnext/hub_node/doctype/marketplace_settings/test_marketplace_settings.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt -from __future__ import unicode_literals - -import unittest - - -class TestMarketplaceSettings(unittest.TestCase): - pass diff --git a/erpnext/hub_node/legacy.py b/erpnext/hub_node/legacy.py deleted file mode 100644 index 2e4c2668436..00000000000 --- a/erpnext/hub_node/legacy.py +++ /dev/null @@ -1,148 +0,0 @@ -from __future__ import unicode_literals - -import json - -import frappe -from frappe.contacts.doctype.contact.contact import get_default_contact -from frappe.frappeclient import FrappeClient -from frappe.utils import nowdate -from frappe.utils.nestedset import get_root_of - - -def get_list(doctype, start, limit, fields, filters, order_by): - pass - -def get_hub_connection(): - if frappe.db.exists('Data Migration Connector', 'Hub Connector'): - hub_connector = frappe.get_doc('Data Migration Connector', 'Hub Connector') - hub_connection = hub_connector.get_connection() - return hub_connection.connection - - # read-only connection - hub_connection = FrappeClient(frappe.conf.hub_url) - return hub_connection - -def make_opportunity(buyer_name, email_id): - buyer_name = "HUB-" + buyer_name - - if not frappe.db.exists('Lead', {'email_id': email_id}): - lead = frappe.new_doc("Lead") - lead.lead_name = buyer_name - lead.email_id = email_id - lead.save(ignore_permissions=True) - - o = frappe.new_doc("Opportunity") - o.opportunity_from = "Lead" - o.lead = frappe.get_all("Lead", filters={"email_id": email_id}, fields = ["name"])[0]["name"] - o.save(ignore_permissions=True) - -@frappe.whitelist() -def make_rfq_and_send_opportunity(item, supplier): - supplier = make_supplier(supplier) - contact = make_contact(supplier) - item = make_item(item) - rfq = make_rfq(item, supplier, contact) - status = send_opportunity(contact) - - return { - 'rfq': rfq, - 'hub_document_created': status - } - -def make_supplier(supplier): - # make supplier if not already exists - supplier = frappe._dict(json.loads(supplier)) - - if not frappe.db.exists('Supplier', {'supplier_name': supplier.supplier_name}): - supplier_doc = frappe.get_doc({ - 'doctype': 'Supplier', - 'supplier_name': supplier.supplier_name, - 'supplier_group': supplier.supplier_group, - 'supplier_email': supplier.supplier_email - }).insert() - else: - supplier_doc = frappe.get_doc('Supplier', supplier.supplier_name) - - return supplier_doc - -def make_contact(supplier): - contact_name = get_default_contact('Supplier', supplier.supplier_name) - # make contact if not already exists - if not contact_name: - contact = frappe.get_doc({ - 'doctype': 'Contact', - 'first_name': supplier.supplier_name, - 'is_primary_contact': 1, - 'links': [ - {'link_doctype': 'Supplier', 'link_name': supplier.supplier_name} - ] - }) - contact.add_email(supplier.supplier_email, is_primary=True) - contact.insert() - else: - contact = frappe.get_doc('Contact', contact_name) - - return contact - -def make_item(item): - # make item if not already exists - item = frappe._dict(json.loads(item)) - - if not frappe.db.exists('Item', {'item_code': item.item_code}): - item_doc = frappe.get_doc({ - 'doctype': 'Item', - 'item_code': item.item_code, - 'item_group': item.item_group, - 'is_item_from_hub': 1 - }).insert() - else: - item_doc = frappe.get_doc('Item', item.item_code) - - return item_doc - -def make_rfq(item, supplier, contact): - # make rfq - rfq = frappe.get_doc({ - 'doctype': 'Request for Quotation', - 'transaction_date': nowdate(), - 'status': 'Draft', - 'company': frappe.db.get_single_value('Marketplace Settings', 'company'), - 'message_for_supplier': 'Please supply the specified items at the best possible rates', - 'suppliers': [ - { 'supplier': supplier.name, 'contact': contact.name } - ], - 'items': [ - { - 'item_code': item.item_code, - 'qty': 1, - 'schedule_date': nowdate(), - 'warehouse': item.default_warehouse or get_root_of("Warehouse"), - 'description': item.description, - 'uom': item.stock_uom - } - ] - }).insert() - - rfq.save() - rfq.submit() - return rfq - -def send_opportunity(contact): - # Make Hub Message on Hub with lead data - doc = { - 'doctype': 'Lead', - 'lead_name': frappe.db.get_single_value('Marketplace Settings', 'company'), - 'email_id': frappe.db.get_single_value('Marketplace Settings', 'user') - } - - args = frappe._dict(dict( - doctype='Hub Message', - reference_doctype='Lead', - data=json.dumps(doc), - user=contact.email_id - )) - - connection = get_hub_connection() - response = connection.insert('Hub Message', args) - - return response.ok diff --git a/erpnext/modules.txt b/erpnext/modules.txt index a9f94ce1335..15a24a746f7 100644 --- a/erpnext/modules.txt +++ b/erpnext/modules.txt @@ -20,9 +20,8 @@ Agriculture ERPNext Integrations Non Profit Hotels -Hub Node Quality Management Communication Loan Management Payroll -Telephony +Telephony \ No newline at end of file diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 1dac50c6e19..c533d480f82 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -58,11 +58,7 @@ erpnext.patches.v11_0.set_department_for_doctypes erpnext.patches.v11_0.update_allow_transfer_for_manufacture erpnext.patches.v11_0.add_item_group_defaults erpnext.patches.v11_0.add_expense_claim_default_account -execute:frappe.delete_doc("Page", "hub") -erpnext.patches.v11_0.reset_publish_in_hub_for_all_items -erpnext.patches.v11_0.update_hub_url # 2018-08-31 # 2018-09-03 erpnext.patches.v11_0.make_job_card -erpnext.patches.v10_0.delete_hub_documents # 12-08-2018 erpnext.patches.v11_0.add_default_dispatch_notification_template erpnext.patches.v11_0.add_market_segments erpnext.patches.v11_0.add_sales_stages @@ -153,7 +149,6 @@ erpnext.patches.v12_0.set_cost_center_in_child_table_of_expense_claim erpnext.patches.v12_0.add_eway_bill_in_delivery_note erpnext.patches.v12_0.set_lead_title_field erpnext.patches.v12_0.set_permission_einvoicing -erpnext.patches.v12_0.set_published_in_hub_tracked_item erpnext.patches.v12_0.set_job_offer_applicant_email erpnext.patches.v12_0.create_irs_1099_field_united_states erpnext.patches.v12_0.move_bank_account_swift_number_to_bank @@ -312,3 +307,4 @@ erpnext.patches.v12_0.update_production_plan_status erpnext.patches.v13_0.healthcare_deprecation_warning erpnext.patches.v14_0.delete_healthcare_doctypes erpnext.patches.v13_0.create_pan_field_for_india #2 +erpnext.patches.v14_0.delete_hub_doctypes diff --git a/erpnext/patches/v10_0/delete_hub_documents.py b/erpnext/patches/v10_0/delete_hub_documents.py deleted file mode 100644 index 16c7abfc978..00000000000 --- a/erpnext/patches/v10_0/delete_hub_documents.py +++ /dev/null @@ -1,19 +0,0 @@ -from __future__ import unicode_literals - -import frappe - - -def execute(): - for dt, dn in (("Page", "Hub"), ("DocType", "Hub Settings"), ("DocType", "Hub Category")): - frappe.delete_doc(dt, dn, ignore_missing=True) - - if frappe.db.exists("DocType", "Data Migration Plan"): - data_migration_plans = frappe.get_all("Data Migration Plan", filters={"module": 'Hub Node'}) - for plan in data_migration_plans: - plan_doc = frappe.get_doc("Data Migration Plan", plan.name) - for m in plan_doc.get("mappings"): - frappe.delete_doc("Data Migration Mapping", m.mapping, force=True) - docs = frappe.get_all("Data Migration Run", filters={"data_migration_plan": plan.name}) - for doc in docs: - frappe.delete_doc("Data Migration Run", doc.name) - frappe.delete_doc("Data Migration Plan", plan.name) diff --git a/erpnext/patches/v11_0/reset_publish_in_hub_for_all_items.py b/erpnext/patches/v11_0/reset_publish_in_hub_for_all_items.py deleted file mode 100644 index a664baf6dd6..00000000000 --- a/erpnext/patches/v11_0/reset_publish_in_hub_for_all_items.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import unicode_literals - -import frappe - - -def execute(): - frappe.reload_doc('stock', 'doctype', 'item') - frappe.db.sql("""update `tabItem` set publish_in_hub = 0""") diff --git a/erpnext/patches/v11_0/update_hub_url.py b/erpnext/patches/v11_0/update_hub_url.py deleted file mode 100644 index c89b9b50607..00000000000 --- a/erpnext/patches/v11_0/update_hub_url.py +++ /dev/null @@ -1,8 +0,0 @@ -from __future__ import unicode_literals - -import frappe - - -def execute(): - frappe.reload_doc('hub_node', 'doctype', 'Marketplace Settings') - frappe.db.set_value('Marketplace Settings', 'Marketplace Settings', 'marketplace_url', 'https://hubmarket.org') diff --git a/erpnext/patches/v12_0/set_published_in_hub_tracked_item.py b/erpnext/patches/v12_0/set_published_in_hub_tracked_item.py deleted file mode 100644 index 73c6ce8220e..00000000000 --- a/erpnext/patches/v12_0/set_published_in_hub_tracked_item.py +++ /dev/null @@ -1,14 +0,0 @@ -from __future__ import unicode_literals - -import frappe - - -def execute(): - frappe.reload_doc("Hub Node", "doctype", "Hub Tracked Item") - if not frappe.db.a_row_exists("Hub Tracked Item"): - return - - frappe.db.sql(''' - Update `tabHub Tracked Item` - SET published = 1 - ''') diff --git a/erpnext/patches/v14_0/delete_hub_doctypes.py b/erpnext/patches/v14_0/delete_hub_doctypes.py new file mode 100644 index 00000000000..d1e9e31f0c2 --- /dev/null +++ b/erpnext/patches/v14_0/delete_hub_doctypes.py @@ -0,0 +1,10 @@ +import frappe + + +def execute(): + + doctypes = frappe.get_all("DocType", {"module": "Hub Node", "custom": 0}, pluck='name') + for doctype in doctypes: + frappe.delete_doc("DocType", doctype, ignore_missing=True) + + frappe.delete_doc("Module Def", "Hub Node", ignore_missing=True, force=True) diff --git a/erpnext/public/build.json b/erpnext/public/build.json index 6b70dab8037..f8e817770d5 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -1,14 +1,10 @@ { "css/erpnext.css": [ "public/less/erpnext.less", - "public/less/hub.less", "public/scss/call_popup.scss", "public/scss/point-of-sale.scss", "public/scss/hierarchy_chart.scss" ], - "css/marketplace.css": [ - "public/less/hub.less" - ], "js/erpnext-web.min.js": [ "public/js/website_utils.js", "public/js/shopping_cart.js" @@ -17,9 +13,6 @@ "public/scss/website.scss", "public/scss/shopping_cart.scss" ], - "js/marketplace.min.js": [ - "public/js/hub/marketplace.js" - ], "js/erpnext.min.js": [ "public/js/conf.js", "public/js/utils.js", @@ -41,7 +34,6 @@ "public/js/utils/supplier_quick_entry.js", "public/js/education/student_button.html", "public/js/education/assessment_result_tool.html", - "public/js/hub/hub_factory.js", "public/js/call_popup/call_popup.js", "public/js/utils/dimension_tree_filter.js", "public/js/telephony.js", diff --git a/erpnext/public/images/hub_logo.svg b/erpnext/public/images/hub_logo.svg deleted file mode 100644 index 4af482176ea..00000000000 --- a/erpnext/public/images/hub_logo.svg +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - diff --git a/erpnext/public/js/erpnext.bundle.js b/erpnext/public/js/erpnext.bundle.js index febdb24da34..5259bdcc765 100644 --- a/erpnext/public/js/erpnext.bundle.js +++ b/erpnext/public/js/erpnext.bundle.js @@ -18,7 +18,6 @@ import "./utils/customer_quick_entry"; import "./utils/supplier_quick_entry"; import "./education/student_button.html"; import "./education/assessment_result_tool.html"; -import "./hub/hub_factory"; import "./call_popup/call_popup"; import "./utils/dimension_tree_filter"; import "./telephony"; diff --git a/erpnext/public/js/hub/PageContainer.vue b/erpnext/public/js/hub/PageContainer.vue deleted file mode 100644 index 54c359766d3..00000000000 --- a/erpnext/public/js/hub/PageContainer.vue +++ /dev/null @@ -1,119 +0,0 @@ - - - diff --git a/erpnext/public/js/hub/Sidebar.vue b/erpnext/public/js/hub/Sidebar.vue deleted file mode 100644 index 66c291ec52f..00000000000 --- a/erpnext/public/js/hub/Sidebar.vue +++ /dev/null @@ -1,110 +0,0 @@ - - diff --git a/erpnext/public/js/hub/components/CommentInput.vue b/erpnext/public/js/hub/components/CommentInput.vue deleted file mode 100644 index 31562c7a284..00000000000 --- a/erpnext/public/js/hub/components/CommentInput.vue +++ /dev/null @@ -1,39 +0,0 @@ - - diff --git a/erpnext/public/js/hub/components/DetailHeaderItem.vue b/erpnext/public/js/hub/components/DetailHeaderItem.vue deleted file mode 100644 index a6c5f066f28..00000000000 --- a/erpnext/public/js/hub/components/DetailHeaderItem.vue +++ /dev/null @@ -1,26 +0,0 @@ - - - diff --git a/erpnext/public/js/hub/components/DetailView.vue b/erpnext/public/js/hub/components/DetailView.vue deleted file mode 100644 index 942c1ebdb34..00000000000 --- a/erpnext/public/js/hub/components/DetailView.vue +++ /dev/null @@ -1,86 +0,0 @@ - - - - - diff --git a/erpnext/public/js/hub/components/EmptyState.vue b/erpnext/public/js/hub/components/EmptyState.vue deleted file mode 100644 index e3a33a08306..00000000000 --- a/erpnext/public/js/hub/components/EmptyState.vue +++ /dev/null @@ -1,50 +0,0 @@ - - - - - diff --git a/erpnext/public/js/hub/components/Image.vue b/erpnext/public/js/hub/components/Image.vue deleted file mode 100644 index 9acf4210322..00000000000 --- a/erpnext/public/js/hub/components/Image.vue +++ /dev/null @@ -1,40 +0,0 @@ - - diff --git a/erpnext/public/js/hub/components/ItemCard.vue b/erpnext/public/js/hub/components/ItemCard.vue deleted file mode 100644 index 675ad86645b..00000000000 --- a/erpnext/public/js/hub/components/ItemCard.vue +++ /dev/null @@ -1,142 +0,0 @@ - - - - - diff --git a/erpnext/public/js/hub/components/ItemCardsContainer.vue b/erpnext/public/js/hub/components/ItemCardsContainer.vue deleted file mode 100644 index 0a20bcdc636..00000000000 --- a/erpnext/public/js/hub/components/ItemCardsContainer.vue +++ /dev/null @@ -1,62 +0,0 @@ - - - - - diff --git a/erpnext/public/js/hub/components/ItemListCard.vue b/erpnext/public/js/hub/components/ItemListCard.vue deleted file mode 100644 index 7f6fb77d76e..00000000000 --- a/erpnext/public/js/hub/components/ItemListCard.vue +++ /dev/null @@ -1,21 +0,0 @@ - - diff --git a/erpnext/public/js/hub/components/NotificationMessage.vue b/erpnext/public/js/hub/components/NotificationMessage.vue deleted file mode 100644 index c26672635c3..00000000000 --- a/erpnext/public/js/hub/components/NotificationMessage.vue +++ /dev/null @@ -1,38 +0,0 @@ - - - - - diff --git a/erpnext/public/js/hub/components/Rating.vue b/erpnext/public/js/hub/components/Rating.vue deleted file mode 100644 index 33290b8e202..00000000000 --- a/erpnext/public/js/hub/components/Rating.vue +++ /dev/null @@ -1,16 +0,0 @@ - - - diff --git a/erpnext/public/js/hub/components/ReviewArea.vue b/erpnext/public/js/hub/components/ReviewArea.vue deleted file mode 100644 index aa83bb0e465..00000000000 --- a/erpnext/public/js/hub/components/ReviewArea.vue +++ /dev/null @@ -1,140 +0,0 @@ - - diff --git a/erpnext/public/js/hub/components/ReviewTimelineItem.vue b/erpnext/public/js/hub/components/ReviewTimelineItem.vue deleted file mode 100644 index d0e83f3b1cd..00000000000 --- a/erpnext/public/js/hub/components/ReviewTimelineItem.vue +++ /dev/null @@ -1,53 +0,0 @@ - - - diff --git a/erpnext/public/js/hub/components/SearchInput.vue b/erpnext/public/js/hub/components/SearchInput.vue deleted file mode 100644 index 4b1ce6e4ef9..00000000000 --- a/erpnext/public/js/hub/components/SearchInput.vue +++ /dev/null @@ -1,26 +0,0 @@ - - - diff --git a/erpnext/public/js/hub/components/SectionHeader.vue b/erpnext/public/js/hub/components/SectionHeader.vue deleted file mode 100644 index 05a2f838a0a..00000000000 --- a/erpnext/public/js/hub/components/SectionHeader.vue +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/erpnext/public/js/hub/components/TimelineItem.vue b/erpnext/public/js/hub/components/TimelineItem.vue deleted file mode 100644 index d13c842beb8..00000000000 --- a/erpnext/public/js/hub/components/TimelineItem.vue +++ /dev/null @@ -1,9 +0,0 @@ -/* Saving this for later */ - diff --git a/erpnext/public/js/hub/components/edit_details_dialog.js b/erpnext/public/js/hub/components/edit_details_dialog.js deleted file mode 100644 index 97c5f83a13a..00000000000 --- a/erpnext/public/js/hub/components/edit_details_dialog.js +++ /dev/null @@ -1,41 +0,0 @@ -function edit_details_dialog(params) { - let dialog = new frappe.ui.Dialog({ - title: __('Update Details'), - fields: [ - { - label: 'Item Name', - fieldname: 'item_name', - fieldtype: 'Data', - default: params.defaults.item_name, - reqd: 1 - }, - { - label: 'Hub Category', - fieldname: 'hub_category', - fieldtype: 'Autocomplete', - default: params.defaults.hub_category, - options: [], - reqd: 1 - }, - { - label: 'Description', - fieldname: 'description', - fieldtype: 'Text', - default: params.defaults.description, - options: [], - reqd: 1 - } - ], - primary_action_label: params.primary_action.label || __('Update Details'), - primary_action: params.primary_action.fn - }); - - hub.call('get_categories').then(categories => { - categories = categories.map(d => d.name); - dialog.fields_dict.hub_category.set_data(categories); - }); - - return dialog; -} - -export { edit_details_dialog }; diff --git a/erpnext/public/js/hub/components/item_publish_dialog.js b/erpnext/public/js/hub/components/item_publish_dialog.js deleted file mode 100644 index 08de5b30b6c..00000000000 --- a/erpnext/public/js/hub/components/item_publish_dialog.js +++ /dev/null @@ -1,39 +0,0 @@ -function ItemPublishDialog(primary_action, secondary_action) { - let dialog = new frappe.ui.Dialog({ - title: __('Edit Publishing Details'), - fields: [ - { - label: __('Item Code'), - fieldname: 'item_code', - fieldtype: 'Data', - read_only: 1 - }, - { - label: __('Hub Category'), - fieldname: 'hub_category', - fieldtype: 'Autocomplete', - options: [], - reqd: 1 - }, - { - label: __('Images'), - fieldname: 'image_list', - fieldtype: 'MultiSelect', - options: [], - reqd: 1 - } - ], - primary_action_label: primary_action.label || __('Set Details'), - primary_action: primary_action.fn, - secondary_action: secondary_action.fn - }); - - hub.call('get_categories').then(categories => { - categories = categories.map(d => d.name); - dialog.fields_dict.hub_category.set_data(categories); - }); - - return dialog; -} - -export { ItemPublishDialog }; diff --git a/erpnext/public/js/hub/components/profile_dialog.js b/erpnext/public/js/hub/components/profile_dialog.js deleted file mode 100644 index 8e3abc37eb8..00000000000 --- a/erpnext/public/js/hub/components/profile_dialog.js +++ /dev/null @@ -1,56 +0,0 @@ -const ProfileDialog = (title = __('Edit Profile'), action={}) => { - const fields = [ - { - fieldtype: 'Link', - fieldname: 'company', - label: __('Company'), - options: 'Company' - }, - { - fieldtype: 'Read Only', - fieldname: 'email', - label: __('Email') - }, - { - label: __('About your company'), - fieldname: 'company_description', - fieldtype: 'Text' - } - ]; - - let dialog = new frappe.ui.Dialog({ - title: title, - fields: fields, - primary_action_label: action.label || __('Update'), - primary_action: () => { - const form_values = dialog.get_values(); - let values_filled = true; - - // TODO: Say "we notice that the company description and logo isn't set. Please set them in master." - // Only then allow to register - - const mandatory_fields = ['company', 'company_description']; - mandatory_fields.forEach(field => { - const value = form_values[field]; - if (!value) { - dialog.set_df_property(field, 'reqd', 1); - values_filled = false; - } - }); - if (!values_filled) return; - - action.on_submit(form_values); - } - }); - - // Post create - const default_company = frappe.defaults.get_default('company'); - dialog.set_value('company', default_company); - dialog.set_value('email', frappe.session.user); - - return dialog; -} - -export { - ProfileDialog -} diff --git a/erpnext/public/js/hub/components/reviews.js b/erpnext/public/js/hub/components/reviews.js deleted file mode 100644 index e34d68038f7..00000000000 --- a/erpnext/public/js/hub/components/reviews.js +++ /dev/null @@ -1,80 +0,0 @@ -function get_review_html(review) { - let username = review.username || review.user || __("Anonymous"); - - let image_html = review.user_image - ? `
` - : `
${frappe.get_abbr(username)}
` - - let edit_html = review.own - ? ` -
- - ${'data.edit'} - -
` - : ''; - - let rating_html = get_rating_html(review.rating); - - return get_timeline_item(review, image_html, edit_html, rating_html); -} - -function get_timeline_item(data, image_html, edit_html, rating_html) { - return `
- -
-
-
${edit_html}
- -
- - ${image_html} - - -
- - - ${data.username} - - - - - -
-
-
-
-

- ${rating_html} -

-
${data.subject}
-

- ${data.content} -

-
-
-
-
-
`; -} - -function get_rating_html(rating) { - let rating_html = ``; - for (var i = 0; i < 5; i++) { - let star_class = 'fa-star'; - if (i >= rating) star_class = 'fa-star-o'; - rating_html += ``; - } - return rating_html; -} - -export { - get_review_html, - get_rating_html -} diff --git a/erpnext/public/js/hub/hub_call.js b/erpnext/public/js/hub/hub_call.js deleted file mode 100644 index 5545a4935b3..00000000000 --- a/erpnext/public/js/hub/hub_call.js +++ /dev/null @@ -1,68 +0,0 @@ -frappe.provide('hub'); -frappe.provide('erpnext.hub'); - -erpnext.hub.cache = {}; -hub.call = function call_hub_method(method, args={}, clear_cache_on_event) { // eslint-disable-line - return new Promise((resolve, reject) => { - - // cache - const key = method + JSON.stringify(args); - if (erpnext.hub.cache[key]) { - resolve(erpnext.hub.cache[key]); - } - - // cache invalidation - const clear_cache = () => delete erpnext.hub.cache[key]; - - if (!clear_cache_on_event) { - invalidate_after_5_mins(clear_cache); - } else { - erpnext.hub.on(clear_cache_on_event, () => { - clear_cache(key); - }); - } - - let res; - if (hub.is_server) { - res = frappe.call({ - method: 'hub.hub.api.' + method, - args - }); - } else { - res = frappe.call({ - method: 'erpnext.hub_node.api.call_hub_method', - args: { - method, - params: args - } - }); - } - - res.then(r => { - if (r.message) { - const response = r.message; - if (response.error) { - frappe.throw({ - title: __('Marketplace Error'), - message: response.error - }); - } - - erpnext.hub.cache[key] = response; - erpnext.hub.trigger(`response:${key}`, { response }); - resolve(response); - } - reject(r); - - }).fail(reject); - }); -}; - -function invalidate_after_5_mins(clear_cache) { - // cache invalidation after 5 minutes - const timeout = 5 * 60 * 1000; - - setTimeout(() => { - clear_cache(); - }, timeout); -} diff --git a/erpnext/public/js/hub/hub_factory.js b/erpnext/public/js/hub/hub_factory.js deleted file mode 100644 index 9c67c1cf9f0..00000000000 --- a/erpnext/public/js/hub/hub_factory.js +++ /dev/null @@ -1,34 +0,0 @@ -frappe.provide('erpnext.hub'); - -frappe.views.MarketplaceFactory = class MarketplaceFactory extends frappe.views.Factory { - show() { - is_marketplace_disabled() - .then(disabled => { - if (disabled) { - frappe.show_not_found('Marketplace'); - return; - } - - if (frappe.pages.marketplace) { - frappe.container.change_to('marketplace'); - erpnext.hub.marketplace.refresh(); - } else { - this.make('marketplace'); - } - }); - } - - make(page_name) { - frappe.require('marketplace.bundle.js', () => { - erpnext.hub.marketplace = new erpnext.hub.Marketplace({ - parent: this.make_page(true, page_name) - }); - }); - } -}; - -function is_marketplace_disabled() { - return frappe.call({ - method: "erpnext.hub_node.doctype.marketplace_settings.marketplace_settings.is_marketplace_enabled" - }).then(r => r.message) -} diff --git a/erpnext/public/js/hub/marketplace.bundle.js b/erpnext/public/js/hub/marketplace.bundle.js deleted file mode 100644 index a1596e0043b..00000000000 --- a/erpnext/public/js/hub/marketplace.bundle.js +++ /dev/null @@ -1,225 +0,0 @@ -import Vue from 'vue/dist/vue.js'; -import './vue-plugins'; - -// components -import PageContainer from './PageContainer.vue'; -import Sidebar from './Sidebar.vue'; -import { ProfileDialog } from './components/profile_dialog'; - -// helpers -import './hub_call'; - -frappe.provide('hub'); -frappe.provide('erpnext.hub'); -frappe.provide('frappe.route'); - -frappe.utils.make_event_emitter(frappe.route); -frappe.utils.make_event_emitter(erpnext.hub); - -erpnext.hub.Marketplace = class Marketplace { - constructor({ parent }) { - this.$parent = $(parent); - this.page = parent.page; - - this.update_hub_settings().then(() => { - - this.setup_header(); - this.make_sidebar(); - this.make_body(); - this.setup_events(); - this.refresh(); - - if (!hub.is_server) { - if (!hub.is_seller_registered()) { - this.page.set_primary_action('Become a Seller', this.show_register_dialog.bind(this)) - } else { - this.page.set_secondary_action('Add Users', this.show_add_user_dialog.bind(this)); - } - } - }); - } - - setup_header() { - if (hub.is_server) return; - this.page.set_title(__('Marketplace')); - } - - setup_events() { - this.$parent.on('click', '[data-route]', (e) => { - const $target = $(e.currentTarget); - const route = $target.data().route; - frappe.set_route(route); - }); - - // generic action handler - this.$parent.on('click', '[data-action]', e => { - const $target = $(e.currentTarget); - const action = $target.data().action; - - if (action && this[action]) { - this[action].apply(this, $target); - } - }) - } - - make_sidebar() { - this.$sidebar = this.$parent.find('.layout-side-section').addClass('hidden-xs'); - - new Vue({ - el: $('
').appendTo(this.$sidebar)[0], - render: h => h(Sidebar) - }); - } - - make_body() { - this.$body = this.$parent.find('.layout-main-section'); - this.$page_container = $('
').appendTo(this.$body); - - new Vue({ - el: '.hub-page-container', - render: h => h(PageContainer) - }); - - if (!hub.is_server) { - erpnext.hub.on('seller-registered', () => { - this.page.clear_primary_action(); - }); - } - } - - refresh() { - - } - - show_register_dialog() { - if(frappe.session.user === 'Administrator') { - frappe.msgprint(__('You need to be a user other than Administrator with System Manager and Item Manager roles to register on Marketplace.')); - return; - } - - if (!is_subset(['System Manager', 'Item Manager'], frappe.user_roles)) { - frappe.msgprint(__('You need to be a user with System Manager and Item Manager roles to register on Marketplace.')); - return; - } - - this.register_dialog = ProfileDialog( - __('Become a Seller'), - { - label: __('Register'), - on_submit: this.register_marketplace.bind(this) - } - ); - - this.register_dialog.show(); - } - - register_marketplace({company, company_description}) { - frappe.call({ - method: 'erpnext.hub_node.api.register_marketplace', - args: { - company, - company_description - } - }).then((r) => { - if (r.message && r.message.ok) { - this.register_dialog.hide(); - - this.update_hub_settings() - .then(() => { - frappe.set_route('marketplace', 'publish'); - erpnext.hub.trigger('seller-registered'); - }); - } - }); - } - - show_add_user_dialog() { - if (!is_subset(['System Manager', 'Item Manager'], frappe.user_roles)) { - frappe.msgprint(__('You need to be a user with System Manager and Item Manager roles to add users to Marketplace.')); - return; - } - - this.get_unregistered_users() - .then(r => { - const user_list = r.message; - - const d = new frappe.ui.Dialog({ - title: __('Add Users to Marketplace'), - fields: [ - { - label: __('Users'), - fieldname: 'users', - fieldtype: 'MultiSelect', - reqd: 1, - get_data() { - return user_list; - } - } - ], - primary_action({ users }) { - const selected_users = users.split(',').map(d => d.trim()).filter(Boolean); - - if (!selected_users.every(user => user_list.includes(user))) { - d.set_df_property('users', 'description', __('Some emails are invalid')); - return; - } else { - d.set_df_property('users', 'description', ''); - } - - frappe.call('erpnext.hub_node.api.register_users', { - user_list: selected_users - }) - .then(r => { - d.hide(); - - if (r.message && r.message.length) { - frappe.show_alert(__('Added {0} users', [r.message.length])); - } - }); - } - }); - - d.show(); - }); - } - - get_unregistered_users() { - return frappe.call('erpnext.hub_node.api.get_unregistered_users') - } - - update_hub_settings() { - return hub.get_settings().then(doc => { - hub.settings = doc; - }); - } -} - -Object.assign(hub, { - is_seller_registered() { - return hub.settings.registered; - }, - - is_user_registered() { - return this.is_seller_registered() && hub.settings.users - .filter(hub_user => hub_user.user === frappe.session.user) - .length === 1; - }, - - get_settings() { - if (frappe.session.user === 'Guest') { - return Promise.resolve({ - registered: 0 - }); - } - return frappe.db.get_doc('Marketplace Settings'); - } -}); - -/** - * Returns true if list_a is subset of list_b - * @param {Array} list_a - * @param {Array} list_b - */ -function is_subset(list_a, list_b) { - return list_a.every(item => list_b.includes(item)); -} diff --git a/erpnext/public/js/hub/pages/Buying.vue b/erpnext/public/js/hub/pages/Buying.vue deleted file mode 100644 index ebf593aca4d..00000000000 --- a/erpnext/public/js/hub/pages/Buying.vue +++ /dev/null @@ -1,56 +0,0 @@ - - diff --git a/erpnext/public/js/hub/pages/Category.vue b/erpnext/public/js/hub/pages/Category.vue deleted file mode 100644 index 16d06018ff0..00000000000 --- a/erpnext/public/js/hub/pages/Category.vue +++ /dev/null @@ -1,76 +0,0 @@ - - - - - diff --git a/erpnext/public/js/hub/pages/FeaturedItems.vue b/erpnext/public/js/hub/pages/FeaturedItems.vue deleted file mode 100644 index 8380b2b2c0b..00000000000 --- a/erpnext/public/js/hub/pages/FeaturedItems.vue +++ /dev/null @@ -1,116 +0,0 @@ - - - - - diff --git a/erpnext/public/js/hub/pages/Home.vue b/erpnext/public/js/hub/pages/Home.vue deleted file mode 100644 index 8fe824566db..00000000000 --- a/erpnext/public/js/hub/pages/Home.vue +++ /dev/null @@ -1,114 +0,0 @@ - - - - - diff --git a/erpnext/public/js/hub/pages/Item.vue b/erpnext/public/js/hub/pages/Item.vue deleted file mode 100644 index 93002a7b27a..00000000000 --- a/erpnext/public/js/hub/pages/Item.vue +++ /dev/null @@ -1,356 +0,0 @@ - - - - - diff --git a/erpnext/public/js/hub/pages/Messages.vue b/erpnext/public/js/hub/pages/Messages.vue deleted file mode 100644 index 73506e99266..00000000000 --- a/erpnext/public/js/hub/pages/Messages.vue +++ /dev/null @@ -1,104 +0,0 @@ - - diff --git a/erpnext/public/js/hub/pages/NotFound.vue b/erpnext/public/js/hub/pages/NotFound.vue deleted file mode 100644 index 8901b97802d..00000000000 --- a/erpnext/public/js/hub/pages/NotFound.vue +++ /dev/null @@ -1,36 +0,0 @@ - - - - - diff --git a/erpnext/public/js/hub/pages/Publish.vue b/erpnext/public/js/hub/pages/Publish.vue deleted file mode 100644 index ecba4b1e5a8..00000000000 --- a/erpnext/public/js/hub/pages/Publish.vue +++ /dev/null @@ -1,212 +0,0 @@ - - - - - diff --git a/erpnext/public/js/hub/pages/PublishedItems.vue b/erpnext/public/js/hub/pages/PublishedItems.vue deleted file mode 100644 index cbb22164e6e..00000000000 --- a/erpnext/public/js/hub/pages/PublishedItems.vue +++ /dev/null @@ -1,74 +0,0 @@ - - - - - diff --git a/erpnext/public/js/hub/pages/SavedItems.vue b/erpnext/public/js/hub/pages/SavedItems.vue deleted file mode 100644 index 7007ddcf8e7..00000000000 --- a/erpnext/public/js/hub/pages/SavedItems.vue +++ /dev/null @@ -1,116 +0,0 @@ - - - - - diff --git a/erpnext/public/js/hub/pages/Search.vue b/erpnext/public/js/hub/pages/Search.vue deleted file mode 100644 index c10841e9848..00000000000 --- a/erpnext/public/js/hub/pages/Search.vue +++ /dev/null @@ -1,81 +0,0 @@ - - - - - diff --git a/erpnext/public/js/hub/pages/Seller.vue b/erpnext/public/js/hub/pages/Seller.vue deleted file mode 100644 index 3c9b800f4a0..00000000000 --- a/erpnext/public/js/hub/pages/Seller.vue +++ /dev/null @@ -1,201 +0,0 @@ - - - - - diff --git a/erpnext/public/js/hub/pages/SellerItems.vue b/erpnext/public/js/hub/pages/SellerItems.vue deleted file mode 100644 index 852fbaee3ff..00000000000 --- a/erpnext/public/js/hub/pages/SellerItems.vue +++ /dev/null @@ -1,57 +0,0 @@ - - - - - diff --git a/erpnext/public/js/hub/pages/Selling.vue b/erpnext/public/js/hub/pages/Selling.vue deleted file mode 100644 index 8743027885d..00000000000 --- a/erpnext/public/js/hub/pages/Selling.vue +++ /dev/null @@ -1,66 +0,0 @@ - - diff --git a/erpnext/public/js/hub/vue-plugins.js b/erpnext/public/js/hub/vue-plugins.js deleted file mode 100644 index 4912d684991..00000000000 --- a/erpnext/public/js/hub/vue-plugins.js +++ /dev/null @@ -1,58 +0,0 @@ -import Vue from 'vue/dist/vue.js'; - -// Global components -import ItemCardsContainer from './components/ItemCardsContainer.vue'; -import SectionHeader from './components/SectionHeader.vue'; -import SearchInput from './components/SearchInput.vue'; -import DetailView from './components/DetailView.vue'; -import DetailHeaderItem from './components/DetailHeaderItem.vue'; -import EmptyState from './components/EmptyState.vue'; -import Image from './components/Image.vue'; - -Vue.prototype.__ = window.__; -Vue.prototype.frappe = window.frappe; - -Vue.component('item-cards-container', ItemCardsContainer); -Vue.component('section-header', SectionHeader); -Vue.component('search-input', SearchInput); -Vue.component('detail-view', DetailView); -Vue.component('detail-header-item', DetailHeaderItem); -Vue.component('empty-state', EmptyState); -Vue.component('base-image', Image); - -Vue.directive('route', { - bind(el, binding) { - const route = binding.value; - if (!route) return; - el.classList.add('cursor-pointer'); - el.dataset.route = route; - el.addEventListener('click', () => frappe.set_route(route)); - }, - unbind(el) { - el.classList.remove('cursor-pointer'); - } -}); - -const handleImage = (el, src) => { - let img = new Image(); - // add loading class - el.src = ''; - el.classList.add('img-loading'); - - img.onload = () => { - // image loaded, remove loading class - el.classList.remove('img-loading'); - // set src - el.src = src; - } - img.onerror = () => { - el.classList.remove('img-loading'); - el.classList.add('no-image'); - el.src = null; - } - img.src = src; -} - -Vue.filter('striphtml', function (text) { - return strip_html(text || ''); -}); diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index c473395a9aa..d61d94c3c69 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -202,7 +202,6 @@ def install(country=None): {'doctype': "Party Type", "party_type": "Student", "account_type": "Receivable"}, {'doctype': "Party Type", "party_type": "Donor", "account_type": "Receivable"}, - {'doctype': "Opportunity Type", "name": "Hub"}, {'doctype': "Opportunity Type", "name": _("Sales")}, {'doctype': "Opportunity Type", "name": _("Support")}, {'doctype': "Opportunity Type", "name": _("Maintenance")}, diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index db5caf91645..4b314a00a42 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -17,7 +17,6 @@ "variant_of", "item_name", "item_group", - "is_item_from_hub", "stock_uom", "column_break0", "disabled", @@ -134,12 +133,7 @@ "website_specifications", "web_long_description", "website_content", - "total_projected_qty", - "hub_publishing_sb", - "publish_in_hub", - "hub_category_to_publish", - "hub_warehouse", - "synced_with_hub" + "total_projected_qty" ], "fields": [ { @@ -202,14 +196,6 @@ "reqd": 1, "search_index": 1 }, - { - "default": "0", - "depends_on": "eval:!doc.is_fixed_asset", - "fieldname": "is_item_from_hub", - "fieldtype": "Check", - "label": "Is Item from Hub", - "read_only": 1 - }, { "fieldname": "stock_uom", "fieldtype": "Link", @@ -996,41 +982,6 @@ "print_hide": 1, "read_only": 1 }, - { - "collapsible": 1, - "depends_on": "eval:(!doc.is_item_from_hub && !doc.is_fixed_asset)", - "fieldname": "hub_publishing_sb", - "fieldtype": "Section Break", - "label": "Hub Publishing Details" - }, - { - "default": "0", - "description": "Publish Item to hub.erpnext.com", - "fieldname": "publish_in_hub", - "fieldtype": "Check", - "label": "Publish in Hub" - }, - { - "fieldname": "hub_category_to_publish", - "fieldtype": "Data", - "label": "Hub Category to Publish", - "read_only": 1 - }, - { - "description": "Publish \"In Stock\" or \"Not in Stock\" on Hub based on stock available in this warehouse.", - "fieldname": "hub_warehouse", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Hub Warehouse", - "options": "Warehouse" - }, - { - "default": "0", - "fieldname": "synced_with_hub", - "fieldtype": "Check", - "label": "Synced With Hub", - "read_only": 1 - }, { "depends_on": "eval:!doc.__islocal && !doc.is_fixed_asset", "fieldname": "over_delivery_receipt_allowance", @@ -1078,10 +1029,11 @@ "index_web_pages_for_search": 1, "links": [], "max_attachments": 1, - "modified": "2021-08-26 12:23:07.277077", + "modified": "2021-10-27 21:04:00.324786", "modified_by": "Administrator", "module": "Stock", "name": "Item", + "naming_rule": "By fieldname", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 8cc9f74a42a..04e4653d93c 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -123,7 +123,6 @@ class Item(WebsiteGenerator): self.validate_barcode() self.validate_warehouse_for_reorder() self.update_bom_item_desc() - self.synced_with_hub = 0 self.validate_has_variants() self.validate_attributes_in_variants() From 09a5616e2dce7d0d9962326781cd7c0a4bcaebbc Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 28 Oct 2021 19:01:24 +0530 Subject: [PATCH 069/105] fix: Rewrite patch using query builder --- .../update_category_in_ltds_certificate.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/erpnext/patches/v13_0/update_category_in_ltds_certificate.py b/erpnext/patches/v13_0/update_category_in_ltds_certificate.py index f8a0646fcbb..4d4645269cb 100644 --- a/erpnext/patches/v13_0/update_category_in_ltds_certificate.py +++ b/erpnext/patches/v13_0/update_category_in_ltds_certificate.py @@ -2,9 +2,17 @@ import frappe def execute(): + company = frappe.get_all('Company', filters = {'country': 'India'}) + if not company: + return - frappe.db.sql(""" - UPDATE `tabLower Deduction Certificate` l, `tabSupplier` s - SET l.tax_withholding_category = s.tax_withholding_category - WHERE l.supplier = s.name - """) \ No newline at end of file + ldc = frappe.qb.DocType("Lower Deduction Certificate").as_("ldc") + supplier = frappe.qb.DocType("Supplier") + + frappe.qb.update(ldc).inner_join(supplier).on( + ldc.supplier == supplier.name + ).set( + ldc.tax_withholding_category, supplier.tax_withholding_category + ).where( + ldc.tax_withholding_category.isnull() + ).run() \ No newline at end of file From 8844fdbf230ef167c0051935a8f922c5c6bb13de Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 28 Oct 2021 19:25:55 +0530 Subject: [PATCH 070/105] fix: Remove print statements --- erpnext/controllers/accounts_controller.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index bfbbf4d140b..9267eb5b8e6 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -820,14 +820,10 @@ class AccountsController(TransactionBase): self.unlink_ref_doc_from_po() def unlink_ref_doc_from_po(self): - print("\n"*5, "*"*50, "\n"*5) - so_items = [] for item in self.items: so_items.append(item.name) - print("SO Items: ", so_items) - linked_po = frappe.get_all( 'Purchase Order Item', filters = { @@ -838,8 +834,6 @@ class AccountsController(TransactionBase): pluck='parent' ) - print("Before unlinking: ", linked_po) - if linked_po: frappe.db.set_value( 'Purchase Order Item', { @@ -854,19 +848,6 @@ class AccountsController(TransactionBase): frappe.msgprint(_("Purchase Orders {0} are un-linked").format("\n".join(linked_po))) - linked_po = frappe.get_all( - 'Purchase Order Item', - filters = { - 'sales_order': self.name, - 'sales_order_item': ['in', so_items], - 'docstatus': ['<', 2] - }, - pluck='parent' - ) - print("After unlinking: ", linked_po) - - print("\n"*5, "*"*50, "\n"*5) - def get_tax_map(self): tax_map = {} for tax in self.get('taxes'): From 1a9d2684e2c8e9f89ea82dbade5cbe9b72f9e364 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 28 Oct 2021 19:27:45 +0530 Subject: [PATCH 071/105] fix: Remove debugger --- erpnext/selling/doctype/sales_order/sales_order.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index c9490fcae44..dcf478bda6e 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -207,12 +207,6 @@ class SalesOrder(SellingController): from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count update_coupon_code_count(self.coupon_code,'cancelled') - def cancel(self): - import pdb - - pdb.set_trace() - super(SalesOrder, self).cancel() - def update_project(self): if frappe.db.get_single_value('Selling Settings', 'sales_update_frequency') != "Each Transaction": return From d24cfff3886b8502c8f98cb4421c5e37a46ec3f2 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 28 Oct 2021 20:06:48 +0530 Subject: [PATCH 072/105] fix: Remove unused imports --- erpnext/controllers/accounts_controller.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 9267eb5b8e6..14ad4228c42 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -19,8 +19,7 @@ from frappe.utils import ( get_link_to_form, getdate, nowdate, - now, - today + today, ) from six import text_type From 051aaa708d7ab8a0019e19c9c8b8371afa03abc6 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 29 Oct 2021 11:35:34 +0530 Subject: [PATCH 073/105] fix: Ignore PO on So cancel --- erpnext/controllers/accounts_controller.py | 4 ++-- erpnext/selling/doctype/sales_order/sales_order.js | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 14ad4228c42..37b8f9d6d30 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -823,7 +823,7 @@ class AccountsController(TransactionBase): for item in self.items: so_items.append(item.name) - linked_po = frappe.get_all( + linked_po = list(set(frappe.get_all( 'Purchase Order Item', filters = { 'sales_order': self.name, @@ -831,7 +831,7 @@ class AccountsController(TransactionBase): 'docstatus': ['<', 2] }, pluck='parent' - ) + ))) if linked_po: frappe.db.set_value( diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index f6926906b82..d46c46f90e6 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -78,6 +78,8 @@ frappe.ui.form.on("Sales Order", { }); erpnext.queries.setup_warehouse_query(frm); + + frm.ignore_doctypes_on_cancel_all = ['Purchase Order']; }, delivery_date: function(frm) { From aa9e78bed19b72584a5248afd83ca82011727e13 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 29 Oct 2021 12:45:19 +0530 Subject: [PATCH 074/105] fix: Accounting Dimension filters not honouring user permissions --- erpnext/controllers/queries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index eeb659dee58..05ece4defee 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -566,7 +566,7 @@ def get_filtered_dimensions(doctype, txt, searchfield, start, page_len, filters) query_filters.append(['name', query_selector, dimensions]) - output = frappe.get_all(doctype, filters=query_filters) + output = frappe.get_list(doctype, filters=query_filters) result = [d.name for d in output] return [(d,) for d in set(result)] From afe09d4e80c3000ce866f0b43ecced878f715273 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 29 Oct 2021 13:45:07 +0530 Subject: [PATCH 075/105] test: remove unnecessary creation of new company (#28137) --- erpnext/tests/test_woocommerce.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/erpnext/tests/test_woocommerce.py b/erpnext/tests/test_woocommerce.py index 881f286baeb..3ce68d89bcd 100644 --- a/erpnext/tests/test_woocommerce.py +++ b/erpnext/tests/test_woocommerce.py @@ -12,12 +12,6 @@ from erpnext.erpnext_integrations.connectors.woocommerce_connection import order class TestWoocommerce(unittest.TestCase): def setUp(self): - if not frappe.db.exists('Company', 'Woocommerce'): - company = frappe.new_doc("Company") - company.company_name = "Woocommerce" - company.abbr = "W" - company.default_currency = "INR" - company.save() woo_settings = frappe.get_doc("Woocommerce Settings") if not woo_settings.secret: @@ -26,14 +20,14 @@ class TestWoocommerce(unittest.TestCase): woo_settings.api_consumer_key = "ck_fd43ff5756a6abafd95fadb6677100ce95a758a1" woo_settings.api_consumer_secret = "cs_94360a1ad7bef7fa420a40cf284f7b3e0788454e" woo_settings.enable_sync = 1 - woo_settings.company = "Woocommerce" - woo_settings.tax_account = "Sales Expenses - W" - woo_settings.f_n_f_account = "Expenses - W" + woo_settings.company = "_Test Company" + woo_settings.tax_account = "Sales Expenses - _TC" + woo_settings.f_n_f_account = "Expenses - _TC" woo_settings.creation_user = "Administrator" woo_settings.save(ignore_permissions=True) def test_sales_order_for_woocommerce(self): - frappe.flags.woocomm_test_order_data = {"id":75,"parent_id":0,"number":"74","order_key":"wc_order_5aa1281c2dacb","created_via":"checkout","version":"3.3.3","status":"processing","currency":"INR","date_created":"2018-03-08T12:10:04","date_created_gmt":"2018-03-08T12:10:04","date_modified":"2018-03-08T12:10:04","date_modified_gmt":"2018-03-08T12:10:04","discount_total":"0.00","discount_tax":"0.00","shipping_total":"150.00","shipping_tax":"0.00","cart_tax":"0.00","total":"649.00","total_tax":"0.00","prices_include_tax":False,"customer_id":12,"customer_ip_address":"103.54.99.5","customer_user_agent":"mozilla\\/5.0 (x11; linux x86_64) applewebkit\\/537.36 (khtml, like gecko) chrome\\/64.0.3282.186 safari\\/537.36","customer_note":"","billing":{"first_name":"Tony","last_name":"Stark","company":"Woocommerce","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN","email":"tony@gmail.com","phone":"123457890"},"shipping":{"first_name":"Tony","last_name":"Stark","company":"","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN"},"payment_method":"cod","payment_method_title":"Cash on delivery","transaction_id":"","date_paid":"","date_paid_gmt":"","date_completed":"","date_completed_gmt":"","cart_hash":"8e76b020d5790066496f244860c4703f","meta_data":[],"line_items":[{"id":80,"name":"Marvel","product_id":56,"variation_id":0,"quantity":1,"tax_class":"","subtotal":"499.00","subtotal_tax":"0.00","total":"499.00","total_tax":"0.00","taxes":[],"meta_data":[],"sku":"","price":499}],"tax_lines":[],"shipping_lines":[{"id":81,"method_title":"Flat rate","method_id":"flat_rate:1","total":"150.00","total_tax":"0.00","taxes":[],"meta_data":[{"id":623,"key":"Items","value":"Marvel × 1"}]}],"fee_lines":[],"coupon_lines":[],"refunds":[]} + frappe.flags.woocomm_test_order_data = {"id":75,"parent_id":0,"number":"74","order_key":"wc_order_5aa1281c2dacb","created_via":"checkout","version":"3.3.3","status":"processing","currency":"INR","date_created":"2018-03-08T12:10:04","date_created_gmt":"2018-03-08T12:10:04","date_modified":"2018-03-08T12:10:04","date_modified_gmt":"2018-03-08T12:10:04","discount_total":"0.00","discount_tax":"0.00","shipping_total":"150.00","shipping_tax":"0.00","cart_tax":"0.00","total":"649.00","total_tax":"0.00","prices_include_tax":False,"customer_id":12,"customer_ip_address":"103.54.99.5","customer_user_agent":"mozilla\\/5.0 (x11; linux x86_64) applewebkit\\/537.36 (khtml, like gecko) chrome\\/64.0.3282.186 safari\\/537.36","customer_note":"","billing":{"first_name":"Tony","last_name":"Stark","company":"_Test Company","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN","email":"tony@gmail.com","phone":"123457890"},"shipping":{"first_name":"Tony","last_name":"Stark","company":"","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN"},"payment_method":"cod","payment_method_title":"Cash on delivery","transaction_id":"","date_paid":"","date_paid_gmt":"","date_completed":"","date_completed_gmt":"","cart_hash":"8e76b020d5790066496f244860c4703f","meta_data":[],"line_items":[{"id":80,"name":"Marvel","product_id":56,"variation_id":0,"quantity":1,"tax_class":"","subtotal":"499.00","subtotal_tax":"0.00","total":"499.00","total_tax":"0.00","taxes":[],"meta_data":[],"sku":"","price":499}],"tax_lines":[],"shipping_lines":[{"id":81,"method_title":"Flat rate","method_id":"flat_rate:1","total":"150.00","total_tax":"0.00","taxes":[],"meta_data":[{"id":623,"key":"Items","value":"Marvel × 1"}]}],"fee_lines":[],"coupon_lines":[],"refunds":[]} order() self.assertTrue(frappe.get_value("Customer",{"woocommerce_email":"tony@gmail.com"})) From 292419bc9ebc405ef8a4861125070abb0d321db1 Mon Sep 17 00:00:00 2001 From: Marica Date: Fri, 29 Oct 2021 13:49:27 +0530 Subject: [PATCH 076/105] fix: Skip empty rows while updating unsaved BOM cost (#28136) - Dont try to get valuation rate if row has no item code - Dont try to add exploded items if row has no item code --- erpnext/manufacturing/doctype/bom/bom.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 232e3a0b0ff..2cd8f8c15af 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -307,6 +307,9 @@ class BOM(WebsiteGenerator): existing_bom_cost = self.total_cost for d in self.get("items"): + if not d.item_code: + continue + rate = self.get_rm_rate({ "company": self.company, "item_code": d.item_code, @@ -599,7 +602,7 @@ class BOM(WebsiteGenerator): for d in self.get('items'): if d.bom_no: self.get_child_exploded_items(d.bom_no, d.stock_qty) - else: + elif d.item_code: self.add_to_cur_exploded_items(frappe._dict({ 'item_code' : d.item_code, 'item_name' : d.item_name, From 8ccd3fee9e7092d6bb7d81e8ef6d59e25c13a29d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 29 Oct 2021 16:08:52 +0530 Subject: [PATCH 077/105] fix: COA importer importing all accounts as group --- .../doctype/account/chart_of_accounts/chart_of_accounts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py index 05caafe1c47..3596c340175 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py +++ b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py @@ -81,7 +81,7 @@ def add_suffix_if_duplicate(account_name, account_number, accounts): def identify_is_group(child): if child.get("is_group"): is_group = child.get("is_group") - elif len(set(child.keys()) - set(["account_type", "root_type", "is_group", "tax_rate", "account_number"])): + elif len(set(child.keys()) - set(["account_name", "account_type", "root_type", "is_group", "tax_rate", "account_number"])): is_group = 1 else: is_group = 0 From 75a76e634d17507a568ff007d20226ffa8f7dff3 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Fri, 29 Oct 2021 16:45:04 +0530 Subject: [PATCH 078/105] fix: POS consolidated invoice rounded total issue (#28006) --- .../pos_invoice_merge_log.py | 11 +++++ erpnext/controllers/taxes_and_totals.py | 40 ++++++++++++------- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index 4f26ed43db7..28bd10283e7 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -114,6 +114,8 @@ class POSInvoiceMergeLog(Document): def merge_pos_invoice_into(self, invoice, data): items, payments, taxes = [], [], [] loyalty_amount_sum, loyalty_points_sum = 0, 0 + rounding_adjustment, base_rounding_adjustment = 0, 0 + rounded_total, base_rounded_total = 0, 0 for doc in data: map_doc(doc, invoice, table_map={ "doctype": invoice.doctype }) @@ -162,6 +164,11 @@ class POSInvoiceMergeLog(Document): found = True if not found: payments.append(payment) + rounding_adjustment += doc.rounding_adjustment + rounded_total += doc.rounded_total + base_rounding_adjustment += doc.rounding_adjustment + base_rounded_total += doc.rounded_total + if loyalty_points_sum: invoice.redeem_loyalty_points = 1 @@ -171,6 +178,10 @@ class POSInvoiceMergeLog(Document): invoice.set('items', items) invoice.set('payments', payments) invoice.set('taxes', taxes) + invoice.set('rounding_adjustment',rounding_adjustment) + invoice.set('rounding_adjustment',base_rounding_adjustment) + invoice.set('base_rounded_total',base_rounded_total) + invoice.set('rounded_total',rounded_total) invoice.additional_discount_percentage = 0 invoice.discount_amount = 0.0 invoice.taxes_and_charges = None diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 70cc8a58bfe..7e7f598bc43 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -260,7 +260,9 @@ class calculate_taxes_and_totals(object): self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"]) def calculate_taxes(self): - self.doc.rounding_adjustment = 0 + if not self.doc.get('is_consolidated'): + self.doc.rounding_adjustment = 0 + # maintain actual tax rate based on idx actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))] for tax in self.doc.get("taxes") if tax.charge_type == "Actual"]) @@ -312,7 +314,9 @@ class calculate_taxes_and_totals(object): # adjust Discount Amount loss in last tax iteration if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \ - and self.doc.discount_amount and self.doc.apply_discount_on == "Grand Total": + and self.doc.discount_amount \ + and self.doc.apply_discount_on == "Grand Total" \ + and not self.doc.get('is_consolidated'): self.doc.rounding_adjustment = flt(self.doc.grand_total - flt(self.doc.discount_amount) - tax.total, self.doc.precision("rounding_adjustment")) @@ -405,11 +409,16 @@ class calculate_taxes_and_totals(object): self.doc.rounding_adjustment = diff def calculate_totals(self): - self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment) \ - if self.doc.get("taxes") else flt(self.doc.net_total) + if self.doc.get("taxes"): + self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment) + else: + self.doc.grand_total = flt(self.doc.net_total) - self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total + if self.doc.get("taxes"): + self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total - flt(self.doc.rounding_adjustment), self.doc.precision("total_taxes_and_charges")) + else: + self.doc.total_taxes_and_charges = 0.0 self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"]) @@ -446,19 +455,20 @@ class calculate_taxes_and_totals(object): self.doc.total_net_weight += d.total_weight def set_rounded_total(self): - if self.doc.meta.get_field("rounded_total"): - if self.doc.is_rounded_total_disabled(): - self.doc.rounded_total = self.doc.base_rounded_total = 0 - return + if not self.doc.get('is_consolidated'): + if self.doc.meta.get_field("rounded_total"): + if self.doc.is_rounded_total_disabled(): + self.doc.rounded_total = self.doc.base_rounded_total = 0 + return - self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total, - self.doc.currency, self.doc.precision("rounded_total")) + self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total, + self.doc.currency, self.doc.precision("rounded_total")) - #if print_in_rate is set, we would have already calculated rounding adjustment - self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total, - self.doc.precision("rounding_adjustment")) + #if print_in_rate is set, we would have already calculated rounding adjustment + self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total, + self.doc.precision("rounding_adjustment")) - self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"]) + self._set_in_company_currency(self.doc, ["rounding_adjustment", "rounded_total"]) def _cleanup(self): if not self.doc.get('is_consolidated'): From b44945380dd0af192b12cdf96df7ac23b77a79c3 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Wed, 20 Oct 2021 17:37:52 +0530 Subject: [PATCH 079/105] fix: incorrect amount of serial_nos fetched --- erpnext/stock/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index c4a0497b744..b131125c670 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -104,7 +104,7 @@ def get_stock_balance(item_code, warehouse, posting_date=None, posting_time=None serial_nos = last_entry.get("serial_no") if (serial_nos and - len(get_serial_nos_data(serial_nos)) < last_entry.qty_after_transaction): + len(get_serial_nos_data(serial_nos)) <= last_entry.qty_after_transaction): serial_nos = get_serial_nos_data_after_transactions(args) return ((last_entry.qty_after_transaction, last_entry.valuation_rate, serial_nos) @@ -121,6 +121,7 @@ def get_serial_nos_data_after_transactions(args): WHERE item_code = %(item_code)s and warehouse = %(warehouse)s and timestamp(posting_date, posting_time) < timestamp(%(posting_date)s, %(posting_time)s) + and is_cancelled = 0 order by posting_date, posting_time asc """, args, as_dict=1) for d in data: From deb6b38fab47339098c903b253a5e478a2a63b65 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Thu, 21 Oct 2021 17:17:11 +0530 Subject: [PATCH 080/105] refactor: replaced db.sql with qb --- erpnext/stock/utils.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index b131125c670..518bdf11e3c 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -115,14 +115,25 @@ def get_stock_balance(item_code, warehouse, posting_date=None, posting_time=None return last_entry.qty_after_transaction if last_entry else 0.0 def get_serial_nos_data_after_transactions(args): + from pypika import CustomFunction + serial_nos = [] - data = frappe.db.sql(""" SELECT serial_no, actual_qty - FROM `tabStock Ledger Entry` - WHERE - item_code = %(item_code)s and warehouse = %(warehouse)s - and timestamp(posting_date, posting_time) < timestamp(%(posting_date)s, %(posting_time)s) - and is_cancelled = 0 - order by posting_date, posting_time asc """, args, as_dict=1) + args = frappe._dict(args) + sle = frappe.qb.DocType('Stock Ledger Entry') + Timestamp = CustomFunction('timestamp', ['date', 'time']) + + data = frappe.qb.from_( + sle + ).select( + 'serial_no','actual_qty' + ).where( + (sle.item_code == args.item_code) + & (sle.warehouse == args.warehouse) + & (Timestamp(sle.posting_date, sle.posting_time) < Timestamp(args.posting_date, args.posting_time)) + & (sle.is_cancelled == 0) + ).orderby( + sle.posting_date, sle.posting_time + ).run(as_dict=1) for d in data: if d.actual_qty > 0: From 2aa019ae4c148a01ed57b8cffb461cb90748791a Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 29 Oct 2021 14:32:13 +0530 Subject: [PATCH 081/105] fix: fetch serial nos from ledger unconditionally --- erpnext/stock/utils.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 518bdf11e3c..463b314291a 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -101,11 +101,7 @@ def get_stock_balance(item_code, warehouse, posting_date=None, posting_time=None if with_valuation_rate: if with_serial_no: - serial_nos = last_entry.get("serial_no") - - if (serial_nos and - len(get_serial_nos_data(serial_nos)) <= last_entry.qty_after_transaction): - serial_nos = get_serial_nos_data_after_transactions(args) + serial_nos = get_serial_nos_data_after_transactions(args) return ((last_entry.qty_after_transaction, last_entry.valuation_rate, serial_nos) if last_entry else (0.0, 0.0, 0.0)) From f4b60a48f5bb8b8b9e8446aa0ab441f84b321e55 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 29 Oct 2021 14:56:54 +0530 Subject: [PATCH 082/105] refactor: simplify sr no fetching --- erpnext/stock/utils.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 463b314291a..38ca25ee547 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -113,12 +113,12 @@ def get_stock_balance(item_code, warehouse, posting_date=None, posting_time=None def get_serial_nos_data_after_transactions(args): from pypika import CustomFunction - serial_nos = [] + serial_nos = set() args = frappe._dict(args) sle = frappe.qb.DocType('Stock Ledger Entry') Timestamp = CustomFunction('timestamp', ['date', 'time']) - data = frappe.qb.from_( + stock_ledger_entries = frappe.qb.from_( sle ).select( 'serial_no','actual_qty' @@ -131,11 +131,12 @@ def get_serial_nos_data_after_transactions(args): sle.posting_date, sle.posting_time ).run(as_dict=1) - for d in data: - if d.actual_qty > 0: - serial_nos.extend(get_serial_nos_data(d.serial_no)) + for stock_ledger_entry in stock_ledger_entries: + changed_serial_no = get_serial_nos_data(stock_ledger_entry.serial_no) + if stock_ledger_entry.actual_qty > 0: + serial_nos.update(changed_serial_no) else: - serial_nos = list(set(serial_nos) - set(get_serial_nos_data(d.serial_no))) + serial_nos.difference_update(changed_serial_no) return '\n'.join(serial_nos) From ff9cfe0d14849d2103700ce84244c25b22075581 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 29 Oct 2021 16:30:12 +0530 Subject: [PATCH 083/105] fix: sort by creation to break tie --- erpnext/stock/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 38ca25ee547..e1d5a89082e 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -128,7 +128,7 @@ def get_serial_nos_data_after_transactions(args): & (Timestamp(sle.posting_date, sle.posting_time) < Timestamp(args.posting_date, args.posting_time)) & (sle.is_cancelled == 0) ).orderby( - sle.posting_date, sle.posting_time + sle.posting_date, sle.posting_time, sle.creation ).run(as_dict=1) for stock_ledger_entry in stock_ledger_entries: From 15e9b5170d9c17e25d96ba142ca3be3bb7f68156 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Fri, 29 Oct 2021 18:07:11 +0530 Subject: [PATCH 084/105] fix: Make status filter optional (#28126) --- .../fixed_asset_register/fixed_asset_register.js | 5 ++--- .../fixed_asset_register/fixed_asset_register.py | 11 ++++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index 75f42a9f783..06989a95da7 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -16,9 +16,8 @@ frappe.query_reports["Fixed Asset Register"] = { fieldname:"status", label: __("Status"), fieldtype: "Select", - options: "In Location\nDisposed", - default: 'In Location', - reqd: 1 + options: "\nIn Location\nDisposed", + default: 'In Location' }, { "fieldname":"filter_based_on", diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index e370b9d0cb3..63685fef465 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -45,12 +45,13 @@ def get_conditions(filters): if filters.get('cost_center'): conditions["cost_center"] = filters.get('cost_center') - # In Store assets are those that are not sold or scrapped - operand = 'not in' - if status not in 'In Location': - operand = 'in' + if status: + # In Store assets are those that are not sold or scrapped + operand = 'not in' + if status not in 'In Location': + operand = 'in' - conditions['status'] = (operand, ['Sold', 'Scrapped']) + conditions['status'] = (operand, ['Sold', 'Scrapped']) return conditions From 1a6e98ed488e5ed2e55f14b9f1d5166abfecb0f4 Mon Sep 17 00:00:00 2001 From: Anuja Pawar <60467153+Anuja-pawar@users.noreply.github.com> Date: Fri, 29 Oct 2021 20:52:47 +0530 Subject: [PATCH 085/105] fix(Payment Reconciliation): clear child tables on company/party change (#28008) --- .../payment_reconciliation.js | 132 +++++++++--------- erpnext/accounts/utils.py | 3 +- 2 files changed, 70 insertions(+), 65 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index 412833bd192..ad5a84094ea 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -4,9 +4,14 @@ frappe.provide("erpnext.accounts"); erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationController extends frappe.ui.form.Controller { onload() { - var me = this; + const default_company = frappe.defaults.get_default('company'); + this.frm.set_value('company', default_company); - this.frm.set_query("party_type", function() { + this.frm.set_value('party_type', ''); + this.frm.set_value('party', ''); + this.frm.set_value('receivable_payable_account', ''); + + this.frm.set_query("party_type", () => { return { "filters": { "name": ["in", Object.keys(frappe.boot.party_account_types)], @@ -14,44 +19,30 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo } }); - this.frm.set_query('receivable_payable_account', function() { - check_mandatory(me.frm); + this.frm.set_query('receivable_payable_account', () => { return { filters: { - "company": me.frm.doc.company, + "company": this.frm.doc.company, "is_group": 0, - "account_type": frappe.boot.party_account_types[me.frm.doc.party_type] + "account_type": frappe.boot.party_account_types[this.frm.doc.party_type] } }; }); - this.frm.set_query('bank_cash_account', function() { - check_mandatory(me.frm, true); + this.frm.set_query('bank_cash_account', () => { return { filters:[ - ['Account', 'company', '=', me.frm.doc.company], + ['Account', 'company', '=', this.frm.doc.company], ['Account', 'is_group', '=', 0], ['Account', 'account_type', 'in', ['Bank', 'Cash']] ] }; }); - - this.frm.set_value('party_type', ''); - this.frm.set_value('party', ''); - this.frm.set_value('receivable_payable_account', ''); - - var check_mandatory = (frm, only_company=false) => { - var title = __("Mandatory"); - if (only_company && !frm.doc.company) { - frappe.throw({message: __("Please Select a Company First"), title: title}); - } else if (!frm.doc.company || !frm.doc.party_type) { - frappe.throw({message: __("Please Select Both Company and Party Type First"), title: title}); - } - }; } refresh() { this.frm.disable_save(); + this.frm.set_df_property('invoices', 'cannot_delete_rows', true); this.frm.set_df_property('payments', 'cannot_delete_rows', true); this.frm.set_df_property('allocation', 'cannot_delete_rows', true); @@ -85,76 +76,92 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo } company() { - var me = this; + this.frm.set_value('party', ''); this.frm.set_value('receivable_payable_account', ''); - me.frm.clear_table("allocation"); - me.frm.clear_table("invoices"); - me.frm.clear_table("payments"); - me.frm.refresh_fields(); - me.frm.trigger('party'); + } + + party_type() { + this.frm.set_value('party', ''); } party() { - var me = this; - if (!me.frm.doc.receivable_payable_account && me.frm.doc.party_type && me.frm.doc.party) { + this.frm.set_value('receivable_payable_account', ''); + this.frm.trigger("clear_child_tables"); + + if (!this.frm.doc.receivable_payable_account && this.frm.doc.party_type && this.frm.doc.party) { return frappe.call({ method: "erpnext.accounts.party.get_party_account", args: { - company: me.frm.doc.company, - party_type: me.frm.doc.party_type, - party: me.frm.doc.party + company: this.frm.doc.company, + party_type: this.frm.doc.party_type, + party: this.frm.doc.party }, - callback: function(r) { + callback: (r) => { if (!r.exc && r.message) { - me.frm.set_value("receivable_payable_account", r.message); + this.frm.set_value("receivable_payable_account", r.message); } - me.frm.refresh(); + this.frm.refresh(); + } }); } } + receivable_payable_account() { + this.frm.trigger("clear_child_tables"); + this.frm.refresh(); + } + + clear_child_tables() { + this.frm.clear_table("invoices"); + this.frm.clear_table("payments"); + this.frm.clear_table("allocation"); + this.frm.refresh_fields(); + } + get_unreconciled_entries() { - var me = this; + this.frm.clear_table("allocation"); return this.frm.call({ - doc: me.frm.doc, + doc: this.frm.doc, method: 'get_unreconciled_entries', - callback: function(r, rt) { - if (!(me.frm.doc.payments.length || me.frm.doc.invoices.length)) { - frappe.throw({message: __("No invoice and payment records found for this party")}); + callback: () => { + if (!(this.frm.doc.payments.length || this.frm.doc.invoices.length)) { + frappe.throw({message: __("No Unreconciled Invoices and Payments found for this party and account")}); + } else if (!(this.frm.doc.invoices.length)) { + frappe.throw({message: __("No Outstanding Invoices found for this party")}); + } else if (!(this.frm.doc.payments.length)) { + frappe.throw({message: __("No Unreconciled Payments found for this party")}); } - me.frm.refresh(); + this.frm.refresh(); } }); } allocate() { - var me = this; - let payments = me.frm.fields_dict.payments.grid.get_selected_children(); + let payments = this.frm.fields_dict.payments.grid.get_selected_children(); if (!(payments.length)) { - payments = me.frm.doc.payments; + payments = this.frm.doc.payments; } - let invoices = me.frm.fields_dict.invoices.grid.get_selected_children(); + let invoices = this.frm.fields_dict.invoices.grid.get_selected_children(); if (!(invoices.length)) { - invoices = me.frm.doc.invoices; + invoices = this.frm.doc.invoices; } - return me.frm.call({ - doc: me.frm.doc, + return this.frm.call({ + doc: this.frm.doc, method: 'allocate_entries', args: { payments: payments, invoices: invoices }, - callback: function() { - me.frm.refresh(); + callback: () => { + this.frm.refresh(); } }); } reconcile() { - var me = this; - var show_dialog = me.frm.doc.allocation.filter(d => d.difference_amount && !d.difference_account); + var show_dialog = this.frm.doc.allocation.filter(d => d.difference_amount && !d.difference_account); if (show_dialog && show_dialog.length) { @@ -186,10 +193,10 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo label: __("Difference Account"), fieldname: 'difference_account', reqd: 1, - get_query: function() { + get_query: () => { return { filters: { - company: me.frm.doc.company, + company: this.frm.doc.company, is_group: 0 } } @@ -203,7 +210,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo }] }, ], - primary_action: function() { + primary_action: () => { const args = dialog.get_values()["allocation"]; args.forEach(d => { @@ -211,7 +218,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo "difference_account", d.difference_account); }); - me.reconcile_payment_entries(); + this.reconcile_payment_entries(); dialog.hide(); }, primary_action_label: __('Reconcile Entries') @@ -237,15 +244,12 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo } reconcile_payment_entries() { - var me = this; - return this.frm.call({ - doc: me.frm.doc, + doc: this.frm.doc, method: 'reconcile', - callback: function(r, rt) { - me.frm.clear_table("allocation"); - me.frm.refresh_fields(); - me.frm.refresh(); + callback: () => { + this.frm.clear_table("allocation"); + this.frm.refresh(); } }); } diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index fdd8d092ebb..fb23d6fc499 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -450,7 +450,8 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False): # new row with references new_row = journal_entry.append("accounts") - new_row.update(jv_detail.as_dict().copy()) + + new_row.update((frappe.copy_doc(jv_detail)).as_dict()) new_row.set(d["dr_or_cr"], d["allocated_amount"]) new_row.set('debit' if d['dr_or_cr'] == 'debit_in_account_currency' else 'credit', From ad5cf467c03b695fe3f12a98ab05367189db969e Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira Date: Sat, 30 Oct 2021 13:07:36 +0530 Subject: [PATCH 086/105] fix: update tax template name for 18% GST --- erpnext/setup/setup_wizard/data/country_wise_tax.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/setup_wizard/data/country_wise_tax.json b/erpnext/setup/setup_wizard/data/country_wise_tax.json index b7e895db363..8a1338583ba 100644 --- a/erpnext/setup/setup_wizard/data/country_wise_tax.json +++ b/erpnext/setup/setup_wizard/data/country_wise_tax.json @@ -1195,7 +1195,7 @@ "*": { "item_tax_templates": [ { - "title": "GST 9%", + "title": "GST 18%", "taxes": [ { "tax_type": { From 541c892f9757f01a4ce7b72e5bc537260e65a3f8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 30 Oct 2021 18:22:46 +0530 Subject: [PATCH 087/105] fix: Error for missing PAN no field --- .../tax_withholding_category.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index c36f3cb201b..6ab0f9618aa 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -58,15 +58,24 @@ def get_party_tax_withholding_details(inv, tax_withholding_category=None): pan_no = '' parties = [] party_type, party = get_party_details(inv) + has_pan_field = frappe.get_meta(party_type).has_field("pan") if not tax_withholding_category: - tax_withholding_category, pan_no = frappe.db.get_value(party_type, party, ['tax_withholding_category', 'pan']) + if has_pan_field: + fields = ['tax_withholding_category', 'pan'] + else: + fields = ['tax_withholding_category'] + + tax_withholding_details = frappe.db.get_value(party_type, party, fields, as_dict=1) + + tax_withholding_category = tax_withholding_details.get('tax_withholding_category') + pan_no = tax_withholding_details.get('pan') if not tax_withholding_category: return # if tax_withholding_category passed as an argument but not pan_no - if not pan_no: + if not pan_no and has_pan_field: pan_no = frappe.db.get_value(party_type, party, 'pan') # Get others suppliers with the same PAN No From cae29b71d863daf102fd002b7f32592817633026 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 31 Oct 2021 09:24:07 +0530 Subject: [PATCH 088/105] fix: patch update_category_in_ltds_certificate --- erpnext/patches/v13_0/update_category_in_ltds_certificate.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/patches/v13_0/update_category_in_ltds_certificate.py b/erpnext/patches/v13_0/update_category_in_ltds_certificate.py index 4d4645269cb..a5f5a23449a 100644 --- a/erpnext/patches/v13_0/update_category_in_ltds_certificate.py +++ b/erpnext/patches/v13_0/update_category_in_ltds_certificate.py @@ -6,6 +6,8 @@ def execute(): if not company: return + frappe.reload_doc('regional', 'doctype', 'lower_deduction_certificate') + ldc = frappe.qb.DocType("Lower Deduction Certificate").as_("ldc") supplier = frappe.qb.DocType("Supplier") From 623776dd48325399441175ed04c2fd2a1d3f34c1 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Mon, 1 Nov 2021 13:37:31 +0530 Subject: [PATCH 089/105] feat: added company field in prospect (#28139) * feat: added company field in prospect * fix: review changes --- erpnext/crm/doctype/prospect/prospect.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/prospect/prospect.json b/erpnext/crm/doctype/prospect/prospect.json index 3d6fba5123a..0e872ace043 100644 --- a/erpnext/crm/doctype/prospect/prospect.json +++ b/erpnext/crm/doctype/prospect/prospect.json @@ -20,6 +20,7 @@ "website", "column_break_13", "prospect_owner", + "company", "leads_section", "prospect_lead", "address_and_contact_section", @@ -153,14 +154,23 @@ "fieldname": "address_and_contact_section", "fieldtype": "Section Break", "label": "Address and Contact" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2021-08-27 16:24:42.961967", + "migration_hash": "f39fb8f4e18a0e7fd391f0b4b52d8375", + "modified": "2021-11-01 13:10:36.759249", "modified_by": "Administrator", "module": "CRM", "name": "Prospect", + "naming_rule": "By fieldname", "owner": "Administrator", "permissions": [ { From a0727b2e824e5299a893197624872122e3d6eb74 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 1 Nov 2021 13:17:40 +0530 Subject: [PATCH 090/105] fix: pass company while fetching valuation rate If company is not supplied and valuation rate is 0, then default company is used for checking if perpetual inventory is enabled or not. This makes little sense as different companies can have different setting for perpetual inventory. --- erpnext/stock/stock_ledger.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index bdbec52f7e4..ce82f089d29 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -600,7 +600,7 @@ class update_entries_after(object): if not allow_zero_rate: self.wh_data.valuation_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)) + currency=erpnext.get_company_currency(sle.company), company=sle.company) def get_incoming_value_for_serial_nos(self, sle, serial_nos): # get rate from serial nos within same company @@ -667,7 +667,7 @@ class update_entries_after(object): if not allow_zero_valuation_rate: self.wh_data.valuation_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)) + currency=erpnext.get_company_currency(sle.company), company=sle.company) def get_fifo_values(self, sle): incoming_rate = flt(sle.incoming_rate) @@ -700,7 +700,7 @@ class update_entries_after(object): 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)) + currency=erpnext.get_company_currency(sle.company), company=sle.company) else: _rate = 0 From f7ffe04a4b0a65b02f20f6ca180a86c1d5a3874f Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 1 Nov 2021 13:21:14 +0530 Subject: [PATCH 091/105] fix: use warehouse to find company --- erpnext/stock/stock_ledger.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index ce82f089d29..70f4bcaef79 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -911,10 +911,11 @@ def get_sle_by_voucher_detail_no(voucher_detail_no, excluded_sle=None): def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no, allow_zero_rate=False, currency=None, company=None, raise_error_if_no_rate=True): - # Get valuation rate from last sle for the same item and warehouse - if not company: - company = erpnext.get_default_company() + if not company: + company = frappe.get_cached_value("Warehouse", warehouse, "company") + + # Get valuation rate from last sle for the same item and warehouse last_valuation_rate = frappe.db.sql("""select valuation_rate from `tabStock Ledger Entry` force index (item_warehouse) where From 1eab3a44f6721dbbc0d8213147a57c0da68acf47 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 1 Nov 2021 14:16:57 +0530 Subject: [PATCH 092/105] fix(ux): stock levels take time after item merge Item merge creates a repost and depending on number of entries it can take from 1 to n hours for it to finish. (depending upon queued up reposts) Added message so users don't feel confused till this operation is finished. --- erpnext/stock/doctype/item/item.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 04e4653d93c..fa42c7d934b 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -676,6 +676,8 @@ class Item(WebsiteGenerator): def after_rename(self, old_name, new_name, merge): if merge: self.validate_duplicate_item_in_stock_reconciliation(old_name, new_name) + frappe.msgprint(_("It can take upto few hours for accurate stock values to be visible after merging items."), + indicator="orange", title="Note") if self.route: invalidate_cache_for_item(self) From 27cbeb920e90f976145e8ca5c696dbd49bd7ec0d Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 26 Sep 2021 14:10:52 +0530 Subject: [PATCH 093/105] test(patch): run patch tests for major releases Run patch tests one at a time v10 db -> v12-> v13 -> .... -> frappe:corresponding base branch and PR. --- .github/workflows/patch.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml index 92a19621d15..f8abb6c7741 100644 --- a/.github/workflows/patch.yml +++ b/.github/workflows/patch.yml @@ -86,4 +86,27 @@ jobs: cd ~/frappe-bench/ wget https://erpnext.com/files/v10-erpnext.sql.gz bench --site test_site --force restore ~/frappe-bench/v10-erpnext.sql.gz + + git -C "apps/frappe" remote set-url upstream https://github.com/frappe/frappe.git + git -C "apps/erpnext" remote set-url upstream https://github.com/frappe/erpnext.git + + for version in $(seq 12 13) + do + echo "Updating to v$version" + branch_name="version-$version" + + git -C "apps/frappe" fetch --depth 1 upstream $branch_name:$branch_name + git -C "apps/erpnext" fetch --depth 1 upstream $branch_name:$branch_name + + git -C "apps/frappe" checkout -q -f $branch_name + git -C "apps/erpnext" checkout -q -f $branch_name + + bench setup requirements --python + bench --site test_site migrate + done + + + echo "Updating to latest version" + git -C "apps/frappe" checkout -q -f "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}" + git -C "apps/erpnext" checkout -q -f "$GITHUB_SHA" bench --site test_site migrate From c587f18735336e93edee1e4e1cd85413a74cb82a Mon Sep 17 00:00:00 2001 From: Revant Nandgaonkar Date: Tue, 2 Nov 2021 12:09:41 +0530 Subject: [PATCH 094/105] chore: migrate docker to github actions --- .github/workflows/docker-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index 4b1147e79f9..21ba981df44 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -11,4 +11,4 @@ jobs: - name: curl run: | apk add curl bash - curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token ${{ secrets.TRAVIS_CI_TOKEN }}" -d '{"request":{"branch":"master"}}' https://api.travis-ci.com/repo/frappe%2Ffrappe_docker/requests + curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: Bearer ${{ secrets.GITHUB_PAT }}" https://api.github.com/repos/frappe/frappe_docker/actions/workflows/build_stable.yml/dispatches -d '{"ref":"main"}' From 048210a8f67fe0544216361d7342619581318570 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 2 Nov 2021 15:49:58 +0530 Subject: [PATCH 095/105] fix: Remove warehouse filter on Batch field for Material Receipt --- erpnext/stock/doctype/stock_entry/stock_entry.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index ac8303eda3d..80203a9ef47 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -88,7 +88,9 @@ frappe.ui.form.on('Stock Entry', { } } - filters["warehouse"] = item.s_warehouse || item.t_warehouse; + if (frm.doc.purpose != "Material Receipt") { + filters["warehouse"] = item.s_warehouse || item.t_warehouse; + } return { query : "erpnext.controllers.queries.get_batch_no", From 48886ee70524132d1f4cf6751b56ccbc96d765a5 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 2 Nov 2021 15:57:41 +0530 Subject: [PATCH 096/105] chore: Add comment above fix for future reference --- erpnext/stock/doctype/stock_entry/stock_entry.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 80203a9ef47..c4b8131305e 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -88,6 +88,8 @@ frappe.ui.form.on('Stock Entry', { } } + // User could want to select a manually created empty batch (no warehouse) + // or a pre-existing batch if (frm.doc.purpose != "Material Receipt") { filters["warehouse"] = item.s_warehouse || item.t_warehouse; } From d72709dd817b879fa3bac2387689e0a9d4728e8f Mon Sep 17 00:00:00 2001 From: Anuja Pawar <60467153+Anuja-pawar@users.noreply.github.com> Date: Tue, 2 Nov 2021 16:28:46 +0530 Subject: [PATCH 097/105] fix(Payment Entry): splitting outstanding rows as per payment terms (#27946) --- .../doctype/payment_entry/payment_entry.py | 44 +++++++++++-------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 9b4a91d4e96..8bbe3db914f 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -389,7 +389,7 @@ class PaymentEntry(AccountsController): invoice_paid_amount_map[invoice_key]['outstanding'] = term.outstanding invoice_paid_amount_map[invoice_key]['discounted_amt'] = ref.total_amount * (term.discount / 100) - for key, allocated_amount in iteritems(invoice_payment_amount_map): + for idx, (key, allocated_amount) in enumerate(iteritems(invoice_payment_amount_map), 1): if not invoice_paid_amount_map.get(key): frappe.throw(_('Payment term {0} not used in {1}').format(key[0], key[1])) @@ -407,7 +407,7 @@ class PaymentEntry(AccountsController): (allocated_amount - discounted_amt, discounted_amt, allocated_amount, key[1], key[0])) else: if allocated_amount > outstanding: - frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0])) + frappe.throw(_('Row #{0}: Cannot allocate more than {1} against payment term {2}').format(idx, outstanding, key[0])) if allocated_amount and outstanding: frappe.db.sql(""" @@ -1053,12 +1053,6 @@ def get_outstanding_reference_documents(args): party_account_currency = get_account_currency(args.get("party_account")) company_currency = frappe.get_cached_value('Company', args.get("company"), "default_currency") - # Get negative outstanding sales /purchase invoices - negative_outstanding_invoices = [] - if args.get("party_type") not in ["Student", "Employee"] and not args.get("voucher_no"): - negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), args.get("party"), - args.get("party_account"), args.get("company"), party_account_currency, company_currency) - # Get positive outstanding sales /purchase invoices/ Fees condition = "" if args.get("voucher_type") and args.get("voucher_no"): @@ -1105,6 +1099,12 @@ def get_outstanding_reference_documents(args): orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"), args.get("party"), args.get("company"), party_account_currency, company_currency, filters=args) + # Get negative outstanding sales /purchase invoices + negative_outstanding_invoices = [] + if args.get("party_type") not in ["Student", "Employee"] and not args.get("voucher_no"): + negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), args.get("party"), + args.get("party_account"), party_account_currency, company_currency, condition=condition) + data = negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed if not data: @@ -1137,22 +1137,26 @@ def split_invoices_based_on_payment_terms(outstanding_invoices): 'invoice_amount': flt(d.invoice_amount), 'outstanding_amount': flt(d.outstanding_amount), 'payment_amount': payment_term.payment_amount, - 'payment_term': payment_term.payment_term, - 'allocated_amount': payment_term.outstanding + 'payment_term': payment_term.payment_term })) + outstanding_invoices_after_split = [] if invoice_ref_based_on_payment_terms: for idx, ref in invoice_ref_based_on_payment_terms.items(): - voucher_no = outstanding_invoices[idx]['voucher_no'] - voucher_type = outstanding_invoices[idx]['voucher_type'] + voucher_no = ref[0]['voucher_no'] + voucher_type = ref[0]['voucher_type'] - frappe.msgprint(_("Spliting {} {} into {} rows as per payment terms").format( + frappe.msgprint(_("Spliting {} {} into {} row(s) as per Payment Terms").format( voucher_type, voucher_no, len(ref)), alert=True) - outstanding_invoices.pop(idx - 1) - outstanding_invoices += invoice_ref_based_on_payment_terms[idx] + outstanding_invoices_after_split += invoice_ref_based_on_payment_terms[idx] - return outstanding_invoices + existing_row = list(filter(lambda x: x.get('voucher_no') == voucher_no, outstanding_invoices)) + index = outstanding_invoices.index(existing_row[0]) + outstanding_invoices.pop(index) + + outstanding_invoices_after_split += outstanding_invoices + return outstanding_invoices_after_split def get_orders_to_be_billed(posting_date, party_type, party, company, party_account_currency, company_currency, cost_center=None, filters=None): @@ -1219,7 +1223,7 @@ def get_orders_to_be_billed(posting_date, party_type, party, return order_list def get_negative_outstanding_invoices(party_type, party, party_account, - company, party_account_currency, company_currency, cost_center=None): + party_account_currency, company_currency, cost_center=None, condition=None): voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice" supplier_condition = "" if voucher_type == "Purchase Invoice": @@ -1241,19 +1245,21 @@ def get_negative_outstanding_invoices(party_type, party, party_account, `tab{voucher_type}` where {party_type} = %s and {party_account} = %s and docstatus = 1 and - company = %s and outstanding_amount < 0 + outstanding_amount < 0 {supplier_condition} + {condition} order by posting_date, name """.format(**{ "supplier_condition": supplier_condition, + "condition": condition, "rounded_total_field": rounded_total_field, "grand_total_field": grand_total_field, "voucher_type": voucher_type, "party_type": scrub(party_type), "party_account": "debit_to" if party_type == "Customer" else "credit_to", "cost_center": cost_center - }), (party, party_account, company), as_dict=True) + }), (party, party_account), as_dict=True) @frappe.whitelist() From f047c6ffc80775789be50707d9a1c0320c87a4fe Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 2 Nov 2021 16:43:31 +0530 Subject: [PATCH 098/105] fix: Test for WDV --- erpnext/assets/doctype/asset/test_asset.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 81c679f851b..170122527a5 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -563,10 +563,16 @@ class TestDepreciationMethods(AssetSetup): company_flag = frappe.flags.company frappe.flags.company = "_Test Company" + finance_book = frappe.new_doc("Finance Book") + finance_book.finance_book_name = "Income Tax" + finance_book.for_income_tax = 1 + finance_book.insert(ignore_if_duplicate = True) + asset = create_asset( calculate_depreciation = 1, available_for_use_date = "2030-07-12", purchase_date = "2030-01-01", + finance_book = finance_book.name, depreciation_method = "Written Down Value", expected_value_after_useful_life = 12500, depreciation_start_date = "2030-12-31", From 890a7868584db345028420c4148e41dc0f33cbe3 Mon Sep 17 00:00:00 2001 From: hrwx Date: Mon, 1 Nov 2021 14:16:51 +0000 Subject: [PATCH 099/105] fix: do not generate multiple invoices --- .../doctype/subscription/subscription.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index de9550233f9..b75530c4b01 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -502,9 +502,11 @@ class Subscription(Document): # Check invoice dates and make sure it doesn't have outstanding invoices return getdate() >= getdate(self.current_invoice_start) - def is_current_invoice_generated(self): + def is_current_invoice_generated(self, _current_start_date=None, _current_end_date=None): invoice = self.get_current_invoice() - _current_start_date, _current_end_date = self.update_subscription_period(date=add_days(self.current_invoice_end, 1), return_date=True) + + if not (_current_start_date and _current_end_date): + _current_start_date, _current_end_date = self.update_subscription_period(date=add_days(self.current_invoice_end, 1), return_date=True) if invoice and getdate(_current_start_date) <= getdate(invoice.posting_date) <= getdate(_current_end_date): return True @@ -523,7 +525,9 @@ class Subscription(Document): if getdate() > getdate(self.current_invoice_end) and self.is_prepaid_to_invoice(): self.update_subscription_period(add_days(self.current_invoice_end, 1)) - if not self.is_current_invoice_generated() and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice()): + if not self.is_current_invoice_generated(self.current_invoice_start, self.current_invoice_end) \ + and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice()): + prorate = frappe.db.get_single_value('Subscription Settings', 'prorate') self.generate_invoice(prorate) @@ -560,8 +564,10 @@ class Subscription(Document): self.set_status_grace_period() # Generate invoices periodically even if current invoice are unpaid - if self.generate_new_invoices_past_due_date and not self.is_current_invoice_generated() and (self.is_postpaid_to_invoice() - or self.is_prepaid_to_invoice()): + if self.generate_new_invoices_past_due_date and not \ + self.is_current_invoice_generated(self.current_invoice_start, self.current_invoice_end) \ + and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice()): + prorate = frappe.db.get_single_value('Subscription Settings', 'prorate') self.generate_invoice(prorate) From 7681600b5e6e2421dfdfee39661aaf87d8327e3b Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 2 Nov 2021 17:47:44 +0530 Subject: [PATCH 100/105] fix: test wdv method for indian region --- erpnext/assets/doctype/asset/test_asset.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 170122527a5..f162d9f39dc 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -1030,6 +1030,7 @@ def create_asset(**args): if asset.calculate_depreciation: asset.append("finance_books", { + "finance_book": args.finance_book, "depreciation_method": args.depreciation_method or "Straight Line", "frequency_of_depreciation": args.frequency_of_depreciation or 12, "total_number_of_depreciations": args.total_number_of_depreciations or 5, From 857d87da97d230367e1e5ac1e3fe8baa13b8130a Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Tue, 2 Nov 2021 17:52:45 +0530 Subject: [PATCH 101/105] fix: replaced "=" with "in" for multiple statuses in query #28193 fix: replaced "=" with "in" for multiple statuses in query --- .../purchase_order_analysis/purchase_order_analysis.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index 1b25dd45d2d..a566d568119 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -41,10 +41,13 @@ def get_conditions(filters): if filters.get("from_date") and filters.get("to_date"): conditions += " and po.transaction_date between %(from_date)s and %(to_date)s" - for field in ['company', 'name', 'status']: + for field in ['company', 'name']: if filters.get(field): conditions += f" and po.{field} = %({field})s" + if filters.get('status'): + conditions += " and po.status in %(status)s" + if filters.get('project'): conditions += " and poi.project = %(project)s" From 734b57deec069fc3f573604c60a3157da81eff45 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 2 Nov 2021 18:34:55 +0530 Subject: [PATCH 102/105] fix: Serial Nos not set in the row after scanning in popup - Avoid whitspaces while calculating length of serial nos --- erpnext/selling/sales_common.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index ddd4c4e6a5a..a86e60494e8 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -206,8 +206,10 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran var me = this; var item = frappe.get_doc(cdt, cdn); - if (item.serial_no && item.qty === item.serial_no.split(`\n`).length) { - return; + // check if serial nos entered are as much as qty in row + if (item.serial_no) { + let serial_nos = item.serial_no.split(`\n`).filter(sn => sn.trim()); // filter out whitespaces + if (item.qty === serial_nos.length) return; } if (item.serial_no && !item.batch_no) { From 66348e1a035d98e91cb7228e9bd2b7286c5cc274 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 2 Nov 2021 20:26:40 +0530 Subject: [PATCH 103/105] fix: Error on LDC creation --- .../lower_deduction_certificate/lower_deduction_certificate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py index 7afbc00980c..821e0171bbd 100644 --- a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py +++ b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py @@ -31,7 +31,7 @@ class LowerDeductionCertificate(Document): <= fiscal_year.year_end_date): frappe.throw(_("Valid Upto date not in Fiscal Year {0}").format(frappe.bold(self.fiscal_year))) - def tax_withholding_category(self): + def validate_supplier_against_tax_category(self): duplicate_certificate = frappe.db.get_value('Lower Deduction Certificate', {'supplier': self.supplier, 'tax_withholding_category': self.tax_withholding_category, 'name': ("!=", self.name)}, ['name', 'valid_from', 'valid_upto'], as_dict=True) From c2697bca76318ff4a5839df2dcb4743af2ef196c Mon Sep 17 00:00:00 2001 From: Revant Nandgaonkar Date: Wed, 3 Nov 2021 13:52:37 +0530 Subject: [PATCH 104/105] ci: change GITHUB_PAT to CI_PAT --- .github/workflows/docker-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index 21ba981df44..5b607a99406 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -11,4 +11,4 @@ jobs: - name: curl run: | apk add curl bash - curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: Bearer ${{ secrets.GITHUB_PAT }}" https://api.github.com/repos/frappe/frappe_docker/actions/workflows/build_stable.yml/dispatches -d '{"ref":"main"}' + curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: Bearer ${{ secrets.CI_PAT }}" https://api.github.com/repos/frappe/frappe_docker/actions/workflows/build_stable.yml/dispatches -d '{"ref":"main"}' From cb064b06adb5c82ec021ad2e005102f50da60fef Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Wed, 3 Nov 2021 17:23:57 +0530 Subject: [PATCH 105/105] fix: added job_card_item link in material request (#28222) * fix: added job_card_item links in material request * fix: add no copy to row references --- erpnext/manufacturing/doctype/job_card/job_card.py | 3 ++- .../doctype/material_request/material_request.py | 3 ++- .../material_request_item/material_request_item.json | 12 +++++++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index e1d79be81c4..b3b94071f0b 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -605,7 +605,8 @@ def make_material_request(source_name, target_doc=None): "doctype": "Material Request Item", "field_map": { "required_qty": "qty", - "uom": "stock_uom" + "uom": "stock_uom", + "name": "job_card_item" }, "postprocess": update_item, } diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 17df9777b19..ec01cae2567 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -502,7 +502,8 @@ def make_stock_entry(source_name, target_doc=None): "field_map": { "name": "material_request_item", "parent": "material_request", - "uom": "stock_uom" + "uom": "stock_uom", + "job_card_item": "job_card_item" }, "postprocess": update_item, "condition": lambda doc: doc.ordered_qty < doc.stock_qty diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.json b/erpnext/stock/doctype/material_request_item/material_request_item.json index 25bbbbd4b3e..2bad42a0ebb 100644 --- a/erpnext/stock/doctype/material_request_item/material_request_item.json +++ b/erpnext/stock/doctype/material_request_item/material_request_item.json @@ -52,6 +52,7 @@ "sales_order_item", "production_plan", "material_request_plan_item", + "job_card_item", "col_break4", "expense_account", "section_break_46", @@ -444,16 +445,25 @@ { "fieldname": "qty_info_col_break", "fieldtype": "Column Break" + }, + { + "fieldname": "job_card_item", + "fieldtype": "Data", + "hidden": 1, + "no_copy": 1, + "print_hide": 1, + "label": "Job Card Item" } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-10-02 11:44:36.553064", + "modified": "2021-11-03 14:40:24.409826", "modified_by": "Administrator", "module": "Stock", "name": "Material Request Item", + "naming_rule": "Random", "owner": "Administrator", "permissions": [], "sort_field": "modified",