From 6590d782989de2f2c7f4689045147397a6b01bf6 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 30 Apr 2024 12:50:21 +0530 Subject: [PATCH 01/32] fix: PSOA ageing (cherry picked from commit fed2d1190581449d78cb160b214e0b47130f5169) --- .../process_statement_of_accounts.py | 2 +- ...cess_statement_of_accounts_accounts_receivable.html | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py index c73c13eb118..e0ec144e314 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py @@ -158,7 +158,7 @@ def set_ageing(doc, entry): ageing_filters = frappe._dict( { "company": doc.company, - "report_date": doc.to_date, + "report_date": doc.posting_date, "ageing_based_on": doc.ageing_based_on, "range1": 30, "range2": 60, diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html index 647600a9fea..bf8de073853 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html @@ -340,10 +340,11 @@ - - - - + + + + + @@ -352,6 +353,7 @@ +
30 Days60 Days90 Days120 Days0 - 30 Days30 - 60 Days60 - 90 Days90 - 120 DaysAbove 120 Days
{{ frappe.utils.fmt_money(ageing.range2, currency=data[0]["currency"]) }} {{ frappe.utils.fmt_money(ageing.range3, currency=data[0]["currency"]) }} {{ frappe.utils.fmt_money(ageing.range4, currency=data[0]["currency"]) }}{{ frappe.utils.fmt_money(ageing.range5, currency=filters.presentation_currency) }}
From a7533ff7f6a51dd41c299d05eefdb9e3e592140d Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Fri, 15 Mar 2024 12:14:34 +0530 Subject: [PATCH 02/32] fix: update description of Supplier Invoice Number (cherry picked from commit d721de13aaf25a02c2bc84fae3ba69c7c76819b7) --- .../accounts/doctype/accounts_settings/accounts_settings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 0e238e08f62..5449ccf316a 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -105,7 +105,7 @@ }, { "default": "0", - "description": "Enabling ensure each Purchase Invoice has a unique value in Supplier Invoice No. field", + "description": "Enabling ensure each Purchase Invoice has a unique value in Supplier Invoice No. field based on the fiscal year", "fieldname": "check_supplier_invoice_uniqueness", "fieldtype": "Check", "label": "Check Supplier Invoice Number Uniqueness" @@ -461,7 +461,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2024-01-30 14:04:26.553554", + "modified": "2024-03-15 12:11:36.085158", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", From 4036ef87647bb5bba906833f66d89cb0978cbb90 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 16 Mar 2024 19:11:59 +0530 Subject: [PATCH 03/32] chore: better description (cherry picked from commit eccd5b4c5dd4110d8a99a8883f07049f3763820b) --- .../accounts/doctype/accounts_settings/accounts_settings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 5449ccf316a..305ee72edcc 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -105,7 +105,7 @@ }, { "default": "0", - "description": "Enabling ensure each Purchase Invoice has a unique value in Supplier Invoice No. field based on the fiscal year", + "description": "Enabling this ensures each Purchase Invoice has a unique value in Supplier Invoice No. field within a particular fiscal year", "fieldname": "check_supplier_invoice_uniqueness", "fieldtype": "Check", "label": "Check Supplier Invoice Number Uniqueness" @@ -490,4 +490,4 @@ "sort_order": "ASC", "states": [], "track_changes": 1 -} \ No newline at end of file +} From b8db9033204da35b0e214d86d81f31f42a0c3586 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 14 May 2024 05:20:02 +0530 Subject: [PATCH 04/32] fix: 'Bill for Rejected Quantity in Purchase Invoice' feature not working (backport #41437) (#41445) fix: 'Bill for Rejected Quantity in Purchase Invoice' feature not working (#41437) (cherry picked from commit 33093598222d36d9728c7b209a87fbaa6e376486) Co-authored-by: rohitwaghchaure --- .../purchase_receipt/purchase_receipt.py | 6 +++ .../purchase_receipt/test_purchase_receipt.py | 37 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 2e751ad5251..af7b6c57a82 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -1163,7 +1163,12 @@ def make_purchase_invoice(source_name, target_doc=None, args=None): qty = item_row.qty if frappe.db.get_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice"): qty = item_row.received_qty + pending_qty = qty - invoiced_qty_map.get(item_row.name, 0) + + if frappe.db.get_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice"): + return pending_qty, 0 + returned_qty = flt(returned_qty_map.get(item_row.name, 0)) if returned_qty: if returned_qty >= pending_qty: @@ -1172,6 +1177,7 @@ def make_purchase_invoice(source_name, target_doc=None, args=None): else: pending_qty -= returned_qty returned_qty = 0 + return pending_qty, returned_qty doclist = get_mapped_doc( diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 54a695126c7..3a4e6963476 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -895,6 +895,8 @@ class TestPurchaseReceipt(FrappeTestCase): create_purchase_order, ) + frappe.db.set_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice", 0) + po = create_purchase_order() pr = create_pr_against_po(po.name) @@ -914,6 +916,7 @@ class TestPurchaseReceipt(FrappeTestCase): po.cancel() def test_make_purchase_invoice_from_pr_with_returned_qty_duplicate_items(self): + frappe.db.set_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice", 0) pr1 = make_purchase_receipt(qty=8, do_not_submit=True) pr1.append( "items", @@ -2783,6 +2786,40 @@ class TestPurchaseReceipt(FrappeTestCase): frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1) + def test_purchase_receipt_bill_for_rejected_quantity_in_purchase_invoice(self): + item_code = make_item( + "_Test Purchase Receipt Bill For Rejected Quantity", + properties={"is_stock_item": 1}, + ).name + + pr = make_purchase_receipt(item_code=item_code, qty=5, rate=100) + + return_pr = make_purchase_receipt( + item_code=item_code, + is_return=1, + return_against=pr.name, + qty=-2, + do_not_submit=1, + ) + return_pr.items[0].purchase_receipt_item = pr.items[0].name + return_pr.submit() + old_value = frappe.db.get_single_value( + "Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice" + ) + + frappe.db.set_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice", 0) + pi = make_purchase_invoice(pr.name) + self.assertEqual(pi.items[0].qty, 3) + + frappe.db.set_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice", 1) + pi = make_purchase_invoice(pr.name) + pi.submit() + self.assertEqual(pi.items[0].qty, 5) + + frappe.db.set_single_value( + "Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice", old_value + ) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier From d988c7bd0698e2e300681384bcff847dd7d072bc Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 14 May 2024 05:21:17 +0530 Subject: [PATCH 05/32] fix: Unknown column 'tabBatch.batch_no' in 'where clause' (backport #41418) (#41444) fix: Unknown column 'tabBatch.batch_no' in 'where clause' (#41418) fix: unknown column 'tabBatch.batch_no' in 'where clause' (cherry picked from commit 6bd13d745238ef3f6199a210f9b5cac5cc4025dc) Co-authored-by: rohitwaghchaure --- .../serial_and_batch_summary/serial_and_batch_summary.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py index 7bd8d704fda..15d9a12bc65 100644 --- a/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py +++ b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py @@ -220,7 +220,7 @@ def get_serial_nos(doctype, txt, searchfield, start, page_len, filters): def get_batch_nos(doctype, txt, searchfield, start, page_len, filters): query_filters = {} - if txt: + if filters.get("voucher_no") and txt: query_filters["batch_no"] = ["like", f"%{txt}%"] if filters.get("voucher_no"): @@ -239,5 +239,8 @@ def get_batch_nos(doctype, txt, searchfield, start, page_len, filters): ) else: + if txt: + query_filters["name"] = ["like", f"%{txt}%"] + query_filters["item"] = filters.get("item_code") return frappe.get_all("Batch", filters=query_filters, as_list=True) From 55edbec6fa508ce9e8e8efc9e672fdcfe71affae Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 11 May 2024 19:35:52 +0530 Subject: [PATCH 06/32] fix: Duplicate party name column in AR/AP report (cherry picked from commit 7501fe8ebd9c0735fcdb62529f275d970a86cca5) --- .../accounts_receivable/accounts_receivable.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 2bd493cd4d0..64dba0183dc 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -1028,20 +1028,6 @@ class ReceivablePayableReport: fieldtype="Link", options="Contact", ) - if self.filters.party_type == "Customer": - self.add_column( - _("Customer Name"), - fieldname="customer_name", - fieldtype="Link", - options="Customer", - ) - elif self.filters.party_type == "Supplier": - self.add_column( - _("Supplier Name"), - fieldname="supplier_name", - fieldtype="Link", - options="Supplier", - ) self.add_column(label=_("Cost Center"), fieldname="cost_center", fieldtype="Data") self.add_column(label=_("Voucher Type"), fieldname="voucher_type", fieldtype="Data") From 5c3a0965bc1de4886a05329154f32902938d6926 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 14 May 2024 14:22:51 +0530 Subject: [PATCH 07/32] fix: zero valuation rate for batched item (backport #41446) (#41447) fix: zero valuation rate for batched item (#41446) (cherry picked from commit e3a80ebdf3a077bbf8293aff10e9b7d434af48c2) Co-authored-by: rohitwaghchaure --- .../purchase_receipt/test_purchase_receipt.py | 44 +++++++++++++++++++ .../serial_and_batch_bundle.py | 3 +- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 3a4e6963476..ede2dc00714 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -2820,6 +2820,50 @@ class TestPurchaseReceipt(FrappeTestCase): "Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice", old_value ) + def test_zero_valuation_rate_for_batched_item(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + + item = make_item( + "_Test Zero Valuation Rate For the Batch Item", + { + "is_purchase_item": 1, + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "TZVRFORBATCH.#####", + "valuation_rate": 200, + }, + ) + + pi = make_purchase_receipt( + qty=10, + rate=0, + item_code=item.name, + ) + + pi.reload() + batch_no = get_batch_from_bundle(pi.items[0].serial_and_batch_bundle) + + se = make_stock_entry( + purpose="Material Issue", + item_code=item.name, + source=pi.items[0].warehouse, + qty=10, + batch_no=batch_no, + use_serial_batch_fields=0, + ) + + se.submit() + + se.reload() + + self.assertEqual(se.items[0].valuation_rate, 0) + self.assertEqual(se.items[0].basic_rate, 0) + + sabb_doc = frappe.get_doc("Serial and Batch Bundle", se.items[0].serial_and_batch_bundle) + for row in sabb_doc.entries: + self.assertEqual(row.incoming_rate, 0) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier 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 67f946eb7c8..a1a8a85e745 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 @@ -249,8 +249,7 @@ class SerialandBatchBundle(Document): if self.has_serial_no: d.incoming_rate = abs(sn_obj.serial_no_incoming_rate.get(d.serial_no, 0.0)) else: - if sn_obj.batch_avg_rate.get(d.batch_no): - d.incoming_rate = abs(sn_obj.batch_avg_rate.get(d.batch_no)) + d.incoming_rate = abs(flt(sn_obj.batch_avg_rate.get(d.batch_no))) available_qty = flt(sn_obj.available_qty.get(d.batch_no), d.precision("qty")) if self.docstatus == 1: From 9b3f3095983f834e407e0841151b2592c891b327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CKhushi?= <“khushirawat.sophia@gamil.com”> Date: Mon, 22 Apr 2024 14:13:10 +0530 Subject: [PATCH 08/32] refactor: renamed purchase receipt amount field to purchase amount (cherry picked from commit 31841b4ab239f133f9df6d868137a6fb0e248554) # Conflicts: # erpnext/assets/doctype/asset/asset.json # erpnext/assets/doctype/asset/asset.py # erpnext/patches.txt --- .../purchase_invoice/purchase_invoice.py | 2 +- erpnext/assets/doctype/asset/asset.js | 2 +- erpnext/assets/doctype/asset/asset.json | 39 ++++++++++++------- erpnext/assets/doctype/asset/asset.py | 21 ++++++---- erpnext/assets/doctype/asset/test_asset.py | 2 +- .../asset_capitalization.py | 6 +-- .../test_asset_capitalization.py | 8 ++-- erpnext/controllers/buying_controller.py | 2 +- erpnext/patches.txt | 5 +++ ...chase_receipt_amount_to_purchase_amount.py | 8 ++++ .../purchase_receipt/purchase_receipt.py | 2 +- 11 files changed, 63 insertions(+), 34 deletions(-) create mode 100644 erpnext/patches/v15_0/rename_purchase_receipt_amount_to_purchase_amount.py diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 272a180ca8d..496ffcd2648 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1182,7 +1182,7 @@ class PurchaseInvoice(BuyingController): asset.name, { "gross_purchase_amount": purchase_amount, - "purchase_receipt_amount": purchase_amount, + "purchase_amount": purchase_amount, }, ) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 0f71e5d6f60..5ed62cb132f 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -652,7 +652,7 @@ frappe.ui.form.on("Asset", { ); frm.set_value("gross_purchase_amount", purchase_amount); - frm.set_value("purchase_receipt_amount", purchase_amount); + frm.set_value("purchase_amount", purchase_amount); frm.set_value("asset_quantity", asset_quantity); frm.set_value("cost_center", item.cost_center || purchase_doc.cost_center); if (item.asset_location) { diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index 39a0867d984..785bccb689c 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -15,6 +15,7 @@ "asset_owner_company", "is_existing_asset", "is_composite_asset", + "is_composite_component", "supplier", "customer", "image", @@ -72,7 +73,7 @@ "status", "booked_fixed_asset", "column_break_51", - "purchase_receipt_amount", + "purchase_amount", "default_finance_book", "depr_entry_posting_status", "amended_from", @@ -202,8 +203,7 @@ "fieldname": "purchase_date", "fieldtype": "Date", "label": "Purchase Date", - "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" + "mandatory_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset" }, { "fieldname": "disposal_date", @@ -234,7 +234,7 @@ "fieldname": "available_for_use_date", "fieldtype": "Date", "label": "Available-for-use Date", - "mandatory_depends_on": "eval:(!doc.is_composite_asset || doc.docstatus==1)" + "mandatory_depends_on": "eval:(!doc.is_composite_component && doc.docstatus==1)" }, { "default": "0", @@ -408,15 +408,6 @@ "options": "Purchase Receipt", "print_hide": 1 }, - { - "fieldname": "purchase_receipt_amount", - "fieldtype": "Currency", - "hidden": 1, - "label": "Purchase Receipt Amount", - "no_copy": 1, - "print_hide": 1, - "read_only": 1 - }, { "depends_on": "eval:!doc.is_composite_asset && !doc.is_existing_asset", "fieldname": "purchase_invoice", @@ -518,7 +509,7 @@ }, { "default": "0", - "depends_on": "eval:!doc.is_existing_asset", + "depends_on": "eval:(!doc.is_existing_asset && !doc.is_composite_component)", "fieldname": "is_composite_asset", "fieldtype": "Check", "label": "Is Composite Asset" @@ -546,6 +537,22 @@ "label": "Additional Asset Cost", "options": "Company:company:default_currency", "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:(!doc.is_existing_asset && !doc.is_composite_asset)", + "fieldname": "is_composite_component", + "fieldtype": "Check", + "label": "Is Composite Component" + }, + { + "fieldname": "purchase_amount", + "fieldtype": "Currency", + "hidden": 1, + "label": "Purchase Amount", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "idx": 72, @@ -589,7 +596,11 @@ "link_fieldname": "target_asset" } ], +<<<<<<< HEAD "modified": "2024-01-15 17:35:49.226603", +======= + "modified": "2024-04-18 16:45:47.306032", +>>>>>>> 31841b4ab2 (refactor: renamed purchase receipt amount field to purchase amount) "modified_by": "Administrator", "module": "Assets", "name": "Asset", diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 3a05a598b1c..4fd5970f5bd 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -80,6 +80,7 @@ class Asset(AccountsController): insured_value: DF.Data | None insurer: DF.Data | None is_composite_asset: DF.Check + is_composite_component: DF.Check is_existing_asset: DF.Check is_fully_depreciated: DF.Check item_code: DF.Link @@ -92,10 +93,10 @@ class Asset(AccountsController): number_of_depreciations_booked: DF.Int opening_accumulated_depreciation: DF.Currency policy_number: DF.Data | None + purchase_amount: DF.Currency purchase_date: DF.Date | None purchase_invoice: DF.Link | None purchase_receipt: DF.Link | None - purchase_receipt_amount: DF.Currency split_from: DF.Link | None status: DF.Literal[ "Draft", @@ -354,7 +355,7 @@ class Asset(AccountsController): if self.is_existing_asset: return - if self.gross_purchase_amount and self.gross_purchase_amount != self.purchase_receipt_amount: + if self.gross_purchase_amount and self.gross_purchase_amount != self.purchase_amount: error_message = _( "Gross Purchase Amount should be equal to purchase amount of one single Asset." ) @@ -696,7 +697,11 @@ class Asset(AccountsController): purchase_document = self.get_purchase_document() fixed_asset_account, cwip_account = self.get_fixed_asset_account(), self.get_cwip_account() +<<<<<<< HEAD if purchase_document and self.purchase_receipt_amount and self.available_for_use_date <= nowdate(): +======= + if purchase_document and self.purchase_amount and getdate(self.available_for_use_date) <= getdate(): +>>>>>>> 31841b4ab2 (refactor: renamed purchase receipt amount field to purchase amount) gl_entries.append( self.get_gl_dict( { @@ -704,8 +709,8 @@ class Asset(AccountsController): "against": fixed_asset_account, "remarks": self.get("remarks") or _("Accounting Entry for Asset"), "posting_date": self.available_for_use_date, - "credit": self.purchase_receipt_amount, - "credit_in_account_currency": self.purchase_receipt_amount, + "credit": self.purchase_amount, + "credit_in_account_currency": self.purchase_amount, "cost_center": self.cost_center, }, item=self, @@ -719,8 +724,8 @@ class Asset(AccountsController): "against": cwip_account, "remarks": self.get("remarks") or _("Accounting Entry for Asset"), "posting_date": self.available_for_use_date, - "debit": self.purchase_receipt_amount, - "debit_in_account_currency": self.purchase_receipt_amount, + "debit": self.purchase_amount, + "debit_in_account_currency": self.purchase_amount, "cost_center": self.cost_center, }, item=self, @@ -1116,8 +1121,8 @@ def create_new_asset_after_split(asset, split_qty): ) new_asset.gross_purchase_amount = new_gross_purchase_amount - if asset.purchase_receipt_amount: - new_asset.purchase_receipt_amount = new_gross_purchase_amount + if asset.purchase_amount: + new_asset.purchase_amount = new_gross_purchase_amount new_asset.opening_accumulated_depreciation = opening_accumulated_depreciation new_asset.asset_quantity = split_qty new_asset.split_from = asset.name diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 1b3951e86a6..5f5268180d4 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -1698,7 +1698,7 @@ def create_asset(**args): "opening_accumulated_depreciation": args.opening_accumulated_depreciation or 0, "number_of_depreciations_booked": args.number_of_depreciations_booked or 0, "gross_purchase_amount": args.gross_purchase_amount or 100000, - "purchase_receipt_amount": args.purchase_receipt_amount or 100000, + "purchase_amount": args.purchase_amount or 100000, "maintenance_required": args.maintenance_required or 0, "warehouse": args.warehouse or "_Test Warehouse - _TC", "available_for_use_date": args.available_for_use_date or "2020-06-06", diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 8d3bcfc153d..66e8bd83e24 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -616,8 +616,8 @@ class AssetCapitalization(StockController): asset_doc.available_for_use_date = self.posting_date asset_doc.purchase_date = self.posting_date asset_doc.gross_purchase_amount = total_target_asset_value - asset_doc.purchase_receipt_amount = total_target_asset_value - asset_doc.purchase_receipt_amount = total_target_asset_value + asset_doc.purchase_amount = total_target_asset_value + asset_doc.purchase_amount = total_target_asset_value asset_doc.capitalized_in = self.name asset_doc.flags.ignore_validate = True asset_doc.flags.asset_created_via_asset_capitalization = True @@ -653,7 +653,7 @@ class AssetCapitalization(StockController): asset_doc = frappe.get_doc("Asset", self.target_asset) asset_doc.gross_purchase_amount = total_target_asset_value - asset_doc.purchase_receipt_amount = total_target_asset_value + asset_doc.purchase_amount = total_target_asset_value asset_doc.capitalized_in = self.name asset_doc.flags.ignore_validate = True asset_doc.save() diff --git a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py index 86a18c07d1f..31723ef3be3 100644 --- a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py @@ -89,7 +89,7 @@ class TestAssetCapitalization(unittest.TestCase): # Test Target Asset values target_asset = frappe.get_doc("Asset", asset_capitalization.target_asset) self.assertEqual(target_asset.gross_purchase_amount, total_amount) - self.assertEqual(target_asset.purchase_receipt_amount, total_amount) + self.assertEqual(target_asset.purchase_amount, total_amount) # Test Consumed Asset values self.assertEqual(consumed_asset.db_get("status"), "Capitalized") @@ -179,7 +179,7 @@ class TestAssetCapitalization(unittest.TestCase): # Test Target Asset values target_asset = frappe.get_doc("Asset", asset_capitalization.target_asset) self.assertEqual(target_asset.gross_purchase_amount, total_amount) - self.assertEqual(target_asset.purchase_receipt_amount, total_amount) + self.assertEqual(target_asset.purchase_amount, total_amount) # Test Consumed Asset values self.assertEqual(consumed_asset.db_get("status"), "Capitalized") @@ -256,7 +256,7 @@ class TestAssetCapitalization(unittest.TestCase): # Test Target Asset values target_asset = frappe.get_doc("Asset", asset_capitalization.target_asset) self.assertEqual(target_asset.gross_purchase_amount, total_amount) - self.assertEqual(target_asset.purchase_receipt_amount, total_amount) + self.assertEqual(target_asset.purchase_amount, total_amount) # Test General Ledger Entries expected_gle = { @@ -526,7 +526,7 @@ def create_depreciation_asset(**args): asset.available_for_use_date = args.available_for_use_date or asset.purchase_date asset.gross_purchase_amount = args.asset_value or 100000 - asset.purchase_receipt_amount = asset.gross_purchase_amount + asset.purchase_amount = asset.gross_purchase_amount finance_book = asset.append("finance_books") finance_book.depreciation_start_date = args.depreciation_start_date or "2020-12-31" diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 58bcc8c5fbe..14bf8ad7436 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -787,7 +787,7 @@ class BuyingController(SubcontractingController): "supplier": self.supplier, "purchase_date": self.posting_date, "calculate_depreciation": 0, - "purchase_receipt_amount": purchase_amount, + "purchase_amount": purchase_amount, "gross_purchase_amount": purchase_amount, "asset_quantity": asset_quantity, "purchase_receipt": self.name if self.doctype == "Purchase Receipt" else None, diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 0465f2ca53e..1e476d14892 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -363,3 +363,8 @@ erpnext.patches.v14_0.set_maintain_stock_for_bom_item erpnext.patches.v15_0.delete_orphaned_asset_movement_item_records erpnext.patches.v15_0.fix_debit_credit_in_transaction_currency erpnext.patches.v15_0.remove_cancelled_asset_capitalization_from_asset +<<<<<<< HEAD +======= +erpnext.patches.v15_0.fix_debit_credit_in_transaction_currency +erpnext.patches.v15_0.rename_purchase_receipt_amount_to_purchase_amount +>>>>>>> 31841b4ab2 (refactor: renamed purchase receipt amount field to purchase amount) diff --git a/erpnext/patches/v15_0/rename_purchase_receipt_amount_to_purchase_amount.py b/erpnext/patches/v15_0/rename_purchase_receipt_amount_to_purchase_amount.py new file mode 100644 index 00000000000..8af3ed26323 --- /dev/null +++ b/erpnext/patches/v15_0/rename_purchase_receipt_amount_to_purchase_amount.py @@ -0,0 +1,8 @@ +import frappe +from frappe.model.utils.rename_field import rename_field + + +def execute(): + frappe.reload_doc("assets", "doctype", "asset") + if frappe.db.has_column("Asset", "purchase_receipt_amount"): + rename_field("Asset", "purchase_receipt_amount", "purchase_amount") diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index af7b6c57a82..5f898b94268 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -858,7 +858,7 @@ class PurchaseReceipt(BuyingController): asset.name, { "gross_purchase_amount": purchase_amount, - "purchase_receipt_amount": purchase_amount, + "purchase_amount": purchase_amount, }, ) From 0aca1e8e05dead191ee323df7f0615e6e12457f4 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Fri, 10 May 2024 14:39:59 +0530 Subject: [PATCH 09/32] fix: removed unrelated code modification (cherry picked from commit 360c3b36edd76faa23d620c04073111804287461) --- erpnext/assets/doctype/asset/asset.json | 15 ++++----------- erpnext/assets/doctype/asset/asset.py | 1 - .../asset_capitalization/asset_capitalization.py | 1 - 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index 785bccb689c..da1a6eb07a9 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -15,7 +15,6 @@ "asset_owner_company", "is_existing_asset", "is_composite_asset", - "is_composite_component", "supplier", "customer", "image", @@ -203,7 +202,8 @@ "fieldname": "purchase_date", "fieldtype": "Date", "label": "Purchase Date", - "mandatory_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset" + "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" }, { "fieldname": "disposal_date", @@ -234,7 +234,7 @@ "fieldname": "available_for_use_date", "fieldtype": "Date", "label": "Available-for-use Date", - "mandatory_depends_on": "eval:(!doc.is_composite_component && doc.docstatus==1)" + "mandatory_depends_on": "eval:(!doc.is_composite_asset || doc.docstatus==1)" }, { "default": "0", @@ -509,7 +509,7 @@ }, { "default": "0", - "depends_on": "eval:(!doc.is_existing_asset && !doc.is_composite_component)", + "depends_on": "eval:!doc.is_existing_asset", "fieldname": "is_composite_asset", "fieldtype": "Check", "label": "Is Composite Asset" @@ -538,13 +538,6 @@ "options": "Company:company:default_currency", "read_only": 1 }, - { - "default": "0", - "depends_on": "eval:(!doc.is_existing_asset && !doc.is_composite_asset)", - "fieldname": "is_composite_component", - "fieldtype": "Check", - "label": "Is Composite Component" - }, { "fieldname": "purchase_amount", "fieldtype": "Currency", diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 4fd5970f5bd..ffdd8b358df 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -80,7 +80,6 @@ class Asset(AccountsController): insured_value: DF.Data | None insurer: DF.Data | None is_composite_asset: DF.Check - is_composite_component: DF.Check is_existing_asset: DF.Check is_fully_depreciated: DF.Check item_code: DF.Link diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 66e8bd83e24..6c329b0da78 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -617,7 +617,6 @@ class AssetCapitalization(StockController): asset_doc.purchase_date = self.posting_date asset_doc.gross_purchase_amount = total_target_asset_value asset_doc.purchase_amount = total_target_asset_value - asset_doc.purchase_amount = total_target_asset_value asset_doc.capitalized_in = self.name asset_doc.flags.ignore_validate = True asset_doc.flags.asset_created_via_asset_capitalization = True From 3eff9c97794a8caedb3862fc2fbeb499c2620684 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Fri, 10 May 2024 12:12:36 +0530 Subject: [PATCH 10/32] fix(wip): depreciation calculation after asset value adjustment (cherry picked from commit d3200fb67fdb17067aaf5c490f89c374c08d82fa) --- .../asset_depreciation_schedule.py | 100 ++++++------------ .../test_asset_depreciation_schedule.py | 46 ++++++++ 2 files changed, 77 insertions(+), 69 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 abbca68fea0..d00a709ca10 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -637,49 +637,18 @@ def get_straight_line_or_manual_depr_amount( elif asset.flags.decrease_in_asset_value_due_to_value_adjustment: if row.daily_prorata_based: amount = flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) - total_days = ( - date_diff( - get_last_day( - add_months( - row.depreciation_start_date, - flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked - 1) - * row.frequency_of_depreciation, - ) - ), - add_days( - get_last_day( - add_months( - row.depreciation_start_date, - flt( - row.total_number_of_depreciations - - asset.number_of_depreciations_booked - - number_of_pending_depreciations - - 1 - ) - * row.frequency_of_depreciation, - ) - ), - 1, - ), - ) - + 1 - ) + total_years = flt(number_of_pending_depreciations * row.frequency_of_depreciation) / 12 + every_year_depr = amount / total_years - daily_depr_amount = amount / total_days - - to_date = get_last_day( - add_months(row.depreciation_start_date, schedule_idx * row.frequency_of_depreciation) + year_start_date = add_years( + row.depreciation_start_date, (row.frequency_of_depreciation * schedule_idx) // 12 ) - from_date = add_days( - get_last_day( - add_months( - row.depreciation_start_date, (schedule_idx - 1) * row.frequency_of_depreciation - ) - ), - 1, + year_end_date = add_days(add_years(year_start_date, 1), -1) + daily_depr_amount = every_year_depr / (date_diff(year_end_date, year_start_date) + 1) + total_depreciable_days = _get_total_days( + row.depreciation_start_date, schedule_idx, row.frequency_of_depreciation ) - - return daily_depr_amount * (date_diff(to_date, from_date) + 1) + return daily_depr_amount * total_depreciable_days else: return ( flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) @@ -692,40 +661,24 @@ def get_straight_line_or_manual_depr_amount( - flt(asset.opening_accumulated_depreciation) - flt(row.expected_value_after_useful_life) ) - - total_days = ( - date_diff( - get_last_day( - add_months( - row.depreciation_start_date, - flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked - 1) - * row.frequency_of_depreciation, - ) - ), - add_days( - get_last_day( - add_months(row.depreciation_start_date, -1 * row.frequency_of_depreciation) - ), - 1, - ), + total_years = ( + flt( + (row.total_number_of_depreciations - asset.number_of_depreciations_booked) + * row.frequency_of_depreciation ) - + 1 + / 12 ) + every_year_depr = amount / total_years - daily_depr_amount = amount / total_days - - to_date = get_last_day( - add_months(row.depreciation_start_date, schedule_idx * row.frequency_of_depreciation) + year_start_date = add_years( + row.depreciation_start_date, (row.frequency_of_depreciation * schedule_idx) // 12 ) - from_date = add_days( - get_last_day( - add_months( - row.depreciation_start_date, (schedule_idx - 1) * row.frequency_of_depreciation - ) - ), - 1, + year_end_date = add_days(add_years(year_start_date, 1), -1) + daily_depr_amount = every_year_depr / (date_diff(year_end_date, year_start_date) + 1) + total_depreciable_days = _get_total_days( + row.depreciation_start_date, schedule_idx, row.frequency_of_depreciation ) - return daily_depr_amount * (date_diff(to_date, from_date) + 1) + return daily_depr_amount * total_depreciable_days else: return ( flt(asset.gross_purchase_amount) @@ -734,6 +687,15 @@ def get_straight_line_or_manual_depr_amount( ) / flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked) +def _get_total_days(depreciation_start_date, schedule_idx, frequency_of_depreciation): + from_date = add_months(depreciation_start_date, (schedule_idx - 1) * frequency_of_depreciation) + to_date = add_months(from_date, frequency_of_depreciation) + if is_last_day_of_the_month(depreciation_start_date): + to_date = get_last_day(to_date) + from_date = add_days(get_last_day(from_date), 1) + return date_diff(to_date, from_date) + 1 + + def get_shift_depr_amount(asset_depr_schedule, asset, row, schedule_idx): if asset_depr_schedule.get("__islocal") and not asset.flags.shift_allocation: return ( diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py index c55063f2ebf..5971d1662f9 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py @@ -3,10 +3,12 @@ import frappe from frappe.tests.utils import FrappeTestCase +from frappe.utils import cstr from erpnext.assets.doctype.asset.test_asset import create_asset, create_asset_data from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import ( get_asset_depr_schedule_doc, + get_depr_schedule, ) @@ -25,3 +27,47 @@ class TestAssetDepreciationSchedule(FrappeTestCase): ) self.assertRaises(frappe.ValidationError, second_asset_depr_schedule.insert) + + def test_daily_prorata_based_depr_on_sl_methond(self): + asset = create_asset( + calculate_depreciation=1, + depreciation_method="Straight Line", + daily_prorata_based=1, + available_for_use_date="2020-01-01", + depreciation_start_date="2020-01-31", + frequency_of_depreciation=1, + total_number_of_depreciations=24, + ) + + expected_schedules = [ + ["2020-01-31", 4234.97, 4234.97], + ["2020-02-29", 3961.75, 8196.72], + ["2020-03-31", 4234.97, 12431.69], + ["2020-04-30", 4098.36, 16530.05], + ["2020-05-31", 4234.97, 20765.02], + ["2020-06-30", 4098.36, 24863.38], + ["2020-07-31", 4234.97, 29098.35], + ["2020-08-31", 4234.97, 33333.32], + ["2020-09-30", 4098.36, 37431.68], + ["2020-10-31", 4234.97, 41666.65], + ["2020-11-30", 4098.36, 45765.01], + ["2020-12-31", 4234.97, 49999.98], + ["2021-01-31", 4246.58, 54246.56], + ["2021-02-28", 3835.62, 58082.18], + ["2021-03-31", 4246.58, 62328.76], + ["2021-04-30", 4109.59, 66438.35], + ["2021-05-31", 4246.58, 70684.93], + ["2021-06-30", 4109.59, 74794.52], + ["2021-07-31", 4246.58, 79041.1], + ["2021-08-31", 4246.58, 83287.68], + ["2021-09-30", 4109.59, 87397.27], + ["2021-10-31", 4246.58, 91643.85], + ["2021-11-30", 4109.59, 95753.44], + ["2021-12-31", 4246.56, 100000.0], + ] + + schedules = [ + [cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount] + for d in get_depr_schedule(asset.name, "Draft") + ] + self.assertEqual(schedules, expected_schedules) From 0b21026eef76410309bd10768a90449d1cff2f83 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Fri, 10 May 2024 15:21:34 +0530 Subject: [PATCH 11/32] refactor: removed code duplicacies (cherry picked from commit 6b24143f7224df042c4d61d3919cfe71bc350526) --- .../asset_depreciation_schedule.py | 52 +++++++++---------- 1 file changed, 25 insertions(+), 27 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 d00a709ca10..de9067da5fd 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -637,18 +637,14 @@ def get_straight_line_or_manual_depr_amount( elif asset.flags.decrease_in_asset_value_due_to_value_adjustment: if row.daily_prorata_based: amount = flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) - total_years = flt(number_of_pending_depreciations * row.frequency_of_depreciation) / 12 - every_year_depr = amount / total_years - year_start_date = add_years( - row.depreciation_start_date, (row.frequency_of_depreciation * schedule_idx) // 12 + return get_daily_prorata_based_straight_line_depr( + asset, + row, + schedule_idx, + number_of_pending_depreciations, + amount, ) - year_end_date = add_days(add_years(year_start_date, 1), -1) - daily_depr_amount = every_year_depr / (date_diff(year_end_date, year_start_date) + 1) - total_depreciable_days = _get_total_days( - row.depreciation_start_date, schedule_idx, row.frequency_of_depreciation - ) - return daily_depr_amount * total_depreciable_days else: return ( flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) @@ -661,24 +657,9 @@ def get_straight_line_or_manual_depr_amount( - flt(asset.opening_accumulated_depreciation) - flt(row.expected_value_after_useful_life) ) - total_years = ( - flt( - (row.total_number_of_depreciations - asset.number_of_depreciations_booked) - * row.frequency_of_depreciation - ) - / 12 + return get_daily_prorata_based_straight_line_depr( + asset, row, schedule_idx, number_of_pending_depreciations, amount ) - every_year_depr = amount / total_years - - year_start_date = add_years( - row.depreciation_start_date, (row.frequency_of_depreciation * schedule_idx) // 12 - ) - year_end_date = add_days(add_years(year_start_date, 1), -1) - daily_depr_amount = every_year_depr / (date_diff(year_end_date, year_start_date) + 1) - total_depreciable_days = _get_total_days( - row.depreciation_start_date, schedule_idx, row.frequency_of_depreciation - ) - return daily_depr_amount * total_depreciable_days else: return ( flt(asset.gross_purchase_amount) @@ -687,6 +668,23 @@ def get_straight_line_or_manual_depr_amount( ) / flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked) +def get_daily_prorata_based_straight_line_depr( + asset, row, schedule_idx, number_of_pending_depreciations, amount, total_years +): + total_years = flt(number_of_pending_depreciations * row.frequency_of_depreciation) / 12 + every_year_depr = amount / total_years + + year_start_date = add_years( + row.depreciation_start_date, (row.frequency_of_depreciation * schedule_idx) // 12 + ) + year_end_date = add_days(add_years(year_start_date, 1), -1) + daily_depr_amount = every_year_depr / (date_diff(year_end_date, year_start_date) + 1) + total_depreciable_days = _get_total_days( + row.depreciation_start_date, schedule_idx, row.frequency_of_depreciation + ) + return daily_depr_amount * total_depreciable_days + + def _get_total_days(depreciation_start_date, schedule_idx, frequency_of_depreciation): from_date = add_months(depreciation_start_date, (schedule_idx - 1) * frequency_of_depreciation) to_date = add_months(from_date, frequency_of_depreciation) From ce2c6c31651536d735bcda0b3729012d905fd058 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Mon, 13 May 2024 00:10:43 +0530 Subject: [PATCH 12/32] fix(minor): removed extra parameter (cherry picked from commit 98e7dfe97fd08c4607f3423755e9c75629d07994) --- .../asset_depreciation_schedule/asset_depreciation_schedule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 de9067da5fd..27da403cd35 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -669,7 +669,7 @@ def get_straight_line_or_manual_depr_amount( def get_daily_prorata_based_straight_line_depr( - asset, row, schedule_idx, number_of_pending_depreciations, amount, total_years + asset, row, schedule_idx, number_of_pending_depreciations, amount ): total_years = flt(number_of_pending_depreciations * row.frequency_of_depreciation) / 12 every_year_depr = amount / total_years From 8cca74d5ef720788512108e0a2610db42935c6a9 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 14 May 2024 19:29:23 +0530 Subject: [PATCH 13/32] fix: resolved conflict --- erpnext/assets/doctype/asset/asset.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index da1a6eb07a9..3a2a942bdf2 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -589,11 +589,7 @@ "link_fieldname": "target_asset" } ], -<<<<<<< HEAD - "modified": "2024-01-15 17:35:49.226603", -======= "modified": "2024-04-18 16:45:47.306032", ->>>>>>> 31841b4ab2 (refactor: renamed purchase receipt amount field to purchase amount) "modified_by": "Administrator", "module": "Assets", "name": "Asset", @@ -637,4 +633,4 @@ "states": [], "title_field": "asset_name", "track_changes": 1 -} \ No newline at end of file +} From 3a72f4bd309602358457406652433d11e0995f2c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 14 May 2024 19:34:29 +0530 Subject: [PATCH 14/32] fix: resolved conflict --- erpnext/assets/doctype/asset/asset.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index ffdd8b358df..4edfcd11c89 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -696,11 +696,7 @@ class Asset(AccountsController): purchase_document = self.get_purchase_document() fixed_asset_account, cwip_account = self.get_fixed_asset_account(), self.get_cwip_account() -<<<<<<< HEAD - if purchase_document and self.purchase_receipt_amount and self.available_for_use_date <= nowdate(): -======= if purchase_document and self.purchase_amount and getdate(self.available_for_use_date) <= getdate(): ->>>>>>> 31841b4ab2 (refactor: renamed purchase receipt amount field to purchase amount) gl_entries.append( self.get_gl_dict( { From 520e1e9c8f5771b5209e4d2fc5b8a7bd774ff06a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 14 May 2024 19:37:34 +0530 Subject: [PATCH 15/32] fix: resolved conflict --- erpnext/patches.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 1e476d14892..f2868b90d5c 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -363,8 +363,4 @@ erpnext.patches.v14_0.set_maintain_stock_for_bom_item erpnext.patches.v15_0.delete_orphaned_asset_movement_item_records erpnext.patches.v15_0.fix_debit_credit_in_transaction_currency erpnext.patches.v15_0.remove_cancelled_asset_capitalization_from_asset -<<<<<<< HEAD -======= -erpnext.patches.v15_0.fix_debit_credit_in_transaction_currency erpnext.patches.v15_0.rename_purchase_receipt_amount_to_purchase_amount ->>>>>>> 31841b4ab2 (refactor: renamed purchase receipt amount field to purchase amount) From e18be9b21e7905ab4c8eb3d3be94ea3010b821c5 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 14 May 2024 17:56:06 +0530 Subject: [PATCH 16/32] fix: Asset cancelation issue (cherry picked from commit fa2b6c4490015a4c36e354f5702aed0e5974b228) --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 4de20a17389..eea4698228d 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -450,11 +450,12 @@ class JournalEntry(AccountsController): def unlink_asset_reference(self): for d in self.get("accounts"): + root_type = frappe.get_value("Account", d.account, "root_type") if ( self.voucher_type == "Depreciation Entry" and d.reference_type == "Asset" and d.reference_name - and d.account_type == "Depreciation" + and root_type == "Expense" and d.debit ): asset = frappe.get_doc("Asset", d.reference_name) From ccda17ede2a50ca1bf1612493019d54138dddf12 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 14 May 2024 20:03:58 +0530 Subject: [PATCH 17/32] style: code optimization (cherry picked from commit e843683ad1d90ef046690c66e582237473e1b108) --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index eea4698228d..402f19ee968 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -450,12 +450,11 @@ class JournalEntry(AccountsController): def unlink_asset_reference(self): for d in self.get("accounts"): - root_type = frappe.get_value("Account", d.account, "root_type") if ( self.voucher_type == "Depreciation Entry" and d.reference_type == "Asset" and d.reference_name - and root_type == "Expense" + and frappe.get_cached_value("Account", d.account, "root_type") == "Expense" and d.debit ): asset = frappe.get_doc("Asset", d.reference_name) From 1016ec2a148859c2016ee4e7af36e96e5f89bae8 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Fri, 26 Apr 2024 12:33:22 +0530 Subject: [PATCH 18/32] fix(wip): daily depreciation bug (cherry picked from commit f337392f3ee94e7d2e27c9b6b2bfd8d5a2dcf1d5) --- .../asset_depreciation_schedule.py | 114 +++++++++++++++++- 1 file changed, 112 insertions(+), 2 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 27da403cd35..2b208035b56 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -285,6 +285,7 @@ class AssetDepreciationSchedule(Document): number_of_pending_depreciations = final_number_of_depreciations - start yearly_opening_wdv = value_after_depreciation current_fiscal_year_end_date = None + prev_per_day_depr = True for n in range(start, final_number_of_depreciations): # If depreciation is already completed (for double declining balance) if skip_row: @@ -302,7 +303,7 @@ class AssetDepreciationSchedule(Document): else: prev_depreciation_amount = 0 - depreciation_amount = get_depreciation_amount( + depreciation_amount, prev_per_day_depr = get_depreciation_amount( self, asset_doc, value_after_depreciation, @@ -312,6 +313,7 @@ class AssetDepreciationSchedule(Document): prev_depreciation_amount, has_wdv_or_dd_non_yearly_pro_rata, number_of_pending_depreciations, + prev_per_day_depr, ) if not has_pro_rata or ( n < (cint(final_number_of_depreciations) - 1) or final_number_of_depreciations == 2 @@ -351,6 +353,7 @@ class AssetDepreciationSchedule(Document): and (has_pro_rata or has_wdv_or_dd_non_yearly_pro_rata) and not self.opening_accumulated_depreciation and not self.flags.wdv_it_act_applied + and not self.daily_prorata_based ): from_date = add_days( asset_doc.available_for_use_date, -1 @@ -599,11 +602,12 @@ def get_depreciation_amount( prev_depreciation_amount=0, has_wdv_or_dd_non_yearly_pro_rata=False, number_of_pending_depreciations=0, + prev_per_day_depr=0, ): if fb_row.depreciation_method in ("Straight Line", "Manual"): return get_straight_line_or_manual_depr_amount( asset_depr_schedule, asset, fb_row, schedule_idx, number_of_pending_depreciations - ) + ), None else: return get_wdv_or_dd_depr_amount( asset, @@ -614,6 +618,7 @@ def get_depreciation_amount( prev_depreciation_amount, has_wdv_or_dd_non_yearly_pro_rata, asset_depr_schedule, + prev_per_day_depr, ) @@ -739,6 +744,7 @@ def get_wdv_or_dd_depr_amount( prev_depreciation_amount, has_wdv_or_dd_non_yearly_pro_rata, asset_depr_schedule, + prev_per_day_depr, ): return get_default_wdv_or_dd_depr_amount( asset, @@ -748,6 +754,7 @@ def get_wdv_or_dd_depr_amount( prev_depreciation_amount, has_wdv_or_dd_non_yearly_pro_rata, asset_depr_schedule, + prev_per_day_depr, ) @@ -759,6 +766,42 @@ def get_default_wdv_or_dd_depr_amount( prev_depreciation_amount, has_wdv_or_dd_non_yearly_pro_rata, asset_depr_schedule, + prev_per_day_depr, +): + if cint(fb_row.frequency_of_depreciation) == 12: + return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100) + else: + if not fb_row.daily_prorata_based: + 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 + else: + return _get_daily_prorata_based_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, + prev_per_day_depr, + ) + + +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) @@ -785,6 +828,73 @@ def get_default_wdv_or_dd_depr_amount( return prev_depreciation_amount +def _get_daily_prorata_based_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, + prev_per_day_depr, +): + if has_wdv_or_dd_non_yearly_pro_rata: + if schedule_idx == 0: + print(">>>>>", depreciable_value) + per_day_depr = get_per_day_depr(fb_row, depreciable_value, fb_row.depreciation_start_date) + from_date = asset.available_for_use_date + to_date = add_days(fb_row.depreciation_start_date, -1) + total_days = date_diff(to_date, from_date) + 1 + print("892", per_day_depr, from_date, to_date, total_days) + return (per_day_depr * total_days), per_day_depr + + elif schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 1: + from_date, to_date = get_dates( + fb_row.depreciation_start_date, schedule_idx, cint(fb_row.frequency_of_depreciation) + ) + per_day_depr = get_per_day_depr(fb_row, depreciable_value, from_date) + days_in_month = date_diff(to_date, from_date) + 1 + return (per_day_depr * days_in_month), per_day_depr + + else: + from_date, to_date = get_dates( + fb_row.depreciation_start_date, schedule_idx, cint(fb_row.frequency_of_depreciation) + ) + days_in_month = date_diff(to_date, from_date) + 1 + return (prev_per_day_depr * days_in_month), prev_per_day_depr + else: + if schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 0: + from_date, to_date = get_dates( + fb_row.depreciation_start_date, schedule_idx, cint(fb_row.frequency_of_depreciation) + ) + per_day_depr = get_per_day_depr(fb_row, depreciable_value, from_date) + days_in_month = date_diff(to_date, from_date) + 1 + return (per_day_depr * days_in_month), per_day_depr + else: + from_date, to_date = get_dates( + fb_row.depreciation_start_date, schedule_idx, cint(fb_row.frequency_of_depreciation) + ) + days_in_month = date_diff(to_date, from_date) + 1 + return (prev_per_day_depr * days_in_month), prev_per_day_depr + + +def get_per_day_depr( + fb_row, + depreciable_value, + from_date, +): + to_date = add_years(from_date, 1) + total_days = date_diff(to_date, from_date) + 1 + per_day_depr = (flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100)) / total_days + return per_day_depr + + +def get_dates(depreciation_start_date, schedule_idx, frequency_of_depreciation): + from_date = add_months(depreciation_start_date, (schedule_idx - 1) * frequency_of_depreciation) + to_date = add_days(add_months(from_date, frequency_of_depreciation), -1) + return from_date, to_date + + def make_draft_asset_depr_schedules_if_not_present(asset_doc): asset_depr_schedules_names = [] From c335c2c85a26ee9c4c0b306c4cf07ae18c936cb0 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Mon, 29 Apr 2024 12:03:06 +0530 Subject: [PATCH 19/32] fix: daily prorata based depreciation bug in wdv method (cherry picked from commit b8a98a273b965a25a697944762fad0cd34ca1598) --- erpnext/assets/doctype/asset/test_asset.py | 3 +- .../asset_depreciation_schedule.py | 71 +++++++++++-------- .../test_asset_depreciation_schedule.py | 65 +++++++++++++++++ 3 files changed, 110 insertions(+), 29 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 5f5268180d4..1f1af802e34 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -1000,7 +1000,7 @@ class TestDepreciationBasics(AssetSetup): asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset.name, "Active") - depreciation_amount = get_depreciation_amount( + depreciation_amount, prev_per_day_depr = get_depreciation_amount( asset_depr_schedule_doc, asset, 100000, 100000, asset.finance_books[0] ) self.assertEqual(depreciation_amount, 30000) @@ -1723,6 +1723,7 @@ def create_asset(**args): "depreciation_start_date": args.depreciation_start_date, "daily_prorata_based": args.daily_prorata_based or 0, "shift_based": args.shift_based or 0, + "rate_of_depreciation": args.rate_of_depreciation or 40, }, ) 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 2b208035b56..27e15aea73a 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -302,7 +302,6 @@ class AssetDepreciationSchedule(Document): prev_depreciation_amount = self.get("depreciation_schedule")[n - 1].depreciation_amount else: prev_depreciation_amount = 0 - depreciation_amount, prev_per_day_depr = get_depreciation_amount( self, asset_doc, @@ -353,7 +352,13 @@ class AssetDepreciationSchedule(Document): and (has_pro_rata or has_wdv_or_dd_non_yearly_pro_rata) and not self.opening_accumulated_depreciation and not self.flags.wdv_it_act_applied - and not self.daily_prorata_based + and ( + row.depreciation_method in ("Straight Line", "Manual") + or ( + row.depreciation_method in ("Written Down Value", "Double Declining Balance") + and not row.daily_prorata_based + ) + ) ): from_date = add_days( asset_doc.available_for_use_date, -1 @@ -768,30 +773,27 @@ def get_default_wdv_or_dd_depr_amount( asset_depr_schedule, prev_per_day_depr, ): - if cint(fb_row.frequency_of_depreciation) == 12: - return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100) + if not fb_row.daily_prorata_based: + 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 else: - if not fb_row.daily_prorata_based: - 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 - else: - return _get_daily_prorata_based_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, - prev_per_day_depr, - ) + return _get_daily_prorata_based_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, + prev_per_day_depr, + ) def _get_default_wdv_or_dd_depr_amount( @@ -838,14 +840,27 @@ def _get_daily_prorata_based_default_wdv_or_dd_depr_amount( asset_depr_schedule, prev_per_day_depr, ): - if has_wdv_or_dd_non_yearly_pro_rata: + if cint(fb_row.frequency_of_depreciation) == 12: if schedule_idx == 0: - print(">>>>>", depreciable_value) per_day_depr = get_per_day_depr(fb_row, depreciable_value, fb_row.depreciation_start_date) from_date = asset.available_for_use_date to_date = add_days(fb_row.depreciation_start_date, -1) total_days = date_diff(to_date, from_date) + 1 - print("892", per_day_depr, from_date, to_date, total_days) + return (per_day_depr * total_days), per_day_depr + else: + from_date, to_date = get_dates( + fb_row.depreciation_start_date, schedule_idx, cint(fb_row.frequency_of_depreciation) + ) + days_in_month = date_diff(to_date, from_date) + 1 + return (prev_per_day_depr * days_in_month), prev_per_day_depr + + if has_wdv_or_dd_non_yearly_pro_rata: + if schedule_idx == 0: + per_day_depr = get_per_day_depr(fb_row, depreciable_value, fb_row.depreciation_start_date) + from_date = asset.available_for_use_date + to_date = add_days(fb_row.depreciation_start_date, -1) + total_days = date_diff(to_date, from_date) + 1 + return (per_day_depr * total_days), per_day_depr elif schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 1: diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py index 5971d1662f9..ce877f42376 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py @@ -71,3 +71,68 @@ class TestAssetDepreciationSchedule(FrappeTestCase): for d in get_depr_schedule(asset.name, "Draft") ] self.assertEqual(schedules, expected_schedules) + # Test for Written Down Value Method + def test_daily_prorata_based_depreciation_for_wdv_method(self): + # Frequency of deprciation = 3 + asset = create_asset( + item_code="Macbook Pro", + calculate_depreciation=1, + depreciation_method="Written Down Value", + daily_prorata_based=1, + available_for_use_date="2021-02-20", + depreciation_start_date="2021-03-31", + frequency_of_depreciation=3, + total_number_of_depreciations=18, + rate_of_depreciation=40, + submit=1, + ) + asset_depr_schedule = frappe.get_last_doc("Asset Depreciation Schedule", {"asset": asset.name}) + self.assertEqual(asset_depr_schedule.depreciation_schedule[1].depreciation_amount, 9521.45) + + # Frequency of deprciation = 6 + asset = create_asset( + item_code="Macbook Pro", + calculate_depreciation=1, + depreciation_method="Written Down Value", + daily_prorata_based=1, + available_for_use_date="2020-02-20", + depreciation_start_date="2020-03-01", + frequency_of_depreciation=6, + total_number_of_depreciations=18, + rate_of_depreciation=40, + submit=1, + ) + asset_depr_schedule = frappe.get_last_doc("Asset Depreciation Schedule", {"asset": asset.name}) + self.assertEqual(asset_depr_schedule.depreciation_schedule[1].depreciation_amount, 19889.52) + + # Frequency of deprciation = 12 + asset = create_asset( + item_code="Macbook Pro", + calculate_depreciation=1, + depreciation_method="Written Down Value", + daily_prorata_based=1, + available_for_use_date="2020-02-20", + depreciation_start_date="2020-03-01", + frequency_of_depreciation=12, + total_number_of_depreciations=4, + rate_of_depreciation=40, + submit=1, + ) + asset_depr_schedule = frappe.get_last_doc("Asset Depreciation Schedule", {"asset": asset.name}) + self.assertEqual(asset_depr_schedule.depreciation_schedule[0].depreciation_amount, 1092.90) + + # Test for Straight Line Method + def test_daily_prorata_based_depreciation_for_straight_line_method(self): + asset = create_asset( + item_code="Macbook Pro", + calculate_depreciation=1, + depreciation_method="Straight Line", + daily_prorata_based=0, + available_for_use_date="2020-02-20", + depreciation_start_date="2020-03-01", + frequency_of_depreciation=12, + total_number_of_depreciations=4, + submit=1, + ) + asset_depr_schedule = frappe.get_last_doc("Asset Depreciation Schedule", {"asset": asset.name}) + self.assertEqual(asset_depr_schedule.depreciation_schedule[0].depreciation_amount, 751.37) From 01b25b5821753a2111c7e82a2a4f26801447a423 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Mon, 29 Apr 2024 16:27:13 +0530 Subject: [PATCH 20/32] fix: incorrect total days calculation (cherry picked from commit fccd37d32d86450cecaf73a4d4aa4bcf67177d97) --- .../asset_depreciation_schedule.py | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 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 27e15aea73a..2dc4e08902d 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -848,11 +848,10 @@ def _get_daily_prorata_based_default_wdv_or_dd_depr_amount( total_days = date_diff(to_date, from_date) + 1 return (per_day_depr * total_days), per_day_depr else: - from_date, to_date = get_dates( + from_date, days_in_month = _get_total_days( fb_row.depreciation_start_date, schedule_idx, cint(fb_row.frequency_of_depreciation) ) - days_in_month = date_diff(to_date, from_date) + 1 - return (prev_per_day_depr * days_in_month), prev_per_day_depr + return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100), None if has_wdv_or_dd_non_yearly_pro_rata: if schedule_idx == 0: @@ -860,36 +859,31 @@ def _get_daily_prorata_based_default_wdv_or_dd_depr_amount( from_date = asset.available_for_use_date to_date = add_days(fb_row.depreciation_start_date, -1) total_days = date_diff(to_date, from_date) + 1 - return (per_day_depr * total_days), per_day_depr elif schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 1: - from_date, to_date = get_dates( + from_date, days_in_month = _get_total_days( fb_row.depreciation_start_date, schedule_idx, cint(fb_row.frequency_of_depreciation) ) per_day_depr = get_per_day_depr(fb_row, depreciable_value, from_date) - days_in_month = date_diff(to_date, from_date) + 1 return (per_day_depr * days_in_month), per_day_depr else: - from_date, to_date = get_dates( + from_date, days_in_month = _get_total_days( fb_row.depreciation_start_date, schedule_idx, cint(fb_row.frequency_of_depreciation) ) - days_in_month = date_diff(to_date, from_date) + 1 return (prev_per_day_depr * days_in_month), prev_per_day_depr else: if schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 0: - from_date, to_date = get_dates( + from_date, days_in_month = _get_total_days( fb_row.depreciation_start_date, schedule_idx, cint(fb_row.frequency_of_depreciation) ) per_day_depr = get_per_day_depr(fb_row, depreciable_value, from_date) - days_in_month = date_diff(to_date, from_date) + 1 return (per_day_depr * days_in_month), per_day_depr else: - from_date, to_date = get_dates( + from_date, days_in_month = _get_total_days( fb_row.depreciation_start_date, schedule_idx, cint(fb_row.frequency_of_depreciation) ) - days_in_month = date_diff(to_date, from_date) + 1 return (prev_per_day_depr * days_in_month), prev_per_day_depr @@ -904,10 +898,13 @@ def get_per_day_depr( return per_day_depr -def get_dates(depreciation_start_date, schedule_idx, frequency_of_depreciation): +def _get_total_days(depreciation_start_date, schedule_idx, frequency_of_depreciation): from_date = add_months(depreciation_start_date, (schedule_idx - 1) * frequency_of_depreciation) - to_date = add_days(add_months(from_date, frequency_of_depreciation), -1) - return from_date, to_date + to_date = add_months(from_date, frequency_of_depreciation) + if is_last_day_of_the_month(depreciation_start_date): + to_date = get_last_day(to_date) + from_date = get_last_day(from_date) + return from_date, date_diff(to_date, from_date) def make_draft_asset_depr_schedules_if_not_present(asset_doc): From 12f383f252c6f3bf6d8d22f752c226f786b3d4f0 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 30 Apr 2024 15:52:05 +0530 Subject: [PATCH 21/32] test: made minor change in existing test (cherry picked from commit d40b55468ce76ba35caea20fcb20d3db2177aec5) --- erpnext/assets/doctype/asset/test_asset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 1f1af802e34..7e0c3ad6888 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -1723,7 +1723,7 @@ def create_asset(**args): "depreciation_start_date": args.depreciation_start_date, "daily_prorata_based": args.daily_prorata_based or 0, "shift_based": args.shift_based or 0, - "rate_of_depreciation": args.rate_of_depreciation or 40, + "rate_of_depreciation": args.rate_of_depreciation or 0, }, ) From 72f3fb2a2ca84ff3ddb2fc25f20394cd6dfebeae Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 7 May 2024 11:15:49 +0530 Subject: [PATCH 22/32] fix: pro rata based depreciation with opening accumulated depreciation (cherry picked from commit c0c8c1bb02815e084931a450326fa1cd4f0baf0d) --- .../asset_depreciation_schedule.py | 21 +---- .../test_asset_depreciation_schedule.py | 87 ++++++++++++------- 2 files changed, 58 insertions(+), 50 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 2dc4e08902d..88c04419da8 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -352,13 +352,6 @@ class AssetDepreciationSchedule(Document): and (has_pro_rata or has_wdv_or_dd_non_yearly_pro_rata) and not self.opening_accumulated_depreciation and not self.flags.wdv_it_act_applied - and ( - row.depreciation_method in ("Straight Line", "Manual") - or ( - row.depreciation_method in ("Written Down Value", "Double Declining Balance") - and not row.daily_prorata_based - ) - ) ): from_date = add_days( asset_doc.available_for_use_date, -1 @@ -842,11 +835,7 @@ def _get_daily_prorata_based_default_wdv_or_dd_depr_amount( ): if cint(fb_row.frequency_of_depreciation) == 12: if schedule_idx == 0: - per_day_depr = get_per_day_depr(fb_row, depreciable_value, fb_row.depreciation_start_date) - from_date = asset.available_for_use_date - to_date = add_days(fb_row.depreciation_start_date, -1) - total_days = date_diff(to_date, from_date) + 1 - return (per_day_depr * total_days), per_day_depr + return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100), None else: from_date, days_in_month = _get_total_days( fb_row.depreciation_start_date, schedule_idx, cint(fb_row.frequency_of_depreciation) @@ -855,11 +844,7 @@ def _get_daily_prorata_based_default_wdv_or_dd_depr_amount( if has_wdv_or_dd_non_yearly_pro_rata: if schedule_idx == 0: - per_day_depr = get_per_day_depr(fb_row, depreciable_value, fb_row.depreciation_start_date) - from_date = asset.available_for_use_date - to_date = add_days(fb_row.depreciation_start_date, -1) - total_days = date_diff(to_date, from_date) + 1 - return (per_day_depr * total_days), per_day_depr + return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100), None elif schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 1: from_date, days_in_month = _get_total_days( @@ -892,7 +877,7 @@ def get_per_day_depr( depreciable_value, from_date, ): - to_date = add_years(from_date, 1) + to_date = add_days(add_years(from_date, 1), -1) total_days = date_diff(to_date, from_date) + 1 per_day_depr = (flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100)) / total_days return per_day_depr diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py index ce877f42376..236c45f7f41 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py @@ -72,8 +72,8 @@ class TestAssetDepreciationSchedule(FrappeTestCase): ] self.assertEqual(schedules, expected_schedules) # Test for Written Down Value Method - def test_daily_prorata_based_depreciation_for_wdv_method(self): - # Frequency of deprciation = 3 + # Frequency of deprciation = 3 + def test_for_daily_prorata_based_depreciation_wdv_method_frequency_3_months(self): asset = create_asset( item_code="Macbook Pro", calculate_depreciation=1, @@ -82,57 +82,80 @@ class TestAssetDepreciationSchedule(FrappeTestCase): available_for_use_date="2021-02-20", depreciation_start_date="2021-03-31", frequency_of_depreciation=3, - total_number_of_depreciations=18, + total_number_of_depreciations=6, rate_of_depreciation=40, - submit=1, ) - asset_depr_schedule = frappe.get_last_doc("Asset Depreciation Schedule", {"asset": asset.name}) - self.assertEqual(asset_depr_schedule.depreciation_schedule[1].depreciation_amount, 9521.45) - # Frequency of deprciation = 6 + expected_schedules = [ + ["2021-03-31", 4383.56, 4383.56], + ["2021-06-30", 9535.45, 13919.01], + ["2021-09-30", 9640.23, 23559.24], + ["2021-12-31", 9640.23, 33199.47], + ["2022-03-31", 9430.66, 42630.13], + ["2022-06-30", 5721.27, 48351.4], + ["2022-08-20", 51648.6, 100000.0], + ] + + schedules = [ + [cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount] + for d in get_depr_schedule(asset.name, "Draft") + ] + self.assertEqual(schedules, expected_schedules) + + # Frequency of deprciation = 6 + def test_for_daily_prorata_based_depreciation_wdv_method_frequency_6_months(self): asset = create_asset( item_code="Macbook Pro", calculate_depreciation=1, depreciation_method="Written Down Value", daily_prorata_based=1, available_for_use_date="2020-02-20", - depreciation_start_date="2020-03-01", + depreciation_start_date="2020-02-29", frequency_of_depreciation=6, - total_number_of_depreciations=18, + total_number_of_depreciations=6, rate_of_depreciation=40, - submit=1, ) - asset_depr_schedule = frappe.get_last_doc("Asset Depreciation Schedule", {"asset": asset.name}) - self.assertEqual(asset_depr_schedule.depreciation_schedule[1].depreciation_amount, 19889.52) - # Frequency of deprciation = 12 + expected_schedules = [ + ["2020-02-29", 1092.90, 1092.90], + ["2020-08-31", 19944.01, 21036.91], + ["2021-02-28", 19618.83, 40655.74], + ["2021-08-31", 11966.4, 52622.14], + ["2022-02-28", 11771.3, 64393.44], + ["2022-08-31", 7179.84, 71573.28], + ["2023-02-20", 28426.72, 100000.0], + ] + + schedules = [ + [cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount] + for d in get_depr_schedule(asset.name, "Draft") + ] + self.assertEqual(schedules, expected_schedules) + + # Frequency of deprciation = 12 + def test_for_daily_prorata_based_depreciation_wdv_method_frequency_12_months(self): asset = create_asset( item_code="Macbook Pro", calculate_depreciation=1, depreciation_method="Written Down Value", daily_prorata_based=1, available_for_use_date="2020-02-20", - depreciation_start_date="2020-03-01", + depreciation_start_date="2020-03-31", frequency_of_depreciation=12, total_number_of_depreciations=4, rate_of_depreciation=40, - submit=1, ) - asset_depr_schedule = frappe.get_last_doc("Asset Depreciation Schedule", {"asset": asset.name}) - self.assertEqual(asset_depr_schedule.depreciation_schedule[0].depreciation_amount, 1092.90) - # Test for Straight Line Method - def test_daily_prorata_based_depreciation_for_straight_line_method(self): - asset = create_asset( - item_code="Macbook Pro", - calculate_depreciation=1, - depreciation_method="Straight Line", - daily_prorata_based=0, - available_for_use_date="2020-02-20", - depreciation_start_date="2020-03-01", - frequency_of_depreciation=12, - total_number_of_depreciations=4, - submit=1, - ) - asset_depr_schedule = frappe.get_last_doc("Asset Depreciation Schedule", {"asset": asset.name}) - self.assertEqual(asset_depr_schedule.depreciation_schedule[0].depreciation_amount, 751.37) + expected_schedules = [ + ["2020-03-31", 4480.87, 4480.87], + ["2021-03-31", 38207.65, 42688.52], + ["2022-03-31", 22924.59, 65613.11], + ["2023-03-31", 13754.76, 79367.87], + ["2024-02-20", 20632.13, 100000], + ] + + schedules = [ + [cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount] + for d in get_depr_schedule(asset.name, "Draft") + ] + self.assertEqual(schedules, expected_schedules) From b57919e2cea50ebb2d53c8b3f71ef51ff1d71198 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 14 May 2024 19:13:41 +0530 Subject: [PATCH 23/32] refactor: code optimization (cherry picked from commit 7b264e5e11b79269072fb627317d2224385af30d) --- .../asset_depreciation_schedule.py | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 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 88c04419da8..492ccc3c16d 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -842,34 +842,43 @@ def _get_daily_prorata_based_default_wdv_or_dd_depr_amount( ) return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100), None - if has_wdv_or_dd_non_yearly_pro_rata: + if has_wdv_or_dd_non_yearly_pro_rata: # If applicable days for ther first month is less than full month if schedule_idx == 0: return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100), None - elif schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 1: - from_date, days_in_month = _get_total_days( - fb_row.depreciation_start_date, schedule_idx, cint(fb_row.frequency_of_depreciation) - ) - per_day_depr = get_per_day_depr(fb_row, depreciable_value, from_date) - return (per_day_depr * days_in_month), per_day_depr - + elif schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 1: # Year changes + return get_monthly_depr_amount(fb_row, schedule_idx, depreciable_value) else: - from_date, days_in_month = _get_total_days( - fb_row.depreciation_start_date, schedule_idx, cint(fb_row.frequency_of_depreciation) - ) - return (prev_per_day_depr * days_in_month), prev_per_day_depr + return get_monthly_depr_amount_based_on_prev_per_day_depr(fb_row, schedule_idx, prev_per_day_depr) else: - if schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 0: - from_date, days_in_month = _get_total_days( - fb_row.depreciation_start_date, schedule_idx, cint(fb_row.frequency_of_depreciation) - ) - per_day_depr = get_per_day_depr(fb_row, depreciable_value, from_date) - return (per_day_depr * days_in_month), per_day_depr + if schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 0: # year changes + return get_monthly_depr_amount(fb_row, schedule_idx, depreciable_value) else: - from_date, days_in_month = _get_total_days( - fb_row.depreciation_start_date, schedule_idx, cint(fb_row.frequency_of_depreciation) - ) - return (prev_per_day_depr * days_in_month), prev_per_day_depr + return get_monthly_depr_amount_based_on_prev_per_day_depr(fb_row, schedule_idx, prev_per_day_depr) + + +def get_monthly_depr_amount(fb_row, schedule_idx, depreciable_value): + """ " + Returns monthly depreciation amount when year changes + 1. Calculate per day depr based on new year + 2. Calculate monthly amount based on new per day amount + """ + from_date, days_in_month = _get_total_days( + fb_row.depreciation_start_date, schedule_idx, cint(fb_row.frequency_of_depreciation) + ) + per_day_depr = get_per_day_depr(fb_row, depreciable_value, from_date) + return (per_day_depr * days_in_month), per_day_depr + + +def get_monthly_depr_amount_based_on_prev_per_day_depr(fb_row, schedule_idx, prev_per_day_depr): + """ " + Returns monthly depreciation amount based on prev per day depr + Calculate per day depr only for the first month + """ + from_date, days_in_month = _get_total_days( + fb_row.depreciation_start_date, schedule_idx, cint(fb_row.frequency_of_depreciation) + ) + return (prev_per_day_depr * days_in_month), prev_per_day_depr def get_per_day_depr( From 955ce9b670caf55d5b1c31e10c54f62bfb8d1dd6 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Wed, 15 May 2024 01:10:54 +0530 Subject: [PATCH 24/32] fix: removed same named function (cherry picked from commit d22df324ec69b76d77da6478263043893c4f51e2) --- .../asset_depreciation_schedule.py | 26 +++---------------- 1 file changed, 4 insertions(+), 22 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 492ccc3c16d..c1ea42ba020 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -682,21 +682,12 @@ def get_daily_prorata_based_straight_line_depr( ) year_end_date = add_days(add_years(year_start_date, 1), -1) daily_depr_amount = every_year_depr / (date_diff(year_end_date, year_start_date) + 1) - total_depreciable_days = _get_total_days( + from_date, total_depreciable_days = _get_total_days( row.depreciation_start_date, schedule_idx, row.frequency_of_depreciation ) return daily_depr_amount * total_depreciable_days -def _get_total_days(depreciation_start_date, schedule_idx, frequency_of_depreciation): - from_date = add_months(depreciation_start_date, (schedule_idx - 1) * frequency_of_depreciation) - to_date = add_months(from_date, frequency_of_depreciation) - if is_last_day_of_the_month(depreciation_start_date): - to_date = get_last_day(to_date) - from_date = add_days(get_last_day(from_date), 1) - return date_diff(to_date, from_date) + 1 - - def get_shift_depr_amount(asset_depr_schedule, asset, row, schedule_idx): if asset_depr_schedule.get("__islocal") and not asset.flags.shift_allocation: return ( @@ -766,7 +757,7 @@ def get_default_wdv_or_dd_depr_amount( asset_depr_schedule, prev_per_day_depr, ): - if not fb_row.daily_prorata_based: + if not fb_row.daily_prorata_based or cint(fb_row.frequency_of_depreciation) == 12: return _get_default_wdv_or_dd_depr_amount( asset, fb_row, @@ -833,15 +824,6 @@ def _get_daily_prorata_based_default_wdv_or_dd_depr_amount( asset_depr_schedule, prev_per_day_depr, ): - if cint(fb_row.frequency_of_depreciation) == 12: - if schedule_idx == 0: - return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100), None - else: - from_date, days_in_month = _get_total_days( - fb_row.depreciation_start_date, schedule_idx, cint(fb_row.frequency_of_depreciation) - ) - return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100), None - if has_wdv_or_dd_non_yearly_pro_rata: # If applicable days for ther first month is less than full month if schedule_idx == 0: return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100), None @@ -897,8 +879,8 @@ def _get_total_days(depreciation_start_date, schedule_idx, frequency_of_deprecia to_date = add_months(from_date, frequency_of_depreciation) if is_last_day_of_the_month(depreciation_start_date): to_date = get_last_day(to_date) - from_date = get_last_day(from_date) - return from_date, date_diff(to_date, from_date) + from_date = add_days(get_last_day(from_date), 1) + return from_date, date_diff(to_date, from_date) + 1 def make_draft_asset_depr_schedules_if_not_present(asset_doc): From 6d43d840dbf61ab6b068b8a920dd1dd79042c08c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 15 May 2024 08:18:05 +0530 Subject: [PATCH 25/32] style: new line before function (cherry picked from commit 7d86881579b7404323c398434b44250b1ae40e7d) --- .../test_asset_depreciation_schedule.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py index 236c45f7f41..6e4966ac6cf 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py @@ -71,6 +71,7 @@ class TestAssetDepreciationSchedule(FrappeTestCase): for d in get_depr_schedule(asset.name, "Draft") ] self.assertEqual(schedules, expected_schedules) + # Test for Written Down Value Method # Frequency of deprciation = 3 def test_for_daily_prorata_based_depreciation_wdv_method_frequency_3_months(self): From fd3efd53bea50a8cad656f18dfafb0486ad1cf79 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 15 May 2024 09:09:16 +0530 Subject: [PATCH 26/32] style: linter issue --- .../assets/doctype/asset_capitalization/asset_capitalization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 6c329b0da78..573dd92c585 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -145,7 +145,7 @@ class AssetCapitalization(StockController): def on_trash(self): frappe.db.set_value("Asset", self.target_asset, "capitalized_in", None) - super(AssetCapitalization, self).on_trash() + super().on_trash() def cancel_target_asset(self): if self.entry_type == "Capitalization" and self.target_asset: From a1ce514cf3e2abaed3ccc76b4885cb6a3ca1b28e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 15 May 2024 15:15:24 +0530 Subject: [PATCH 27/32] fix: validate reorder group warehouse (backport #41478) (#41480) fix: validate reorder group warehouse (#41478) (cherry picked from commit 0363afcfd0906cd790779e93dd73646b937be3b5) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/item/item.py | 29 ++++++++++++++++++++++++- erpnext/stock/doctype/item/test_item.py | 21 ++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 1c43233d7c2..1ceb949d691 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -5,7 +5,7 @@ import copy import json import frappe -from frappe import _ +from frappe import _, bold from frappe.model.document import Document from frappe.query_builder import Interval from frappe.query_builder.functions import Count, CurDate, UnixTimestamp @@ -469,6 +469,13 @@ class Item(Document): def validate_warehouse_for_reorder(self): """Validate Reorder level table for duplicate and conditional mandatory""" warehouse_material_request_type: list[tuple[str, str]] = [] + + _warehouse_before_save = frappe._dict() + if not self.is_new() and self._doc_before_save: + _warehouse_before_save = { + d.name: d.warehouse for d in self._doc_before_save.get("reorder_levels") or [] + } + for d in self.get("reorder_levels"): if not d.warehouse_group: d.warehouse_group = d.warehouse @@ -485,6 +492,19 @@ class Item(Document): if d.warehouse_reorder_level and not d.warehouse_reorder_qty: frappe.throw(_("Row #{0}: Please set reorder quantity").format(d.idx)) + if d.warehouse_group and d.warehouse: + if _warehouse_before_save.get(d.name) == d.warehouse: + continue + + child_warehouses = get_child_warehouses(d.warehouse_group) + if d.warehouse not in child_warehouses: + frappe.throw( + _( + "Row #{0}: The warehouse {1} is not a child warehouse of a group warehouse {2}" + ).format(d.idx, bold(d.warehouse), bold(d.warehouse_group)), + title=_("Incorrect Check in (group) Warehouse for Reorder"), + ) + def stock_ledger_created(self): if not hasattr(self, "_stock_ledger_created"): self._stock_ledger_created = len( @@ -1360,3 +1380,10 @@ def get_asset_naming_series(): from erpnext.assets.doctype.asset.asset import get_asset_naming_series return get_asset_naming_series() + + +@frappe.request_cache +def get_child_warehouses(warehouse): + from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses + + return get_child_warehouses(warehouse) diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 2b3d3b72a02..d5f13e62a5c 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -862,6 +862,27 @@ class TestItem(FrappeTestCase): self.assertEqual(data[0].description, item.description) self.assertTrue("description" in data[0]) + def test_group_warehouse_for_reorder_item(self): + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + item_doc = make_item("_Test Group Warehouse For Reorder Item", {"is_stock_item": 1}) + warehouse = create_warehouse("_Test Warehouse - _TC") + warehouse_doc = frappe.get_doc("Warehouse", warehouse) + warehouse_doc.db_set("parent_warehouse", "") + + item_doc.append( + "reorder_levels", + { + "warehouse": warehouse, + "warehouse_reorder_level": 10, + "warehouse_reorder_qty": 100, + "material_request_type": "Purchase", + "warehouse_group": "_Test Warehouse Group - _TC", + }, + ) + + self.assertRaises(frappe.ValidationError, item_doc.save) + def set_item_variant_settings(fields): doc = frappe.get_doc("Item Variant Settings") From a070ad786d636fca77f99da798d9b272d70d2131 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 15 May 2024 18:05:39 +0530 Subject: [PATCH 28/32] fix: not able to submit landed cost voucher (backport #41481) (#41486) fix: not able to submit landed cost voucher (#41481) (cherry picked from commit 81a9521f04df8786b07d780cbbc838bbd08bfb6a) Co-authored-by: rohitwaghchaure --- erpnext/controllers/stock_controller.py | 1 + .../test_landed_cost_voucher.py | 122 ++++++++++++++++++ .../serial_and_batch_bundle.py | 3 + erpnext/stock/serial_batch_bundle.py | 3 + 4 files changed, 129 insertions(+) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 49252bd5b54..63a8c842c9a 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -205,6 +205,7 @@ class StockController(AccountsController): "company": self.company, "is_rejected": 1 if row.get("rejected_warehouse") else 0, "use_serial_batch_fields": row.use_serial_batch_fields, + "via_landed_cost_voucher": via_landed_cost_voucher, "do_not_submit": True if not via_landed_cost_voucher else False, } diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py index 13b7f97b7c4..39f9ecb915d 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py @@ -946,6 +946,128 @@ class TestLandedCostVoucher(FrappeTestCase): frappe.db.get_value("Serial and Batch Bundle", row.serial_and_batch_bundle, "avg_rate"), ) + def test_do_not_validate_against_landed_cost_voucher_for_serial_for_legacy_pr(self): + from erpnext.stock.doctype.item.test_item import make_item + from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import get_auto_batch_nos + + frappe.flags.ignore_serial_batch_bundle_validation = True + frappe.flags.use_serial_and_batch_fields = True + sn_item = "Test Don't Validate Against LCV For Serial NO for Legacy PR" + sn_item_doc = make_item( + sn_item, + { + "has_serial_no": 1, + "serial_no_series": "SN-ALCVTDVLCVSNO-.####", + "is_stock_item": 1, + }, + ) + + serial_nos = [ + "SN-ALCVTDVLCVSNO-0001", + "SN-ALCVTDVLCVSNO-0002", + "SN-ALCVTDVLCVSNO-0003", + "SN-ALCVTDVLCVSNO-0004", + "SN-ALCVTDVLCVSNO-0005", + ] + + for sn in serial_nos: + if not frappe.db.exists("Serial No", sn): + sn_doc = frappe.get_doc( + { + "doctype": "Serial No", + "item_code": sn_item, + "serial_no": sn, + } + ) + sn_doc.insert() + + warehouse = "_Test Warehouse - _TC" + company = frappe.db.get_value("Warehouse", warehouse, "company") + + pr = make_purchase_receipt( + company=company, + warehouse=warehouse, + item_code=sn_item, + qty=5, + rate=100, + uom=sn_item_doc.stock_uom, + stock_uom=sn_item_doc.stock_uom, + ) + + pr.reload() + + for sn in serial_nos: + sn_doc = frappe.get_doc("Serial No", sn) + sn_doc.db_set( + { + "warehouse": warehouse, + "status": "Active", + } + ) + + for row in pr.items: + if row.item_code == sn_item: + row.db_set("serial_no", ", ".join(serial_nos)) + + stock_ledger_entries = frappe.get_all("Stock Ledger Entry", filters={"voucher_no": pr.name}) + for sle in stock_ledger_entries: + doc = frappe.get_doc("Stock Ledger Entry", sle.name) + if doc.item_code == sn_item: + doc.db_set("serial_no", ", ".join(serial_nos)) + + dn = create_delivery_note( + company=company, + warehouse=warehouse, + item_code=sn_item, + qty=5, + rate=100, + uom=sn_item_doc.stock_uom, + stock_uom=sn_item_doc.stock_uom, + ) + + stock_ledger_entries = frappe.get_all("Stock Ledger Entry", filters={"voucher_no": dn.name}) + for sle in stock_ledger_entries: + doc = frappe.get_doc("Stock Ledger Entry", sle.name) + if doc.item_code == sn_item: + doc.db_set("serial_no", ", ".join(serial_nos)) + + frappe.flags.ignore_serial_batch_bundle_validation = False + frappe.flags.use_serial_and_batch_fields = False + + lcv = make_landed_cost_voucher( + company=pr.company, + receipt_document_type="Purchase Receipt", + receipt_document=pr.name, + charges=20, + distribute_charges_based_on="Qty", + do_not_save=True, + ) + + lcv.get_items_from_purchase_receipts() + lcv.save() + lcv.submit() + + pr.reload() + + for row in pr.items: + self.assertEqual(row.valuation_rate, 104) + self.assertTrue(row.serial_and_batch_bundle) + self.assertEqual( + row.valuation_rate, + frappe.db.get_value("Serial and Batch Bundle", row.serial_and_batch_bundle, "avg_rate"), + ) + + lcv.cancel() + pr.reload() + + for row in pr.items: + self.assertEqual(row.valuation_rate, 100) + self.assertTrue(row.serial_and_batch_bundle) + self.assertEqual( + row.valuation_rate, + frappe.db.get_value("Serial and Batch Bundle", row.serial_and_batch_bundle, "avg_rate"), + ) + def make_landed_cost_voucher(**args): args = frappe._dict(args) 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 a1a8a85e745..4c9fc881986 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 @@ -428,6 +428,9 @@ class SerialandBatchBundle(Document): self.throw_error_message(f"The {self.voucher_type} # {self.voucher_no} should be submit first.") def check_future_entries_exists(self): + if self.flags and self.flags.via_landed_cost_voucher: + return + if not self.has_serial_no: return diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 573d7280ca1..21a96c41cb4 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -840,6 +840,9 @@ class SerialBatchCreation: self.set_auto_serial_batch_entries_for_inward() self.add_serial_nos_for_batch_item() + if hasattr(self, "via_landed_cost_voucher") and self.via_landed_cost_voucher: + doc.flags.via_landed_cost_voucher = self.via_landed_cost_voucher + self.set_serial_batch_entries(doc) if not doc.get("entries"): return frappe._dict({}) From 8101b51899514c202d79b30f7ce46949ebd63778 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 16 May 2024 15:14:43 +0530 Subject: [PATCH 29/32] fix: bold total in exponential smoothing forecasting (backport #41393) (#41404) fix: bold total in exponential smoothing forecasting (#41393) * fix: bold total in exponential smoothing forecasting * fix: bold total in exponential smoothing forecasting (cherry picked from commit ba60b5911ad37d2fed486d51a7e98f148ffbca86) Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com> --- .../exponential_smoothing_forecasting.js | 7 +++++++ .../exponential_smoothing_forecasting.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js index 23fa9ab41b0..4a34d126f88 100644 --- a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js +++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js @@ -93,4 +93,11 @@ frappe.query_reports["Exponential Smoothing Forecasting"] = { }, }, ], + formatter: function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + if (column.fieldname === "item_code" && value.includes("Total Quantity")) { + value = "" + value + ""; + } + return value; + }, }; diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py index 85648d6b326..0f5fa959dc5 100644 --- a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py +++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py @@ -144,7 +144,7 @@ class ForecastingReport(ExponentialSmoothingForecast): if not self.data: return - total_row = {"item_code": _(frappe.bold("Total Quantity"))} + total_row = {"item_code": _("Total Quantity")} for value in self.data: for period in self.period_list: From e36880da2d181ae441cd17fdb86e8b6fe2a2be45 Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:37:11 +0530 Subject: [PATCH 30/32] fix: print format bold for field "total" (cherry picked from commit 3c9640df27a0a3fac7f90d829997652c8f235916) --- erpnext/templates/print_formats/includes/total.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/templates/print_formats/includes/total.html b/erpnext/templates/print_formats/includes/total.html index 879203bbf25..f964047bd08 100644 --- a/erpnext/templates/print_formats/includes/total.html +++ b/erpnext/templates/print_formats/includes/total.html @@ -1,14 +1,14 @@ -
+
{% if doc.flags.show_inclusive_tax_in_print %}
-
+
{{ doc.get_formatted("net_total", doc) }}
{% else %}
-
+
{{ doc.get_formatted("total", doc) }}
{% endif %} From 7620ae203e5ccfe62f78e5442fdeebb61b599e66 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 16 May 2024 15:59:06 +0530 Subject: [PATCH 31/32] chore(BOM Explorer): display items in the same order as in the BOM (backport #41496) (#41498) chore(BOM Explorer): display items in the same order as in the BOM (#41496) (cherry picked from commit bd381cc0c607b2fc44ebbd854689686d37d67c7a) Co-authored-by: Samuel Danieli <23150094+scdanieli@users.noreply.github.com> --- erpnext/manufacturing/report/bom_explorer/bom_explorer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/report/bom_explorer/bom_explorer.py b/erpnext/manufacturing/report/bom_explorer/bom_explorer.py index 2aa31be0f0e..97c85502c98 100644 --- a/erpnext/manufacturing/report/bom_explorer/bom_explorer.py +++ b/erpnext/manufacturing/report/bom_explorer/bom_explorer.py @@ -21,7 +21,8 @@ def get_exploded_items(bom, data, indent=0, qty=1): exploded_items = frappe.get_all( "BOM Item", filters={"parent": bom}, - fields=["qty", "bom_no", "qty", "item_code", "item_name", "description", "uom"], + fields=["qty", "bom_no", "qty", "item_code", "item_name", "description", "uom", "idx"], + order_by="idx ASC", ) for item in exploded_items: From 938888cddd8dd343b8658f7e2cc71b367f59fd28 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 16 May 2024 16:33:02 +0530 Subject: [PATCH 32/32] fix: stock levels for batch (backport #41494) (#41501) fix: stock levels for batch (#41494) (cherry picked from commit 500c546691ff0425142d26ba7aaf1223fa3183d2) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/batch/batch.js | 7 ++++++- erpnext/stock/doctype/batch/batch.py | 2 ++ erpnext/stock/doctype/batch/batch_list.js | 4 ++-- .../serial_and_batch_bundle/serial_and_batch_bundle.py | 8 ++++---- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/erpnext/stock/doctype/batch/batch.js b/erpnext/stock/doctype/batch/batch.js index 3719c96c6e7..4ed428421ca 100644 --- a/erpnext/stock/doctype/batch/batch.js +++ b/erpnext/stock/doctype/batch/batch.js @@ -47,9 +47,14 @@ frappe.ui.form.on("Batch", { }, make_dashboard: (frm) => { if (!frm.is_new()) { + let for_stock_levels = 0; + if (!frm.doc.batch_qty && frm.doc.expiry_date) { + for_stock_levels = 1; + } + frappe.call({ method: "erpnext.stock.doctype.batch.batch.get_batch_qty", - args: { batch_no: frm.doc.name, item_code: frm.doc.item }, + args: { batch_no: frm.doc.name, item_code: frm.doc.item, for_stock_levels: for_stock_levels }, callback: (r) => { if (!r.message) { return; diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index 8726642cb43..77b87aa995c 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -199,6 +199,7 @@ def get_batch_qty( posting_date=None, posting_time=None, ignore_voucher_nos=None, + for_stock_levels=False, ): """Returns batch actual qty if warehouse is passed, or returns dict of qty by warehouse if warehouse is None @@ -222,6 +223,7 @@ def get_batch_qty( "posting_time": posting_time, "batch_no": batch_no, "ignore_voucher_nos": ignore_voucher_nos, + "for_stock_levels": for_stock_levels, } ) diff --git a/erpnext/stock/doctype/batch/batch_list.js b/erpnext/stock/doctype/batch/batch_list.js index 2060d6e8763..644ef131399 100644 --- a/erpnext/stock/doctype/batch/batch_list.js +++ b/erpnext/stock/doctype/batch/batch_list.js @@ -3,8 +3,6 @@ frappe.listview_settings["Batch"] = { get_indicator: (doc) => { if (doc.disabled) { return [__("Disabled"), "gray", "disabled,=,1"]; - } else if (!doc.batch_qty) { - return [__("Empty"), "gray", "batch_qty,=,0|disabled,=,0"]; } else if ( doc.expiry_date && frappe.datetime.get_diff(doc.expiry_date, frappe.datetime.nowdate()) <= 0 @@ -14,6 +12,8 @@ frappe.listview_settings["Batch"] = { "red", "expiry_date,not in,|expiry_date,<=,Today|batch_qty,>,0|disabled,=,0", ]; + } else if (!doc.batch_qty) { + return [__("Empty"), "gray", "batch_qty,=,0|disabled,=,0"]; } else { return [__("Active"), "green", "batch_qty,>,0|disabled,=,0"]; } 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 4c9fc881986..5e16115db01 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 @@ -1865,14 +1865,14 @@ def get_available_batches(kwargs): batch_ledger.warehouse, Sum(batch_ledger.qty).as_("qty"), ) - .where( - (batch_table.disabled == 0) - & ((batch_table.expiry_date >= today()) | (batch_table.expiry_date.isnull())) - ) + .where(batch_table.disabled == 0) .where(stock_ledger_entry.is_cancelled == 0) .groupby(batch_ledger.batch_no, batch_ledger.warehouse) ) + if not kwargs.get("for_stock_levels"): + query = query.where((batch_table.expiry_date >= today()) | (batch_table.expiry_date.isnull())) + if kwargs.get("posting_date"): if kwargs.get("posting_time") is None: kwargs.posting_time = nowtime()