From 53208dfb3e67fd18626a447826a3152624c0287f Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 8 Jan 2024 15:50:50 +0530 Subject: [PATCH 01/50] fix: fetch name for fy (cherry picked from commit d0ea598cdfb203882fca887d2ba34938c4c75b8e) --- erpnext/accounts/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index a34282eef1..af1c3c6d10 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1268,7 +1268,7 @@ def parse_naming_series_variable(doc, variable): else: date = getdate() company = None - return get_fiscal_year(date=date, company=company)[0] + return get_fiscal_year(date=date, company=company).name @frappe.whitelist() From 2448ba6bc4da574907101b4d6b9ebd79e9a1d8d9 Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Mon, 1 Jan 2024 00:23:19 +0530 Subject: [PATCH 02/50] fix: asset WDV depreciation calc according to IT act (cherry picked from commit 026824880daa16bcdbd1c28fd61d59adad8d10c0) --- erpnext/assets/doctype/asset/depreciation.py | 8 +++ .../asset_depreciation_schedule.py | 64 +++++++++++-------- .../asset_finance_book.json | 3 +- 3 files changed, 48 insertions(+), 27 deletions(-) diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index 66930c0e7c..dbb18b543f 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -19,6 +19,7 @@ from frappe.utils import ( ) from frappe.utils.user import get_users_with_role +import erpnext from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_checks_for_pl_and_bs_accounts, ) @@ -522,6 +523,13 @@ def depreciate_asset(asset_doc, date, notes): make_depreciation_entry_for_all_asset_depr_schedules(asset_doc, date) + cancel_depreciation_entries(asset_doc, date) + + +@erpnext.allow_regional +def cancel_depreciation_entries(asset_doc, date): + pass + def reset_depreciation_schedule(asset_doc, date, notes): if not asset_doc.calculate_depreciation: diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py index 4c94be5320..74d2aa71ff 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -341,10 +341,7 @@ class AssetDepreciationSchedule(Document): n == 0 and (has_pro_rata or has_wdv_or_dd_non_yearly_pro_rata) and not self.opening_accumulated_depreciation - and get_updated_rate_of_depreciation_for_wdv_and_dd( - asset_doc, value_after_depreciation, row, False - ) - == row.rate_of_depreciation + and not self.flags.wdv_it_act_applied ): from_date = add_days( asset_doc.available_for_use_date, -1 @@ -596,26 +593,17 @@ def get_depreciation_amount( asset_depr_schedule, asset, fb_row, schedule_idx, number_of_pending_depreciations ) else: - rate_of_depreciation = get_updated_rate_of_depreciation_for_wdv_and_dd( - asset, depreciable_value, fb_row - ) return get_wdv_or_dd_depr_amount( + asset, + fb_row, depreciable_value, - rate_of_depreciation, - fb_row.frequency_of_depreciation, schedule_idx, prev_depreciation_amount, has_wdv_or_dd_non_yearly_pro_rata, + asset_depr_schedule, ) -@erpnext.allow_regional -def get_updated_rate_of_depreciation_for_wdv_and_dd( - asset, depreciable_value, fb_row, show_msg=True -): - return fb_row.rate_of_depreciation - - def get_straight_line_or_manual_depr_amount( asset_depr_schedule, asset, row, schedule_idx, number_of_pending_depreciations ): @@ -751,30 +739,56 @@ def get_asset_shift_factors_map(): return dict(frappe.db.get_all("Asset Shift Factor", ["shift_name", "shift_factor"], as_list=True)) +@erpnext.allow_regional def get_wdv_or_dd_depr_amount( + asset, + fb_row, depreciable_value, - rate_of_depreciation, - frequency_of_depreciation, schedule_idx, prev_depreciation_amount, has_wdv_or_dd_non_yearly_pro_rata, + asset_depr_schedule, ): - if cint(frequency_of_depreciation) == 12: - return flt(depreciable_value) * (flt(rate_of_depreciation) / 100) + return get_default_wdv_or_dd_depr_amount( + asset, + fb_row, + depreciable_value, + schedule_idx, + prev_depreciation_amount, + has_wdv_or_dd_non_yearly_pro_rata, + asset_depr_schedule, + ) + + +def get_default_wdv_or_dd_depr_amount( + asset, + fb_row, + depreciable_value, + schedule_idx, + prev_depreciation_amount, + has_wdv_or_dd_non_yearly_pro_rata, + asset_depr_schedule, +): + if cint(fb_row.frequency_of_depreciation) == 12: + return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100) else: if has_wdv_or_dd_non_yearly_pro_rata: if schedule_idx == 0: - return flt(depreciable_value) * (flt(rate_of_depreciation) / 100) - elif schedule_idx % (12 / cint(frequency_of_depreciation)) == 1: + return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100) + elif schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 1: return ( - flt(depreciable_value) * flt(frequency_of_depreciation) * (flt(rate_of_depreciation) / 1200) + flt(depreciable_value) + * flt(fb_row.frequency_of_depreciation) + * (flt(fb_row.rate_of_depreciation) / 1200) ) else: return prev_depreciation_amount else: - if schedule_idx % (12 / cint(frequency_of_depreciation)) == 0: + if schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 0: return ( - flt(depreciable_value) * flt(frequency_of_depreciation) * (flt(rate_of_depreciation) / 1200) + flt(depreciable_value) + * flt(fb_row.frequency_of_depreciation) + * (flt(fb_row.rate_of_depreciation) / 1200) ) else: return prev_depreciation_amount diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json index 25ae7a492c..ba5b5f8782 100644 --- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json +++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json @@ -94,7 +94,6 @@ }, { "default": "0", - "depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"", "fieldname": "daily_prorata_based", "fieldtype": "Check", "label": "Depreciate based on daily pro-rata" @@ -110,7 +109,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-11-29 00:57:07.579777", + "modified": "2023-12-29 08:49:39.876439", "modified_by": "Administrator", "module": "Assets", "name": "Asset Finance Book", From 5dc2e80987f2d8fea0513f05bcd5006f5d13da08 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 9 Jan 2024 15:57:38 +0530 Subject: [PATCH 03/50] fix: Ignore default payment terms template for opening invoices (cherry picked from commit 53bf44d2b870aad620c07fc72007c1a51e4eca9f) --- erpnext/controllers/accounts_controller.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 7986c3d480..9a6044f8d7 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1947,7 +1947,7 @@ class AccountsController(TransactionBase): self.remove(item) def set_payment_schedule(self): - if self.doctype == "Sales Invoice" and self.is_pos: + if (self.doctype == "Sales Invoice" and self.is_pos) or self.get("is_opening") == "Yes": self.payment_terms_template = "" return @@ -2130,7 +2130,7 @@ class AccountsController(TransactionBase): ) def validate_payment_schedule_amount(self): - if self.doctype == "Sales Invoice" and self.is_pos: + if (self.doctype == "Sales Invoice" and self.is_pos) or self.get("is_opening") == "Yes": return party_account_currency = self.get("party_account_currency") From d882305c72fbe10f2c096a52345128a32e1ac019 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 10 Jan 2024 22:18:16 +0530 Subject: [PATCH 04/50] fix: performance issue related to stock entry (backport #39301) (#39303) fix: performance issue related to stock entry (#39301) (cherry picked from commit c67b0a3a6408075785211da20603fbcd829825bb) Co-authored-by: rohitwaghchaure --- .../manufacturing/doctype/production_plan/production_plan.js | 2 +- .../manufacturing/doctype/production_plan/production_plan.py | 4 ++-- .../doctype/production_plan/test_production_plan.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index cd92263543..c9c474db7f 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -173,7 +173,7 @@ frappe.ui.form.on('Production Plan', { method: "set_status", freeze: true, doc: frm.doc, - args: {close : close}, + args: {close : close, update_bin: true}, callback: function() { frm.reload_doc(); } diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 3943b13b82..51658a03a7 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -579,7 +579,7 @@ class ProductionPlan(Document): frappe.delete_doc("Work Order", d.name) @frappe.whitelist() - def set_status(self, close=None): + def set_status(self, close=None, update_bin=False): self.status = {0: "Draft", 1: "Submitted", 2: "Cancelled"}.get(self.docstatus) if close: @@ -599,7 +599,7 @@ class ProductionPlan(Document): if close is not None: self.db_set("status", self.status) - if self.docstatus == 1 and self.status != "Completed": + if update_bin and self.docstatus == 1 and self.status != "Completed": self.update_bin_qty() def update_ordered_status(self): diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index fedeb7a477..1c748a809b 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -1486,14 +1486,14 @@ class TestProductionPlan(FrappeTestCase): before_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")) pln.reload() - pln.set_status(close=True) + pln.set_status(close=True, update_bin=True) bin_name = get_or_make_bin(rm_item, rm_warehouse) after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")) self.assertAlmostEqual(after_qty, before_qty - 10) pln.reload() - pln.set_status(close=False) + pln.set_status(close=False, update_bin=True) bin_name = get_or_make_bin(rm_item, rm_warehouse) after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")) From 7ca8e49b389b68e07c81fcf8e38aa830767fd04a Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 10 Jan 2024 20:26:51 +0530 Subject: [PATCH 05/50] fix: circular dependency error while deleting QC (cherry picked from commit 7cc324e31ebf533b0ffde8a2a3c4013756959d7a) --- .../quality_inspection/quality_inspection.py | 3 +++ .../test_quality_inspection.py | 27 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py index 4464ea820e..8816341065 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -111,6 +111,9 @@ class QualityInspection(Document): def on_cancel(self): self.update_qc_reference() + def on_trash(self): + self.update_qc_reference() + def validate_readings_status_mandatory(self): for reading in self.readings: if not reading.status: diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py index f5f8c3afd1..c423ca84ff 100644 --- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py @@ -250,6 +250,33 @@ class TestQualityInspection(FrappeTestCase): qa.delete() dn.delete() + def test_delete_quality_inspection_linked_with_stock_entry(self): + item_code = create_item("_Test Cicuular Dependecy Item with QA").name + + se = make_stock_entry( + item_code=item_code, target="_Test Warehouse - _TC", qty=1, basic_rate=100, do_not_submit=True + ) + + se.inspection_required = 1 + se.save() + + qa = create_quality_inspection( + item_code=item_code, reference_type="Stock Entry", reference_name=se.name, do_not_submit=True + ) + + se.reload() + se.items[0].quality_inspection = qa.name + se.save() + + qa.delete() + + se.reload() + + qc = se.items[0].quality_inspection + self.assertFalse(qc) + + se.delete() + def create_quality_inspection(**args): args = frappe._dict(args) From 758b9b8a81d018933702b1929b6041613b0922b7 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 8 Jan 2024 18:17:01 +0530 Subject: [PATCH 06/50] fix: incorrect percentage received in purchase invoice (cherry picked from commit 8d2c78867e1d563aafa740b3dc5f8a4fae68e52f) --- .../doctype/purchase_invoice/purchase_invoice.py | 12 ++++++++++++ .../doctype/purchase_receipt/purchase_receipt.py | 1 + 2 files changed, 13 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index aa52600a88..cb0b8e1fb1 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -296,6 +296,18 @@ class PurchaseInvoice(BuyingController): self.reset_default_field_value("set_warehouse", "items", "warehouse") self.reset_default_field_value("rejected_warehouse", "items", "rejected_warehouse") self.reset_default_field_value("set_from_warehouse", "items", "from_warehouse") + self.set_percentage_received() + + def set_percentage_received(self): + total_billed_qty = 0.0 + total_received_qty = 0.0 + for row in self.items: + if row.purchase_receipt and row.pr_detail and row.received_qty: + total_billed_qty += row.qty + total_received_qty += row.received_qty + + if total_billed_qty and total_received_qty: + self.per_received = total_received_qty / total_billed_qty * 100 def validate_release_date(self): if self.release_date and getdate(nowdate()) >= getdate(self.release_date): diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 517cc0342a..970949106c 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -1228,6 +1228,7 @@ def make_purchase_invoice(source_name, target_doc=None, args=None): "field_map": { "name": "pr_detail", "parent": "purchase_receipt", + "qty": "received_qty", "purchase_order_item": "po_detail", "purchase_order": "purchase_order", "is_fixed_asset": "is_fixed_asset", From 04037b7e386bc7a0fb178d5e1fa232b951435d2a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 11 Jan 2024 16:24:43 +0530 Subject: [PATCH 07/50] fix: broken dimension filters in Sales/Purchase register (cherry picked from commit 7b3f9386d7af54181a59c42e9e07ee3d6f54903e) --- erpnext/accounts/report/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py index 0912c7270d..aed338a723 100644 --- a/erpnext/accounts/report/utils.py +++ b/erpnext/accounts/report/utils.py @@ -368,7 +368,7 @@ def filter_invoices_based_on_dimensions(filters, query, parent_doc): dimension.document_type, filters.get(dimension.fieldname) ) fieldname = dimension.fieldname - query = query.where(parent_doc[fieldname] == filters.fieldname) + query = query.where(parent_doc[fieldname].isin(filters[fieldname])) return query From a51f956f3e8cc8285350335bffa7b0e7706a8b27 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 8 Jan 2024 17:11:26 +0530 Subject: [PATCH 08/50] fix: project filters on Delivery Note and Sales Order (cherry picked from commit 9ba6ff67d5727ee97f12a9cbee120e78c20d6cec) --- erpnext/public/js/utils/dimension_tree_filter.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js index bb23f1512b..3f70c09f66 100644 --- a/erpnext/public/js/utils/dimension_tree_filter.js +++ b/erpnext/public/js/utils/dimension_tree_filter.js @@ -16,6 +16,8 @@ erpnext.accounts.dimensions = { }, callback: function(r) { me.accounting_dimensions = r.message[0]; + // Ignoring "Project" as it is already handled specifically in Sales Order and Delivery Note + me.accounting_dimensions = me.accounting_dimensions.filter(x=>{return x.document_type != "Project"}); me.default_dimensions = r.message[1]; me.setup_filters(frm, doctype); } From 9aae439b1f773ce634c41fe4d63a57169978b23a Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 10 Jan 2024 22:05:02 +0530 Subject: [PATCH 09/50] fix: date in master document for dictionary condition (cherry picked from commit d96a777edd9cad698b9d9bb90863c1080eacb36f) --- erpnext/accounts/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index af1c3c6d10..8f899c3e76 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1263,12 +1263,12 @@ def get_autoname_with_number(number_value, doc_title, company): def parse_naming_series_variable(doc, variable): if variable == "FY": if doc: - date = doc.get("posting_date") or doc.get("transaction_date") + date = doc.get("posting_date") or doc.get("transaction_date") or getdate() company = doc.get("company") else: date = getdate() company = None - return get_fiscal_year(date=date, company=company).name + return get_fiscal_year(date=date, company=company)[0] @frappe.whitelist() From 2beb3f87180f91b43e24718d69375022e0d7b4ac Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 10 Jan 2024 22:06:06 +0530 Subject: [PATCH 10/50] test: naming series variable parsing (cherry picked from commit bbdf98a8f0a53b0b4c5a73eb1842401d67314760) --- erpnext/accounts/test/test_utils.py | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/erpnext/accounts/test/test_utils.py b/erpnext/accounts/test/test_utils.py index 3cb5e42e7a..51141d3828 100644 --- a/erpnext/accounts/test/test_utils.py +++ b/erpnext/accounts/test/test_utils.py @@ -23,6 +23,9 @@ class TestUtils(unittest.TestCase): super(TestUtils, cls).setUpClass() make_test_objects("Address", ADDRESS_RECORDS) + def tearDown(self): + frappe.db.rollback() + def test_get_party_shipping_address(self): address = get_party_shipping_address("Customer", "_Test Customer 1") self.assertEqual(address, "_Test Billing Address 2 Title-Billing") @@ -126,6 +129,38 @@ class TestUtils(unittest.TestCase): self.assertEqual(len(payment_entry.references), 1) self.assertEqual(payment_entry.difference_amount, 0) + def test_naming_series_variable_parsing(self): + """ + Tests parsing utility used by Naming Series Variable hook for FY + """ + from frappe.custom.doctype.property_setter.property_setter import make_property_setter + from frappe.utils import nowdate + + from erpnext.accounts.utils import get_fiscal_year + from erpnext.buying.doctype.supplier.test_supplier import create_supplier + + # Configure Supplier Naming in Buying Settings + frappe.db.set_default("supp_master_name", "Auto Name") + + # Configure Autoname in Supplier DocType + make_property_setter( + "Supplier", None, "naming_rule", "Expression", "Data", for_doctype="Doctype" + ) + make_property_setter( + "Supplier", None, "autoname", "SUP-.FY.-.#####", "Data", for_doctype="Doctype" + ) + + # Create Fiscal Year for Current Year + fiscal_year = get_fiscal_year(nowdate())[0] + + # Create Supplier + supplier = create_supplier() + + # Check Naming Series in generated Supplier ID + doc_name = supplier.name.split("-") + self.assertEqual(len(doc_name), 3) + self.assertSequenceEqual(doc_name[0:2], ("SUP", fiscal_year)) + ADDRESS_RECORDS = [ { From e6f3a14289e8a52864792bd9498eca3f31077ba7 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 11 Jan 2024 12:22:08 +0530 Subject: [PATCH 11/50] fix: reset default after test (cherry picked from commit 813b7a96fbb0ff048c112dec5551edf5980d0955) --- erpnext/accounts/test/test_utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/test/test_utils.py b/erpnext/accounts/test/test_utils.py index 51141d3828..c439d4b190 100644 --- a/erpnext/accounts/test/test_utils.py +++ b/erpnext/accounts/test/test_utils.py @@ -23,7 +23,8 @@ class TestUtils(unittest.TestCase): super(TestUtils, cls).setUpClass() make_test_objects("Address", ADDRESS_RECORDS) - def tearDown(self): + @classmethod + def tearDownClass(cls): frappe.db.rollback() def test_get_party_shipping_address(self): @@ -150,7 +151,6 @@ class TestUtils(unittest.TestCase): "Supplier", None, "autoname", "SUP-.FY.-.#####", "Data", for_doctype="Doctype" ) - # Create Fiscal Year for Current Year fiscal_year = get_fiscal_year(nowdate())[0] # Create Supplier @@ -160,6 +160,7 @@ class TestUtils(unittest.TestCase): doc_name = supplier.name.split("-") self.assertEqual(len(doc_name), 3) self.assertSequenceEqual(doc_name[0:2], ("SUP", fiscal_year)) + frappe.db.set_default("supp_master_name", "Supplier Name") ADDRESS_RECORDS = [ From 21eed78fa1b7a8c6f8419aa29223301655e5a817 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 3 Jan 2024 13:39:28 +0100 Subject: [PATCH 12/50] fix: consider all years in holiday list (cherry picked from commit 300aaa39fecf5b65fadf9ec18d122b6d8e327fca) --- erpnext/setup/doctype/holiday_list/holiday_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.py b/erpnext/setup/doctype/holiday_list/holiday_list.py index 1afee41ac3..6381234a0b 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.py +++ b/erpnext/setup/doctype/holiday_list/holiday_list.py @@ -87,7 +87,7 @@ class HolidayList(Document): for holiday_date, holiday_name in country_holidays( self.country, subdiv=self.subdivision, - years=[from_date.year, to_date.year], + years=list(range(from_date.year, to_date.year + 1)), language=frappe.local.lang, ).items(): if holiday_date in existing_holidays: From 8a84f8a4659a9599d5f007c3a7b9e7e440a89328 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 4 Jan 2024 11:04:25 +0100 Subject: [PATCH 13/50] test: improve test for local holidays (cherry picked from commit 60329ade9e4377741ef1578b4a9cc9529e3766c1) --- .../doctype/holiday_list/test_holiday_list.py | 51 +++++++++++++++++-- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/erpnext/setup/doctype/holiday_list/test_holiday_list.py b/erpnext/setup/doctype/holiday_list/test_holiday_list.py index 7eeb27d864..c0e71f5d25 100644 --- a/erpnext/setup/doctype/holiday_list/test_holiday_list.py +++ b/erpnext/setup/doctype/holiday_list/test_holiday_list.py @@ -48,17 +48,58 @@ class TestHolidayList(unittest.TestCase): def test_local_holidays(self): holiday_list = frappe.new_doc("Holiday List") - holiday_list.from_date = "2023-04-01" - holiday_list.to_date = "2023-04-30" + holiday_list.from_date = "2022-01-01" + holiday_list.to_date = "2024-12-31" holiday_list.country = "DE" holiday_list.subdivision = "SN" holiday_list.get_local_holidays() - holidays = [holiday.holiday_date for holiday in holiday_list.holidays] - self.assertNotIn(date(2023, 1, 1), holidays) + holidays = holiday_list.get_holidays() + self.assertIn(date(2022, 1, 1), holidays) + self.assertIn(date(2022, 4, 15), holidays) + self.assertIn(date(2022, 4, 18), holidays) + self.assertIn(date(2022, 5, 1), holidays) + self.assertIn(date(2022, 5, 26), holidays) + self.assertIn(date(2022, 6, 6), holidays) + self.assertIn(date(2022, 10, 3), holidays) + self.assertIn(date(2022, 10, 31), holidays) + self.assertIn(date(2022, 11, 16), holidays) + self.assertIn(date(2022, 12, 25), holidays) + self.assertIn(date(2022, 12, 26), holidays) + self.assertIn(date(2023, 1, 1), holidays) self.assertIn(date(2023, 4, 7), holidays) self.assertIn(date(2023, 4, 10), holidays) - self.assertNotIn(date(2023, 5, 1), holidays) + self.assertIn(date(2023, 5, 1), holidays) + self.assertIn(date(2023, 5, 18), holidays) + self.assertIn(date(2023, 5, 29), holidays) + self.assertIn(date(2023, 10, 3), holidays) + self.assertIn(date(2023, 10, 31), holidays) + self.assertIn(date(2023, 11, 22), holidays) + self.assertIn(date(2023, 12, 25), holidays) + self.assertIn(date(2023, 12, 26), holidays) + self.assertIn(date(2024, 1, 1), holidays) + self.assertIn(date(2024, 3, 29), holidays) + self.assertIn(date(2024, 4, 1), holidays) + self.assertIn(date(2024, 5, 1), holidays) + self.assertIn(date(2024, 5, 9), holidays) + self.assertIn(date(2024, 5, 20), holidays) + self.assertIn(date(2024, 10, 3), holidays) + self.assertIn(date(2024, 10, 31), holidays) + self.assertIn(date(2024, 11, 20), holidays) + self.assertIn(date(2024, 12, 25), holidays) + self.assertIn(date(2024, 12, 26), holidays) + + # check some random dates that should not be local holidays + self.assertNotIn(date(2022, 1, 2), holidays) + self.assertNotIn(date(2023, 4, 16), holidays) + self.assertNotIn(date(2024, 4, 19), holidays) + self.assertNotIn(date(2022, 5, 2), holidays) + self.assertNotIn(date(2023, 5, 27), holidays) + self.assertNotIn(date(2024, 6, 7), holidays) + self.assertNotIn(date(2022, 10, 4), holidays) + self.assertNotIn(date(2023, 10, 30), holidays) + self.assertNotIn(date(2024, 11, 17), holidays) + self.assertNotIn(date(2022, 12, 24), holidays) def test_localized_country_names(self): lang = frappe.local.lang From 16860c228da0a3452fa89dcae6fe169698c6ddbe Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 27 Dec 2023 23:05:37 +0100 Subject: [PATCH 14/50] fix: unreconcile Bank Transaction on cancel of payment voucher (cherry picked from commit 0a95b38166e36946f89286d39081b0f529e88cb6) # Conflicts: # erpnext/accounts/doctype/bank_transaction/bank_transaction.py # erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js # erpnext/accounts/doctype/sales_invoice/sales_invoice.js --- .../bank_transaction/bank_transaction.py | 22 +++++++++++++++++++ .../doctype/journal_entry/journal_entry.js | 2 +- .../doctype/payment_entry/payment_entry.js | 2 +- .../purchase_invoice/purchase_invoice.js | 4 ++++ .../doctype/sales_invoice/sales_invoice.js | 4 ++++ erpnext/controllers/accounts_controller.py | 5 +++++ 6 files changed, 37 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index 1d6cb8e2c0..26047b7afb 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -3,7 +3,11 @@ import frappe from frappe import _ +<<<<<<< HEAD from frappe.model.document import Document +======= +from frappe.model.docstatus import DocStatus +>>>>>>> 0a95b38166 (fix: unreconcile Bank Transaction on cancel of payment voucher) from frappe.utils import flt @@ -415,3 +419,21 @@ def unclear_reference_payment(doctype, docname, bt_name): bt = frappe.get_doc("Bank Transaction", bt_name) set_voucher_clearance(doctype, docname, None, bt) return docname + + +def remove_from_bank_transaction(doctype, docname): + """Remove a (cancelled) voucher from all Bank Transactions.""" + for bt_name in get_reconciled_bank_transactions(doctype, docname): + bt = frappe.get_doc("Bank Transaction", bt_name) + if bt.docstatus == DocStatus.cancelled(): + continue + + modified = False + + for pe in bt.payment_entries: + if pe.payment_document == doctype and pe.payment_entry == docname: + bt.remove(pe) + modified = True + + if modified: + bt.save() diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 266154d87f..07fb5e857c 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -8,7 +8,7 @@ frappe.provide("erpnext.journal_entry"); frappe.ui.form.on("Journal Entry", { setup: function(frm) { frm.add_fetch("bank_account", "account", "account"); - frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', "Repost Payment Ledger", 'Asset', 'Asset Movement', 'Asset Depreciation Schedule', "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries"]; + frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', "Repost Payment Ledger", 'Asset', 'Asset Movement', 'Asset Depreciation Schedule', "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Bank Transaction"]; }, refresh: function(frm) { diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 81ffee3f6e..9402e3da09 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -9,7 +9,7 @@ erpnext.accounts.taxes.setup_tax_filters("Advance Taxes and Charges"); frappe.ui.form.on('Payment Entry', { onload: function(frm) { - frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', 'Repost Payment Ledger','Repost Accounting Ledger', 'Unreconcile Payment', 'Unreconcile Payment Entries']; + frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', 'Repost Payment Ledger','Repost Accounting Ledger', 'Unreconcile Payment', 'Unreconcile Payment Entries', "Bank Transaction"]; if(frm.doc.__islocal) { if (!frm.doc.paid_from) frm.set_value("paid_from_account_currency", null); diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 44d4d81643..f1f815a1cd 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -35,7 +35,11 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. super.onload(); // Ignore linked advances +<<<<<<< HEAD this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Serial and Batch Bundle"]; +======= + this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Bank Transaction"]; +>>>>>>> 0a95b38166 (fix: unreconcile Bank Transaction on cancel of payment voucher) if(!this.frm.doc.__islocal) { // show credit_to in print format diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index ba2cd82516..c6487e360a 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -37,9 +37,13 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e super.onload(); this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet', 'POS Invoice Merge Log', +<<<<<<< HEAD 'POS Closing Entry', 'Journal Entry', 'Payment Entry', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", 'Serial and Batch Bundle' ]; +======= + 'POS Closing Entry', 'Journal Entry', 'Payment Entry', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Bank Transaction"]; +>>>>>>> 0a95b38166 (fix: unreconcile Bank Transaction on cancel of payment voucher) if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) { // show debit_to in print format diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 9a6044f8d7..6b1be396d2 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1414,11 +1414,16 @@ class AccountsController(TransactionBase): reconcile_against_document(lst) def on_cancel(self): + from erpnext.accounts.doctype.bank_transaction.bank_transaction import ( + remove_from_bank_transaction, + ) from erpnext.accounts.utils import ( cancel_exchange_gain_loss_journal, unlink_ref_doc_from_payment_entries, ) + remove_from_bank_transaction(self.doctype, self.name) + if self.doctype in ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]: # Cancel Exchange Gain/Loss Journal before unlinking cancel_exchange_gain_loss_journal(self) From b2fc5e49888286746c4eaf5fa02b585ddb860af2 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 28 Dec 2023 00:00:04 +0100 Subject: [PATCH 15/50] test: cancel voucher linked to Bank Transaction (cherry picked from commit 517bedeb7ee68d7794d09a5f49b97ab64f652abf) --- .../bank_transaction/test_bank_transaction.py | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py index 4a6491d086..7bb3f4183b 100644 --- a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py @@ -2,10 +2,10 @@ # See license.txt import json -import unittest import frappe from frappe import utils +from frappe.model.docstatus import DocStatus from frappe.tests.utils import FrappeTestCase from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import ( @@ -81,6 +81,29 @@ class TestBankTransaction(FrappeTestCase): clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date") self.assertFalse(clearance_date) + def test_cancel_voucher(self): + bank_transaction = frappe.get_doc( + "Bank Transaction", + dict(description="1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G"), + ) + payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1700)) + vouchers = json.dumps( + [ + { + "payment_doctype": "Payment Entry", + "payment_name": payment.name, + "amount": bank_transaction.unallocated_amount, + } + ] + ) + reconcile_vouchers(bank_transaction.name, vouchers) + payment.reload() + payment.cancel() + bank_transaction.reload() + self.assertEqual(bank_transaction.docstatus, DocStatus.submitted()) + self.assertEqual(bank_transaction.unallocated_amount, 1700) + self.assertEqual(bank_transaction.payment_entries, []) + # Check if ERPNext can correctly filter a linked payments based on the debit/credit amount def test_debit_credit_output(self): bank_transaction = frappe.get_doc( From 6b2e3503d9790ffb1c863ce1189530b19543f91d Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 11 Jan 2024 14:56:20 +0100 Subject: [PATCH 16/50] chore: resolve merge confilcts --- .../bank_transaction/bank_transaction.py | 5 +---- .../purchase_invoice/purchase_invoice.js | 16 ++++++++++----- .../doctype/sales_invoice/sales_invoice.js | 20 ++++++++++++------- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index 26047b7afb..c38d27355f 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -3,11 +3,8 @@ import frappe from frappe import _ -<<<<<<< HEAD -from frappe.model.document import Document -======= from frappe.model.docstatus import DocStatus ->>>>>>> 0a95b38166 (fix: unreconcile Bank Transaction on cancel of payment voucher) +from frappe.model.document import Document from frappe.utils import flt diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index f1f815a1cd..a2d210dc54 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -35,11 +35,17 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. super.onload(); // Ignore linked advances -<<<<<<< HEAD - this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Serial and Batch Bundle"]; -======= - this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Bank Transaction"]; ->>>>>>> 0a95b38166 (fix: unreconcile Bank Transaction on cancel of payment voucher) + this.frm.ignore_doctypes_on_cancel_all = [ + "Journal Entry", + "Payment Entry", + "Purchase Invoice", + "Repost Payment Ledger", + "Repost Accounting Ledger", + "Unreconcile Payment", + "Unreconcile Payment Entries", + "Serial and Batch Bundle", + "Bank Transaction", + ]; if(!this.frm.doc.__islocal) { // show credit_to in print format diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index c6487e360a..7cda11addc 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -36,14 +36,20 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e var me = this; super.onload(); - this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet', 'POS Invoice Merge Log', -<<<<<<< HEAD - 'POS Closing Entry', 'Journal Entry', 'Payment Entry', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", - 'Serial and Batch Bundle' + this.frm.ignore_doctypes_on_cancel_all = [ + "POS Invoice", + "Timesheet", + "POS Invoice Merge Log", + "POS Closing Entry", + "Journal Entry", + "Payment Entry", + "Repost Payment Ledger", + "Repost Accounting Ledger", + "Unreconcile Payment", + "Unreconcile Payment Entries", + "Serial and Batch Bundle", + "Bank Transaction", ]; -======= - 'POS Closing Entry', 'Journal Entry', 'Payment Entry', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Bank Transaction"]; ->>>>>>> 0a95b38166 (fix: unreconcile Bank Transaction on cancel of payment voucher) if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) { // show debit_to in print format From d7840559a00e16dbb88369f87de1a72939d0dff4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 12 Jan 2024 16:49:00 +0530 Subject: [PATCH 17/50] chore: remove share, print and email permissions from Buying Settings (backport #39337) (#39339) chore: remove share, print and email permissions from Buying Settings (cherry picked from commit 3c46abca6c362f8ddd7d269d8f28ee07ee557cc8) Co-authored-by: s-aga-r --- .../buying_settings/buying_settings.json | 27 +++++-------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json index b05de7d0b2..ddcbd555ae 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.json +++ b/erpnext/buying/doctype/buying_settings/buying_settings.json @@ -214,7 +214,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2024-01-05 15:26:02.320942", + "modified": "2024-01-12 16:42:01.894346", "modified_by": "Administrator", "module": "Buying", "name": "Buying Settings", @@ -240,39 +240,24 @@ "write": 1 }, { - "email": 1, - "print": 1, "read": 1, - "role": "Accounts User", - "share": 1 + "role": "Accounts User" }, { - "email": 1, - "print": 1, "read": 1, - "role": "Accounts Manager", - "share": 1 + "role": "Accounts Manager" }, { - "email": 1, - "print": 1, "read": 1, - "role": "Stock Manager", - "share": 1 + "role": "Stock Manager" }, { - "email": 1, - "print": 1, "read": 1, - "role": "Stock User", - "share": 1 + "role": "Stock User" }, { - "email": 1, - "print": 1, "read": 1, - "role": "Purchase User", - "share": 1 + "role": "Purchase User" } ], "sort_field": "modified", From 04c96ddc6c1337547f479a0874816d4faacfb80d Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 27 Dec 2023 19:32:48 +0530 Subject: [PATCH 18/50] fix: use child table values instead of global min max (cherry picked from commit 43fed29514c0d839312f33e1d7d490ae73d9830f) --- erpnext/stock/doctype/item/item.js | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 6e810e5987..b964c843a7 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -600,26 +600,12 @@ $.extend(erpnext.item, { } }); } else { - frappe.call({ - method: "frappe.client.get", - args: { - doctype: "Item Attribute", - name: d.attribute - } - }).then((r) => { - if(r.message) { - const from = r.message.from_range; - const to = r.message.to_range; - const increment = r.message.increment; - - let values = []; - for(var i = from; i <= to; i = flt(i + increment, 6)) { - values.push(i); - } - attr_val_fields[d.attribute] = values; - resolve(); - } - }); + let values = []; + for(var i = d.from_range; i <= d.to_range; i = flt(i + d.increment, 6)) { + values.push(i); + } + attr_val_fields[d.attribute] = values; + resolve(); } }); From 50fe4191d37172381f1a41b7fec1abbc4d1a2b56 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 12 Jan 2024 14:11:37 +0530 Subject: [PATCH 19/50] fix: added indexing to improve performance (cherry picked from commit ac81323fec7287a986feaf7ba496395bf3f6a424) --- erpnext/stock/doctype/stock_entry/stock_entry.json | 8 +++++--- .../doctype/stock_entry_detail/stock_entry_detail.json | 5 +++-- .../doctype/stock_entry_detail/stock_entry_detail.py | 1 - 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index 564c380017..d45296f131 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -104,7 +104,8 @@ "in_standard_filter": 1, "label": "Stock Entry Type", "options": "Stock Entry Type", - "reqd": 1 + "reqd": 1, + "search_index": 1 }, { "depends_on": "eval:doc.purpose == 'Material Transfer'", @@ -546,7 +547,8 @@ "label": "Job Card", "options": "Job Card", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "fieldname": "amended_from", @@ -679,7 +681,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-06-19 18:23:40.748114", + "modified": "2024-01-12 11:56:58.644882", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry", diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index 0c08fb2ed3..bd84a2b0d9 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -561,7 +561,8 @@ "label": "Job Card Item", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "default": "0", @@ -589,7 +590,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-05-09 12:41:18.210864", + "modified": "2024-01-12 11:56:04.626103", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py index b2fc1f52e1..a6dd0faadf 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py @@ -20,7 +20,6 @@ class StockEntryDetail(Document): allow_alternative_item: DF.Check allow_zero_valuation_rate: DF.Check amount: DF.Currency - attach_something_here: DF.Attach | None barcode: DF.Data | None basic_amount: DF.Currency basic_rate: DF.Currency From ca18853785757380a6070f36c0d907693de6b167 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Fri, 12 Jan 2024 15:26:35 +0530 Subject: [PATCH 20/50] fix: SBB Total Qty validation for SE (cherry picked from commit c20241fcb5d7269c08c38f51c92bf73289650708) --- erpnext/controllers/stock_controller.py | 6 +----- .../serial_and_batch_bundle/serial_and_batch_bundle.py | 4 +++- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index fd417f3270..a86d7388df 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -387,11 +387,7 @@ class StockController(AccountsController): } for row in self.get(table_name): - for field in [ - "serial_and_batch_bundle", - "current_serial_and_batch_bundle", - "rejected_serial_and_batch_bundle", - ]: + for field in QTY_FIELD.keys(): if row.get(field): frappe.get_doc("Serial and Batch Bundle", row.get(field)).set_serial_and_batch_values( self, row, qty_field=QTY_FIELD[field] diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 620b9606a7..2b87fcd175 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -490,8 +490,10 @@ class SerialandBatchBundle(Document): qty_field = "qty" precision = row.precision - if row.get("doctype") in ["Subcontracting Receipt Supplied Item"]: + if row.get("doctype") == "Subcontracting Receipt Supplied Item": qty_field = "consumed_qty" + elif row.get("doctype") == "Stock Entry Detail": + qty_field = "transfer_qty" qty = row.get(qty_field) if qty_field == "qty" and row.get("stock_qty"): From d510b46f13412a4a5b3c6e9e760dc6da6ed14e9e Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Mon, 8 Jan 2024 18:47:44 +0100 Subject: [PATCH 21/50] Update sales_taxes_and_charges.json Change Rate label to existing Tax Rate label so it can be correctly translated in other languages (cherry picked from commit 2b93be1139bcb963a89c10f261a1dd2c83f01980) --- .../sales_taxes_and_charges/sales_taxes_and_charges.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json index e236577e11..527e4c866b 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json +++ b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json @@ -108,7 +108,7 @@ "fieldname": "rate", "fieldtype": "Float", "in_list_view": 1, - "label": "Rate", + "label": "Tax Rate", "oldfieldname": "rate", "oldfieldtype": "Currency" }, @@ -227,4 +227,4 @@ "sort_field": "modified", "sort_order": "ASC", "states": [] -} \ No newline at end of file +} From 61595c7ede5ed4c61b9ca2cd28f7dd7e901997c1 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Sun, 14 Jan 2024 09:48:05 +0530 Subject: [PATCH 22/50] fix: modified date was not updated (cherry picked from commit f567af49a697568ba1d5dc3507953c332cb1a60a) --- .../sales_taxes_and_charges/sales_taxes_and_charges.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json index 527e4c866b..f9e5f4129c 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json +++ b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json @@ -218,7 +218,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-10-17 13:08:17.776528", + "modified": "2022-10-18 13:08:17.776528", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Taxes and Charges", From bf4e514cde1d677b5d950e4f3f354085c98cd366 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 14 Jan 2024 10:21:38 +0530 Subject: [PATCH 23/50] chore: removed extra field --- .../closing_stock_balance/closing_stock_balance.json | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.json b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.json index 225da6d15e..acb96e7a2f 100644 --- a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.json +++ b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.json @@ -103,15 +103,6 @@ "print_hide": 1, "read_only": 1 }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Closing Stock Balance", - "print_hide": 1, - "read_only": 1 - }, { "fieldname": "include_uom", "fieldtype": "Link", @@ -122,7 +113,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-05-17 11:46:04.448220", + "modified": "2023-05-18 11:46:04.448220", "modified_by": "Administrator", "module": "Stock", "name": "Closing Stock Balance", From eec6c25f9e386294d08b44430219de385050856b Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 14 Jan 2024 10:05:26 +0530 Subject: [PATCH 24/50] fix: added item group in stock reco (cherry picked from commit 116ff8241caeb98df28c91143a076dfa9e3179d6) --- .../stock_reconciliation_item.json | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json index d9cbf95710..fc4ae6a5fa 100644 --- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json +++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json @@ -10,8 +10,9 @@ "has_item_scanned", "item_code", "item_name", - "warehouse", + "item_group", "column_break_6", + "warehouse", "qty", "valuation_rate", "amount", @@ -52,6 +53,7 @@ "reqd": 1 }, { + "fetch_from": "item_code.item_name", "fieldname": "item_name", "fieldtype": "Data", "in_global_search": 1, @@ -213,11 +215,18 @@ "fieldname": "add_serial_batch_bundle", "fieldtype": "Button", "label": "Add Serial / Batch No" + }, + { + "fetch_from": "item_code.item_group", + "fieldname": "item_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group" } ], "istable": 1, "links": [], - "modified": "2023-11-02 15:47:07.929550", + "modified": "2024-01-14 10:04:23.599951", "modified_by": "Administrator", "module": "Stock", "name": "Stock Reconciliation Item", From f0480173fbac4ec727ca4e75fe79b896e2947c25 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Sun, 14 Jan 2024 10:43:34 +0530 Subject: [PATCH 25/50] fix: modified date --- .../sales_taxes_and_charges/sales_taxes_and_charges.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json index f9e5f4129c..9e0a7983b7 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json +++ b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json @@ -218,7 +218,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-10-18 13:08:17.776528", + "modified": "2024-01-14 10:08:17.776528", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Taxes and Charges", From 6bdf944ecfdbcf528be25d5eb933ccc8d4eb4925 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Mon, 8 Jan 2024 18:45:39 +0100 Subject: [PATCH 26/50] Update purchase_taxes_and_charges.json label Rate to Tax Rate Change Rate label to existing Tax Rate label so it can be correctly translated in other languages (cherry picked from commit bd464197c41329ddf3cff50cd3eb876df9c6c382) --- .../purchase_taxes_and_charges.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json index 347cae05b7..249e7518f0 100644 --- a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json +++ b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json @@ -126,7 +126,7 @@ "fieldname": "rate", "fieldtype": "Float", "in_list_view": 1, - "label": "Rate", + "label": "Tax Rate", "oldfieldname": "rate", "oldfieldtype": "Currency" }, @@ -239,4 +239,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} \ No newline at end of file +} From 2dc8686a81509dfcc93936a690f34a5892d0b39c Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Sun, 14 Jan 2024 10:15:50 +0530 Subject: [PATCH 27/50] fix: modified date was not set (cherry picked from commit 566876ae7a9ac4ce70f0c72dd69c933ed022bf46) --- .../purchase_taxes_and_charges/purchase_taxes_and_charges.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json index 249e7518f0..f10e9842a4 100644 --- a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json +++ b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json @@ -230,7 +230,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2021-08-05 20:04:36.618240", + "modified": "2021-08-06 20:04:36.618240", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Taxes and Charges", From cd870ee30ca0004e66b094dbc51aa2943cd40a0e Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Sun, 14 Jan 2024 10:45:43 +0530 Subject: [PATCH 28/50] fix: modified date (cherry picked from commit 6827edb2c51ffe44f5af32fc63e065481bb85662) --- .../purchase_taxes_and_charges/purchase_taxes_and_charges.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json index f10e9842a4..adab54b375 100644 --- a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json +++ b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json @@ -230,7 +230,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2021-08-06 20:04:36.618240", + "modified": "2024-01-14 10:04:36.618240", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Taxes and Charges", From 1394a1c5e99a9d5d50c3fa839f40b376aa55a8e0 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 14 Jan 2024 23:23:55 +0530 Subject: [PATCH 29/50] fix: batches not coming correctly in the batch selector (cherry picked from commit 114f2b432628b55201dfbfcac8c4e6a65c577cc7) --- erpnext/controllers/queries.py | 39 +++++++++++++++------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index e858820965..47dc3e7dea 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -416,23 +416,14 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters): meta = frappe.get_meta(doctype, cached=True) searchfields = meta.get_search_fields() - query = get_batches_from_stock_ledger_entries(searchfields, txt, filters) - bundle_query = get_batches_from_serial_and_batch_bundle(searchfields, txt, filters) - - data = ( - frappe.qb.from_((query) + (bundle_query)) - .select("batch_no", "qty", "manufacturing_date", "expiry_date") - .offset(start) - .limit(page_len) + batches = get_batches_from_stock_ledger_entries(searchfields, txt, filters, start, page_len) + batches.extend( + get_batches_from_serial_and_batch_bundle(searchfields, txt, filters, start, page_len) ) - for field in searchfields: - data = data.select(field) + filtered_batches = get_filterd_batches(batches) - data = data.run() - data = get_filterd_batches(data) - - return data + return filtered_batches def get_filterd_batches(data): @@ -452,7 +443,7 @@ def get_filterd_batches(data): return filterd_batch -def get_batches_from_stock_ledger_entries(searchfields, txt, filters): +def get_batches_from_stock_ledger_entries(searchfields, txt, filters, start=0, page_len=100): stock_ledger_entry = frappe.qb.DocType("Stock Ledger Entry") batch_table = frappe.qb.DocType("Batch") @@ -474,6 +465,8 @@ def get_batches_from_stock_ledger_entries(searchfields, txt, filters): & (stock_ledger_entry.batch_no.isnotnull()) ) .groupby(stock_ledger_entry.batch_no, stock_ledger_entry.warehouse) + .offset(start) + .limit(page_len) ) query = query.select( @@ -488,16 +481,16 @@ def get_batches_from_stock_ledger_entries(searchfields, txt, filters): query = query.select(batch_table[field]) if txt: - txt_condition = batch_table.name.like(txt) + txt_condition = batch_table.name.like("%{0}%".format(txt)) for field in searchfields + ["name"]: - txt_condition |= batch_table[field].like(txt) + txt_condition |= batch_table[field].like("%{0}%".format(txt)) query = query.where(txt_condition) - return query + return query.run(as_list=1) or [] -def get_batches_from_serial_and_batch_bundle(searchfields, txt, filters): +def get_batches_from_serial_and_batch_bundle(searchfields, txt, filters, start=0, page_len=100): bundle = frappe.qb.DocType("Serial and Batch Entry") stock_ledger_entry = frappe.qb.DocType("Stock Ledger Entry") batch_table = frappe.qb.DocType("Batch") @@ -522,6 +515,8 @@ def get_batches_from_serial_and_batch_bundle(searchfields, txt, filters): & (stock_ledger_entry.serial_and_batch_bundle.isnotnull()) ) .groupby(bundle.batch_no, bundle.warehouse) + .offset(start) + .limit(page_len) ) bundle_query = bundle_query.select( @@ -536,13 +531,13 @@ def get_batches_from_serial_and_batch_bundle(searchfields, txt, filters): bundle_query = bundle_query.select(batch_table[field]) if txt: - txt_condition = batch_table.name.like(txt) + txt_condition = batch_table.name.like("%{0}%".format(txt)) for field in searchfields + ["name"]: - txt_condition |= batch_table[field].like(txt) + txt_condition |= batch_table[field].like("%{0}%".format(txt)) bundle_query = bundle_query.where(txt_condition) - return bundle_query + return bundle_query.run(as_list=1) @frappe.whitelist() From b43f70325c89f2b18e9e8467ccd7e6c8971794f3 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 14 Jan 2024 15:52:37 +0530 Subject: [PATCH 30/50] fix: incorrect sql error if account name has '%' (cherry picked from commit 641c3de0caf3dd542a353edd78c8c18f686b8cae) --- .../report/customer_ledger_summary/customer_ledger_summary.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py index 0464f99d20..0f77bb38ec 100644 --- a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py +++ b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py @@ -376,6 +376,10 @@ class PartyLedgerSummaryReport(object): if not income_or_expense_accounts: # prevent empty 'in' condition income_or_expense_accounts.append("") + else: + # escape '%' in account name + # ignoring frappe.db.escape as it replaces single quotes with double quotes + income_or_expense_accounts = [x.replace("%", "%%") for x in income_or_expense_accounts] accounts_query = ( qb.from_(gl) From dbbba046ab0be6dee68b869174653278fdd5e60d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 12 Jan 2024 16:33:41 +0530 Subject: [PATCH 31/50] refactor: disallow bank transactions on different currencies (cherry picked from commit cdd0acc672e471d94693c04079ab78ba48b82a30) --- .../doctype/bank_transaction/bank_transaction.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index c38d27355f..15d7786de7 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -49,6 +49,22 @@ class BankTransaction(Document): def validate(self): self.validate_duplicate_references() + self.validate_currency() + + def validate_currency(self): + """ + Bank Transaction should be on the same currency as the Bank Account. + """ + if self.currency and self.bank_account: + account = frappe.get_cached_value("Bank Account", self.bank_account, "account") + account_currency = frappe.get_cached_value("Account", account, "account_currency") + + if self.currency != account_currency: + frappe.throw( + _("Currency {0} cannot be different from Bank Account({1}) Currency: {2}").format( + frappe.bold(self.currency), frappe.bold(self.bank_account), frappe.bold(account_currency) + ) + ) def set_status(self): if self.docstatus == 2: From f609b8ae5d7f09bbb4daee59488ac4cbc7073276 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 14 Jan 2024 18:04:15 +0530 Subject: [PATCH 32/50] refactor: better error message (cherry picked from commit b4354cbc8dfb22fafaed2cd39b92c438e5f199db) --- erpnext/accounts/doctype/bank_transaction/bank_transaction.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index 15d7786de7..fef3b569ed 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -61,7 +61,9 @@ class BankTransaction(Document): if self.currency != account_currency: frappe.throw( - _("Currency {0} cannot be different from Bank Account({1}) Currency: {2}").format( + _( + "Transaction currency: {0} cannot be different from Bank Account({1}) currency: {2}" + ).format( frappe.bold(self.currency), frappe.bold(self.bank_account), frappe.bold(account_currency) ) ) From 32c61117285fb7c58cb68d2d3120579382587836 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 14 Jan 2024 18:09:19 +0530 Subject: [PATCH 33/50] refactor(test): supply default currency to Bank Transaction (cherry picked from commit a27a4db3de6d6df7dd340d4212c114b7f4c0762c) --- .../bank_reconciliation_tool/test_bank_reconciliation_tool.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/test_bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/test_bank_reconciliation_tool.py index 5a6bb6976f..adf5925443 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/test_bank_reconciliation_tool.py +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/test_bank_reconciliation_tool.py @@ -76,6 +76,7 @@ class TestBankReconciliationTool(AccountsTestMixin, FrappeTestCase): "deposit": 100, "bank_account": self.bank_account, "reference_number": "123", + "currency": "INR", } ) .save() From e282ba78c179a7b6893d120e7d5db3d90baed72c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 12:08:44 +0530 Subject: [PATCH 34/50] ci: bump node in release workflow (backport #39377) (#39378) * ci: bump node in release workflow (cherry picked from commit aef87cced7da0524c7ca2dadfcd111aaf13ef0c2) # Conflicts: # .github/workflows/release.yml * chore: `conflicts` --------- Co-authored-by: s-aga-r --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4546a69668..5f0abc70c5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v2 with: - node-version: 18 + node-version: 20 - name: Setup dependencies run: | From 61f073f8b1a68b6fedcb049de460b1d35cb95c66 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 12 Jan 2024 12:43:54 +0530 Subject: [PATCH 35/50] refactor: prevent foreign currency subscription for a party (cherry picked from commit 6b5fa2c673accab13a174a2945b333bd1b991cdb) --- .../doctype/subscription/subscription.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index 94f5e29f06..20c9167bc9 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -16,6 +16,7 @@ from frappe.utils.data import ( date_diff, flt, get_last_day, + get_link_to_form, getdate, nowdate, ) @@ -317,6 +318,37 @@ class Subscription(Document): if self.is_new(): self.set_subscription_status() + self.validate_party_billing_currency() + + def validate_party_billing_currency(self): + """ + Subscription should be of the same currency as the Party's default billing currency or company default. + """ + if self.party: + party_billing_currency = frappe.get_cached_value( + self.party_type, self.party, "default_currency" + ) or frappe.get_cached_value("Company", self.company, "default_currency") + + plans = [x.plan for x in self.plans] + subscription_plan_currencies = frappe.db.get_all( + "Subscription Plan", filters={"name": ("in", plans)}, fields=["name", "currency"] + ) + unsupported_plans = [] + for x in subscription_plan_currencies: + if x.currency != party_billing_currency: + unsupported_plans.append("{0}".format(get_link_to_form("Subscription Plan", x.name))) + + if unsupported_plans: + unsupported_plans = [ + _( + "Below Subscription Plans are of different currency to the party default billing currency/Company currency: {0}" + ).format(frappe.bold(party_billing_currency)) + ] + unsupported_plans + + frappe.throw( + unsupported_plans, frappe.ValidationError, "Unsupported Subscription Plans", as_list=True + ) + def validate_trial_period(self) -> None: """ Runs sanity checks on trial period dates for the `Subscription` From c288db0356e42ee49c45e314d4d9445ccc4f11b1 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 14 Jan 2024 17:59:43 +0530 Subject: [PATCH 36/50] refactor: making currency mandatory for subcscription plans (cherry picked from commit 19975dcb7bb688f5b93b8e7757d6a906cb68a2bc) --- .../doctype/subscription_plan/subscription_plan.json | 7 +++++-- .../doctype/subscription_plan/subscription_plan.py | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.json b/erpnext/accounts/doctype/subscription_plan/subscription_plan.json index 563df79eec..bc1f579cf0 100644 --- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.json +++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.json @@ -41,7 +41,8 @@ "fieldname": "currency", "fieldtype": "Link", "label": "Currency", - "options": "Currency" + "options": "Currency", + "reqd": 1 }, { "fieldname": "column_break_3", @@ -148,10 +149,11 @@ } ], "links": [], - "modified": "2021-12-10 15:24:15.794477", + "modified": "2024-01-14 17:59:34.687977", "modified_by": "Administrator", "module": "Accounts", "name": "Subscription Plan", + "naming_rule": "By fieldname", "owner": "Administrator", "permissions": [ { @@ -193,5 +195,6 @@ ], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py index 118d254780..cdfa3e56d9 100644 --- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py +++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py @@ -24,7 +24,7 @@ class SubscriptionPlan(Document): billing_interval_count: DF.Int cost: DF.Currency cost_center: DF.Link | None - currency: DF.Link | None + currency: DF.Link item: DF.Link payment_gateway: DF.Link | None plan_name: DF.Data From 32afe7de313a3ada02003adbab6227134b1f5050 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 14 Jan 2024 18:00:45 +0530 Subject: [PATCH 37/50] refactor(test): supply default currency for subscription plans (cherry picked from commit 1387b0ba7f9edf71f838a270e5c8aa40b30b3a70) --- .../doctype/subscription/test_subscription.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py index 785fd04b82..37326fd035 100644 --- a/erpnext/accounts/doctype/subscription/test_subscription.py +++ b/erpnext/accounts/doctype/subscription/test_subscription.py @@ -463,7 +463,7 @@ class TestSubscription(FrappeTestCase): subscription = create_subscription( start_date="2018-01-01", generate_invoice_at="Beginning of the current subscription period", - plans=[{"plan": "_Test Plan Multicurrency", "qty": 1}], + plans=[{"plan": "_Test Plan Multicurrency", "qty": 1, "currency": "USD"}], party="_Test Subscription Customer", ) @@ -528,13 +528,21 @@ class TestSubscription(FrappeTestCase): def make_plans(): - create_plan(plan_name="_Test Plan Name", cost=900) - create_plan(plan_name="_Test Plan Name 2", cost=1999) + create_plan(plan_name="_Test Plan Name", cost=900, currency="INR") + create_plan(plan_name="_Test Plan Name 2", cost=1999, currency="INR") create_plan( - plan_name="_Test Plan Name 3", cost=1999, billing_interval="Day", billing_interval_count=14 + plan_name="_Test Plan Name 3", + cost=1999, + billing_interval="Day", + billing_interval_count=14, + currency="INR", ) create_plan( - plan_name="_Test Plan Name 4", cost=20000, billing_interval="Month", billing_interval_count=3 + plan_name="_Test Plan Name 4", + cost=20000, + billing_interval="Month", + billing_interval_count=3, + currency="INR", ) create_plan( plan_name="_Test Plan Multicurrency", cost=50, billing_interval="Month", currency="USD" From 80956b795605dbb8ff04420e6e735acf1bf504b6 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 15 Jan 2024 20:22:30 +0530 Subject: [PATCH 38/50] fix: possible typerror in utils.js and remove unwanted debugging statements (cherry picked from commit 60b26ad8b262752e7d491b3fe21b398c0928bfaf) --- .../report/budget_variance_report/budget_variance_report.js | 4 ---- erpnext/public/js/utils.js | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js index 9c356bf28e..d6a4755d6a 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js @@ -84,10 +84,6 @@ function get_filters() { options: budget_against_options, default: "Cost Center", reqd: 1, - get_data: function() { - console.log(this.options); - return ["Emacs", "Rocks"]; - }, on_change: function() { frappe.query_report.set_filter_value("budget_against_filter", []); frappe.query_report.refresh(); diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 598167b337..de46271e47 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -21,7 +21,7 @@ $.extend(erpnext, { }, toggle_naming_series: function() { - if(cur_frm.fields_dict.naming_series) { + if(cur_frm && cur_frm.fields_dict.naming_series) { cur_frm.toggle_display("naming_series", cur_frm.doc.__islocal?true:false); } }, From cd1820f680e85ec9a82b9bda2fa1c2d08aeacea0 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 16 Jan 2024 10:47:51 +0530 Subject: [PATCH 39/50] refactor(test): set default currency for party --- erpnext/accounts/doctype/subscription/test_subscription.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py index 37326fd035..a46642ad50 100644 --- a/erpnext/accounts/doctype/subscription/test_subscription.py +++ b/erpnext/accounts/doctype/subscription/test_subscription.py @@ -460,11 +460,13 @@ class TestSubscription(FrappeTestCase): self.assertEqual(len(subscription.invoices), 1) def test_multi_currency_subscription(self): + party = "_Test Subscription Customer" + frappe.db.set_value("Customer", party, "default_currency", "USD") subscription = create_subscription( start_date="2018-01-01", generate_invoice_at="Beginning of the current subscription period", plans=[{"plan": "_Test Plan Multicurrency", "qty": 1, "currency": "USD"}], - party="_Test Subscription Customer", + party=party, ) subscription.process() From 8816b2740acefdb468fa370524b0ba3a9d4f0399 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 16 Jan 2024 15:12:28 +0530 Subject: [PATCH 40/50] fix: permission issue for the BIN (cherry picked from commit 6e4d4a55cd70964d8ab8870105591abaa1b17b9e) --- erpnext/stock/doctype/bin/bin.json | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/bin/bin.json b/erpnext/stock/doctype/bin/bin.json index 10d9511357..39e0917ce6 100644 --- a/erpnext/stock/doctype/bin/bin.json +++ b/erpnext/stock/doctype/bin/bin.json @@ -186,7 +186,7 @@ "idx": 1, "in_create": 1, "links": [], - "modified": "2023-11-01 16:51:17.079107", + "modified": "2024-01-16 15:11:46.140323", "modified_by": "Administrator", "module": "Stock", "name": "Bin", @@ -213,6 +213,21 @@ "read": 1, "report": 1, "role": "Stock User" + }, + { + "read": 1, + "report": 1, + "role": "Stock Manager" + }, + { + "read": 1, + "report": 1, + "role": "Purchase Manager" + }, + { + "read": 1, + "report": 1, + "role": "Sales Manager" } ], "quick_entry": 1, From 07e2901e4b47fbcba86ed1c5fc290097398aeb31 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 16 Jan 2024 13:38:53 +0530 Subject: [PATCH 41/50] fix: project query controller logic (cherry picked from commit 4eefb445a748100f3c36094188e38c127ad80051) --- erpnext/controllers/queries.py | 54 ++++++++++++++++------------------ 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 47dc3e7dea..b49a8ba177 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -6,8 +6,9 @@ import json from collections import OrderedDict, defaultdict import frappe -from frappe import scrub +from frappe import qb, scrub from frappe.desk.reportview import get_filters_cond, get_match_cond +from frappe.query_builder import Criterion from frappe.query_builder.functions import Concat, Sum from frappe.utils import nowdate, today, unique @@ -339,37 +340,32 @@ def bom(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_project_name(doctype, txt, searchfield, start, page_len, filters): - doctype = "Project" - cond = "" + proj = qb.DocType("Project") + qb_filter_and_conditions = [] + qb_filter_or_conditions = [] if filters and filters.get("customer"): - cond = """(`tabProject`.customer = %s or - ifnull(`tabProject`.customer,"")="") and""" % ( - frappe.db.escape(filters.get("customer")) - ) + qb_filter_and_conditions.append(proj.customer == filters.get("customer")) - fields = get_fields(doctype, ["name", "project_name"]) - searchfields = frappe.get_meta(doctype).get_search_fields() - searchfields = " or ".join(["`tabProject`." + field + " like %(txt)s" for field in searchfields]) + qb_filter_and_conditions.append(proj.status.notin(["Completed", "Cancelled"])) - return frappe.db.sql( - """select {fields} from `tabProject` - where - `tabProject`.status not in ('Completed', 'Cancelled') - and {cond} {scond} {match_cond} - order by - (case when locate(%(_txt)s, `tabProject`.name) > 0 then locate(%(_txt)s, `tabProject`.name) else 99999 end), - `tabProject`.idx desc, - `tabProject`.name asc - limit {page_len} offset {start}""".format( - fields=", ".join(["`tabProject`.{0}".format(f) for f in fields]), - cond=cond, - scond=searchfields, - match_cond=get_match_cond(doctype), - start=start, - page_len=page_len, - ), - {"txt": "%{0}%".format(txt), "_txt": txt.replace("%", "")}, - ) + q = qb.from_(proj) + + fields = get_fields("Project", ["name", "project_name"]) + for x in fields: + q = q.select(proj[x]) + + # ignore 'customer' and 'status' on searchfields as they must be exactly matched + searchfields = [ + x for x in frappe.get_meta(doctype).get_search_fields() if x not in ["customer", "status"] + ] + if txt: + for x in searchfields: + qb_filter_or_conditions.append(proj[x].like(f"%{txt}%")) + + q = q.where(Criterion.all(qb_filter_and_conditions)).where(Criterion.any(qb_filter_or_conditions)) + if page_len: + q = q.limit(page_len) + return q.run() @frappe.whitelist() From 1b8f572e802186f8b5e6396f8634c0076625ef35 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 16 Jan 2024 14:28:09 +0530 Subject: [PATCH 42/50] fix(test): test case for project query (cherry picked from commit 3349dde5e2914bd9e2dbe0ce4de94023bfee2e7f) --- erpnext/controllers/queries.py | 2 +- erpnext/controllers/tests/test_queries.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index b49a8ba177..a05e7b01d3 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -350,7 +350,7 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): q = qb.from_(proj) - fields = get_fields("Project", ["name", "project_name"]) + fields = get_fields(doctype, ["name", "project_name"]) for x in fields: q = q.select(proj[x]) diff --git a/erpnext/controllers/tests/test_queries.py b/erpnext/controllers/tests/test_queries.py index 60d1733021..3a3bc1cd72 100644 --- a/erpnext/controllers/tests/test_queries.py +++ b/erpnext/controllers/tests/test_queries.py @@ -68,7 +68,7 @@ class TestQueries(unittest.TestCase): self.assertGreaterEqual(len(query(txt="_Test Item Home Desktop Manufactured")), 1) def test_project_query(self): - query = add_default_params(queries.get_project_name, "BOM") + query = add_default_params(queries.get_project_name, "Project") self.assertGreaterEqual(len(query(txt="_Test Project")), 1) From 5a65a10dba312df379088567e94a88f921ba259f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 16 Jan 2024 14:35:06 +0530 Subject: [PATCH 43/50] refactor: better ordering of query result (cherry picked from commit bfe42fdccb13ab797ac7252ada58df49af43ad54) --- erpnext/controllers/queries.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index a05e7b01d3..604a25127c 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -8,9 +8,10 @@ from collections import OrderedDict, defaultdict import frappe from frappe import qb, scrub from frappe.desk.reportview import get_filters_cond, get_match_cond -from frappe.query_builder import Criterion -from frappe.query_builder.functions import Concat, Sum +from frappe.query_builder import Criterion, CustomFunction +from frappe.query_builder.functions import Concat, Locate, Sum from frappe.utils import nowdate, today, unique +from pypika import Order import erpnext from erpnext.stock.get_item_details import _get_item_tax_template @@ -343,6 +344,8 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): proj = qb.DocType("Project") qb_filter_and_conditions = [] qb_filter_or_conditions = [] + ifelse = CustomFunction("IF", ["condition", "then", "else"]) + if filters and filters.get("customer"): qb_filter_and_conditions.append(proj.customer == filters.get("customer")) @@ -354,17 +357,29 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): for x in fields: q = q.select(proj[x]) - # ignore 'customer' and 'status' on searchfields as they must be exactly matched + # don't consider 'customer' and 'status' fields for pattern search, as they must be exactly matched searchfields = [ x for x in frappe.get_meta(doctype).get_search_fields() if x not in ["customer", "status"] ] + + # pattern search if txt: for x in searchfields: qb_filter_or_conditions.append(proj[x].like(f"%{txt}%")) q = q.where(Criterion.all(qb_filter_and_conditions)).where(Criterion.any(qb_filter_or_conditions)) + + # ordering + if txt: + # project_name containing search string 'txt' will be given higher precedence + q = q.orderby(ifelse(Locate(txt, proj.project_name) > 0, Locate(txt, proj.project_name), 99999)) + q = q.orderby(proj.idx, order=Order.desc).orderby(proj.name) + if page_len: q = q.limit(page_len) + + if start: + q = q.offset(start) return q.run() From 53f61f1ad171e42c694311191204e2b82bd91dbb Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 17 Jan 2024 10:45:57 +0530 Subject: [PATCH 44/50] fix: consistency in display reserved_stock checkbox on Sales Order Item according global settings and item.is_stock_item (backport #38322) (#39417) fix: consistency in display reserved_stock checkbox on Sales Order Item according global settings and item.is_stock_item (#38322) * fix: consistency in display reserved_stock checkbox on Sales Order Item according global settings and item.is_stock_item * fix: evaluate depends_on for fdata visibility in grid * fix: evaluate depends_on for fdata visibility in grid * chore: change after review * chore: change for review (cherry picked from commit af80d253dbf5d1502f3df4a18b234d392bd96556) Co-authored-by: HENRY Florian --- erpnext/selling/doctype/sales_order/sales_order.js | 3 +++ erpnext/selling/doctype/sales_order/sales_order.py | 12 ++++++++++++ .../doctype/sales_order_item/sales_order_item.json | 12 ++++++++++++ 3 files changed, 27 insertions(+) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index b206e3fe33..56c745c00a 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -94,6 +94,9 @@ frappe.ui.form.on("Sales Order", { frm.set_value("reserve_stock", 0); frm.set_df_property("reserve_stock", "read_only", 1); frm.set_df_property("reserve_stock", "hidden", 1); + frm.fields_dict.items.grid.update_docfield_property('reserve_stock', 'hidden', 1); + frm.fields_dict.items.grid.update_docfield_property('reserve_stock', 'default', 0); + frm.fields_dict.items.grid.update_docfield_property('reserve_stock', 'read_only', 1); } }) } diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 95423612c8..5ef2c50146 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -200,6 +200,7 @@ class SalesOrder(SellingController): self.validate_for_items() self.validate_warehouse() self.validate_drop_ship() + self.validate_reserved_stock() self.validate_serial_no_based_delivery() validate_against_blanket_order(self) validate_inter_company_party( @@ -660,6 +661,17 @@ class SalesOrder(SellingController): ).format(item.item_code) ) + def validate_reserved_stock(self): + """Clean reserved stock flag for non-stock Item""" + + enable_stock_reservation = frappe.db.get_single_value( + "Stock Settings", "enable_stock_reservation" + ) + + for item in self.items: + if item.reserve_stock and (not enable_stock_reservation or not cint(item.is_stock_item)): + item.reserve_stock = 0 + def has_unreserved_stock(self) -> bool: """Returns True if there is any unreserved item in the Sales Order.""" diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index d4ccfc4753..87aeeac368 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -10,6 +10,7 @@ "item_code", "customer_item_code", "ensure_delivery_based_on_produced_serial_no", + "is_stock_item", "reserve_stock", "col_break1", "delivery_date", @@ -867,6 +868,7 @@ { "allow_on_submit": 1, "default": "1", + "depends_on": "eval:doc.is_stock_item", "fieldname": "reserve_stock", "fieldtype": "Check", "label": "Reserve Stock", @@ -891,6 +893,16 @@ "label": "Production Plan Qty", "no_copy": 1, "read_only": 1 + }, + { + "default": "0", + "fetch_from": "item_code.is_stock_item", + "fieldname": "is_stock_item", + "fieldtype": "Check", + "hidden": 1, + "label": "Is Stock Item", + "print_hide": 1, + "report_hide": 1 } ], "idx": 1, From e3cd35b9594f78cfcccab1472cb9ba4bc9ba3632 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 15 Jan 2024 16:54:19 +0530 Subject: [PATCH 45/50] fix: WDV as per IT Act: calculate yearly amount first and then split it based on months (cherry picked from commit 22bd6a54b24129403e0b399938bddcaa9d630cae) --- .../asset_depreciation_schedule.py | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py index 74d2aa71ff..ffb50ebe45 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -7,6 +7,7 @@ from frappe.model.document import Document from frappe.utils import ( add_days, add_months, + add_years, cint, date_diff, flt, @@ -18,6 +19,7 @@ from frappe.utils import ( ) import erpnext +from erpnext.accounts.utils import get_fiscal_year class AssetDepreciationSchedule(Document): @@ -283,12 +285,20 @@ class AssetDepreciationSchedule(Document): depreciation_amount = 0 number_of_pending_depreciations = final_number_of_depreciations - start - + yearly_opening_wdv = value_after_depreciation + current_fiscal_year_end_date = None for n in range(start, final_number_of_depreciations): # If depreciation is already completed (for double declining balance) if skip_row: continue + schedule_date = add_months(row.depreciation_start_date, n * cint(row.frequency_of_depreciation)) + if not current_fiscal_year_end_date: + current_fiscal_year_end_date = get_fiscal_year(row.depreciation_start_date)[2] + elif getdate(schedule_date) > getdate(current_fiscal_year_end_date): + current_fiscal_year_end_date = add_years(current_fiscal_year_end_date, 1) + yearly_opening_wdv = value_after_depreciation + if n > 0 and len(self.get("depreciation_schedule")) > n - 1: prev_depreciation_amount = self.get("depreciation_schedule")[n - 1].depreciation_amount else: @@ -298,6 +308,7 @@ class AssetDepreciationSchedule(Document): self, asset_doc, value_after_depreciation, + yearly_opening_wdv, row, n, prev_depreciation_amount, @@ -401,8 +412,9 @@ class AssetDepreciationSchedule(Document): if not depreciation_amount: continue - value_after_depreciation -= flt( - depreciation_amount, asset_doc.precision("gross_purchase_amount") + value_after_depreciation = flt( + value_after_depreciation - flt(depreciation_amount), + asset_doc.precision("gross_purchase_amount"), ) # Adjust depreciation amount in the last period based on the expected value after useful life @@ -582,6 +594,7 @@ def get_depreciation_amount( asset_depr_schedule, asset, depreciable_value, + yearly_opening_wdv, fb_row, schedule_idx=0, prev_depreciation_amount=0, @@ -597,6 +610,7 @@ def get_depreciation_amount( asset, fb_row, depreciable_value, + yearly_opening_wdv, schedule_idx, prev_depreciation_amount, has_wdv_or_dd_non_yearly_pro_rata, @@ -744,19 +758,23 @@ def get_wdv_or_dd_depr_amount( asset, fb_row, depreciable_value, + yearly_opening_wdv, schedule_idx, prev_depreciation_amount, has_wdv_or_dd_non_yearly_pro_rata, asset_depr_schedule, ): - return get_default_wdv_or_dd_depr_amount( - asset, - fb_row, - depreciable_value, - schedule_idx, - prev_depreciation_amount, - has_wdv_or_dd_non_yearly_pro_rata, - asset_depr_schedule, + return ( + get_default_wdv_or_dd_depr_amount( + asset, + fb_row, + depreciable_value, + schedule_idx, + prev_depreciation_amount, + has_wdv_or_dd_non_yearly_pro_rata, + asset_depr_schedule, + ), + None, ) From 011c5a69f0af1d129e3de12da651074cf6817a3e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 15 Jan 2024 17:54:47 +0530 Subject: [PATCH 46/50] fix: Cancel asset capitalisation record on cancellation of asset and vice-versa (cherry picked from commit efe9f6656f01c46a6ac02e3bb61851564670d6bc) --- erpnext/assets/doctype/asset/asset.json | 5 ++--- erpnext/assets/doctype/asset/asset.py | 11 +++++++++++ .../asset_capitalization/asset_capitalization.py | 8 ++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index d0c9350d77..39a0867d98 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -202,8 +202,7 @@ "fieldname": "purchase_date", "fieldtype": "Date", "label": "Purchase Date", - "mandatory_depends_on": "eval:!doc.is_existing_asset", - "read_only": 1, + "mandatory_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset", "read_only_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset" }, { @@ -590,7 +589,7 @@ "link_fieldname": "target_asset" } ], - "modified": "2024-01-05 17:36:53.131512", + "modified": "2024-01-15 17:35:49.226603", "modified_by": "Administrator", "module": "Assets", "name": "Asset", diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index a7e6ae9afb..bab2bca099 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -162,6 +162,7 @@ class Asset(AccountsController): def on_cancel(self): self.validate_cancellation() self.cancel_movement_entries() + self.cancel_capitalization() self.delete_depreciation_entries() cancel_asset_depr_schedules(self) self.set_status() @@ -517,6 +518,16 @@ class Asset(AccountsController): movement = frappe.get_doc("Asset Movement", movement.get("name")) movement.cancel() + def cancel_capitalization(self): + asset_capitalization = frappe.db.get_value( + "Asset Capitalization", + {"target_asset": self.name, "docstatus": 1, "entry_type": "Capitalization"}, + ) + + if asset_capitalization: + asset_capitalization = frappe.get_doc("Asset Capitalization", asset_capitalization) + asset_capitalization.cancel() + def delete_depreciation_entries(self): if self.calculate_depreciation: for row in self.get("finance_books"): diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 66997ca59c..08cba7a900 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -136,11 +136,19 @@ class AssetCapitalization(StockController): "Stock Ledger Entry", "Repost Item Valuation", "Serial and Batch Bundle", + "Asset", ) + self.cancel_target_asset() self.update_stock_ledger() self.make_gl_entries() self.restore_consumed_asset_items() + def cancel_target_asset(self): + if self.entry_type == "Capitalization" and self.target_asset: + asset_doc = frappe.get_doc("Asset", self.target_asset) + if asset_doc.docstatus == 1: + asset_doc.cancel() + def set_title(self): self.title = self.target_asset_name or self.target_item_name or self.target_item_code From e219042304229c96a6be9694ee96bcbe5c544946 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 17 Jan 2024 10:57:19 +0530 Subject: [PATCH 47/50] fix: Update subscription period (cherry picked from commit 7eefedfb119bcd8d01bbad2e43d87f5ed3b707ee) --- erpnext/accounts/doctype/subscription/subscription.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index 20c9167bc9..d9132e2614 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -595,6 +595,8 @@ class Subscription(Document): ) and self.can_generate_new_invoice(posting_date): self.generate_invoice(posting_date=posting_date) self.update_subscription_period(add_days(self.current_invoice_end, 1)) + elif posting_date and getdate(posting_date) > getdate(self.current_invoice_end): + self.update_subscription_period() if self.cancel_at_period_end and ( getdate(posting_date) >= getdate(self.current_invoice_end) From 9e33216c24d211056ddfe444a3ac5f18983f3cd9 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 17 Jan 2024 14:20:43 +0530 Subject: [PATCH 48/50] fix: Asset module tests (cherry picked from commit 97f69986ff3c56c032fe32d554b9f162df3f8b33) --- erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py | 2 +- erpnext/assets/doctype/asset/test_asset.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py index 181406bad7..8d43716401 100644 --- a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py +++ b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py @@ -39,7 +39,7 @@ def test_record_generator(): ] start = 2012 - end = now_datetime().year + 5 + end = now_datetime().year + 25 for year in range(start, end): test_records.append( { diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index dc80aa5c5c..25d010500f 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -891,7 +891,7 @@ class TestDepreciationMethods(AssetSetup): ["2030-12-31", 28630.14, 28630.14], ["2031-12-31", 35684.93, 64315.07], ["2032-12-31", 17842.46, 82157.53], - ["2033-06-06", 5342.47, 87500.0], + ["2033-06-06", 5342.46, 87499.99], ] schedules = [ @@ -1003,7 +1003,7 @@ class TestDepreciationBasics(AssetSetup): asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset.name, "Active") depreciation_amount = get_depreciation_amount( - asset_depr_schedule_doc, asset, 100000, asset.finance_books[0] + asset_depr_schedule_doc, asset, 100000, 100000, asset.finance_books[0] ) self.assertEqual(depreciation_amount, 30000) From 8bc8bc1822bb2681497c414979424efe0e74bd23 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 17 Jan 2024 16:17:53 +0530 Subject: [PATCH 49/50] fix: composite asset capitalization using asset components (cherry picked from commit 5df40661d2d857eeb987e51ea6f4b465aa3d502e) --- erpnext/assets/doctype/asset/asset.py | 2 ++ .../asset_capitalization.js | 27 ++++++++++++------- .../asset_capitalization.py | 22 ++++++++++++--- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index bab2bca099..73572499f2 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -1038,6 +1038,8 @@ def is_cwip_accounting_enabled(asset_category): @frappe.whitelist() def get_asset_value_after_depreciation(asset_name, finance_book=None): asset = frappe.get_doc("Asset", asset_name) + if not asset.calculate_depreciation: + return flt(asset.value_after_depreciation) return asset.get_value_after_depreciation(finance_book) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js index be78d9ebdc..2f0de97939 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js @@ -21,10 +21,10 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s this.show_stock_ledger(); } - if (this.frm.doc.stock_items && !this.frm.doc.stock_items.length && this.frm.doc.target_asset && this.frm.doc.capitalization_method === "Choose a WIP composite asset") { - this.set_consumed_stock_items_tagged_to_wip_composite_asset(this.frm.doc.target_asset); - this.get_target_asset_details(); - } + // if (this.frm.doc.stock_items && !this.frm.doc.stock_items.length && this.frm.doc.target_asset && this.frm.doc.capitalization_method === "Choose a WIP composite asset") { + // this.set_consumed_stock_items_tagged_to_wip_composite_asset(this.frm.doc.target_asset); + // this.get_target_asset_details(); + // } } setup_queries() { @@ -143,13 +143,20 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s }, callback: function (r) { if (!r.exc && r.message) { - me.frm.clear_table("stock_items"); - - for (let item of r.message) { - me.frm.add_child("stock_items", item); + if(r.message[0] && r.message[0].length) { + me.frm.clear_table("stock_items"); + for (let item of r.message[0]) { + me.frm.add_child("stock_items", item); + } + refresh_field("stock_items"); + } + if (r.message[1] && r.message[1].length) { + me.frm.clear_table("asset_items"); + for (let item of r.message[1]) { + me.frm.add_child("asset_items", item); + } + me.frm.refresh_field("asset_items"); } - - refresh_field("stock_items"); me.calculate_totals(); } diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 08cba7a900..cad74df51e 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -889,7 +889,6 @@ def get_consumed_asset_details(args): out.cost_center = get_default_cost_center( args, item_defaults, item_group_defaults, brand_defaults ) - return out @@ -937,10 +936,27 @@ def get_items_tagged_to_wip_composite_asset(asset): "qty", "valuation_rate", "amount", + "is_fixed_asset", + "parent", ] pr_items = frappe.get_all( - "Purchase Receipt Item", filters={"wip_composite_asset": asset}, fields=fields + "Purchase Receipt Item", filters={"wip_composite_asset": asset, "docstatus": 1}, fields=fields ) - return pr_items + stock_items = [] + asset_items = [] + for d in pr_items: + if not d.is_fixed_asset: + stock_items.append(frappe._dict(d)) + else: + asset_details = frappe.db.get_value( + "Asset", + {"item_code": d.item_code, "purchase_receipt": d.parent}, + ["name as asset", "asset_name"], + as_dict=1, + ) + d.update(asset_details) + asset_items.append(frappe._dict(d)) + + return stock_items, asset_items From ff1647a1d294793be3fb5f36b8931b4669858dda Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 17 Jan 2024 21:53:00 +0530 Subject: [PATCH 50/50] fix: test for asset depreciation --- .../asset_depreciation_schedule.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py index ffb50ebe45..146c03e8c3 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -764,17 +764,14 @@ def get_wdv_or_dd_depr_amount( has_wdv_or_dd_non_yearly_pro_rata, asset_depr_schedule, ): - return ( - get_default_wdv_or_dd_depr_amount( - asset, - fb_row, - depreciable_value, - schedule_idx, - prev_depreciation_amount, - has_wdv_or_dd_non_yearly_pro_rata, - asset_depr_schedule, - ), - None, + return get_default_wdv_or_dd_depr_amount( + asset, + fb_row, + depreciable_value, + schedule_idx, + prev_depreciation_amount, + has_wdv_or_dd_non_yearly_pro_rata, + asset_depr_schedule, )