From de87786db4f742c807edc84a586e467895bd827b Mon Sep 17 00:00:00 2001 From: Sabu Siyad Date: Sat, 11 Feb 2023 12:28:42 +0530 Subject: [PATCH 01/21] fix(ecommerce): throw invalid doctype error in shop by category (#33901) Co-authored-by: Deepesh Garg (cherry picked from commit 0df28c71743e2e91924e2c5062778e2e2e680666) # Conflicts: # erpnext/www/shop-by-category/index.py --- erpnext/www/shop-by-category/index.py | 33 +++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/erpnext/www/shop-by-category/index.py b/erpnext/www/shop-by-category/index.py index 7b493c3c288..cf439c98922 100644 --- a/erpnext/www/shop-by-category/index.py +++ b/erpnext/www/shop-by-category/index.py @@ -51,8 +51,9 @@ def get_tabs(categories): return tab_values -def get_category_records(categories): +def get_category_records(categories: list): categorical_data = {} +<<<<<<< HEAD for category in categories: if category == "item_group": categorical_data["item_group"] = frappe.db.sql( @@ -66,13 +67,28 @@ def get_category_records(categories): and show_in_website = 1 """, as_dict=1, +======= + + for c in categories: + if c == "item_group": + categorical_data["item_group"] = frappe.db.get_all( + "Item Group", + filters={"parent_item_group": "All Item Groups", "show_in_website": 1}, + fields=["name", "parent_item_group", "is_group", "image", "route"], +>>>>>>> 0df28c7174 (fix(ecommerce): throw invalid doctype error in shop by category (#33901)) ) - else: - doctype = frappe.unscrub(category) - fields = ["name"] - if frappe.get_meta(doctype, cached=True).get_field("image"): + + continue + + doctype = frappe.unscrub(c) + fields = ["name"] + + try: + meta = frappe.get_meta(doctype, cached=True) + if meta.get_field("image"): fields += ["image"] +<<<<<<< HEAD categorical_data[category] = frappe.db.sql( f""" Select @@ -82,5 +98,12 @@ def get_category_records(categories): """, as_dict=1, ) +======= + data = frappe.db.get_all(doctype, fields=fields) + categorical_data[c] = data + except BaseException: + frappe.throw(_("DocType {} not found").format(doctype)) + continue +>>>>>>> 0df28c7174 (fix(ecommerce): throw invalid doctype error in shop by category (#33901)) return categorical_data From 2391c37238b55e02afb31c4800fe419825ca0440 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Mon, 6 Feb 2023 13:36:25 +0530 Subject: [PATCH 02/21] fix: update `reserved_qty` when `Sales Order` marked as `Hold` (cherry picked from commit d76759e06690a19dc1a8b4fb201d509cee1b47e9) --- erpnext/stock/stock_balance.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py index 9a7d8bbfe42..c505cae3813 100644 --- a/erpnext/stock/stock_balance.py +++ b/erpnext/stock/stock_balance.py @@ -122,7 +122,7 @@ def get_reserved_qty(item_code, warehouse): and parenttype="Sales Order" and item_code != parent_item and exists (select * from `tabSales Order` so - where name = dnpi_in.parent and docstatus = 1 and status != 'Closed') + where name = dnpi_in.parent and docstatus = 1 and status not in ('On Hold', 'Closed')) ) dnpi) union (select stock_qty as dnpi_qty, qty as so_item_qty, @@ -132,7 +132,7 @@ def get_reserved_qty(item_code, warehouse): and (so_item.delivered_by_supplier is null or so_item.delivered_by_supplier = 0) and exists(select * from `tabSales Order` so where so.name = so_item.parent and so.docstatus = 1 - and so.status != 'Closed')) + and so.status not in ('On Hold', 'Closed'))) ) tab where so_item_qty >= so_item_delivered_qty From 6ae1cc020a0a5555ab5dd5e7fbc26bd74222e6ab Mon Sep 17 00:00:00 2001 From: Sabu Siyad Date: Tue, 14 Feb 2023 19:34:39 +0530 Subject: [PATCH 03/21] resolve conflicts --- erpnext/www/shop-by-category/index.py | 28 --------------------------- 1 file changed, 28 deletions(-) diff --git a/erpnext/www/shop-by-category/index.py b/erpnext/www/shop-by-category/index.py index cf439c98922..219747c9f8a 100644 --- a/erpnext/www/shop-by-category/index.py +++ b/erpnext/www/shop-by-category/index.py @@ -53,21 +53,6 @@ def get_tabs(categories): def get_category_records(categories: list): categorical_data = {} -<<<<<<< HEAD - for category in categories: - if category == "item_group": - categorical_data["item_group"] = frappe.db.sql( - """ - Select - name, parent_item_group, is_group, image, route - from - `tabItem Group` - where - parent_item_group = 'All Item Groups' - and show_in_website = 1 - """, - as_dict=1, -======= for c in categories: if c == "item_group": @@ -75,7 +60,6 @@ def get_category_records(categories: list): "Item Group", filters={"parent_item_group": "All Item Groups", "show_in_website": 1}, fields=["name", "parent_item_group", "is_group", "image", "route"], ->>>>>>> 0df28c7174 (fix(ecommerce): throw invalid doctype error in shop by category (#33901)) ) continue @@ -88,22 +72,10 @@ def get_category_records(categories: list): if meta.get_field("image"): fields += ["image"] -<<<<<<< HEAD - categorical_data[category] = frappe.db.sql( - f""" - Select - {",".join(fields)} - from - `tab{doctype}` - """, - as_dict=1, - ) -======= data = frappe.db.get_all(doctype, fields=fields) categorical_data[c] = data except BaseException: frappe.throw(_("DocType {} not found").format(doctype)) continue ->>>>>>> 0df28c7174 (fix(ecommerce): throw invalid doctype error in shop by category (#33901)) return categorical_data From bc59ea0d55320e2f69db7b8009febbe2e68d4416 Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Tue, 14 Feb 2023 18:33:01 +0530 Subject: [PATCH 04/21] chore: add anand to asset's codeowner (cherry picked from commit d003370f61ef405bd69932005a1c02d35fe947ee) # Conflicts: # CODEOWNERS --- CODEOWNERS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CODEOWNERS b/CODEOWNERS index 6e751cc9aa6..9928f9c983a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -4,8 +4,12 @@ # the repo. Unless a later match takes precedence, erpnext/accounts/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar +<<<<<<< HEAD erpnext/assets/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar erpnext/erpnext_integrations/ @nextchamp-saqib +======= +erpnext/assets/ @anandbaburajan @deepeshgarg007 +>>>>>>> d003370f61 (chore: add anand to asset's codeowner) erpnext/loan_management/ @nextchamp-saqib @deepeshgarg007 erpnext/regional @nextchamp-saqib @deepeshgarg007 @ruthra-kumar erpnext/selling @nextchamp-saqib @deepeshgarg007 @ruthra-kumar From 1223e31e7da79fe2afd8505b08734a139bdb91f2 Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Tue, 14 Feb 2023 20:33:40 +0530 Subject: [PATCH 05/21] Update CODEOWNERS --- CODEOWNERS | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 9928f9c983a..77761dd14e2 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -4,12 +4,8 @@ # the repo. Unless a later match takes precedence, erpnext/accounts/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar -<<<<<<< HEAD -erpnext/assets/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar -erpnext/erpnext_integrations/ @nextchamp-saqib -======= erpnext/assets/ @anandbaburajan @deepeshgarg007 ->>>>>>> d003370f61 (chore: add anand to asset's codeowner) +erpnext/erpnext_integrations/ @nextchamp-saqib erpnext/loan_management/ @nextchamp-saqib @deepeshgarg007 erpnext/regional @nextchamp-saqib @deepeshgarg007 @ruthra-kumar erpnext/selling @nextchamp-saqib @deepeshgarg007 @ruthra-kumar From 117dbe38c45bc56d6972fa6f6f2613367e8e4825 Mon Sep 17 00:00:00 2001 From: HENRY Florian Date: Mon, 13 Feb 2023 16:26:05 +0100 Subject: [PATCH 06/21] fix: should never get cutomer price on purchase document (#34002) * fix: never get cutomer price on purchase document chores: syntax chore: typo in stock_entry get_uom_details (#33998) fix: typo in stock_entry get_uom_details chores: syntax * feat: add test for get_item_detail price list oriented * feat: add test for get_item_detail price price oriented * feat: add test for get_item_detail price price oriented * chore: clean test code (cherry picked from commit 231fe4156f9686eb7dd3b67e9fe0321920655689) --- .../doctype/item_price/test_records.json | 14 +++++++ .../doctype/price_list/test_records.json | 16 ++++++++ erpnext/stock/get_item_details.py | 7 ++++ erpnext/stock/tests/test_get_item_details.py | 40 +++++++++++++++++++ 4 files changed, 77 insertions(+) create mode 100644 erpnext/stock/tests/test_get_item_details.py diff --git a/erpnext/stock/doctype/item_price/test_records.json b/erpnext/stock/doctype/item_price/test_records.json index 0a3d7e81985..afe5ad65b75 100644 --- a/erpnext/stock/doctype/item_price/test_records.json +++ b/erpnext/stock/doctype/item_price/test_records.json @@ -38,5 +38,19 @@ "price_list_rate": 1000, "valid_from": "2017-04-10", "valid_upto": "2017-04-17" + }, + { + "doctype": "Item Price", + "item_code": "_Test Item", + "price_list": "_Test Buying Price List", + "price_list_rate": 100, + "supplier": "_Test Supplier" + }, + { + "doctype": "Item Price", + "item_code": "_Test Item", + "price_list": "_Test Selling Price List", + "price_list_rate": 200, + "customer": "_Test Customer" } ] diff --git a/erpnext/stock/doctype/price_list/test_records.json b/erpnext/stock/doctype/price_list/test_records.json index 7ca949c4026..e02a7adbd8b 100644 --- a/erpnext/stock/doctype/price_list/test_records.json +++ b/erpnext/stock/doctype/price_list/test_records.json @@ -31,5 +31,21 @@ "enabled": 1, "price_list_name": "_Test Price List Rest of the World", "selling": 1 + }, + { + "buying": 0, + "currency": "USD", + "doctype": "Price List", + "enabled": 1, + "price_list_name": "_Test Selling Price List", + "selling": 1 + }, + { + "buying": 1, + "currency": "USD", + "doctype": "Price List", + "enabled": 1, + "price_list_name": "_Test Buying Price List", + "selling": 0 } ] diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 15d77544236..c6243a81dbf 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -88,8 +88,15 @@ def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=Tru update_party_blanket_order(args, out) + # Never try to find a customer price if customer is set in these Doctype + current_customer = args.customer + if args.get("doctype") in ["Purchase Order", "Purchase Receipt", "Purchase Invoice"]: + args.customer = None + out.update(get_price_list_rate(args, item)) + args.customer = current_customer + if args.customer and cint(args.is_pos): out.update(get_pos_profile_item_details(args.company, args, update_data=True)) diff --git a/erpnext/stock/tests/test_get_item_details.py b/erpnext/stock/tests/test_get_item_details.py new file mode 100644 index 00000000000..b53e29e9e8e --- /dev/null +++ b/erpnext/stock/tests/test_get_item_details.py @@ -0,0 +1,40 @@ +import json + +import frappe +from frappe.test_runner import make_test_records +from frappe.tests.utils import FrappeTestCase + +from erpnext.stock.get_item_details import get_item_details + +test_ignore = ["BOM"] +test_dependencies = ["Customer", "Supplier", "Item", "Price List", "Item Price"] + + +class TestGetItemDetail(FrappeTestCase): + def setUp(self): + make_test_records("Price List") + super().setUp() + + def test_get_item_detail_purchase_order(self): + + args = frappe._dict( + { + "item_code": "_Test Item", + "company": "_Test Company", + "customer": "_Test Customer", + "conversion_rate": 1.0, + "price_list_currency": "USD", + "plc_conversion_rate": 1.0, + "doctype": "Purchase Order", + "name": None, + "supplier": "_Test Supplier", + "transaction_date": None, + "conversion_rate": 1.0, + "price_list": "_Test Buying Price List", + "is_subcontracted": 0, + "ignore_pricing_rule": 1, + "qty": 1, + } + ) + details = get_item_details(args) + self.assertEqual(details.get("price_list_rate"), 100) From 4f10f48f7c39c5896f1adab60bcf39aacfd41509 Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Mon, 13 Feb 2023 17:22:54 +0530 Subject: [PATCH 07/21] fix: opening_accumulated_depreciation and precision in charts (cherry picked from commit 47cc8ab6c6e6ad3cf13ba750e9410696be827c23) --- erpnext/assets/doctype/asset/asset.js | 39 ++++++++++--------- erpnext/assets/doctype/asset/asset.py | 8 ++-- .../fixed_asset_register.py | 10 ++--- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index a4a9d6c8bc8..1f158a3a942 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -206,52 +206,53 @@ frappe.ui.form.on('Asset', { return } - var x_intervals = [frm.doc.purchase_date]; + var x_intervals = [frappe.format(frm.doc.purchase_date, { fieldtype: 'Date' })]; var asset_values = [frm.doc.gross_purchase_amount]; - var last_depreciation_date = frm.doc.purchase_date; - - if(frm.doc.opening_accumulated_depreciation) { - last_depreciation_date = frappe.datetime.add_months(frm.doc.next_depreciation_date, - -1*frm.doc.frequency_of_depreciation); - - x_intervals.push(last_depreciation_date); - asset_values.push(flt(frm.doc.gross_purchase_amount) - - flt(frm.doc.opening_accumulated_depreciation)); - } if(frm.doc.calculate_depreciation) { + if(frm.doc.opening_accumulated_depreciation) { + var depreciation_date = frappe.datetime.add_months( + frm.doc.finance_books[0].depreciation_start_date, + -1 * frm.doc.finance_books[0].frequency_of_depreciation + ); + x_intervals.push(frappe.format(depreciation_date, { fieldtype: 'Date' })); + asset_values.push(flt(frm.doc.gross_purchase_amount - frm.doc.opening_accumulated_depreciation, precision('gross_purchase_amount'))); + } + $.each(frm.doc.schedules || [], function(i, v) { - x_intervals.push(v.schedule_date); - var asset_value = flt(frm.doc.gross_purchase_amount) - flt(v.accumulated_depreciation_amount); + x_intervals.push(frappe.format(v.schedule_date, { fieldtype: 'Date' })); + var asset_value = flt(frm.doc.gross_purchase_amount - v.accumulated_depreciation_amount, precision('gross_purchase_amount')); if(v.journal_entry) { - last_depreciation_date = v.schedule_date; asset_values.push(asset_value); } else { if (in_list(["Scrapped", "Sold"], frm.doc.status)) { asset_values.push(null); } else { - asset_values.push(asset_value) + asset_values.push(asset_value); } } }); } else { + if(frm.doc.opening_accumulated_depreciation) { + x_intervals.push(frappe.format(frm.doc.creation.split(" ")[0], { fieldtype: 'Date' })); + asset_values.push(flt(frm.doc.gross_purchase_amount - frm.doc.opening_accumulated_depreciation, precision('gross_purchase_amount'))); + } + let depr_entries = (await frappe.call({ method: "get_manual_depreciation_entries", doc: frm.doc, })).message; $.each(depr_entries || [], function(i, v) { - x_intervals.push(v.posting_date); - last_depreciation_date = v.posting_date; + x_intervals.push(frappe.format(v.posting_date, { fieldtype: 'Date' })); let last_asset_value = asset_values[asset_values.length - 1] asset_values.push(last_asset_value - v.value); }); } if(in_list(["Scrapped", "Sold"], frm.doc.status)) { - x_intervals.push(frm.doc.disposal_date); + x_intervals.push(frappe.format(frm.doc.disposal_date, { fieldtype: 'Date' })); asset_values.push(0); - last_depreciation_date = frm.doc.disposal_date; } frm.dashboard.render_graph({ diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 549241b8f10..9033710b324 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -683,14 +683,16 @@ class Asset(AccountsController): def get_value_after_depreciation(self, finance_book=None): if not self.calculate_depreciation: - return self.value_after_depreciation + return flt(self.value_after_depreciation, self.precision("gross_purchase_amount")) if not finance_book: - return self.get("finance_books")[0].value_after_depreciation + return flt( + self.get("finance_books")[0].value_after_depreciation, self.precision("gross_purchase_amount") + ) for row in self.get("finance_books"): if finance_book == row.finance_book: - return row.value_after_depreciation + return flt(row.value_after_depreciation, self.precision("gross_purchase_amount")) def get_default_finance_book_idx(self): if not self.get("default_finance_book") and self.company: 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 5bfd4840d73..0ab3e16e424 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -5,7 +5,7 @@ import frappe from frappe import _ from frappe.query_builder.functions import Sum -from frappe.utils import cstr, formatdate, getdate +from frappe.utils import cstr, flt, formatdate, getdate from erpnext.accounts.report.financial_statements import ( get_fiscal_year_data, @@ -102,13 +102,9 @@ def get_data(filters): ] assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields) - finance_book_filter = ("is", "not set") - if filters.finance_book: - finance_book_filter = ("=", filters.finance_book) - assets_linked_to_fb = frappe.db.get_all( doctype="Asset Finance Book", - filters={"finance_book": finance_book_filter}, + filters={"finance_book": filters.finance_book or ("is", "not set")}, pluck="parent", ) @@ -194,7 +190,7 @@ def get_depreciation_amount_of_asset(asset, depreciation_amount_map, filters): else: depr_amount = get_manual_depreciation_amount_of_asset(asset, filters) - return depr_amount + return flt(depr_amount, 2) def get_finance_book_value_map(filters): From ff2e617c0c4a6e7ef2665692cfb66ed92ba3ce36 Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Mon, 13 Feb 2023 17:28:46 +0530 Subject: [PATCH 08/21] chore: break look if je processed (cherry picked from commit a220dc0c9c694a9213189b96be8eb9431457280c) --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index a8b8ec6d12e..c50336e1e61 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -346,6 +346,8 @@ class JournalEntry(AccountsController): finance_books.db_update() asset.set_status() + + break else: depr_value = d.debit or d.credit From 6227c16374587975371179cb2ee202a0691b06eb Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Tue, 14 Feb 2023 17:54:51 +0530 Subject: [PATCH 09/21] fix: asset_depreciation_and_balances report doesn't reflect manual depr entries (cherry picked from commit 1535c3d856a50c31ed1e2adaf022ca1a221aab9b) --- .../asset_depreciations_and_balances.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index ad9b1ba58eb..9cea37c4f80 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -135,6 +135,34 @@ def get_assets(filters): where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent and ifnull(ds.journal_entry, '') != '' group by a.asset_category union + SELECT a.asset_category, + ifnull(sum(case when gle.posting_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then + gle.debit + else + 0 + end), 0) as accumulated_depreciation_as_on_from_date, + ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s + and a.disposal_date <= %(to_date)s and gle.posting_date <= a.disposal_date then + gle.debit + else + 0 + end), 0) as depreciation_eliminated_during_the_period, + ifnull(sum(case when gle.posting_date >= %(from_date)s and gle.posting_date <= %(to_date)s + and (ifnull(a.disposal_date, 0) = 0 or gle.posting_date <= a.disposal_date) then + gle.debit + else + 0 + end), 0) as depreciation_amount_during_the_period + from `tabGL Entry` gle + join `tabAsset` a on + gle.against_voucher = a.name + join `tabAsset Category Account` aca on + aca.parent = a.asset_category and aca.company_name = %(company)s + join `tabCompany` company on + company.name = %(company)s + where a.docstatus=1 and a.company=%(company)s and a.calculate_depreciation=0 and a.purchase_date <= %(to_date)s and gle.debit != 0 and gle.is_cancelled = 0 and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account) + group by a.asset_category + union SELECT a.asset_category, ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then 0 From 922b30a5663e9ac74619ece488844b32397fb792 Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Fri, 17 Feb 2023 15:32:55 +0530 Subject: [PATCH 10/21] fix: asset repair status after deletion and asset status after manual depr entry (cherry picked from commit 03f07a20e7ae042f840351728f7625525a8b3ef2) # Conflicts: # erpnext/assets/doctype/asset/asset.py --- .../doctype/journal_entry/journal_entry.py | 22 ++++-------- erpnext/assets/doctype/asset/asset.js | 2 +- erpnext/assets/doctype/asset/asset.py | 34 ++++++++++++++++--- erpnext/assets/doctype/asset/depreciation.py | 2 +- .../doctype/asset_repair/asset_repair.py | 3 ++ 5 files changed, 42 insertions(+), 21 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index c50336e1e61..9ea93f90e03 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -248,21 +248,16 @@ class JournalEntry(AccountsController): ): processed_assets.append(d.reference_name) - asset = frappe.db.get_value( - "Asset", d.reference_name, ["calculate_depreciation", "value_after_depreciation"], as_dict=1 - ) + asset = frappe.get_doc("Asset", d.reference_name) if asset.calculate_depreciation: continue depr_value = d.debit or d.credit - frappe.db.set_value( - "Asset", - d.reference_name, - "value_after_depreciation", - asset.value_after_depreciation - depr_value, - ) + asset.db_set("value_after_depreciation", asset.value_after_depreciation - depr_value) + + asset.set_status() def update_inter_company_jv(self): if ( @@ -351,12 +346,9 @@ class JournalEntry(AccountsController): else: depr_value = d.debit or d.credit - frappe.db.set_value( - "Asset", - d.reference_name, - "value_after_depreciation", - asset.value_after_depreciation + depr_value, - ) + asset.db_set("value_after_depreciation", asset.value_after_depreciation + depr_value) + + asset.set_status() def unlink_inter_company_jv(self): if ( diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 1f158a3a942..ccd8d8171a8 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -246,7 +246,7 @@ frappe.ui.form.on('Asset', { $.each(depr_entries || [], function(i, v) { x_intervals.push(frappe.format(v.posting_date, { fieldtype: 'Date' })); let last_asset_value = asset_values[asset_values.length - 1] - asset_values.push(last_asset_value - v.value); + asset_values.push(flt(last_asset_value - v.value, precision('gross_purchase_amount'))); }); } diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 9033710b324..750ed3fa67e 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -667,11 +667,15 @@ class Asset(AccountsController): if self.journal_entry_for_scrap: status = "Scrapped" - elif self.finance_books: - idx = self.get_default_finance_book_idx() or 0 + else: + expected_value_after_useful_life = 0 + value_after_depreciation = self.value_after_depreciation - expected_value_after_useful_life = self.finance_books[idx].expected_value_after_useful_life - value_after_depreciation = self.finance_books[idx].value_after_depreciation + if self.calculate_depreciation: + idx = self.get_default_finance_book_idx() or 0 + + expected_value_after_useful_life = self.finance_books[idx].expected_value_after_useful_life + value_after_depreciation = self.finance_books[idx].value_after_depreciation if flt(value_after_depreciation) <= expected_value_after_useful_life: status = "Fully Depreciated" @@ -838,6 +842,28 @@ class Asset(AccountsController): self.db_set("booked_fixed_asset", 1) @frappe.whitelist() +<<<<<<< HEAD +======= + def get_manual_depreciation_entries(self): + (_, _, depreciation_expense_account) = get_depreciation_accounts(self) + + gle = frappe.qb.DocType("GL Entry") + + records = ( + frappe.qb.from_(gle) + .select(gle.voucher_no.as_("name"), gle.debit.as_("value"), gle.posting_date) + .where(gle.against_voucher == self.name) + .where(gle.account == depreciation_expense_account) + .where(gle.debit != 0) + .where(gle.is_cancelled == 0) + .orderby(gle.posting_date) + .orderby(gle.creation) + ).run(as_dict=True) + + return records + + @frappe.whitelist() +>>>>>>> 03f07a20e7 (fix: asset repair status after deletion and asset status after manual depr entry) def get_depreciation_rate(self, args, on_validate=False): if isinstance(args, string_types): args = json.loads(args) diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index e83b9b2080a..fc3af44947d 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -144,7 +144,7 @@ def make_depreciation_entry(asset_name, date=None): finance_books.value_after_depreciation -= d.depreciation_amount finance_books.db_update() - frappe.db.set_value("Asset", asset_name, "depr_entry_posting_status", "Successful") + asset.db_set("depr_entry_posting_status", "Successful") asset.set_status() diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 94b4c641333..cd8fe5b18b7 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -82,6 +82,9 @@ class AssetRepair(AccountsController): self.asset_doc.prepare_depreciation_data() self.asset_doc.save() + def after_delete(self): + frappe.get_doc("Asset", self.asset).set_status() + def check_repair_status(self): if self.repair_status == "Pending": frappe.throw(_("Please update Repair Status.")) From 3585b90ce5fda75d40260be99ffb13874985a137 Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Fri, 17 Feb 2023 16:29:09 +0530 Subject: [PATCH 11/21] chore: fixing conflict --- erpnext/assets/doctype/asset/asset.py | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 750ed3fa67e..2bd30c6cbd4 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -707,24 +707,6 @@ class Asset(AccountsController): if d.finance_book == self.default_finance_book: return cint(d.idx) - 1 - @frappe.whitelist() - def get_manual_depreciation_entries(self): - (_, _, depreciation_expense_account) = get_depreciation_accounts(self) - - gle = frappe.qb.DocType("GL Entry") - - records = ( - frappe.qb.from_(gle) - .select(gle.voucher_no.as_("name"), gle.debit.as_("value"), gle.posting_date) - .where(gle.against_voucher == self.name) - .where(gle.account == depreciation_expense_account) - .where(gle.debit != 0) - .where(gle.is_cancelled == 0) - .orderby(gle.posting_date) - ).run(as_dict=True) - - return records - def validate_make_gl_entry(self): purchase_document = self.get_purchase_document() if not purchase_document: @@ -842,8 +824,6 @@ class Asset(AccountsController): self.db_set("booked_fixed_asset", 1) @frappe.whitelist() -<<<<<<< HEAD -======= def get_manual_depreciation_entries(self): (_, _, depreciation_expense_account) = get_depreciation_accounts(self) @@ -863,7 +843,6 @@ class Asset(AccountsController): return records @frappe.whitelist() ->>>>>>> 03f07a20e7 (fix: asset repair status after deletion and asset status after manual depr entry) def get_depreciation_rate(self, args, on_validate=False): if isinstance(args, string_types): args = json.loads(args) From 2408966090d2b341053e2247c481d4f08d05a77e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 19 Feb 2023 11:37:44 +0530 Subject: [PATCH 12/21] fix: Amount for debit and credit notes with 0 qty line items (backport #33902) (#34123) fix: Amount for debit and credit notes with 0 qty line items (#33902) (cherry picked from commit 47c91324b13217db83670c97a6e0488ae78b8b14) Co-authored-by: Deepesh Garg --- erpnext/public/js/controllers/taxes_and_totals.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 92796073be7..be4eab03386 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -119,7 +119,16 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ frappe.model.round_floats_in(item); item.net_rate = item.rate; item.qty = item.qty === undefined ? (me.frm.doc.is_return ? -1 : 1) : item.qty; - item.net_amount = item.amount = flt(item.rate * item.qty, precision("amount", item)); + + if (!(me.frm.doc.is_return || me.frm.doc.is_debit_note)) { + item.net_amount = item.amount = flt(item.rate * item.qty, precision("amount", item)); + } + else { + let qty = item.qty || 1; + qty = me.frm.doc.is_return ? -1 * qty : qty; + item.net_amount = item.amount = flt(item.rate * qty, precision("amount", item)); + } + item.item_tax_amount = 0.0; item.total_weight = flt(item.weight_per_unit * item.stock_qty); From 495d1b2548f233d0b97fc287118ce49d861d61a8 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Mon, 20 Feb 2023 11:14:41 +0530 Subject: [PATCH 13/21] fix(ux): `ReferenceError: me is not defined` Delivery Note (cherry picked from commit 1b010add265cdc3e646fc15d78c3c39428c4ad84) --- erpnext/stock/doctype/delivery_note/delivery_note.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js index cb4161f3f1d..ecfae4bf5f8 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note.js @@ -97,12 +97,12 @@ frappe.ui.form.on("Delivery Note", { } if (frm.doc.docstatus == 1 && !frm.doc.inter_company_reference) { - let internal = me.frm.doc.is_internal_customer; + let internal = frm.doc.is_internal_customer; if (internal) { - let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Receipt" : + let button_label = (frm.doc.company === frm.doc.represents_company) ? "Internal Purchase Receipt" : "Inter Company Purchase Receipt"; - me.frm.add_custom_button(button_label, function() { + frm.add_custom_button(button_label, function() { frappe.model.open_mapped_doc({ method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt', frm: frm, From bdefd700afab28942f51e98ca34d25a1d49603f0 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Mon, 20 Feb 2023 11:43:32 +0530 Subject: [PATCH 14/21] chore: `Linters` (cherry picked from commit 44ee9f0f190f6761ca92f4c5036109c152bd35db) --- erpnext/stock/doctype/delivery_note/delivery_note.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js index ecfae4bf5f8..120161e3f72 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note.js @@ -102,7 +102,7 @@ frappe.ui.form.on("Delivery Note", { let button_label = (frm.doc.company === frm.doc.represents_company) ? "Internal Purchase Receipt" : "Inter Company Purchase Receipt"; - frm.add_custom_button(button_label, function() { + frm.add_custom_button(__(button_label), function() { frappe.model.open_mapped_doc({ method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt', frm: frm, From ccd25684f9b7a3e47269a5437da4918058cd2b5b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 17 Feb 2023 15:26:01 +0530 Subject: [PATCH 15/21] feat(UX): Add option to disable consolidating leave types in balance reports --- .../employee_leave_balance.js | 67 ++++++++++--------- .../employee_leave_balance.py | 35 +++++----- 2 files changed, 57 insertions(+), 45 deletions(-) diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js index 2339350d065..62cc7385560 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js @@ -2,53 +2,60 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Employee Leave Balance"] = { - "filters": [ + filters: [ { - "fieldname": "from_date", - "label": __("From Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.defaults.get_default("year_start_date") + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + reqd: 1, + default: frappe.defaults.get_default("year_start_date") }, { - "fieldname": "to_date", - "label": __("To Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.defaults.get_default("year_end_date") + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + reqd: 1, + default: frappe.defaults.get_default("year_end_date") }, { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_user_default("Company") + label: __("Company"), + fieldname: "company", + fieldtype: "Link", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company") }, { - "fieldname": "department", - "label": __("Department"), - "fieldtype": "Link", - "options": "Department", + fieldname: "department", + label: __("Department"), + fieldtype: "Link", + options: "Department", }, { - "fieldname": "employee", - "label": __("Employee"), - "fieldtype": "Link", - "options": "Employee", + fieldname: "employee", + label: __("Employee"), + fieldtype: "Link", + options: "Employee", }, { - "fieldname": "employee_status", - "label": __("Employee Status"), - "fieldtype": "Select", - "options": [ + fieldname: "employee_status", + label: __("Employee Status"), + fieldtype: "Select", + options: [ "", { "value": "Active", "label": __("Active") }, { "value": "Inactive", "label": __("Inactive") }, { "value": "Suspended", "label": __("Suspended") }, { "value": "Left", "label": __("Left") }, ], - "default": "Active", + default: "Active", + }, + { + fieldname: "consolidate_leave_types", + label: __("Consolidate Leave Types"), + fieldtype: "Check", + default: 1, + depends_on: "eval: !doc.employee", } ], diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index fccf722939e..93699f0b4df 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -24,7 +24,7 @@ def execute(filters: Optional[Filters] = None) -> Tuple: columns = get_columns() data = get_data(filters) - charts = get_chart_data(data) + charts = get_chart_data(data, filters) return columns, data, None, charts @@ -89,7 +89,7 @@ def get_data(filters: Filters) -> List: conditions = get_conditions(filters) user = frappe.session.user - department_approver_map = get_department_leave_approver_map(filters.get("department")) + department_approver_map = get_department_leave_approver_map(filters.department) active_employees = frappe.get_list( "Employee", @@ -97,22 +97,27 @@ def get_data(filters: Filters) -> List: fields=["name", "employee_name", "department", "user_id", "leave_approver"], ) + precision = cint(frappe.db.get_single_value("System Settings", "float_precision", cache=True)) + consolidate_leave_types = len(active_employees) > 1 and filters.consolidate_leave_types + row = None + data = [] for leave_type in leave_types: - if len(active_employees) > 1: + if consolidate_leave_types: data.append({"leave_type": leave_type}) else: row = frappe._dict({"leave_type": leave_type}) for employee in active_employees: - leave_approvers = department_approver_map.get(employee.department_name, []).append( employee.leave_approver ) - if len(active_employees) > 1: + if consolidate_leave_types: row = frappe._dict() + else: + row = frappe._dict({"leave_type": leave_type}) row.employee = employee.name row.employee_name = employee.employee_name @@ -166,17 +171,17 @@ def get_opening_balance( def get_conditions(filters: Filters) -> Dict: conditions = {} - if filters.get("employee"): - conditions["name"] = filters.get("employee") + if filters.employee: + conditions["name"] = filters.employee - if filters.get("company"): - conditions["company"] = filters.get("company") + if filters.company: + conditions["company"] = filters.company - if filters.get("department"): - conditions["department"] = filters.get("department") + if filters.department: + conditions["department"] = filters.department - if filters.get("employee_status"): - conditions["status"] = filters.get("employee_status") + if filters.employee_status: + conditions["status"] = filters.employee_status return conditions @@ -268,12 +273,12 @@ def get_leave_ledger_entries( return records -def get_chart_data(data: List) -> Dict: +def get_chart_data(data: List, filters: Filters) -> Dict: labels = [] datasets = [] employee_data = data - if data and data[0].get("employee_name"): + if data and filters.employee: get_dataset_for_chart(employee_data, datasets, labels) chart = { From acdf7fd8dfd4e69abda2405394f414740777a761 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 17 Feb 2023 16:07:29 +0530 Subject: [PATCH 16/21] fix: don't get chart data if data is empty --- .../hr/report/employee_leave_balance/employee_leave_balance.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index 93699f0b4df..660f33cc4c2 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -278,6 +278,9 @@ def get_chart_data(data: List, filters: Filters) -> Dict: datasets = [] employee_data = data + if not data: + return None + if data and filters.employee: get_dataset_for_chart(employee_data, datasets, labels) From 8add12d5686235781dc4eb8631c786acc51a0916 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 21 Feb 2023 12:33:47 +0530 Subject: [PATCH 17/21] fix: add missing import --- .../hr/report/employee_leave_balance/employee_leave_balance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index 660f33cc4c2..192b255f3e0 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -7,7 +7,7 @@ from typing import Dict, List, Optional, Tuple import frappe from frappe import _ -from frappe.utils import add_days, getdate +from frappe.utils import add_days, cint, getdate from erpnext.hr.doctype.leave_allocation.leave_allocation import get_previous_allocation from erpnext.hr.doctype.leave_application.leave_application import ( From 1fb3a281288b9e84caacbfcad53e346507783185 Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Tue, 21 Feb 2023 13:04:01 +0530 Subject: [PATCH 18/21] fix: fiscal year error for existing assets in fixed asset register (cherry picked from commit 76861eb332061c2e42c75e5ba42f01128024808a) --- .../assets/report/fixed_asset_register/fixed_asset_register.py | 1 + 1 file changed, 1 insertion(+) 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 0ab3e16e424..ae99c491a93 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -151,6 +151,7 @@ def prepare_chart_data(data, filters): filters.filter_based_on, "Monthly", company=filters.company, + ignore_fiscal_year=True, ) for d in period_list: From 26ed460a4f0c21d7651f01d541c0cef8d1dcba4d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 21 Feb 2023 14:23:16 +0530 Subject: [PATCH 19/21] fix: Use normal rounding for Tax Withholding Category (#34114) fix: Use normal rounding for Tax Withholding Category (#34114) (cherry picked from commit 35cdd996a9e4281ed269655ff5d9e22e866e199e) Co-authored-by: Deepesh Garg --- .../tax_withholding_category.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 08ca96afb9f..ea3ec5fc606 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -256,7 +256,7 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N tax_amount = get_tcs_amount(parties, inv, tax_details, vouchers, advance_vouchers) if cint(tax_details.round_off_tax_amount): - tax_amount = round(tax_amount) + tax_amount = normal_round(tax_amount) return tax_amount, tax_deducted, tax_deducted_on_advances, voucher_wise_amount @@ -555,3 +555,20 @@ def is_valid_certificate( valid = True return valid + + +def normal_round(number): + """ + Rounds a number to the nearest integer. + :param number: The number to round. + """ + decimal_part = number - int(number) + + if decimal_part >= 0.5: + decimal_part = 1 + else: + decimal_part = 0 + + number = int(number) + decimal_part + + return number From 9826245d8a8428c095f89f0e553bc0cf23d405d5 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 21 Feb 2023 15:06:21 +0530 Subject: [PATCH 20/21] fix: Filters in item-wise sales history report (#34145) fix: Filters in item-wise sales history report (#34145) (cherry picked from commit c88444a6c488369dfbfffdd71e76d5125afa2226) Co-authored-by: Deepesh Garg --- .../report/item_wise_sales_history/item_wise_sales_history.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py index aa9ccc06099..fcfd77c0182 100644 --- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py +++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py @@ -216,7 +216,7 @@ def get_sales_order_details(company_list, filters): ) if filters.get("item_group"): - query = query.where(db_so_item.item_group == frappe.db.escape(filters.item_group)) + query = query.where(db_so_item.item_group == filters.item_group) if filters.get("from_date"): query = query.where(db_so.transaction_date >= filters.from_date) @@ -225,7 +225,7 @@ def get_sales_order_details(company_list, filters): query = query.where(db_so.transaction_date <= filters.to_date) if filters.get("item_code"): - query = query.where(db_so_item.item_group == frappe.db.escape(filters.item_code)) + query = query.where(db_so_item.item_code == filters.item_code) if filters.get("customer"): query = query.where(db_so.customer == filters.customer) From 92da1ed3c27e78784fe1e383690510dee4c72d38 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 16 Feb 2023 20:42:45 +0530 Subject: [PATCH 21/21] fix: check for duplicate in pos closing and pos merge log entry (cherry picked from commit 47add0b7514b701ef43da810986c73c2852d6b97) --- .../pos_closing_entry/pos_closing_entry.py | 16 ++++++++++++++++ .../pos_invoice_merge_log.py | 18 ++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py index 655c4ec0035..115b415eeda 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py @@ -21,8 +21,24 @@ class POSClosingEntry(StatusUpdater): if frappe.db.get_value("POS Opening Entry", self.pos_opening_entry, "status") != "Open": frappe.throw(_("Selected POS Opening Entry should be open."), title=_("Invalid Opening Entry")) + self.validate_duplicate_pos_invoices() self.validate_pos_invoices() + def validate_duplicate_pos_invoices(self): + pos_occurences = {} + for idx, inv in enumerate(self.pos_transactions, 1): + pos_occurences.setdefault(inv.pos_invoice, []).append(idx) + + error_list = [] + for key, value in pos_occurences.items(): + if len(value) > 1: + error_list.append( + _("{} is added multiple times on rows: {}".format(frappe.bold(key), frappe.bold(value))) + ) + + if error_list: + frappe.throw(error_list, title=_("Duplicate POS Invoices found"), as_list=True) + def validate_pos_invoices(self): invalid_rows = [] for d in self.pos_transactions: 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 fc356f2378d..3cfffc8d368 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 @@ -18,6 +18,22 @@ class POSInvoiceMergeLog(Document): def validate(self): self.validate_customer() self.validate_pos_invoice_status() + self.validate_duplicate_pos_invoices() + + def validate_duplicate_pos_invoices(self): + pos_occurences = {} + for idx, inv in enumerate(self.pos_invoices, 1): + pos_occurences.setdefault(inv.pos_invoice, []).append(idx) + + error_list = [] + for key, value in pos_occurences.items(): + if len(value) > 1: + error_list.append( + _("{} is added multiple times on rows: {}".format(frappe.bold(key), frappe.bold(value))) + ) + + if error_list: + frappe.throw(error_list, title=_("Duplicate POS Invoices found"), as_list=True) def validate_customer(self): if self.merge_invoices_based_on == "Customer Group": @@ -427,6 +443,8 @@ def create_merge_logs(invoice_by_customer, closing_entry=None): if closing_entry: closing_entry.set_status(update=True, status="Failed") + if type(error_message) == list: + error_message = frappe.json.dumps(error_message) closing_entry.db_set("error_message", error_message) raise