From bae61760112baa78ad346934f0c792e21855494e Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 28 May 2024 20:37:52 +0530 Subject: [PATCH 1/5] =?UTF-8?q?refactor:=20renamed=20number=20of=20depreci?= =?UTF-8?q?ations=20booked=20to=20opening=20booked=20de=E2=80=A6=20(#41515?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: renamed number of depreciations booked to opening booked depreciations * feat: introduced new field for showing total number of booked depreciations --- .../doctype/journal_entry/journal_entry.py | 25 ++- erpnext/assets/doctype/asset/asset.json | 16 +- erpnext/assets/doctype/asset/asset.py | 27 ++- erpnext/assets/doctype/asset/depreciation.py | 1 + erpnext/assets/doctype/asset/test_asset.py | 26 +-- .../asset_depreciation_schedule.json | 6 +- .../asset_depreciation_schedule.py | 64 +++++-- .../test_asset_depreciation_schedule.py | 168 ++++++++++++++++++ .../asset_finance_book.json | 25 ++- .../asset_finance_book/asset_finance_book.py | 27 +++ .../doctype/asset_repair/asset_repair.py | 4 +- erpnext/patches.txt | 1 + ...sset_depreciation_schedules_from_assets.py | 2 +- ..._booked_to_opening_booked_depreciations.py | 7 + .../update_gpa_and_ndb_for_assdeprsch.py | 24 +-- 15 files changed, 362 insertions(+), 61 deletions(-) create mode 100644 erpnext/patches/v15_0/rename_number_of_depreciations_booked_to_opening_booked_depreciations.py diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 436b2c3907..be0b585ff7 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -197,7 +197,7 @@ class JournalEntry(AccountsController): self.update_asset_value() self.update_inter_company_jv() self.update_invoice_discounting() - self.update_unreconciled_amount() + self.update_booked_depreciation() def on_update_after_submit(self): if hasattr(self, "repost_required"): @@ -231,6 +231,7 @@ class JournalEntry(AccountsController): self.unlink_inter_company_jv() self.unlink_asset_adjustment_entry() self.update_invoice_discounting() + self.update_booked_depreciation() def get_title(self): return self.pay_to_recd_from or self.accounts[0].account @@ -443,6 +444,28 @@ class JournalEntry(AccountsController): if status: inv_disc_doc.set_status(status=status) + def update_booked_depreciation(self): + for d in self.get("accounts"): + if ( + self.voucher_type == "Depreciation Entry" + and d.reference_type == "Asset" + and d.reference_name + and frappe.get_cached_value("Account", d.account, "root_type") == "Expense" + and d.debit + ): + asset = frappe.get_doc("Asset", d.reference_name) + for fb_row in asset.get("finance_books"): + if fb_row.finance_book == self.finance_book: + depr_schedule = get_depr_schedule(asset.name, "Active", fb_row.finance_book) + total_number_of_booked_depreciations = asset.opening_number_of_booked_depreciations + for je in depr_schedule: + if je.journal_entry: + total_number_of_booked_depreciations += 1 + fb_row.db_set( + "total_number_of_booked_depreciations", total_number_of_booked_depreciations + ) + break + def unlink_advance_entry_reference(self): for d in self.get("accounts"): if d.is_advance == "Yes" and d.reference_type in ("Sales Invoice", "Purchase Invoice"): diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index 3a2a942bdf..99a430cbb4 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -45,7 +45,7 @@ "calculate_depreciation", "column_break_33", "opening_accumulated_depreciation", - "number_of_depreciations_booked", + "opening_number_of_booked_depreciations", "is_fully_depreciated", "section_break_36", "finance_books", @@ -257,12 +257,6 @@ "label": "Opening Accumulated Depreciation", "options": "Company:company:default_currency" }, - { - "depends_on": "eval:(doc.is_existing_asset)", - "fieldname": "number_of_depreciations_booked", - "fieldtype": "Int", - "label": "Number of Depreciations Booked" - }, { "collapsible": 1, "collapsible_depends_on": "eval:doc.calculate_depreciation || doc.is_existing_asset", @@ -546,6 +540,12 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "depends_on": "eval:(doc.is_existing_asset)", + "fieldname": "opening_number_of_booked_depreciations", + "fieldtype": "Int", + "label": "Opening Number of Booked Depreciations" } ], "idx": 72, @@ -589,7 +589,7 @@ "link_fieldname": "target_asset" } ], - "modified": "2024-04-18 16:45:47.306032", + "modified": "2024-05-21 13:46:21.066483", "modified_by": "Administrator", "module": "Assets", "name": "Asset", diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 57a8eb0eef..77cbb90ea8 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -89,8 +89,8 @@ class Asset(AccountsController): maintenance_required: DF.Check naming_series: DF.Literal["ACC-ASS-.YYYY.-"] next_depreciation_date: DF.Date | None - number_of_depreciations_booked: DF.Int opening_accumulated_depreciation: DF.Currency + opening_number_of_booked_depreciations: DF.Int policy_number: DF.Data | None purchase_amount: DF.Currency purchase_date: DF.Date | None @@ -145,7 +145,7 @@ class Asset(AccountsController): "Asset Depreciation Schedules created:
{0}

Please check, edit if needed, and submit the Asset." ).format(asset_depr_schedules_links) ) - + self.set_total_booked_depreciations() self.total_asset_cost = self.gross_purchase_amount self.status = self.get_status() @@ -422,7 +422,7 @@ class Asset(AccountsController): if not self.is_existing_asset: self.opening_accumulated_depreciation = 0 - self.number_of_depreciations_booked = 0 + self.opening_number_of_booked_depreciations = 0 else: depreciable_amount = flt(self.gross_purchase_amount) - flt(row.expected_value_after_useful_life) if flt(self.opening_accumulated_depreciation) > depreciable_amount: @@ -433,15 +433,15 @@ class Asset(AccountsController): ) if self.opening_accumulated_depreciation: - if not self.number_of_depreciations_booked: - frappe.throw(_("Please set Number of Depreciations Booked")) + if not self.opening_number_of_booked_depreciations: + frappe.throw(_("Please set Opening Number of Booked Depreciations")) else: - self.number_of_depreciations_booked = 0 + self.opening_number_of_booked_depreciations = 0 - if flt(row.total_number_of_depreciations) <= cint(self.number_of_depreciations_booked): + if flt(row.total_number_of_depreciations) <= cint(self.opening_number_of_booked_depreciations): frappe.throw( _( - "Row {0}: Total Number of Depreciations cannot be less than or equal to Number of Depreciations Booked" + "Row {0}: Total Number of Depreciations cannot be less than or equal to Opening Number of Booked Depreciations" ).format(row.idx), title=_("Invalid Schedule"), ) @@ -464,6 +464,17 @@ class Asset(AccountsController): ).format(row.idx) ) + def set_total_booked_depreciations(self): + # set value of total number of booked depreciations field + for fb_row in self.get("finance_books"): + total_number_of_booked_depreciations = self.opening_number_of_booked_depreciations + depr_schedule = get_depr_schedule(self.name, "Active", fb_row.finance_book) + if depr_schedule: + for je in depr_schedule: + if je.journal_entry: + total_number_of_booked_depreciations += 1 + fb_row.db_set("total_number_of_booked_depreciations", total_number_of_booked_depreciations) + def validate_expected_value_after_useful_life(self): for row in self.get("finance_books"): depr_schedule = get_depr_schedule(self.name, "Draft", row.finance_book) diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index b968736cee..6af057d642 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -326,6 +326,7 @@ def _make_journal_entry_for_depreciation( if not je.meta.get_workflow(): je.submit() + asset.reload() idx = cint(asset_depr_schedule_doc.finance_book_id) row = asset.get("finance_books")[idx - 1] row.value_after_depreciation -= depr_schedule.depreciation_amount diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index c0ded4b2a5..bb6b102b62 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -352,7 +352,7 @@ class TestAsset(AssetSetup): purchase_date="2020-04-01", expected_value_after_useful_life=0, total_number_of_depreciations=5, - number_of_depreciations_booked=2, + opening_number_of_booked_depreciations=2, frequency_of_depreciation=12, depreciation_start_date="2023-03-31", opening_accumulated_depreciation=24000, @@ -450,7 +450,7 @@ class TestAsset(AssetSetup): purchase_date="2020-01-01", expected_value_after_useful_life=0, total_number_of_depreciations=6, - number_of_depreciations_booked=1, + opening_number_of_booked_depreciations=1, frequency_of_depreciation=10, depreciation_start_date="2021-01-01", opening_accumulated_depreciation=20000, @@ -707,7 +707,7 @@ class TestDepreciationMethods(AssetSetup): calculate_depreciation=1, available_for_use_date="2030-06-06", is_existing_asset=1, - number_of_depreciations_booked=2, + opening_number_of_booked_depreciations=2, opening_accumulated_depreciation=47095.89, expected_value_after_useful_life=10000, depreciation_start_date="2032-12-31", @@ -757,7 +757,7 @@ class TestDepreciationMethods(AssetSetup): available_for_use_date="2030-01-01", is_existing_asset=1, depreciation_method="Double Declining Balance", - number_of_depreciations_booked=1, + opening_number_of_booked_depreciations=1, opening_accumulated_depreciation=50000, expected_value_after_useful_life=10000, depreciation_start_date="2031-12-31", @@ -1091,8 +1091,8 @@ class TestDepreciationBasics(AssetSetup): self.assertRaises(frappe.ValidationError, asset.save) - def test_number_of_depreciations_booked(self): - """Tests if an error is raised when number_of_depreciations_booked is not specified when opening_accumulated_depreciation is.""" + def test_opening_booked_depreciations(self): + """Tests if an error is raised when opening_number_of_booked_depreciations is not specified when opening_accumulated_depreciation is.""" asset = create_asset( item_code="Macbook Pro", @@ -1108,9 +1108,9 @@ class TestDepreciationBasics(AssetSetup): self.assertRaises(frappe.ValidationError, asset.save) def test_number_of_depreciations(self): - """Tests if an error is raised when number_of_depreciations_booked >= total_number_of_depreciations.""" + """Tests if an error is raised when opening_number_of_booked_depreciations >= total_number_of_depreciations.""" - # number_of_depreciations_booked > total_number_of_depreciations + # opening_number_of_booked_depreciations > total_number_of_depreciations asset = create_asset( item_code="Macbook Pro", calculate_depreciation=1, @@ -1119,13 +1119,13 @@ class TestDepreciationBasics(AssetSetup): expected_value_after_useful_life=10000, depreciation_start_date="2020-07-01", opening_accumulated_depreciation=10000, - number_of_depreciations_booked=5, + opening_number_of_booked_depreciations=5, do_not_save=1, ) self.assertRaises(frappe.ValidationError, asset.save) - # number_of_depreciations_booked = total_number_of_depreciations + # opening_number_of_booked_depreciations = total_number_of_depreciations asset_2 = create_asset( item_code="Macbook Pro", calculate_depreciation=1, @@ -1134,7 +1134,7 @@ class TestDepreciationBasics(AssetSetup): expected_value_after_useful_life=10000, depreciation_start_date="2020-07-01", opening_accumulated_depreciation=10000, - number_of_depreciations_booked=5, + opening_number_of_booked_depreciations=5, do_not_save=1, ) @@ -1482,7 +1482,7 @@ class TestDepreciationBasics(AssetSetup): asset = create_asset(calculate_depreciation=1) asset.opening_accumulated_depreciation = 2000 - asset.number_of_depreciations_booked = 1 + asset.opening_number_of_booked_depreciations = 1 asset.finance_books[0].expected_value_after_useful_life = 100 asset.save() @@ -1676,7 +1676,7 @@ def create_asset(**args): "purchase_date": args.purchase_date or "2015-01-01", "calculate_depreciation": args.calculate_depreciation or 0, "opening_accumulated_depreciation": args.opening_accumulated_depreciation or 0, - "number_of_depreciations_booked": args.number_of_depreciations_booked or 0, + "opening_number_of_booked_depreciations": args.opening_number_of_booked_depreciations or 0, "gross_purchase_amount": args.gross_purchase_amount or 100000, "purchase_amount": args.purchase_amount or 100000, "maintenance_required": args.maintenance_required or 0, diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json index 5487cded84..01c0ea7fc7 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json @@ -13,7 +13,7 @@ "column_break_2", "gross_purchase_amount", "opening_accumulated_depreciation", - "number_of_depreciations_booked", + "opening_number_of_booked_depreciations", "finance_book", "finance_book_id", "depreciation_details_section", @@ -169,10 +169,10 @@ "read_only": 1 }, { - "fieldname": "number_of_depreciations_booked", + "fieldname": "opening_number_of_booked_depreciations", "fieldtype": "Int", "hidden": 1, - "label": "Number of Depreciations Booked", + "label": "Opening Number of Booked Depreciations", "print_hide": 1, "read_only": 1 }, 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 448c34f36c..0b07015391 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -50,7 +50,7 @@ class AssetDepreciationSchedule(Document): gross_purchase_amount: DF.Currency naming_series: DF.Literal["ACC-ADS-.YYYY.-"] notes: DF.SmallText | None - number_of_depreciations_booked: DF.Int + opening_number_of_booked_depreciations: DF.Int opening_accumulated_depreciation: DF.Currency rate_of_depreciation: DF.Percent shift_based: DF.Check @@ -150,7 +150,7 @@ class AssetDepreciationSchedule(Document): return ( asset_doc.gross_purchase_amount != self.gross_purchase_amount or asset_doc.opening_accumulated_depreciation != self.opening_accumulated_depreciation - or asset_doc.number_of_depreciations_booked != self.number_of_depreciations_booked + or asset_doc.opening_number_of_booked_depreciations != self.opening_number_of_booked_depreciations ) def not_manual_depr_or_have_manual_depr_details_been_modified(self, row): @@ -183,7 +183,7 @@ class AssetDepreciationSchedule(Document): self.finance_book = row.finance_book self.finance_book_id = row.idx self.opening_accumulated_depreciation = asset_doc.opening_accumulated_depreciation or 0 - self.number_of_depreciations_booked = asset_doc.number_of_depreciations_booked or 0 + self.opening_number_of_booked_depreciations = asset_doc.opening_number_of_booked_depreciations or 0 self.gross_purchase_amount = asset_doc.gross_purchase_amount self.depreciation_method = row.depreciation_method self.total_number_of_depreciations = row.total_number_of_depreciations @@ -248,7 +248,7 @@ class AssetDepreciationSchedule(Document): row.db_update() final_number_of_depreciations = cint(row.total_number_of_depreciations) - cint( - self.number_of_depreciations_booked + self.opening_number_of_booked_depreciations ) has_pro_rata = _check_is_pro_rata(asset_doc, row) @@ -315,7 +315,7 @@ class AssetDepreciationSchedule(Document): if date_of_disposal and getdate(schedule_date) >= getdate(date_of_disposal): from_date = add_months( getdate(asset_doc.available_for_use_date), - (asset_doc.number_of_depreciations_booked * row.frequency_of_depreciation), + (asset_doc.opening_number_of_booked_depreciations * row.frequency_of_depreciation), ) if self.depreciation_schedule: @@ -369,13 +369,16 @@ class AssetDepreciationSchedule(Document): from_date = get_last_day( add_months( getdate(asset_doc.available_for_use_date), - ((self.number_of_depreciations_booked - 1) * row.frequency_of_depreciation), + ( + (self.opening_number_of_booked_depreciations - 1) + * row.frequency_of_depreciation + ), ) ) else: from_date = add_months( getdate(add_days(asset_doc.available_for_use_date, -1)), - (self.number_of_depreciations_booked * row.frequency_of_depreciation), + (self.opening_number_of_booked_depreciations * row.frequency_of_depreciation), ) depreciation_amount, days, months = _get_pro_rata_amt( row, @@ -391,7 +394,8 @@ class AssetDepreciationSchedule(Document): # In case of increase_in_asset_life, the asset.to_date is already set on asset_repair submission asset_doc.to_date = add_months( asset_doc.available_for_use_date, - (n + self.number_of_depreciations_booked) * cint(row.frequency_of_depreciation), + (n + self.opening_number_of_booked_depreciations) + * cint(row.frequency_of_depreciation), ) depreciation_amount_without_pro_rata = depreciation_amount @@ -533,7 +537,7 @@ def _check_is_pro_rata(asset_doc, row, wdv_or_dd_non_yearly=False): has_pro_rata = False # if not existing asset, from_date = available_for_use_date - # otherwise, if number_of_depreciations_booked = 2, available_for_use_date = 01/01/2020 and frequency_of_depreciation = 12 + # otherwise, if opening_number_of_booked_depreciations = 2, available_for_use_date = 01/01/2020 and frequency_of_depreciation = 12 # from_date = 01/01/2022 from_date = _get_modified_available_for_use_date(asset_doc, row, wdv_or_dd_non_yearly) days = date_diff(row.depreciation_start_date, from_date) + 1 @@ -557,12 +561,12 @@ def _get_modified_available_for_use_date(asset_doc, row, wdv_or_dd_non_yearly=Fa if wdv_or_dd_non_yearly: return add_months( asset_doc.available_for_use_date, - (asset_doc.number_of_depreciations_booked * 12), + (asset_doc.opening_number_of_booked_depreciations * 12), ) else: return add_months( asset_doc.available_for_use_date, - (asset_doc.number_of_depreciations_booked * row.frequency_of_depreciation), + (asset_doc.opening_number_of_booked_depreciations * row.frequency_of_depreciation), ) @@ -644,11 +648,47 @@ def get_straight_line_or_manual_depr_amount( ) / number_of_pending_depreciations # if the Depreciation Schedule is being prepared for the first time else: + if row.daily_prorata_based: + amount = ( + flt(asset.gross_purchase_amount) + - flt(asset.opening_accumulated_depreciation) + - flt(row.expected_value_after_useful_life) + ) + return get_daily_prorata_based_straight_line_depr( + asset, row, schedule_idx, number_of_pending_depreciations, amount + ) + else: + return ( + flt(asset.gross_purchase_amount) + - flt(asset.opening_accumulated_depreciation) + - flt(row.expected_value_after_useful_life) + ) / flt(row.total_number_of_depreciations - asset.opening_number_of_booked_depreciations) + + +def get_daily_prorata_based_straight_line_depr( + 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 + + 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) + 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_shift_depr_amount(asset_depr_schedule, asset, row, schedule_idx): + if asset_depr_schedule.get("__islocal") and not asset.flags.shift_allocation: return ( flt(asset.gross_purchase_amount) - flt(asset.opening_accumulated_depreciation) - flt(row.expected_value_after_useful_life) - ) / flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked) + ) / flt(row.total_number_of_depreciations - asset.opening_number_of_booked_depreciations) @erpnext.allow_regional 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 024121d394..217b036590 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 @@ -4,6 +4,9 @@ import frappe from frappe.tests.utils import FrappeTestCase +from erpnext.assets.doctype.asset.depreciation import ( + post_depreciation_entries, +) 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, @@ -25,3 +28,168 @@ class TestAssetDepreciationSchedule(FrappeTestCase): ) self.assertRaises(frappe.ValidationError, second_asset_depr_schedule.insert) + + def test_daily_prorata_based_depr_on_sl_method(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) + + # Test for Written Down Value Method + # 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, + 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=6, + rate_of_depreciation=40, + ) + + 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-02-29", + frequency_of_depreciation=6, + total_number_of_depreciations=6, + rate_of_depreciation=40, + ) + + 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-31", + frequency_of_depreciation=12, + total_number_of_depreciations=4, + rate_of_depreciation=40, + ) + + 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) + + def test_update_total_number_of_booked_depreciations(self): + # check if updates total number of booked depreciations when depreciation gets booked + asset = create_asset( + item_code="Macbook Pro", + calculate_depreciation=1, + opening_accumulated_depreciation=2000, + opening_number_of_booked_depreciations=2, + depreciation_method="Straight Line", + available_for_use_date="2020-03-01", + depreciation_start_date="2020-03-31", + frequency_of_depreciation=1, + total_number_of_depreciations=24, + submit=1, + ) + + post_depreciation_entries(date="2021-03-31") + asset.reload() + """ + opening_number_of_booked_depreciations = 2 + number_of_booked_depreciations till 2021-03-31 = 13 + total_number_of_booked_depreciations = 15 + """ + self.assertEqual(asset.finance_books[0].total_number_of_booked_depreciations, 15) + + # cancel depreciation entry + depr_entry = get_depr_schedule(asset.name, "Active")[0].journal_entry + + frappe.get_doc("Journal Entry", depr_entry).cancel() + asset.reload() + + self.assertEqual(asset.finance_books[0].total_number_of_booked_depreciations, 14) diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json index e10257f87d..a6a7d81de7 100644 --- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json +++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json @@ -8,6 +8,9 @@ "finance_book", "depreciation_method", "total_number_of_depreciations", + "total_number_of_booked_depreciations", + "daily_prorata_based", + "shift_based", "column_break_5", "frequency_of_depreciation", "depreciation_start_date", @@ -86,12 +89,32 @@ "fieldname": "salvage_value_percentage", "fieldtype": "Percent", "label": "Salvage Value Percentage" + }, + { + "default": "0", + "fieldname": "daily_prorata_based", + "fieldtype": "Check", + "label": "Depreciate based on daily pro-rata" + }, + { + "default": "0", + "depends_on": "eval:doc.depreciation_method == \"Straight Line\"", + "fieldname": "shift_based", + "fieldtype": "Check", + "label": "Depreciate based on shifts" + }, + { + "default": "0", + "fieldname": "total_number_of_booked_depreciations", + "fieldtype": "Int", + "label": "Total Number of Booked Depreciations ", + "read_only": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-12-29 08:49:39.876439", + "modified": "2024-05-21 15:48:20.907250", "modified_by": "Administrator", "module": "Assets", "name": "Asset Finance Book", diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.py b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.py index 56e4d4ff3d..ab01956625 100644 --- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.py +++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.py @@ -7,4 +7,31 @@ from frappe.model.document import Document class AssetFinanceBook(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + daily_prorata_based: DF.Check + depreciation_method: DF.Literal[ + "", "Straight Line", "Double Declining Balance", "Written Down Value", "Manual" + ] + depreciation_start_date: DF.Date | None + expected_value_after_useful_life: DF.Currency + finance_book: DF.Link | None + frequency_of_depreciation: DF.Int + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + rate_of_depreciation: DF.Percent + salvage_value_percentage: DF.Percent + shift_based: DF.Check + total_number_of_booked_depreciations: DF.Int + total_number_of_depreciations: DF.Int + value_after_depreciation: DF.Currency + # end: auto-generated types + pass diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 81a97ed10b..0b8450614c 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -345,7 +345,7 @@ class AssetRepair(AccountsController): def calculate_last_schedule_date(self, asset, row, extra_months): asset.flags.increase_in_asset_life = True number_of_pending_depreciations = cint(row.total_number_of_depreciations) - cint( - asset.number_of_depreciations_booked + asset.opening_number_of_booked_depreciations ) depr_schedule = get_depr_schedule(asset.name, "Active", row.finance_book) @@ -378,7 +378,7 @@ class AssetRepair(AccountsController): def calculate_last_schedule_date_before_modification(self, asset, row, extra_months): asset.flags.increase_in_asset_life = True number_of_pending_depreciations = cint(row.total_number_of_depreciations) - cint( - asset.number_of_depreciations_booked + asset.opening_number_of_booked_depreciations ) depr_schedule = get_depr_schedule(asset.name, "Active", row.finance_book) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 8f663fc009..dfcd258c6e 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -395,3 +395,4 @@ erpnext.patches.v14_0.set_maintain_stock_for_bom_item erpnext.patches.v15_0.fix_debit_credit_in_transaction_currency erpnext.patches.v15_0.rename_purchase_receipt_amount_to_purchase_amount erpnext.patches.v14_0.enable_set_priority_for_pricing_rules #1 +erpnext.patches.v15_0.rename_number_of_depreciations_booked_to_opening_booked_depreciations diff --git a/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py b/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py index bc0fd2871a..3087ff0209 100644 --- a/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py +++ b/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py @@ -43,7 +43,7 @@ def get_details_of_draft_or_submitted_depreciable_assets(): asset.name, asset.opening_accumulated_depreciation, asset.gross_purchase_amount, - asset.number_of_depreciations_booked, + asset.opening_number_of_booked_depreciations, asset.docstatus, ) .where(asset.calculate_depreciation == 1) diff --git a/erpnext/patches/v15_0/rename_number_of_depreciations_booked_to_opening_booked_depreciations.py b/erpnext/patches/v15_0/rename_number_of_depreciations_booked_to_opening_booked_depreciations.py new file mode 100644 index 0000000000..1818337455 --- /dev/null +++ b/erpnext/patches/v15_0/rename_number_of_depreciations_booked_to_opening_booked_depreciations.py @@ -0,0 +1,7 @@ +import frappe +from frappe.model.utils.rename_field import rename_field + + +def execute(): + if frappe.db.has_column("Asset", "number_of_depreciations_booked"): + rename_field("Asset", "number_of_depreciations_booked", "opening_number_of_booked_depreciations") diff --git a/erpnext/patches/v15_0/update_gpa_and_ndb_for_assdeprsch.py b/erpnext/patches/v15_0/update_gpa_and_ndb_for_assdeprsch.py index 2198be73d8..4399a95fda 100644 --- a/erpnext/patches/v15_0/update_gpa_and_ndb_for_assdeprsch.py +++ b/erpnext/patches/v15_0/update_gpa_and_ndb_for_assdeprsch.py @@ -5,16 +5,16 @@ def execute(): # not using frappe.qb because https://github.com/frappe/frappe/issues/20292 frappe.db.sql( """UPDATE `tabAsset Depreciation Schedule` - JOIN `tabAsset` - ON `tabAsset Depreciation Schedule`.`asset`=`tabAsset`.`name` - SET - `tabAsset Depreciation Schedule`.`gross_purchase_amount`=`tabAsset`.`gross_purchase_amount`, - `tabAsset Depreciation Schedule`.`number_of_depreciations_booked`=`tabAsset`.`number_of_depreciations_booked` - WHERE - ( - `tabAsset Depreciation Schedule`.`gross_purchase_amount`<>`tabAsset`.`gross_purchase_amount` - OR - `tabAsset Depreciation Schedule`.`number_of_depreciations_booked`<>`tabAsset`.`number_of_depreciations_booked` - ) - AND `tabAsset Depreciation Schedule`.`docstatus`<2""" + JOIN `tabAsset` + ON `tabAsset Depreciation Schedule`.`asset`=`tabAsset`.`name` + SET + `tabAsset Depreciation Schedule`.`gross_purchase_amount`=`tabAsset`.`gross_purchase_amount`, + `tabAsset Depreciation Schedule`.`opening_number_of_booked_depreciations`=`tabAsset`.`opening_number_of_booked_depreciations` + WHERE + ( + `tabAsset Depreciation Schedule`.`gross_purchase_amount`<>`tabAsset`.`gross_purchase_amount` + OR + `tabAsset Depreciation Schedule`.`opening_number_of_booked_depreciations`<>`tabAsset`.`opening_number_of_booked_depreciations` + ) + AND `tabAsset Depreciation Schedule`.`docstatus`<2""" ) -- GitLab From e33956bdc6e11403ce6a963375227cda2eddf492 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Wed, 29 May 2024 06:36:29 +0000 Subject: [PATCH 2/5] fix: merge conflict --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index be0b585ff7..24b82b29cb 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -197,6 +197,7 @@ class JournalEntry(AccountsController): self.update_asset_value() self.update_inter_company_jv() self.update_invoice_discounting() + self.update_unreconciled_amount() # @dokos self.update_booked_depreciation() def on_update_after_submit(self): -- GitLab From 5ab8ff5011a8f3ca9c354a6c002da6985e7dab96 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Wed, 29 May 2024 16:23:30 +0000 Subject: [PATCH 3/5] fix: missing dependency --- .../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 217b036590..4399774c7a 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 @@ -10,6 +10,7 @@ from erpnext.assets.doctype.asset.depreciation import ( 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 ) -- GitLab From 3fa5f3faa27501a97a446bca073d911ef5a192e9 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Wed, 29 May 2024 19:54:09 +0000 Subject: [PATCH 4/5] fix: missing utils import --- .../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 4399774c7a..ec0f40c842 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,6 +3,7 @@ import frappe from frappe.tests.utils import FrappeTestCase +from frappe.utils import cstr from erpnext.assets.doctype.asset.depreciation import ( post_depreciation_entries, -- GitLab From 90c5894d9507f3bb57a290e47b9cefe4a41339c6 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Thu, 30 May 2024 08:45:10 +0200 Subject: [PATCH 5/5] chore: rebase assets module --- erpnext/assets/doctype/asset/asset.js | 701 ++++++++++-------- erpnext/assets/doctype/asset/asset.py | 93 +-- erpnext/assets/doctype/asset/asset_list.js | 32 +- erpnext/assets/doctype/asset/depreciation.py | 47 +- erpnext/assets/doctype/asset/test_asset.py | 170 +++-- .../asset_activity/asset_activity.json | 4 +- .../doctype/asset_activity/asset_activity.py | 14 + .../asset_capitalization.js | 183 +++-- .../asset_capitalization.json | 4 +- .../asset_capitalization.py | 57 ++ .../test_asset_capitalization.py | 22 +- .../asset_capitalization_asset_item.json | 7 +- .../asset_capitalization_asset_item.py | 22 + .../asset_capitalization_service_item.json | 4 +- .../asset_capitalization_service_item.py | 21 + .../asset_capitalization_stock_item.json | 4 +- .../doctype/asset_category/asset_category.js | 71 +- .../asset_category/asset_category.json | 8 +- .../doctype/asset_category/asset_category.py | 26 +- .../asset_category/test_asset_category.py | 7 +- .../asset_category_account.json | 251 ++----- .../asset_category_account.py | 19 +- .../asset_depreciation_schedule.js | 59 +- .../asset_depreciation_schedule.json | 25 +- .../asset_depreciation_schedule.py | 280 +++++-- .../test_asset_depreciation_schedule.py | 4 +- .../asset_finance_book.json | 9 +- .../asset_finance_book/asset_finance_book.py | 1 - .../asset_maintenance/asset_maintenance.js | 73 +- .../asset_maintenance/asset_maintenance.json | 279 +++---- .../asset_maintenance/asset_maintenance.py | 33 +- .../asset_maintenance_log.js | 12 +- .../asset_maintenance_log.json | 402 +++++----- .../asset_maintenance_log.py | 28 + .../asset_maintenance_log_calendar.js | 24 +- .../asset_maintenance_log_list.js | 15 +- .../test_asset_maintenance_log.py | 1 - .../asset_maintenance_task.json | 7 +- .../asset_maintenance_task.py | 28 +- .../asset_maintenance_team.js | 6 +- .../asset_maintenance_team.json | 173 ++--- .../asset_maintenance_team.py | 20 +- .../test_asset_maintenance_team.py | 1 - .../doctype/asset_movement/asset_movement.js | 97 +-- .../asset_movement/asset_movement.json | 4 +- .../doctype/asset_movement/asset_movement.py | 47 +- .../asset_movement/test_asset_movement.py | 30 +- .../asset_movement_item.json | 9 +- .../asset_movement_item.py | 20 + .../doctype/asset_repair/asset_repair.js | 96 +-- .../doctype/asset_repair/asset_repair.json | 6 +- .../doctype/asset_repair/asset_repair.py | 44 +- .../doctype/asset_repair/asset_repair_list.js | 12 +- .../doctype/asset_repair/test_asset_repair.py | 16 +- .../asset_repair_consumed_item.json | 4 +- .../asset_repair_consumed_item.py | 19 + .../asset_shift_allocation/__init__.py | 0 .../asset_shift_allocation.js | 14 + .../asset_shift_allocation.json | 111 +++ .../asset_shift_allocation.py | 273 +++++++ .../test_asset_shift_allocation.py | 113 +++ .../doctype/asset_shift_factor/__init__.py | 0 .../asset_shift_factor/asset_shift_factor.js | 8 + .../asset_shift_factor.json | 74 ++ .../asset_shift_factor/asset_shift_factor.py | 35 + .../test_asset_shift_factor.py | 9 + .../asset_value_adjustment.js | 42 +- .../asset_value_adjustment.json | 369 ++++----- .../asset_value_adjustment.py | 4 +- .../test_asset_value_adjustment.py | 14 +- .../depreciation_schedule.json | 11 +- .../depreciation_schedule.py | 18 + .../linked_location/linked_location.json | 99 +-- .../linked_location/linked_location.py | 15 +- erpnext/assets/doctype/location/location.js | 15 +- erpnext/assets/doctype/location/location.json | 26 +- erpnext/assets/doctype/location/location.py | 29 +- .../assets/doctype/location/location_tree.js | 16 +- .../assets/doctype/location/test_location.py | 5 +- .../maintenance_team_member.js | 6 +- .../maintenance_team_member.json | 183 ++--- .../maintenance_team_member.py | 17 +- .../test_maintenance_team_member.py | 1 - erpnext/regional/france/assets.py | 33 +- 84 files changed, 3169 insertions(+), 2022 deletions(-) create mode 100644 erpnext/assets/doctype/asset_shift_allocation/__init__.py create mode 100644 erpnext/assets/doctype/asset_shift_allocation/asset_shift_allocation.js create mode 100644 erpnext/assets/doctype/asset_shift_allocation/asset_shift_allocation.json create mode 100644 erpnext/assets/doctype/asset_shift_allocation/asset_shift_allocation.py create mode 100644 erpnext/assets/doctype/asset_shift_allocation/test_asset_shift_allocation.py create mode 100644 erpnext/assets/doctype/asset_shift_factor/__init__.py create mode 100644 erpnext/assets/doctype/asset_shift_factor/asset_shift_factor.js create mode 100644 erpnext/assets/doctype/asset_shift_factor/asset_shift_factor.json create mode 100644 erpnext/assets/doctype/asset_shift_factor/asset_shift_factor.py create mode 100644 erpnext/assets/doctype/asset_shift_factor/test_asset_shift_factor.py diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index cb3ce813b9..46dbe07420 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -4,77 +4,77 @@ frappe.provide("erpnext.asset"); frappe.provide("erpnext.accounts.dimensions"); -frappe.ui.form.on('Asset', { - onload: function(frm) { - frm.set_query("item_code", function() { +frappe.ui.form.on("Asset", { + onload: function (frm) { + frm.set_query("item_code", function () { return { - "filters": { - "is_fixed_asset": 1, - "is_stock_item": 0 - } + filters: { + is_fixed_asset: 1, + is_stock_item: 0, + }, }; }); - frm.set_query("warehouse", function() { + frm.set_query("warehouse", function () { return { - "filters": { - "company": frm.doc.company, - "is_group": 0 - } + filters: { + company: frm.doc.company, + is_group: 0, + }, }; }); - frm.set_query("department", function() { + frm.set_query("department", function () { return { - "filters": { - "company": frm.doc.company, - } + filters: { + company: frm.doc.company, + }, }; }); erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, - company: function(frm) { + company: function (frm) { erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, - setup: function(frm) { - frm.ignore_doctypes_on_cancel_all = ['Journal Entry']; + setup: function (frm) { + frm.ignore_doctypes_on_cancel_all = ["Journal Entry"]; frm.make_methods = { - 'Asset Movement': () => { + "Asset Movement": () => { frappe.call({ - method: "erpnext.assets.doctype.asset.asset.make_asset_movement", - freeze: true, - args:{ - "assets": [{ name: cur_frm.doc.name }] - }, - callback: function (r) { - if (r.message) { - var doc = frappe.model.sync(r.message)[0]; - frappe.set_route("Form", doc.doctype, doc.name); - } - } - }); + method: "erpnext.assets.doctype.asset.asset.make_asset_movement", + freeze: true, + args: { + assets: [{ name: frm.doc.name }], + }, + callback: function (r) { + if (r.message) { + var doc = frappe.model.sync(r.message)[0]; + frappe.set_route("Form", doc.doctype, doc.name); + } + }, + }); }, - } + }; frm.set_query("purchase_receipt", (doc) => { return { query: "erpnext.controllers.queries.get_purchase_receipts", - filters: { item_code: doc.item_code } - } + filters: { item_code: doc.item_code }, + }; }); frm.set_query("purchase_invoice", (doc) => { return { query: "erpnext.controllers.queries.get_purchase_invoices", - filters: { item_code: doc.item_code } - } + filters: { item_code: doc.item_code }, + }; }); }, - refresh: function(frm) { + refresh: function (frm) { frappe.ui.form.trigger("Asset", "is_existing_asset"); frm.toggle_display("next_depreciation_date", frm.doc.docstatus < 1); @@ -114,41 +114,65 @@ frappe.ui.form.on('Asset', { } if (frm.doc.maintenance_required && !frm.doc.maintenance_schedule) { - frm.add_custom_button(__("Maintain Asset"), function() { - frm.trigger("create_asset_maintenance"); - }, __("Manage")); + frm.add_custom_button( + __("Maintain Asset"), + function () { + frm.trigger("create_asset_maintenance"); + }, + __("Manage") + ); } - frm.add_custom_button(__("Repair Asset"), function() { - frm.trigger("create_asset_repair"); - }, __("Manage")); + frm.add_custom_button( + __("Repair Asset"), + function () { + frm.trigger("create_asset_repair"); + }, + __("Manage") + ); - frm.add_custom_button(__("Split Asset"), function() { - frm.trigger("split_asset"); - }, __("Manage")); + frm.add_custom_button( + __("Split Asset"), + function () { + frm.trigger("split_asset"); + }, + __("Manage") + ); - if (frm.doc.status != 'Fully Depreciated') { - frm.add_custom_button(__("Adjust Asset Value"), function() { - frm.trigger("create_asset_value_adjustment"); - }, __("Manage")); + if (frm.doc.status != "Fully Depreciated") { + frm.add_custom_button( + __("Adjust Asset Value"), + function () { + frm.trigger("create_asset_value_adjustment"); + }, + __("Manage") + ); } if (!frm.doc.calculate_depreciation) { - frm.add_custom_button(__("Create Depreciation Entry"), function() { - frm.trigger("make_journal_entry"); - }, __("Manage")); + frm.add_custom_button( + __("Create Depreciation Entry"), + function () { + frm.trigger("make_journal_entry"); + }, + __("Manage") + ); } if (frm.doc.purchase_receipt || !frm.doc.is_existing_asset) { - frm.add_custom_button(__("View General Ledger"), function() { - frappe.route_options = { - "voucher_no": frm.doc.name, - "from_date": frm.doc.available_for_use_date, - "to_date": frm.doc.available_for_use_date, - "company": frm.doc.company - }; - frappe.set_route("query-report", "General Ledger"); - }, __("Manage")); + frm.add_custom_button( + __("View General Ledger"), + function () { + frappe.route_options = { + voucher_no: frm.doc.name, + from_date: frm.doc.available_for_use_date, + to_date: frm.doc.available_for_use_date, + company: frm.doc.company, + }; + frappe.set_route("query-report", "General Ledger"); + }, + __("Manage") + ); } if (frm.doc.depr_entry_posting_status === "Failed") { @@ -164,21 +188,26 @@ frappe.ui.form.on('Asset', { frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation); if (frm.doc.is_composite_asset && !frm.doc.capitalized_in) { - $('.primary-action').prop('hidden', true); - $('.form-message').text('Capitalize this asset to confirm'); + $(".primary-action").prop("hidden", true); + $(".form-message").text("Capitalize this asset to confirm"); - frm.add_custom_button(__("Capitalize Asset"), function() { + frm.add_custom_button(__("Capitalize Asset"), function () { frm.trigger("create_asset_capitalization"); }); } } if (frm.doc.docstatus === 1) { - frm.add_custom_button(__('Accounting Journal Adjustment'), () => { + frm.add_custom_button( + __("Accounting Journal Adjustment"), + () => { frappe.require("assets/erpnext/js/accounting_journal_adjustment.js", () => { - new erpnext.journalAdjustment({doctype: frm.doctype, docnames: [frm.docname]}) + new erpnext.journalAdjustment({ doctype: frm.doctype, docnames: [frm.docname] }); }); - }, __('Create'), true); + }, + __("Create"), + true + ); } }, @@ -195,146 +224,192 @@ frappe.ui.form.on('Asset', { frm.dashboard.set_headline_alert(alert); }, - toggle_reference_doc: function(frm) { + toggle_reference_doc: function (frm) { if (frm.doc.purchase_receipt && frm.doc.purchase_invoice && frm.doc.docstatus === 1) { - frm.set_df_property('purchase_invoice', 'read_only', 1); - frm.set_df_property('purchase_receipt', 'read_only', 1); - } - else if (frm.doc.is_existing_asset || frm.doc.is_composite_asset) { - frm.toggle_reqd('purchase_receipt', 0); - frm.toggle_reqd('purchase_invoice', 0); - } - else if (frm.doc.purchase_receipt) { + frm.set_df_property("purchase_invoice", "read_only", 1); + frm.set_df_property("purchase_receipt", "read_only", 1); + } else if (frm.doc.is_existing_asset || frm.doc.is_composite_asset) { + frm.toggle_reqd("purchase_receipt", 0); + frm.toggle_reqd("purchase_invoice", 0); + } else if (frm.doc.purchase_receipt) { // if purchase receipt link is set then set PI disabled - frm.toggle_reqd('purchase_invoice', 0); - frm.set_df_property('purchase_invoice', 'read_only', 1); - } - else if (frm.doc.purchase_invoice) { + frm.toggle_reqd("purchase_invoice", 0); + frm.set_df_property("purchase_invoice", "read_only", 1); + } else if (frm.doc.purchase_invoice) { // if purchase invoice link is set then set PR disabled - frm.toggle_reqd('purchase_receipt', 0); - frm.set_df_property('purchase_receipt', 'read_only', 1); - } - else { - frm.toggle_reqd('purchase_receipt', 1); - frm.set_df_property('purchase_receipt', 'read_only', 0); - frm.toggle_reqd('purchase_invoice', 1); - frm.set_df_property('purchase_invoice', 'read_only', 0); + frm.toggle_reqd("purchase_receipt", 0); + frm.set_df_property("purchase_receipt", "read_only", 1); + } else { + frm.toggle_reqd("purchase_receipt", 1); + frm.set_df_property("purchase_receipt", "read_only", 0); + frm.toggle_reqd("purchase_invoice", 1); + frm.set_df_property("purchase_invoice", "read_only", 0); } }, - make_journal_entry: function(frm) { + make_journal_entry: function (frm) { frappe.call({ method: "erpnext.assets.doctype.asset.asset.make_journal_entry", args: { - asset_name: frm.doc.name + asset_name: frm.doc.name, }, - callback: function(r) { + callback: function (r) { if (r.message) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); } - } - }) + }, + }); }, - render_depreciation_schedule_view: function(frm, depr_schedule) { + render_depreciation_schedule_view: function (frm, asset_depr_schedule_doc) { let wrapper = $(frm.fields_dict["depreciation_schedule_view"].wrapper).empty(); let data = []; - depr_schedule.forEach((sch) => { + asset_depr_schedule_doc.depreciation_schedule.forEach((sch) => { const row = [ - sch['idx'], - frappe.format(sch['schedule_date'], { fieldtype: 'Date' }), - frappe.format(sch['depreciation_amount'], { fieldtype: 'Currency' }), - frappe.format(sch['accumulated_depreciation_amount'], { fieldtype: 'Currency' }), - sch['journal_entry'] || '' + sch["idx"], + frappe.format(sch["schedule_date"], { fieldtype: "Date" }), + frappe.format(sch["depreciation_amount"], { fieldtype: "Currency" }), + frappe.format(sch["accumulated_depreciation_amount"], { fieldtype: "Currency" }), + sch["journal_entry"] || "", ]; + + if (asset_depr_schedule_doc.shift_based) { + row.push(sch["shift"]); + } + data.push(row); }); + let columns = [ + { name: __("No."), editable: false, resizable: false, format: (value) => value, width: 60 }, + { name: __("Schedule Date"), editable: false, resizable: false, width: 270 }, + { name: __("Depreciation Amount"), editable: false, resizable: false, width: 164 }, + { name: __("Accumulated Depreciation Amount"), editable: false, resizable: false, width: 164 }, + ]; + + if (asset_depr_schedule_doc.shift_based) { + columns.push({ + name: __("Journal Entry"), + editable: false, + resizable: false, + format: (value) => `${value}`, + width: 245, + }); + columns.push({ name: __("Shift"), editable: false, resizable: false, width: 59 }); + } else { + columns.push({ + name: __("Journal Entry"), + editable: false, + resizable: false, + format: (value) => `${value}`, + width: 304, + }); + } + let datatable = new frappe.DataTable(wrapper.get(0), { - columns: [ - {name: __("No."), editable: false, resizable: false, format: value => value, width: 60}, - {name: __("Schedule Date"), editable: false, resizable: false, width: 270}, - {name: __("Depreciation Amount"), editable: false, resizable: false, width: 164}, - {name: __("Accumulated Depreciation Amount"), editable: false, resizable: false, width: 164}, - {name: __("Journal Entry"), editable: false, resizable: false, format: value => `${value}`, width: 304} - ], + columns: columns, data: data, layout: "fluid", serialNoColumn: false, checkboxColumn: true, - cellHeight: 35 + cellHeight: 35, }); - datatable.style.setStyle(`.dt-scrollable`, {'font-size': '0.75rem', 'margin-bottom': '1rem', 'margin-left': '0.35rem', 'margin-right': '0.35rem'}); - datatable.style.setStyle(`.dt-header`, {'margin-left': '0.35rem', 'margin-right': '0.35rem'}); - datatable.style.setStyle(`.dt-cell--header .dt-cell__content`, {'color': 'var(--gray-600)', 'font-size': 'var(--text-sm)'}); - datatable.style.setStyle(`.dt-cell`, {'color': 'var(--text-color)'}); - datatable.style.setStyle(`.dt-cell--col-1`, {'text-align': 'center'}); - datatable.style.setStyle(`.dt-cell--col-2`, {'font-weight': 600}); - datatable.style.setStyle(`.dt-cell--col-3`, {'font-weight': 600}); + datatable.style.setStyle(`.dt-scrollable`, { + "font-size": "0.75rem", + "margin-bottom": "1rem", + "margin-left": "0.35rem", + "margin-right": "0.35rem", + }); + datatable.style.setStyle(`.dt-header`, { "margin-left": "0.35rem", "margin-right": "0.35rem" }); + datatable.style.setStyle(`.dt-cell--header .dt-cell__content`, { + color: "var(--gray-600)", + "font-size": "var(--text-sm)", + }); + datatable.style.setStyle(`.dt-cell`, { color: "var(--text-color)" }); + datatable.style.setStyle(`.dt-cell--col-1`, { "text-align": "center" }); + datatable.style.setStyle(`.dt-cell--col-2`, { "font-weight": 600 }); + datatable.style.setStyle(`.dt-cell--col-3`, { "font-weight": 600 }); }, - setup_chart_and_depr_schedule_view: async function(frm) { - if(frm.doc.finance_books.length > 1) { - return + setup_chart_and_depr_schedule_view: async function (frm) { + if (frm.doc.finance_books.length > 1) { + return; } - var x_intervals = [frappe.format(frm.doc.purchase_date, { fieldtype: 'Date' })]; + var x_intervals = [frappe.format(frm.doc.purchase_date, { fieldtype: "Date" })]; var asset_values = [frm.doc.gross_purchase_amount]; - if(frm.doc.calculate_depreciation) { - if(frm.doc.opening_accumulated_depreciation) { + if (frm.doc.calculate_depreciation) { + if (frm.doc.opening_accumulated_depreciation) { var depreciation_date = frappe.datetime.add_months( frm.doc.finance_books[0].depreciation_start_date, -1 * frm.doc.finance_books[0].frequency_of_depreciation ); - x_intervals.push(frappe.format(depreciation_date, { fieldtype: 'Date' })); - asset_values.push(flt(frm.doc.gross_purchase_amount - frm.doc.opening_accumulated_depreciation, precision('gross_purchase_amount'))); + x_intervals.push(frappe.format(depreciation_date, { fieldtype: "Date" })); + asset_values.push( + flt( + frm.doc.gross_purchase_amount - frm.doc.opening_accumulated_depreciation, + precision("gross_purchase_amount") + ) + ); } - let depr_schedule = (await frappe.call( - "erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule.get_depr_schedule", - { - asset_name: frm.doc.name, - status: "Active", - finance_book: frm.doc.finance_books[0].finance_book || null - } - )).message; - - $.each(depr_schedule || [], function(i, v) { - x_intervals.push(frappe.format(v.schedule_date, { fieldtype: 'Date' })); - var asset_value = flt(frm.doc.gross_purchase_amount - v.accumulated_depreciation_amount, precision('gross_purchase_amount')); - if(v.journal_entry) { + let asset_depr_schedule_doc = ( + await frappe.call( + "erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule.get_asset_depr_schedule_doc", + { + asset_name: frm.doc.name, + status: "Active", + finance_book: frm.doc.finance_books[0].finance_book || null, + } + ) + ).message; + + $.each(asset_depr_schedule_doc.depreciation_schedule || [], function (i, v) { + x_intervals.push(frappe.format(v.schedule_date, { fieldtype: "Date" })); + var asset_value = flt( + frm.doc.gross_purchase_amount - v.accumulated_depreciation_amount, + precision("gross_purchase_amount") + ); + if (v.journal_entry) { asset_values.push(asset_value); } else { if (["Scrapped", "Sold"].includes(frm.doc.status)) { asset_values.push(null); } else { - asset_values.push(asset_value) + asset_values.push(asset_value); } } }); frm.toggle_display(["depreciation_schedule_view"], 1); - frm.events.render_depreciation_schedule_view(frm, depr_schedule); + frm.events.render_depreciation_schedule_view(frm, asset_depr_schedule_doc); } else { - if(frm.doc.opening_accumulated_depreciation) { - x_intervals.push(frappe.format(frm.doc.creation.split(" ")[0], { fieldtype: 'Date' })); - asset_values.push(flt(frm.doc.gross_purchase_amount - frm.doc.opening_accumulated_depreciation, precision('gross_purchase_amount'))); + if (frm.doc.opening_accumulated_depreciation) { + x_intervals.push(frappe.format(frm.doc.creation.split(" ")[0], { fieldtype: "Date" })); + asset_values.push( + flt( + frm.doc.gross_purchase_amount - frm.doc.opening_accumulated_depreciation, + precision("gross_purchase_amount") + ) + ); } - let depr_entries = (await frappe.call({ - method: "get_manual_depreciation_entries", - doc: frm.doc, - })).message; - - $.each(depr_entries || [], function(i, v) { - x_intervals.push(frappe.format(v.posting_date, { fieldtype: 'Date' })); - let last_asset_value = asset_values[asset_values.length - 1] - asset_values.push(flt(last_asset_value - v.value, precision('gross_purchase_amount'))); + let depr_entries = ( + await frappe.call({ + method: "get_manual_depreciation_entries", + doc: frm.doc, + }) + ).message; + + $.each(depr_entries || [], function (i, v) { + x_intervals.push(frappe.format(v.posting_date, { fieldtype: "Date" })); + let last_asset_value = asset_values[asset_values.length - 1]; + asset_values.push(flt(last_asset_value - v.value, precision("gross_purchase_amount"))); }); } @@ -347,145 +422,146 @@ frappe.ui.form.on('Asset', { title: "Asset Value", data: { labels: x_intervals, - datasets: [{ - color: 'green', - values: asset_values, - formatted: asset_values.map(d => d?.toFixed(2)) - }] + datasets: [ + { + color: "green", + values: asset_values, + formatted: asset_values.map((d) => d?.toFixed(2)), + }, + ], }, - type: 'line' + type: "line", }); }, - - item_code: function(frm) { - if(frm.doc.item_code && frm.doc.calculate_depreciation && frm.doc.gross_purchase_amount) { - frm.trigger('set_finance_book'); + item_code: function (frm) { + if (frm.doc.item_code && frm.doc.calculate_depreciation && frm.doc.gross_purchase_amount) { + frm.trigger("set_finance_book"); } else { - frm.set_value('finance_books', []); + frm.set_value("finance_books", []); } }, - set_finance_book: function(frm) { + set_finance_book: function (frm) { frappe.call({ method: "erpnext.assets.doctype.asset.asset.get_item_details", args: { item_code: frm.doc.item_code, asset_category: frm.doc.asset_category, - gross_purchase_amount: frm.doc.gross_purchase_amount + gross_purchase_amount: frm.doc.gross_purchase_amount, }, - callback: function(r, rt) { - if(r.message) { - frm.set_value('finance_books', r.message); + callback: function (r, rt) { + if (r.message) { + frm.set_value("finance_books", r.message); } - } - }) + }, + }); }, - is_existing_asset: function(frm) { + is_existing_asset: function (frm) { frm.trigger("toggle_reference_doc"); }, - is_composite_asset: function(frm) { - if(frm.doc.is_composite_asset) { - frm.set_value('gross_purchase_amount', 0); - frm.set_df_property('gross_purchase_amount', 'read_only', 1); + is_composite_asset: function (frm) { + if (frm.doc.is_composite_asset) { + frm.set_value("gross_purchase_amount", 0); + frm.set_df_property("gross_purchase_amount", "read_only", 1); } else { - frm.set_df_property('gross_purchase_amount', 'read_only', 0); + frm.set_df_property("gross_purchase_amount", "read_only", 0); } frm.trigger("toggle_reference_doc"); }, - make_sales_invoice: function(frm) { + make_sales_invoice: function (frm) { frappe.call({ args: { - "asset": frm.doc.name, - "item_code": frm.doc.item_code, - "company": frm.doc.company, - "serial_no": frm.doc.serial_no + asset: frm.doc.name, + item_code: frm.doc.item_code, + company: frm.doc.company, + serial_no: frm.doc.serial_no, }, method: "erpnext.assets.doctype.asset.asset.make_sales_invoice", - callback: function(r) { + callback: function (r) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - }) + }, + }); }, - create_asset_maintenance: function(frm) { + create_asset_maintenance: function (frm) { frappe.call({ args: { - "asset": frm.doc.name, - "item_code": frm.doc.item_code, - "item_name": frm.doc.item_name, - "asset_category": frm.doc.asset_category, - "company": frm.doc.company + asset: frm.doc.name, + item_code: frm.doc.item_code, + item_name: frm.doc.item_name, + asset_category: frm.doc.asset_category, + company: frm.doc.company, }, method: "erpnext.assets.doctype.asset.asset.create_asset_maintenance", - callback: function(r) { + callback: function (r) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - }) + }, + }); }, - create_asset_repair: function(frm) { + create_asset_repair: function (frm) { frappe.call({ args: { - "asset": frm.doc.name, - "asset_name": frm.doc.asset_name + asset: frm.doc.name, + asset_name: frm.doc.asset_name, }, method: "erpnext.assets.doctype.asset.asset.create_asset_repair", - callback: function(r) { + callback: function (r) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } + }, }); }, - create_asset_capitalization: function(frm) { + create_asset_capitalization: function (frm) { frappe.call({ args: { - "asset": frm.doc.name, + asset: frm.doc.name, }, method: "erpnext.assets.doctype.asset.asset.create_asset_capitalization", - callback: function(r) { + callback: function (r) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } + }, }); }, - split_asset: function(frm) { - const title = __('Split Asset'); + split_asset: function (frm) { + const title = __("Split Asset"); const fields = [ { - fieldname: 'split_qty', - fieldtype: 'Int', - label: __('Split Qty'), - reqd: 1 - } + fieldname: "split_qty", + fieldtype: "Int", + label: __("Split Qty"), + reqd: 1, + }, ]; let dialog = new frappe.ui.Dialog({ title: title, - fields: fields + fields: fields, }); - dialog.set_primary_action(__('Split'), function() { + dialog.set_primary_action(__("Split"), function () { const dialog_data = dialog.get_values(); frappe.call({ args: { - "asset_name": frm.doc.name, - "split_qty": cint(dialog_data.split_qty) + asset_name: frm.doc.name, + split_qty: cint(dialog_data.split_qty), }, method: "erpnext.assets.doctype.asset.asset.split_asset", - callback: function(r) { + callback: function (r) { let doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } + }, }); dialog.hide(); @@ -494,23 +570,23 @@ frappe.ui.form.on('Asset', { dialog.show(); }, - create_asset_value_adjustment: function(frm) { + create_asset_value_adjustment: function (frm) { frappe.call({ args: { - "asset": frm.doc.name, - "asset_category": frm.doc.asset_category, - "company": frm.doc.company + asset: frm.doc.name, + asset_category: frm.doc.asset_category, + company: frm.doc.company, }, method: "erpnext.assets.doctype.asset.asset.create_asset_value_adjustment", freeze: 1, - callback: function(r) { + callback: function (r) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - }) + }, + }); }, - calculate_depreciation: function(frm) { + calculate_depreciation: function (frm) { frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation); if (frm.doc.item_code && frm.doc.calculate_depreciation && frm.doc.gross_purchase_amount) { frm.trigger("set_finance_book"); @@ -519,69 +595,69 @@ frappe.ui.form.on('Asset', { } }, - gross_purchase_amount: function(frm) { + gross_purchase_amount: function (frm) { if (frm.doc.finance_books) { - frm.doc.finance_books.forEach(d => { + frm.doc.finance_books.forEach((d) => { frm.events.set_depreciation_rate(frm, d); - }) + }); } }, purchase_receipt: (frm) => { - frm.trigger('toggle_reference_doc'); + frm.trigger("toggle_reference_doc"); if (frm.doc.purchase_receipt) { if (frm.doc.item_code) { - frappe.db.get_doc('Purchase Receipt', frm.doc.purchase_receipt).then(pr_doc => { - frm.events.set_values_from_purchase_doc(frm, 'Purchase Receipt', pr_doc) + frappe.db.get_doc("Purchase Receipt", frm.doc.purchase_receipt).then((pr_doc) => { + frm.events.set_values_from_purchase_doc(frm, "Purchase Receipt", pr_doc); }); } else { - frm.set_value('purchase_receipt', ''); + frm.set_value("purchase_receipt", ""); frappe.msgprint({ - title: __('Not Allowed'), - message: __("Please select Item Code first") + title: __("Not Allowed"), + message: __("Please select Item Code first"), }); } } }, purchase_invoice: (frm) => { - frm.trigger('toggle_reference_doc'); + frm.trigger("toggle_reference_doc"); if (frm.doc.purchase_invoice) { if (frm.doc.item_code) { - frappe.db.get_doc('Purchase Invoice', frm.doc.purchase_invoice).then(pi_doc => { - frm.events.set_values_from_purchase_doc(frm, 'Purchase Invoice', pi_doc) + frappe.db.get_doc("Purchase Invoice", frm.doc.purchase_invoice).then((pi_doc) => { + frm.events.set_values_from_purchase_doc(frm, "Purchase Invoice", pi_doc); }); } else { - frm.set_value('purchase_invoice', ''); + frm.set_value("purchase_invoice", ""); frappe.msgprint({ - title: __('Not Allowed'), - message: __("Please select Item Code first") + title: __("Not Allowed"), + message: __("Please select Item Code first"), }); } } }, - set_values_from_purchase_doc: function(frm, doctype, purchase_doc) { - frm.set_value('company', purchase_doc.company); + set_values_from_purchase_doc: function (frm, doctype, purchase_doc) { + frm.set_value("company", purchase_doc.company); if (purchase_doc.bill_date) { - frm.set_value('purchase_date', purchase_doc.bill_date); + frm.set_value("purchase_date", purchase_doc.bill_date); } else { - frm.set_value('purchase_date', purchase_doc.posting_date); + frm.set_value("purchase_date", purchase_doc.posting_date); } if (!frm.doc.is_existing_asset && !frm.doc.available_for_use_date) { - frm.set_value('available_for_use_date', frm.doc.purchase_date); + frm.set_value("available_for_use_date", frm.doc.purchase_date); } - const item = purchase_doc.items.find(item => item.item_code === frm.doc.item_code); + const item = purchase_doc.items.find((item) => item.item_code === frm.doc.item_code); if (!item) { - let doctype_field = frappe.scrub(doctype) - frm.set_value(doctype_field, ''); + let doctype_field = frappe.scrub(doctype); + frm.set_value(doctype_field, ""); frappe.msgprint({ - title: __('Invalid {0}', [__(doctype)]), - message: __('The selected {0} does not contain the selected Asset Item.', [__(doctype)]), - indicator: 'red' + title: __("Invalid {0}", [__(doctype)]), + message: __("The selected {0} does not contain the selected Asset Item.", [__(doctype)]), + indicator: "red", }); } - frappe.db.get_value('Item', item.item_code, 'is_grouped_asset', (r) => { + frappe.db.get_value("Item", item.item_code, "is_grouped_asset", (r) => { var asset_quantity = r.is_grouped_asset ? item.qty : 1; var purchase_amount = flt( item.valuation_rate * asset_quantity, @@ -598,129 +674,166 @@ frappe.ui.form.on('Asset', { }); }, - set_depreciation_rate: function(frm, row) { - if (row.total_number_of_depreciations && row.frequency_of_depreciation - && row.expected_value_after_useful_life) { + set_depreciation_rate: function (frm, row) { + if ( + row.total_number_of_depreciations && + row.frequency_of_depreciation && + row.expected_value_after_useful_life + ) { frappe.call({ method: "get_depreciation_rate", doc: frm.doc, args: row, - callback: function(r) { + callback: function (r) { if (r.message) { frappe.flags.dont_change_rate = true; - frappe.model.set_value(row.doctype, row.name, - "rate_of_depreciation", flt(r.message, precision("rate_of_depreciation", row))); + frappe.model.set_value( + row.doctype, + row.name, + "rate_of_depreciation", + flt(r.message, precision("rate_of_depreciation", row)) + ); } - } + }, }); } }, - set_salvage_value_percentage_or_expected_value_after_useful_life: function(frm, row, salvage_value_percentage_changed, expected_value_after_useful_life_changed) { + set_salvage_value_percentage_or_expected_value_after_useful_life: function ( + frm, + row, + salvage_value_percentage_changed, + expected_value_after_useful_life_changed + ) { if (expected_value_after_useful_life_changed) { frappe.flags.from_set_salvage_value_percentage_or_expected_value_after_useful_life = true; - const new_salvage_value_percentage = flt((row.expected_value_after_useful_life * 100) / frm.doc.gross_purchase_amount, precision("salvage_value_percentage", row)); - frappe.model.set_value(row.doctype, row.name, "salvage_value_percentage", new_salvage_value_percentage); + const new_salvage_value_percentage = flt( + (row.expected_value_after_useful_life * 100) / frm.doc.gross_purchase_amount, + precision("salvage_value_percentage", row) + ); + frappe.model.set_value( + row.doctype, + row.name, + "salvage_value_percentage", + new_salvage_value_percentage + ); frappe.flags.from_set_salvage_value_percentage_or_expected_value_after_useful_life = false; } else if (salvage_value_percentage_changed) { frappe.flags.from_set_salvage_value_percentage_or_expected_value_after_useful_life = true; - const new_expected_value_after_useful_life = flt(frm.doc.gross_purchase_amount * (row.salvage_value_percentage / 100), precision('gross_purchase_amount')); - frappe.model.set_value(row.doctype, row.name, "expected_value_after_useful_life", new_expected_value_after_useful_life); + const new_expected_value_after_useful_life = flt( + frm.doc.gross_purchase_amount * (row.salvage_value_percentage / 100), + precision("gross_purchase_amount") + ); + frappe.model.set_value( + row.doctype, + row.name, + "expected_value_after_useful_life", + new_expected_value_after_useful_life + ); frappe.flags.from_set_salvage_value_percentage_or_expected_value_after_useful_life = false; } }, }); -frappe.ui.form.on('Asset Finance Book', { - depreciation_method: function(frm, cdt, cdn) { +frappe.ui.form.on("Asset Finance Book", { + depreciation_method: function (frm, cdt, cdn) { const row = locals[cdt][cdn]; frm.events.set_depreciation_rate(frm, row); }, - expected_value_after_useful_life: function(frm, cdt, cdn) { + expected_value_after_useful_life: function (frm, cdt, cdn) { const row = locals[cdt][cdn]; if (!frappe.flags.from_set_salvage_value_percentage_or_expected_value_after_useful_life) { - frm.events.set_salvage_value_percentage_or_expected_value_after_useful_life(frm, row, false, true); + frm.events.set_salvage_value_percentage_or_expected_value_after_useful_life( + frm, + row, + false, + true + ); } frm.events.set_depreciation_rate(frm, row); }, - salvage_value_percentage: function(frm, cdt, cdn) { + salvage_value_percentage: function (frm, cdt, cdn) { const row = locals[cdt][cdn]; if (!frappe.flags.from_set_salvage_value_percentage_or_expected_value_after_useful_life) { - frm.events.set_salvage_value_percentage_or_expected_value_after_useful_life(frm, row, true, false); + frm.events.set_salvage_value_percentage_or_expected_value_after_useful_life( + frm, + row, + true, + false + ); } }, - frequency_of_depreciation: function(frm, cdt, cdn) { + frequency_of_depreciation: function (frm, cdt, cdn) { const row = locals[cdt][cdn]; frm.events.set_depreciation_rate(frm, row); }, - total_number_of_depreciations: function(frm, cdt, cdn) { + total_number_of_depreciations: function (frm, cdt, cdn) { const row = locals[cdt][cdn]; frm.events.set_depreciation_rate(frm, row); }, - rate_of_depreciation: function(frm, cdt, cdn) { - if(!frappe.flags.dont_change_rate) { + rate_of_depreciation: function (frm, cdt, cdn) { + if (!frappe.flags.dont_change_rate) { frappe.model.set_value(cdt, cdn, "expected_value_after_useful_life", 0); } frappe.flags.dont_change_rate = false; }, - depreciation_start_date: function(frm, cdt, cdn) { + depreciation_start_date: function (frm, cdt, cdn) { const book = locals[cdt][cdn]; - if (frm.doc.available_for_use_date && book.depreciation_start_date == frm.doc.available_for_use_date) { + if ( + frm.doc.available_for_use_date && + book.depreciation_start_date == frm.doc.available_for_use_date + ) { frappe.msgprint(__("Depreciation Posting Date should not be equal to Available for Use Date.")); book.depreciation_start_date = ""; frm.refresh_field("finance_books"); } - } + }, }); -erpnext.asset.scrap_asset = function(frm) { +erpnext.asset.scrap_asset = function (frm) { frappe.confirm(__("Do you really want to scrap this asset?"), function () { frappe.call({ args: { - "asset_name": frm.doc.name + asset_name: frm.doc.name, }, method: "erpnext.assets.doctype.asset.depreciation.scrap_asset", - callback: function(r) { - cur_frm.reload_doc(); - } - }) - }) + callback: (r) => frm.reload_doc(), + }); + }); }; -erpnext.asset.restore_asset = function(frm) { +erpnext.asset.restore_asset = function (frm) { frappe.confirm(__("Do you really want to restore this scrapped asset?"), function () { frappe.call({ args: { - "asset_name": frm.doc.name + asset_name: frm.doc.name, }, method: "erpnext.assets.doctype.asset.depreciation.restore_asset", - callback: function(r) { - cur_frm.reload_doc(); - } - }) - }) + callback: (r) => frm.reload_doc(), + }); + }); }; -erpnext.asset.transfer_asset = function() { +erpnext.asset.transfer_asset = function (frm) { frappe.call({ method: "erpnext.assets.doctype.asset.asset.make_asset_movement", freeze: true, - args:{ - "assets": [{ name: cur_frm.doc.name }], - "purpose": "Transfer" + args: { + assets: [{ name: frm.doc.name }], + purpose: "Transfer", }, callback: function (r) { if (r.message) { var doc = frappe.model.sync(r.message)[0]; frappe.set_route("Form", doc.doctype, doc.name); } - } + }, }); }; diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 77cbb90ea8..d31348d158 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -211,9 +211,7 @@ class Asset(AccountsController): ) if self.is_existing_asset and self.purchase_invoice: - frappe.throw( - _("Purchase Invoice cannot be made against an existing asset {0}").format(self.name) - ) + frappe.throw(_("Purchase Invoice cannot be made against an existing asset {0}").format(self.name)) def prepare_depreciation_data(self): if self.calculate_depreciation: @@ -274,9 +272,9 @@ class Asset(AccountsController): for d in self.finance_books: if d.depreciation_start_date == self.available_for_use_date: frappe.throw( - _("Row #{}: Depreciation Posting Date should not be equal to Available for Use Date.").format( - d.idx - ), + _( + "Row #{}: Depreciation Posting Date should not be equal to Available for Use Date." + ).format(d.idx), title=_("Incorrect Date"), ) @@ -285,9 +283,7 @@ class Asset(AccountsController): self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category") if self.item_code and not self.get("finance_books"): - finance_books = get_item_details( - self.item_code, self.asset_category, self.gross_purchase_amount - ) + finance_books = get_item_details(self.item_code, self.asset_category, self.gross_purchase_amount) self.set("finance_books", finance_books) def validate_finance_books(self): @@ -337,7 +333,9 @@ class Asset(AccountsController): and not frappe.db.get_value("Purchase Invoice", self.purchase_invoice, "update_stock") ): frappe.throw( - _("Update stock must be enabled for the purchase invoice {0}").format(self.purchase_invoice) + _("Update stock must be enabled for the purchase invoice {0}").format( + self.purchase_invoice + ) ) if not self.calculate_depreciation: @@ -351,9 +349,7 @@ class Asset(AccountsController): if self.is_existing_asset: return - if self.available_for_use_date and getdate(self.available_for_use_date) < getdate( - self.purchase_date - ): + if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(self.purchase_date): frappe.throw(_("Available-for-use Date should be after purchase date")) def validate_gross_and_purchase_amount(self): @@ -376,7 +372,7 @@ class Asset(AccountsController): posting_date, posting_time = frappe.db.get_value( reference_doctype, reference_docname, ["posting_date", "posting_time"] ) - transaction_date = get_datetime("{} {}".format(posting_date, posting_time)) + transaction_date = get_datetime(f"{posting_date} {posting_time}") assets = [ { "asset": self.name, @@ -416,7 +412,8 @@ class Asset(AccountsController): if not row.depreciation_start_date: if not self.available_for_use_date: frappe.throw( - _("Row {0}: Depreciation Start Date is required").format(row.idx), title=_("Invalid Schedule") + _("Row {0}: Depreciation Start Date is required").format(row.idx), + title=_("Invalid Schedule"), ) row.depreciation_start_date = get_last_day(self.available_for_use_date) @@ -446,9 +443,7 @@ class Asset(AccountsController): title=_("Invalid Schedule"), ) - if row.depreciation_start_date and getdate(row.depreciation_start_date) < getdate( - self.purchase_date - ): + if row.depreciation_start_date and getdate(row.depreciation_start_date) < getdate(self.purchase_date): frappe.throw( _("Depreciation Row {0}: Next Depreciation Date cannot be before Purchase Date").format( row.idx @@ -578,11 +573,14 @@ class Asset(AccountsController): if self.calculate_depreciation: idx = self.get_default_finance_book_idx() or 0 - expected_value_after_useful_life = self.finance_books[idx].expected_value_after_useful_life + expected_value_after_useful_life = self.finance_books[ + idx + ].expected_value_after_useful_life value_after_depreciation = self.finance_books[idx].value_after_depreciation if ( - flt(value_after_depreciation) <= expected_value_after_useful_life or self.is_fully_depreciated + flt(value_after_depreciation) <= expected_value_after_useful_life + or self.is_fully_depreciated ): status = "Fully Depreciated" elif flt(value_after_depreciation) < flt(self.gross_purchase_amount): @@ -615,9 +613,7 @@ class Asset(AccountsController): @frappe.whitelist() def get_manual_depreciation_entries(self): - (_, _, depreciation_expense_account) = get_depreciation_accounts( - self.asset_category, self.company - ) + (_, _, depreciation_expense_account) = get_depreciation_accounts(self.asset_category, self.company) gle = frappe.qb.DocType("GL Entry") @@ -757,7 +753,8 @@ class Asset(AccountsController): if args.get("depreciation_method") == "Double Declining Balance": return 200.0 / ( ( - flt(args.get("total_number_of_depreciations"), 2) * flt(args.get("frequency_of_depreciation")) + flt(args.get("total_number_of_depreciations"), 2) + * flt(args.get("frequency_of_depreciation")) ) / 12 ) @@ -801,9 +798,7 @@ def update_maintenance_status(): asset = frappe.get_doc("Asset", asset.name) if frappe.db.exists("Asset Repair", {"asset_name": asset.name, "repair_status": "Pending"}): asset.set_status("Out of Order") - elif frappe.db.exists( - "Asset Maintenance Task", {"parent": asset.name, "next_due_date": today()} - ): + elif frappe.db.exists("Asset Maintenance Task", {"parent": asset.name, "next_due_date": today()}): asset.set_status("In Maintenance") else: asset.set_status() @@ -887,9 +882,7 @@ def create_asset_capitalization(asset): @frappe.whitelist() def create_asset_value_adjustment(asset, asset_category, company): asset_value_adjustment = frappe.new_doc("Asset Value Adjustment") - asset_value_adjustment.update( - {"asset": asset, "company": company, "asset_category": asset_category} - ) + asset_value_adjustment.update({"asset": asset, "company": company, "asset_category": asset_category}) return asset_value_adjustment @@ -925,6 +918,8 @@ def get_item_details(item_code, asset_category, gross_purchase_amount): "depreciation_method": d.depreciation_method, "total_number_of_depreciations": d.total_number_of_depreciations, "frequency_of_depreciation": d.frequency_of_depreciation, + "daily_prorata_based": d.daily_prorata_based, + "shift_based": d.shift_based, "salvage_value_percentage": d.salvage_value_percentage, "expected_value_after_useful_life": flt(gross_purchase_amount) * flt(d.salvage_value_percentage / 100), @@ -944,18 +939,14 @@ def get_asset_account(account_name, asset=None, asset_category=None, company=Non ) if not asset and not account: - account = get_asset_category_account( - account_name, asset_category=asset_category, company=company - ) + account = get_asset_category_account(account_name, asset_category=asset_category, company=company) if not account: account = frappe.get_cached_value("Company", company, account_name) if not account: if not asset_category: - frappe.throw( - _("Set {0} in company {1}").format(account_name.replace("_", " ").title(), company) - ) + frappe.throw(_("Set {0} in company {1}").format(account_name.replace("_", " ").title(), company)) else: frappe.throw( _("Set {0} in asset category {1} or company {2}").format( @@ -1055,7 +1046,7 @@ def split_asset(asset_name, split_qty): split_qty = cint(split_qty) if split_qty >= asset.asset_quantity: - frappe.throw(_("Split qty cannot be greater than or equal to asset qty")) + frappe.throw(_("Split qty cannot be grater than or equal to asset qty")) remaining_qty = asset.asset_quantity - split_qty @@ -1085,15 +1076,11 @@ def update_existing_asset(asset, remaining_qty, new_asset_name): add_asset_activity( asset.name, - _("Asset updated after being split into Asset {0}").format( - get_link_to_form("Asset", new_asset_name) - ), + _("Asset updated after being split into Asset {0}").format(get_link_to_form("Asset", new_asset_name)), ) for row in asset.get("finance_books"): - value_after_depreciation = flt( - (row.value_after_depreciation * remaining_qty) / asset.asset_quantity - ) + value_after_depreciation = flt((row.value_after_depreciation * remaining_qty) / asset.asset_quantity) expected_value_after_useful_life = flt( (row.expected_value_after_useful_life * remaining_qty) / asset.asset_quantity ) @@ -1107,9 +1094,7 @@ def update_existing_asset(asset, remaining_qty, new_asset_name): expected_value_after_useful_life, ) - current_asset_depr_schedule_doc = get_asset_depr_schedule_doc( - asset.name, "Active", row.finance_book - ) + current_asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset.name, "Active", row.finance_book) new_asset_depr_schedule_doc = frappe.copy_doc(current_asset_depr_schedule_doc) new_asset_depr_schedule_doc.set_draft_asset_depr_schedule_details(asset, row) @@ -1124,9 +1109,7 @@ def update_existing_asset(asset, remaining_qty, new_asset_name): notes = _( "This schedule was created when Asset {0} was updated after being split into new Asset {1}." - ).format( - get_link_to_form(asset.doctype, asset.name), get_link_to_form(asset.doctype, new_asset_name) - ) + ).format(get_link_to_form(asset.doctype, asset.name), get_link_to_form(asset.doctype, new_asset_name)) new_asset_depr_schedule_doc.notes = notes current_asset_depr_schedule_doc.flags.should_not_cancel_depreciation_entries = True @@ -1150,9 +1133,7 @@ def create_new_asset_after_split(asset, split_qty): new_asset.split_from = asset.name for row in new_asset.get("finance_books"): - row.value_after_depreciation = flt( - (row.value_after_depreciation * split_qty) / asset.asset_quantity - ) + row.value_after_depreciation = flt((row.value_after_depreciation * split_qty) / asset.asset_quantity) row.expected_value_after_useful_life = flt( (row.expected_value_after_useful_life * split_qty) / asset.asset_quantity ) @@ -1161,18 +1142,14 @@ def create_new_asset_after_split(asset, split_qty): add_asset_activity( new_asset.name, - _("Asset created after being split from Asset {0}").format( - get_link_to_form("Asset", asset.name) - ), + _("Asset created after being split from Asset {0}").format(get_link_to_form("Asset", asset.name)), ) new_asset.submit() new_asset.set_status() for row in new_asset.get("finance_books"): - current_asset_depr_schedule_doc = get_asset_depr_schedule_doc( - asset.name, "Active", row.finance_book - ) + current_asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset.name, "Active", row.finance_book) new_asset_depr_schedule_doc = frappe.copy_doc(current_asset_depr_schedule_doc) new_asset_depr_schedule_doc.set_draft_asset_depr_schedule_details(new_asset, row) diff --git a/erpnext/assets/doctype/asset/asset_list.js b/erpnext/assets/doctype/asset/asset_list.js index e587e1dc71..712958adcf 100644 --- a/erpnext/assets/doctype/asset/asset_list.js +++ b/erpnext/assets/doctype/asset/asset_list.js @@ -1,56 +1,46 @@ -frappe.listview_settings['Asset'] = { - add_fields: ['status'], +frappe.listview_settings["Asset"] = { + add_fields: ["status"], get_indicator: function (doc) { if (doc.status === "Fully Depreciated") { return [__("Fully Depreciated"), "green", "status,=,Fully Depreciated"]; - } else if (doc.status === "Partially Depreciated") { - return [__("Partially Depreciated"), "gray", "status,=,Partially Depreciated"]; - + return [__("Partially Depreciated"), "grey", "status,=,Partially Depreciated"]; } else if (doc.status === "Sold") { return [__("Sold"), "green", "status,=,Sold"]; - } else if (["Capitalized", "Decapitalized"].includes(doc.status)) { return [__(doc.status), "grey", "status,=," + doc.status]; - } else if (doc.status === "Scrapped") { - return [__("Scrapped"), "gray", "status,=,Scrapped"]; - + return [__("Scrapped"), "grey", "status,=,Scrapped"]; } else if (doc.status === "In Maintenance") { return [__("In Maintenance"), "orange", "status,=,In Maintenance"]; - } else if (doc.status === "Out of Order") { - return [__("Out of Order"), "gray", "status,=,Out of Order"]; - + return [__("Out of Order"), "grey", "status,=,Out of Order"]; } else if (doc.status === "Issue") { return [__("Issue"), "orange", "status,=,Issue"]; - } else if (doc.status === "Receipt") { return [__("Receipt"), "green", "status,=,Receipt"]; - } else if (doc.status === "Submitted") { return [__("Submitted"), "blue", "status,=,Submitted"]; - } else if (doc.status === "Draft") { return [__("Draft"), "red", "status,=,Draft"]; } }, - onload: function(me) { - me.page.add_action_item(__('Make Asset Movement'), function() { + onload: function (me) { + me.page.add_action_item(__("Make Asset Movement"), function () { const assets = me.get_checked_items(); frappe.call({ method: "erpnext.assets.doctype.asset.asset.make_asset_movement", freeze: true, - args:{ - "assets": assets + args: { + assets: assets, }, callback: function (r) { if (r.message) { var doc = frappe.model.sync(r.message)[0]; frappe.set_route("Form", doc.doctype, doc.name); } - } + }, }); }); }, -} \ No newline at end of file +}; diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index 6af057d642..93f32b43b4 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -36,7 +36,7 @@ from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_sched def post_depreciation_entries(date=None): # Return if automatic booking of asset depreciation is disabled if not cint( - frappe.db.get_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically") + frappe.db.get_single_value("Accounts Settings", "book_asset_depreciation_entry_automatically") ): return @@ -71,7 +71,10 @@ def post_depreciation_entries(date=None): ) not in credit_and_debit_accounts_for_asset_category_and_company: credit_and_debit_accounts_for_asset_category_and_company.update( { - (asset_category, asset_company): get_credit_and_debit_accounts_for_asset_category_and_company( + ( + asset_category, + asset_company, + ): get_credit_and_debit_accounts_for_asset_category_and_company( asset_category, asset_company ), } @@ -135,9 +138,7 @@ def get_depreciable_asset_depr_schedules_data(date): def make_depreciation_entry_for_all_asset_depr_schedules(asset_doc, date=None): for row in asset_doc.get("finance_books"): - asset_depr_schedule_name = get_asset_depr_schedule_name( - asset_doc.name, "Active", row.finance_book - ) + asset_depr_schedule_name = get_asset_depr_schedule_name(asset_doc.name, "Active", row.finance_book) make_depreciation_entry(asset_depr_schedule_name, date) @@ -147,9 +148,7 @@ def get_acc_frozen_upto(): if not acc_frozen_upto: return - frozen_accounts_modifier = frappe.db.get_single_value( - "Accounts Settings", "frozen_accounts_modifier" - ) + frozen_accounts_modifier = frappe.db.get_single_value("Accounts Settings", "frozen_accounts_modifier") if frozen_accounts_modifier not in frappe.get_roles() or frappe.session.user == "Administrator": return getdate(acc_frozen_upto) @@ -278,7 +277,7 @@ def _make_journal_entry_for_depreciation( je.posting_date = depr_schedule.schedule_date je.company = asset.company je.finance_book = asset_depr_schedule_doc.finance_book - je.remark = "Depreciation Entry against {0} worth {1}".format( + je.remark = frappe._("Depreciation Entry against {} worth {}").format( asset.name, depr_schedule.depreciation_amount ) @@ -362,11 +361,7 @@ def get_depreciation_accounts(asset_category, company): if not depreciation_expense_account: depreciation_expense_account = accounts[1] - if ( - not fixed_asset_account - or not accumulated_depreciation_account - or not depreciation_expense_account - ): + if not fixed_asset_account or not accumulated_depreciation_account or not depreciation_expense_account: frappe.throw( _("Please set Depreciation related Accounts in Asset Category {0} or Company {1}").format( asset_category, company @@ -444,9 +439,7 @@ def scrap_asset(asset_name): if asset.docstatus != 1: frappe.throw(_("Asset {0} must be submitted").format(asset.name)) elif asset.status in ("Cancelled", "Sold", "Scrapped", "Capitalized", "Decapitalized"): - frappe.throw( - _("Asset {0} cannot be scrapped, as it is already {1}").format(asset.name, asset.status) - ) + frappe.throw(_("Asset {0} cannot be scrapped, as it is already {1}").format(asset.name, asset.status)) date = today() @@ -457,16 +450,14 @@ def scrap_asset(asset_name): depreciate_asset(asset, date, notes) asset.reload() - depreciation_series = frappe.get_cached_value( - "Company", asset.company, "series_for_depreciation_entry" - ) + depreciation_series = frappe.get_cached_value("Company", asset.company, "series_for_depreciation_entry") je = frappe.new_doc("Journal Entry") je.voucher_type = "Journal Entry" je.naming_series = depreciation_series je.posting_date = date je.company = asset.company - je.remark = "Scrap Entry for asset {0}".format(asset_name) + je.remark = f"Scrap Entry for asset {asset_name}" for entry in get_gl_entries_on_asset_disposal(asset, date): entry.update({"reference_type": "Asset", "reference_name": asset_name}) @@ -514,9 +505,7 @@ def depreciate_asset(asset_doc, date, notes): asset_doc.flags.ignore_validate_update_after_submit = True - make_new_active_asset_depr_schedules_and_cancel_current_ones( - asset_doc, notes, date_of_disposal=date - ) + make_new_active_asset_depr_schedules_and_cancel_current_ones(asset_doc, notes, date_of_disposal=date) asset_doc.save() @@ -537,9 +526,7 @@ def reset_depreciation_schedule(asset_doc, date, notes): asset_doc.flags.ignore_validate_update_after_submit = True - make_new_active_asset_depr_schedules_and_cancel_current_ones( - asset_doc, notes, date_of_return=date - ) + make_new_active_asset_depr_schedules_and_cancel_current_ones(asset_doc, notes, date_of_return=date) modify_depreciation_schedule_for_asset_repairs(asset_doc, notes) @@ -782,9 +769,7 @@ def get_disposal_account_and_cost_center(company): ) if not disposal_account: - frappe.throw( - _("Please set 'Gain/Loss Account on Asset Disposal' in Company {0}").format(company) - ) + frappe.throw(_("Please set 'Gain/Loss Account on Asset Disposal' in Company {0}").format(company)) if not depreciation_cost_center: frappe.throw(_("Please set 'Asset Depreciation Cost Center' in Company {0}").format(company)) @@ -797,7 +782,7 @@ def get_value_after_depreciation_on_disposal_date(asset, disposal_date, finance_ if asset_doc.available_for_use_date > getdate(disposal_date): frappe.throw( - "Disposal date {0} cannot be before available for use date {1} of the asset.".format( + "Disposal date {} cannot be before available for use date {} of the asset.".format( disposal_date, asset_doc.available_for_use_date ) ) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index bb6b102b62..a634550089 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -49,10 +49,12 @@ class AssetSetup(unittest.TestCase): enable_cwip_accounting("Computers") make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location") frappe.db.sql("delete from `tabTax Rule`") + set_system_settings(3) # @dokos @classmethod def tearDownClass(cls): frappe.db.rollback() + set_system_settings() # @dokos class TestAsset(AssetSetup): @@ -109,9 +111,7 @@ class TestAsset(AssetSetup): self.assertRaises(frappe.ValidationError, asset.save) def test_purchase_asset(self): - pr = make_purchase_receipt( - item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location" - ) + pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location") asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name") asset = frappe.get_doc("Asset", asset_name) @@ -211,7 +211,7 @@ class TestAsset(AssetSetup): ) first_asset_depr_schedule = get_asset_depr_schedule_doc(asset.name, "Active") - self.assertEquals(first_asset_depr_schedule.status, "Active") + self.assertEqual(first_asset_depr_schedule.status, "Active") post_depreciation_entries(date=add_months(purchase_date, 2)) asset.load_from_db() @@ -220,25 +220,29 @@ class TestAsset(AssetSetup): asset.gross_purchase_amount - asset.finance_books[0].value_after_depreciation, asset.precision("gross_purchase_amount"), ) - self.assertEquals(accumulated_depr_amount, 18000.0) + self.assertEqual(accumulated_depr_amount, 18000.0) scrap_asset(asset.name) asset.load_from_db() first_asset_depr_schedule.load_from_db() second_asset_depr_schedule = get_asset_depr_schedule_doc(asset.name, "Active") - self.assertEquals(second_asset_depr_schedule.status, "Active") - self.assertEquals(first_asset_depr_schedule.status, "Cancelled") + self.assertEqual(second_asset_depr_schedule.status, "Active") + self.assertEqual(first_asset_depr_schedule.status, "Cancelled") accumulated_depr_amount = flt( asset.gross_purchase_amount - asset.finance_books[0].value_after_depreciation, asset.precision("gross_purchase_amount"), ) pro_rata_amount, _, _ = _get_pro_rata_amt( - asset.finance_books[0], 9000, get_last_day(add_months(purchase_date, 1)), date + asset.finance_books[0], + 9000, + get_last_day(add_months(purchase_date, 1)), + date, + original_schedule_date=get_last_day(nowdate()), ) pro_rata_amount = flt(pro_rata_amount, asset.precision("gross_purchase_amount")) - self.assertEquals( + self.assertEqual( accumulated_depr_amount, flt(18000.0 + pro_rata_amount, asset.precision("gross_purchase_amount")), ) @@ -267,8 +271,8 @@ class TestAsset(AssetSetup): second_asset_depr_schedule.load_from_db() third_asset_depr_schedule = get_asset_depr_schedule_doc(asset.name, "Active") - self.assertEquals(third_asset_depr_schedule.status, "Active") - self.assertEquals(second_asset_depr_schedule.status, "Cancelled") + self.assertEqual(third_asset_depr_schedule.status, "Active") + self.assertEqual(second_asset_depr_schedule.status, "Cancelled") asset.load_from_db() self.assertFalse(asset.journal_entry_for_scrap) @@ -280,7 +284,7 @@ class TestAsset(AssetSetup): ) this_month_depr_amount = 9000.0 if is_last_day_of_the_month(date) else 0 - self.assertEquals(accumulated_depr_amount, 18000.0 + this_month_depr_amount) + self.assertEqual(accumulated_depr_amount, 18000.0 + this_month_depr_amount) def test_gle_made_by_asset_sale(self): date = nowdate() @@ -297,7 +301,7 @@ class TestAsset(AssetSetup): ) first_asset_depr_schedule = get_asset_depr_schedule_doc(asset.name, "Active") - self.assertEquals(first_asset_depr_schedule.status, "Active") + self.assertEqual(first_asset_depr_schedule.status, "Active") post_depreciation_entries(date=add_months(purchase_date, 2)) @@ -313,11 +317,15 @@ class TestAsset(AssetSetup): first_asset_depr_schedule.load_from_db() second_asset_depr_schedule = get_asset_depr_schedule_doc(asset.name, "Active") - self.assertEquals(second_asset_depr_schedule.status, "Active") - self.assertEquals(first_asset_depr_schedule.status, "Cancelled") + self.assertEqual(second_asset_depr_schedule.status, "Active") + self.assertEqual(first_asset_depr_schedule.status, "Cancelled") pro_rata_amount, _, _ = _get_pro_rata_amt( - asset.finance_books[0], 9000, get_last_day(add_months(purchase_date, 1)), date + asset.finance_books[0], + 9000, + get_last_day(add_months(purchase_date, 1)), + date, + original_schedule_date=get_last_day(nowdate()), ) pro_rata_amount = flt(pro_rata_amount, asset.precision("gross_purchase_amount")) @@ -335,7 +343,6 @@ class TestAsset(AssetSetup): ), ("Debtors - _TC", 25000.0, 0.0), ) - gle = get_gl_entries("Sales Invoice", si.name) self.assertSequenceEqual(gle, expected_gle) @@ -382,7 +389,7 @@ class TestAsset(AssetSetup): self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold") - expected_values = [["2023-03-31", 12000, 36000], ["2023-05-23", 1742.47, 37742.47]] + expected_values = [["2023-03-31", 12000, 36000], ["2023-05-23", 1737.7, 37737.7]] second_asset_depr_schedule = get_depr_schedule(asset.name, "Active") @@ -395,7 +402,7 @@ class TestAsset(AssetSetup): expected_gle = ( ( "_Test Accumulated Depreciations - _TC", - 37742.47, + 37737.7, 0.0, ), ( @@ -406,7 +413,7 @@ class TestAsset(AssetSetup): ( "_Test Gain/Loss on Asset Disposal - _TC", 0.0, - 17742.47, + 17737.7, ), ("Debtors - _TC", 40000.0, 0.0), ) @@ -459,7 +466,7 @@ class TestAsset(AssetSetup): ) first_asset_depr_schedule = get_asset_depr_schedule_doc(asset.name, "Active") - self.assertEquals(first_asset_depr_schedule.status, "Active") + self.assertEqual(first_asset_depr_schedule.status, "Active") post_depreciation_entries(date="2021-01-01") @@ -473,9 +480,9 @@ class TestAsset(AssetSetup): second_asset_depr_schedule = get_asset_depr_schedule_doc(asset.name, "Active") first_asset_depr_schedule_of_new_asset = get_asset_depr_schedule_doc(new_asset.name, "Active") - self.assertEquals(second_asset_depr_schedule.status, "Active") - self.assertEquals(first_asset_depr_schedule_of_new_asset.status, "Active") - self.assertEquals(first_asset_depr_schedule.status, "Cancelled") + self.assertEqual(second_asset_depr_schedule.status, "Active") + self.assertEqual(first_asset_depr_schedule_of_new_asset.status, "Active") + self.assertEqual(first_asset_depr_schedule.status, "Cancelled") depr_schedule_of_asset = second_asset_depr_schedule.get("depreciation_schedule") depr_schedule_of_new_asset = first_asset_depr_schedule_of_new_asset.get("depreciation_schedule") @@ -507,9 +514,7 @@ class TestAsset(AssetSetup): self.assertEqual(jv.accounts[3].reference_name, new_asset.name) def test_expense_head(self): - pr = make_purchase_receipt( - item_code="Macbook Pro", qty=2, rate=200000.0, location="Test Location" - ) + pr = make_purchase_receipt(item_code="Macbook Pro", qty=2, rate=200000.0, location="Test Location") doc = make_invoice(pr.name) self.assertEqual("Asset Received But Not Billed - _TC", doc.items[0].expense_account) @@ -619,9 +624,7 @@ class TestAsset(AssetSetup): self.assertFalse(gle) # case 1 -- PR with cwip disabled, Asset with cwip enabled - pr = make_purchase_receipt( - item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location" - ) + pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location") frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 1) frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", cwip_acc) asset = frappe.db.get_value("Asset", {"purchase_receipt": pr.name, "docstatus": 0}, "name") @@ -633,9 +636,7 @@ class TestAsset(AssetSetup): self.assertFalse(gle) # case 2 -- PR with cwip enabled, Asset with cwip disabled - pr = make_purchase_receipt( - item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location" - ) + pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location") frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 0) asset = frappe.db.get_value("Asset", {"purchase_receipt": pr.name, "docstatus": 0}, "name") asset_doc = frappe.get_doc("Asset", asset) @@ -702,6 +703,41 @@ class TestDepreciationMethods(AssetSetup): self.assertEqual(schedules, expected_schedules) + def test_schedule_for_straight_line_method_with_daily_prorata_based( + self, + ): + asset = create_asset( + calculate_depreciation=1, + available_for_use_date="2023-01-01", + purchase_date="2023-01-01", + gross_purchase_amount=12000, + depreciation_start_date="2023-01-31", + total_number_of_depreciations=12, + frequency_of_depreciation=1, + daily_prorata_based=1, + ) + + expected_schedules = [ + ["2023-01-31", 1019.18, 1019.18], + ["2023-02-28", 920.55, 1939.73], + ["2023-03-31", 1019.18, 2958.91], + ["2023-04-30", 986.3, 3945.21], + ["2023-05-31", 1019.18, 4964.39], + ["2023-06-30", 986.3, 5950.69], + ["2023-07-31", 1019.18, 6969.87], + ["2023-08-31", 1019.18, 7989.05], + ["2023-09-30", 986.3, 8975.35], + ["2023-10-31", 1019.18, 9994.53], + ["2023-11-30", 986.3, 10980.83], + ["2023-12-31", 1019.17, 12000.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) + def test_schedule_for_straight_line_method_for_existing_asset(self): asset = create_asset( calculate_depreciation=1, @@ -739,9 +775,9 @@ class TestDepreciationMethods(AssetSetup): self.assertEqual(asset.status, "Draft") expected_schedules = [ - ["2030-12-31", 66666.70, 66666.70], - ["2031-12-31", 22222.21, 88888.91], - ["2032-12-31", 1111.09, 90000.0], + ["2030-12-31", 66667.00, 66667.00], + ["2031-12-31", 22222.11, 88889.11], + ["2032-12-31", 1110.89, 90000.0], ] schedules = [ @@ -767,7 +803,7 @@ class TestDepreciationMethods(AssetSetup): self.assertEqual(asset.status, "Draft") - expected_schedules = [["2031-12-31", 33333.35, 83333.35], ["2032-12-31", 6666.65, 90000.0]] + expected_schedules = [["2031-12-31", 33333.50, 83333.50], ["2032-12-31", 6666.50, 90000.0]] schedules = [ [cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount] @@ -885,12 +921,12 @@ class TestDepreciationMethods(AssetSetup): ) expected_schedules = [ - ["2022-02-28", 310.88, 310.88], - ["2022-03-31", 654.42, 965.30], - ["2022-04-30", 654.42, 1619.72], - ["2022-05-31", 654.42, 2274.14], - ["2022-06-30", 654.42, 2928.56], - ["2022-07-15", 2071.44, 5000.0], + ["2022-02-28", 310.89, 310.89], + ["2022-03-31", 654.45, 965.34], + ["2022-04-30", 654.45, 1619.79], + ["2022-05-31", 654.45, 2274.24], + ["2022-06-30", 654.45, 2928.69], + ["2022-07-15", 2071.31, 5000.0], ] schedules = [ @@ -968,7 +1004,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) @@ -1214,8 +1250,7 @@ class TestDepreciationBasics(AssetSetup): je = frappe.get_doc("Journal Entry", get_depr_schedule(asset.name, "Active")[0].journal_entry) accounting_entries = [ - {"account": entry.account, "debit": entry.debit, "credit": entry.credit} - for entry in je.accounts + {"account": entry.account, "debit": entry.debit, "credit": entry.credit} for entry in je.accounts ] for entry in accounting_entries: @@ -1250,8 +1285,7 @@ class TestDepreciationBasics(AssetSetup): je = frappe.get_doc("Journal Entry", get_depr_schedule(asset.name, "Active")[0].journal_entry) accounting_entries = [ - {"account": entry.account, "debit": entry.debit, "credit": entry.credit} - for entry in je.accounts + {"account": entry.account, "debit": entry.debit, "credit": entry.credit} for entry in je.accounts ] for entry in accounting_entries: @@ -1332,21 +1366,15 @@ class TestDepreciationBasics(AssetSetup): post_depreciation_entries(date="2020-04-01") asset.load_from_db() - asset_depr_schedule_doc_1 = get_asset_depr_schedule_doc( - asset.name, "Active", "Test Finance Book 1" - ) + asset_depr_schedule_doc_1 = get_asset_depr_schedule_doc(asset.name, "Active", "Test Finance Book 1") asset_depr_schedule_doc_1.clear_depr_schedule() self.assertEqual(len(asset_depr_schedule_doc_1.get("depreciation_schedule")), 3) - asset_depr_schedule_doc_2 = get_asset_depr_schedule_doc( - asset.name, "Active", "Test Finance Book 2" - ) + asset_depr_schedule_doc_2 = get_asset_depr_schedule_doc(asset.name, "Active", "Test Finance Book 2") asset_depr_schedule_doc_2.clear_depr_schedule() self.assertEqual(len(asset_depr_schedule_doc_2.get("depreciation_schedule")), 3) - asset_depr_schedule_doc_3 = get_asset_depr_schedule_doc( - asset.name, "Active", "Test Finance Book 3" - ) + asset_depr_schedule_doc_3 = get_asset_depr_schedule_doc(asset.name, "Active", "Test Finance Book 3") asset_depr_schedule_doc_3.clear_depr_schedule() self.assertEqual(len(asset_depr_schedule_doc_3.get("depreciation_schedule")), 0) @@ -1378,14 +1406,10 @@ class TestDepreciationBasics(AssetSetup): ) asset.save() - asset_depr_schedule_doc_1 = get_asset_depr_schedule_doc( - asset.name, "Draft", "Test Finance Book 1" - ) + asset_depr_schedule_doc_1 = get_asset_depr_schedule_doc(asset.name, "Draft", "Test Finance Book 1") self.assertEqual(len(asset_depr_schedule_doc_1.get("depreciation_schedule")), 3) - asset_depr_schedule_doc_2 = get_asset_depr_schedule_doc( - asset.name, "Draft", "Test Finance Book 2" - ) + asset_depr_schedule_doc_2 = get_asset_depr_schedule_doc(asset.name, "Draft", "Test Finance Book 2") self.assertEqual(len(asset_depr_schedule_doc_2.get("depreciation_schedule")), 6) def test_depreciation_entry_cancellation(self): @@ -1487,13 +1511,13 @@ class TestDepreciationBasics(AssetSetup): asset.finance_books[0].expected_value_after_useful_life = 100 asset.save() asset.reload() - self.assertEquals(asset.finance_books[0].value_after_depreciation, 98000.0) + self.assertEqual(asset.finance_books[0].value_after_depreciation, 98000.0) # changing expected_value_after_useful_life shouldn't affect value_after_depreciation asset.finance_books[0].expected_value_after_useful_life = 200 asset.save() asset.reload() - self.assertEquals(asset.finance_books[0].value_after_depreciation, 98000.0) + self.assertEqual(asset.finance_books[0].value_after_depreciation, 98000.0) def test_asset_cost_center(self): asset = create_asset(is_existing_asset=1, do_not_save=1) @@ -1630,6 +1654,15 @@ class TestDepreciationBasics(AssetSetup): self.assertTrue(get_gl_entries("Purchase Receipt", pr.name)) +def set_system_settings(float_precision=2, currency_precision=2): + # @dokos set number and currency precision to match tests calculation results + system_settings = frappe.get_doc("System Settings") + system_settings.float_precision = float_precision + system_settings.currency_precision = currency_precision + system_settings.rounding_method = "Banker's Rounding" + system_settings.save() + + def get_gl_entries(doctype, docname): gl_entry = frappe.qb.DocType("GL Entry") return ( @@ -1701,6 +1734,9 @@ def create_asset(**args): "total_number_of_depreciations": args.total_number_of_depreciations or 5, "expected_value_after_useful_life": args.expected_value_after_useful_life or 0, "depreciation_start_date": args.depreciation_start_date, + "daily_prorata_based": args.daily_prorata_based or 0, + "shift_based": args.shift_based or 0, + "rate_of_depreciation": args.rate_of_depreciation or 0, }, ) @@ -1716,12 +1752,12 @@ def create_asset(**args): return asset -def create_asset_category(): +def create_asset_category(enable_cwip=1): asset_category = frappe.new_doc("Asset Category") asset_category.asset_category_name = "Computers" asset_category.total_number_of_depreciations = 3 asset_category.frequency_of_depreciation = 3 - asset_category.enable_cwip_accounting = 1 + asset_category.enable_cwip_accounting = enable_cwip asset_category.append( "accounts", { diff --git a/erpnext/assets/doctype/asset_activity/asset_activity.json b/erpnext/assets/doctype/asset_activity/asset_activity.json index 2c883a0b21..96110611d0 100644 --- a/erpnext/assets/doctype/asset_activity/asset_activity.json +++ b/erpnext/assets/doctype/asset_activity/asset_activity.json @@ -75,7 +75,7 @@ "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2023-09-29 15:56:17.608643", + "modified": "2024-03-27 13:06:32.933603", "modified_by": "Administrator", "module": "Assets", "name": "Asset Activity", @@ -106,7 +106,7 @@ "share": 1 } ], - "sort_field": "modified", + "sort_field": "creation", "sort_order": "DESC", "states": [] } diff --git a/erpnext/assets/doctype/asset_activity/asset_activity.py b/erpnext/assets/doctype/asset_activity/asset_activity.py index 0b3c313879..7177223b4f 100644 --- a/erpnext/assets/doctype/asset_activity/asset_activity.py +++ b/erpnext/assets/doctype/asset_activity/asset_activity.py @@ -7,6 +7,20 @@ from frappe.utils import now_datetime class AssetActivity(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + asset: DF.Link + date: DF.Datetime + subject: DF.SmallText + user: DF.Link + # end: auto-generated types + pass diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js index 110f2c4f2a..651b75d0a7 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js @@ -3,10 +3,9 @@ frappe.provide("erpnext.assets"); - erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.stock.StockController { setup() { - this.frm.ignore_doctypes_on_cancel_all = ['Serial and Batch Bundle', 'Asset Movement']; + this.frm.ignore_doctypes_on_cancel_all = ["Serial and Batch Bundle", "Asset Movement"]; this.setup_posting_date_time_check(); } @@ -17,7 +16,10 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s refresh() { this.show_general_ledger(); - if ((this.frm.doc.stock_items && this.frm.doc.stock_items.length) || !this.frm.doc.target_is_fixed_asset) { + if ( + (this.frm.doc.stock_items && this.frm.doc.stock_items.length) || + !this.frm.doc.target_is_fixed_asset + ) { this.show_stock_ledger(); } @@ -32,32 +34,32 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s me.setup_warehouse_query(); - me.frm.set_query("target_item_code", function() { + me.frm.set_query("target_item_code", function () { if (me.frm.doc.entry_type == "Capitalization") { - return erpnext.queries.item({"is_stock_item": 0, "is_fixed_asset": 1}); + return erpnext.queries.item({ is_stock_item: 0, is_fixed_asset: 1 }); } else { - return erpnext.queries.item({"is_stock_item": 1, "is_fixed_asset": 0}); + return erpnext.queries.item({ is_stock_item: 1, is_fixed_asset: 0 }); } }); - me.frm.set_query("target_asset", function() { + me.frm.set_query("target_asset", function () { return { - filters: {'is_composite_asset': 1, 'docstatus': 0 } - } + filters: { is_composite_asset: 1, docstatus: 0 }, + }; }); - me.frm.set_query("asset", "asset_items", function() { + me.frm.set_query("asset", "asset_items", function () { var filters = { - 'status': ["not in", ["Draft", "Scrapped", "Sold", "Capitalized", "Decapitalized"]], - 'docstatus': 1 + status: ["not in", ["Draft", "Scrapped", "Sold", "Capitalized", "Decapitalized"]], + docstatus: 1, }; if (me.frm.doc.target_asset) { - filters['name'] = ['!=', me.frm.doc.target_asset]; + filters["name"] = ["!=", me.frm.doc.target_asset]; } return { - filters: filters + filters: filters, }; }); @@ -65,58 +67,67 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s let row = locals[cdt][cdn]; return { filters: { - 'item_code': row.item_code, - 'voucher_type': doc.doctype, - 'voucher_no': ["in", [doc.name, ""]], - 'is_cancelled': 0, - } - } + item_code: row.item_code, + voucher_type: doc.doctype, + voucher_no: ["in", [doc.name, ""]], + is_cancelled: 0, + }, + }; }); - me.frm.set_query("item_code", "stock_items", function() { - return erpnext.queries.item({"is_stock_item": 1}); + me.frm.set_query("item_code", "stock_items", function () { + return erpnext.queries.item({ is_stock_item: 1 }); }); - me.frm.set_query("item_code", "service_items", function() { - return erpnext.queries.item({"is_stock_item": 0, "is_fixed_asset": 0}); + me.frm.set_query("item_code", "service_items", function () { + return erpnext.queries.item({ is_stock_item: 0, is_fixed_asset: 0 }); }); - me.frm.set_query('batch_no', 'stock_items', function(doc, cdt, cdn) { + me.frm.set_query("batch_no", "stock_items", function (doc, cdt, cdn) { var item = locals[cdt][cdn]; if (!item.item_code) { frappe.throw(__("Please enter Item Code to get Batch Number")); } else { var filters = { - 'item_code': item.item_code, - 'posting_date': me.frm.doc.posting_date || frappe.datetime.nowdate(), - 'warehouse': item.warehouse + item_code: item.item_code, + posting_date: me.frm.doc.posting_date || frappe.datetime.nowdate(), + warehouse: item.warehouse, }; return { query: "erpnext.controllers.queries.get_batch_no", - filters: filters + filters: filters, }; } }); - me.frm.set_query('expense_account', 'service_items', function() { + me.frm.set_query("expense_account", "service_items", function () { return { filters: { - "account_type": ['in', ["Tax", "Expense Account", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"]], - "is_group": 0, - "company": me.frm.doc.company - } + account_type: [ + "in", + [ + "Tax", + "Expense Account", + "Income Account", + "Expenses Included In Valuation", + "Expenses Included In Asset Valuation", + ], + ], + is_group: 0, + company: me.frm.doc.company, + }, }; }); - let sbb_field = me.frm.get_docfield('stock_items', 'serial_and_batch_bundle'); + let sbb_field = me.frm.get_docfield("stock_items", "serial_and_batch_bundle"); if (sbb_field) { sbb_field.get_route_options_for_new_doc = (row) => { return { - 'item_code': row.doc.item_code, - 'warehouse': row.doc.warehouse, - 'voucher_type': me.frm.doc.doctype, - } + item_code: row.doc.item_code, + warehouse: row.doc.warehouse, + voucher_type: me.frm.doc.doctype, + }; }; } } @@ -126,7 +137,10 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s } target_asset() { - if (this.frm.doc.target_asset && this.frm.doc.capitalization_method === "Choose a WIP composite asset") { + if ( + this.frm.doc.target_asset && + this.frm.doc.capitalization_method === "Choose a WIP composite asset" + ) { this.set_consumed_stock_items_tagged_to_wip_composite_asset(this.frm.doc.target_asset); this.get_target_asset_details(); } @@ -143,7 +157,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s }, callback: function (r) { if (!r.exc && r.message) { - if(r.message[0] && r.message[0].length) { + if (r.message[0] && r.message[0].length) { me.frm.clear_table("stock_items"); for (let item of r.message[0]) { me.frm.add_child("stock_items", item); @@ -160,7 +174,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s me.calculate_totals(); } - } + }, }); } } @@ -192,7 +206,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s if (this.frm.doc.posting_date) { frappe.run_serially([ () => this.get_all_item_warehouse_details(), - () => this.get_all_asset_values() + () => this.get_all_asset_values(), ]); } } @@ -248,15 +262,15 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s } stock_items_add(doc, cdt, cdn) { - erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, 'stock_items'); + erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, "stock_items"); } asset_items_add(doc, cdt, cdn) { - erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, 'asset_items'); + erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, "asset_items"); } serivce_items_add(doc, cdt, cdn) { - erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, 'service_items'); + erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, "service_items"); } get_target_item_details() { @@ -274,7 +288,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s if (!r.exc) { me.frm.refresh_fields(); } - } + }, }); } } @@ -294,7 +308,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s if (!r.exc) { me.frm.refresh_fields(); } - } + }, }); } } @@ -316,13 +330,13 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s company: me.frm.doc.company, posting_date: me.frm.doc.posting_date, posting_time: me.frm.doc.posting_time, - } + }, }, callback: function (r) { if (!r.exc) { me.calculate_totals(); } - } + }, }); } } @@ -343,13 +357,13 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s finance_book: row.finance_book || me.frm.doc.finance_book, posting_date: me.frm.doc.posting_date, posting_time: me.frm.doc.posting_time, - } + }, }, callback: function (r) { if (!r.exc) { me.calculate_totals(); } - } + }, }); } } @@ -367,13 +381,13 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s qty: flt(row.qty), expense_account: row.expense_account, company: me.frm.doc.company, - } + }, }, callback: function (r) { if (!r.exc) { me.calculate_totals(); } - } + }, }); } } @@ -386,23 +400,23 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s child: item, args: { args: { - 'item_code': item.item_code, - 'warehouse': cstr(item.warehouse), - 'qty': flt(item.stock_qty), - 'serial_no': item.serial_no, - 'posting_date': me.frm.doc.posting_date, - 'posting_time': me.frm.doc.posting_time, - 'company': me.frm.doc.company, - 'voucher_type': me.frm.doc.doctype, - 'voucher_no': me.frm.doc.name, - 'allow_zero_valuation': 1 - } + item_code: item.item_code, + warehouse: cstr(item.warehouse), + qty: flt(item.stock_qty), + serial_no: item.serial_no, + posting_date: me.frm.doc.posting_date, + posting_time: me.frm.doc.posting_time, + company: me.frm.doc.company, + voucher_type: me.frm.doc.doctype, + voucher_no: me.frm.doc.name, + allow_zero_valuation: 1, + }, }, - callback: function(r) { + callback: function (r) { if (!r.exc) { me.calculate_totals(); } - } + }, }); } } @@ -412,11 +426,11 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s return me.frm.call({ method: "set_warehouse_details", doc: me.frm.doc, - callback: function(r) { + callback: function (r) { if (!r.exc) { me.calculate_totals(); } - } + }, }); } @@ -425,11 +439,11 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s return me.frm.call({ method: "set_asset_values", doc: me.frm.doc, - callback: function(r) { + callback: function (r) { if (!r.exc) { me.calculate_totals(); } - } + }, }); } @@ -441,33 +455,38 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s me.frm.doc.service_items_total = 0; $.each(me.frm.doc.stock_items || [], function (i, d) { - d.amount = flt(flt(d.stock_qty) * flt(d.valuation_rate), precision('amount', d)); + d.amount = flt(flt(d.stock_qty) * flt(d.valuation_rate), precision("amount", d)); me.frm.doc.stock_items_total += d.amount; }); $.each(me.frm.doc.asset_items || [], function (i, d) { - d.asset_value = flt(flt(d.asset_value), precision('asset_value', d)); + d.asset_value = flt(flt(d.asset_value), precision("asset_value", d)); me.frm.doc.asset_items_total += d.asset_value; }); $.each(me.frm.doc.service_items || [], function (i, d) { - d.amount = flt(flt(d.qty) * flt(d.rate), precision('amount', d)); + d.amount = flt(flt(d.qty) * flt(d.rate), precision("amount", d)); me.frm.doc.service_items_total += d.amount; }); - me.frm.doc.stock_items_total = flt(me.frm.doc.stock_items_total, precision('stock_items_total')); - me.frm.doc.asset_items_total = flt(me.frm.doc.asset_items_total, precision('asset_items_total')); - me.frm.doc.service_items_total = flt(me.frm.doc.service_items_total, precision('service_items_total')); + me.frm.doc.stock_items_total = flt(me.frm.doc.stock_items_total, precision("stock_items_total")); + me.frm.doc.asset_items_total = flt(me.frm.doc.asset_items_total, precision("asset_items_total")); + me.frm.doc.service_items_total = flt( + me.frm.doc.service_items_total, + precision("service_items_total") + ); - me.frm.doc.total_value = me.frm.doc.stock_items_total + me.frm.doc.asset_items_total + me.frm.doc.service_items_total; - me.frm.doc.total_value = flt(me.frm.doc.total_value, precision('total_value')); + me.frm.doc.total_value = + me.frm.doc.stock_items_total + me.frm.doc.asset_items_total + me.frm.doc.service_items_total; + me.frm.doc.total_value = flt(me.frm.doc.total_value, precision("total_value")); - me.frm.doc.target_qty = flt(me.frm.doc.target_qty, precision('target_qty')); - me.frm.doc.target_incoming_rate = me.frm.doc.target_qty ? me.frm.doc.total_value / flt(me.frm.doc.target_qty) + me.frm.doc.target_qty = flt(me.frm.doc.target_qty, precision("target_qty")); + me.frm.doc.target_incoming_rate = me.frm.doc.target_qty + ? me.frm.doc.total_value / flt(me.frm.doc.target_qty) : me.frm.doc.total_value; me.frm.refresh_fields(); } }; -cur_frm.cscript = new erpnext.assets.AssetCapitalization({frm: cur_frm}); +cur_frm.cscript = new erpnext.assets.AssetCapitalization({ frm: cur_frm }); diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json index 000ec65c45..4e7ac3ee8b 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json @@ -356,7 +356,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-10-03 22:55:59.461456", + "modified": "2024-03-27 13:06:33.080441", "modified_by": "Administrator", "module": "Assets", "name": "Asset Capitalization", @@ -394,7 +394,7 @@ "write": 1 } ], - "sort_field": "modified", + "sort_field": "creation", "sort_order": "DESC", "states": [], "title_field": "title", diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index e6eaba5dd6..9e9176f592 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -49,6 +49,59 @@ force_fields = [ class AssetCapitalization(StockController): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + from erpnext.assets.doctype.asset_capitalization_asset_item.asset_capitalization_asset_item import ( + AssetCapitalizationAssetItem, + ) + from erpnext.assets.doctype.asset_capitalization_service_item.asset_capitalization_service_item import ( + AssetCapitalizationServiceItem, + ) + from erpnext.assets.doctype.asset_capitalization_stock_item.asset_capitalization_stock_item import ( + AssetCapitalizationStockItem, + ) + + amended_from: DF.Link | None + asset_items: DF.Table[AssetCapitalizationAssetItem] + asset_items_total: DF.Currency + capitalization_method: DF.Literal["", "Create a new composite asset", "Choose a WIP composite asset"] + company: DF.Link + cost_center: DF.Link | None + entry_type: DF.Literal["Capitalization", "Decapitalization"] + finance_book: DF.Link | None + naming_series: DF.Literal["ACC-ASC-.YYYY.-"] + posting_date: DF.Date + posting_time: DF.Time + service_items: DF.Table[AssetCapitalizationServiceItem] + service_items_total: DF.Currency + set_posting_time: DF.Check + stock_items: DF.Table[AssetCapitalizationStockItem] + stock_items_total: DF.Currency + target_asset: DF.Link | None + target_asset_location: DF.Link | None + target_asset_name: DF.Data | None + target_batch_no: DF.Link | None + target_fixed_asset_account: DF.Link | None + target_has_batch_no: DF.Check + target_has_serial_no: DF.Check + target_incoming_rate: DF.Currency + target_is_fixed_asset: DF.Check + target_item_code: DF.Link | None + target_item_name: DF.Data | None + target_qty: DF.Float + target_serial_no: DF.SmallText | None + target_stock_uom: DF.Link | None + target_warehouse: DF.Link | None + title: DF.Data | None + total_value: DF.Currency + # end: auto-generated types + def validate(self): self.validate_posting_time() self.set_missing_values(for_validate=True) @@ -90,6 +143,10 @@ class AssetCapitalization(StockController): self.make_gl_entries() self.restore_consumed_asset_items() + def on_trash(self): + frappe.db.set_value("Asset", self.target_asset, "capitalized_in", None) + super().on_trash() + def cancel_target_asset(self): if self.entry_type == "Capitalization" and self.target_asset: asset_doc = frappe.get_doc("Asset", self.target_asset) diff --git a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py index 32c6122609..31723ef3be 100644 --- a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py @@ -321,7 +321,7 @@ class TestAssetCapitalization(unittest.TestCase): ) first_asset_depr_schedule = get_asset_depr_schedule_doc(consumed_asset.name, "Active") - self.assertEquals(first_asset_depr_schedule.status, "Active") + self.assertEqual(first_asset_depr_schedule.status, "Active") # Create and submit Asset Captitalization asset_capitalization = create_asset_capitalization( @@ -355,8 +355,8 @@ class TestAssetCapitalization(unittest.TestCase): first_asset_depr_schedule.load_from_db() second_asset_depr_schedule = get_asset_depr_schedule_doc(consumed_asset.name, "Active") - self.assertEquals(second_asset_depr_schedule.status, "Active") - self.assertEquals(first_asset_depr_schedule.status, "Cancelled") + self.assertEqual(second_asset_depr_schedule.status, "Active") + self.assertEqual(first_asset_depr_schedule.status, "Cancelled") depr_schedule_of_consumed_asset = second_asset_depr_schedule.get("depreciation_schedule") @@ -365,9 +365,7 @@ class TestAssetCapitalization(unittest.TestCase): for d in depr_schedule_of_consumed_asset if getdate(d.schedule_date) == getdate(capitalization_date) ] - self.assertTrue( - consumed_depreciation_schedule and consumed_depreciation_schedule[0].journal_entry - ) + self.assertTrue(consumed_depreciation_schedule and consumed_depreciation_schedule[0].journal_entry) self.assertEqual( consumed_depreciation_schedule[0].depreciation_amount, depreciation_before_disposal_amount ) @@ -390,15 +388,9 @@ class TestAssetCapitalization(unittest.TestCase): def create_asset_capitalization_data(): - create_item( - "Capitalization Target Stock Item", is_stock_item=1, is_fixed_asset=0, is_purchase_item=0 - ) - create_item( - "Capitalization Source Stock Item", is_stock_item=1, is_fixed_asset=0, is_purchase_item=0 - ) - create_item( - "Capitalization Source Service Item", is_stock_item=0, is_fixed_asset=0, is_purchase_item=0 - ) + create_item("Capitalization Target Stock Item", is_stock_item=1, is_fixed_asset=0, is_purchase_item=0) + create_item("Capitalization Source Stock Item", is_stock_item=1, is_fixed_asset=0, is_purchase_item=0) + create_item("Capitalization Source Service Item", is_stock_item=0, is_fixed_asset=0, is_purchase_item=0) def create_asset_capitalization(**args): diff --git a/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json b/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json index ebaaffbad1..9a6b02e544 100644 --- a/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json +++ b/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json @@ -116,13 +116,14 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-09-12 14:30:02.915132", + "modified": "2024-03-27 13:06:33.350191", "modified_by": "Administrator", "module": "Assets", "name": "Asset Capitalization Asset Item", "owner": "Administrator", "permissions": [], - "sort_field": "modified", + "sort_field": "creation", "sort_order": "DESC", + "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.py b/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.py index ba356d6b9f..003973f94d 100644 --- a/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.py +++ b/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.py @@ -6,4 +6,26 @@ from frappe.model.document import Document class AssetCapitalizationAssetItem(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + asset: DF.Link + asset_name: DF.Data | None + asset_value: DF.Currency + cost_center: DF.Link | None + current_asset_value: DF.Currency + finance_book: DF.Link | None + fixed_asset_account: DF.Link | None + item_code: DF.Link + item_name: DF.Data | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.json b/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.json index 75691eaf9d..6d74e873e5 100644 --- a/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.json +++ b/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.json @@ -110,13 +110,13 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2024-03-05 11:23:40.766844", + "modified": "2024-03-27 13:06:33.503815", "modified_by": "Administrator", "module": "Assets", "name": "Asset Capitalization Service Item", "owner": "Administrator", "permissions": [], - "sort_field": "modified", + "sort_field": "creation", "sort_order": "DESC", "states": [], "track_changes": 1 diff --git a/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.py b/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.py index 28d018ee39..93457f8785 100644 --- a/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.py +++ b/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.py @@ -6,4 +6,25 @@ from frappe.model.document import Document class AssetCapitalizationServiceItem(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + amount: DF.Currency + cost_center: DF.Link | None + expense_account: DF.Link + item_code: DF.Link | None + item_name: DF.Data | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + qty: DF.Float + rate: DF.Currency + uom: DF.Link | None + # end: auto-generated types + pass diff --git a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json index 2874aedd87..e863298fbf 100644 --- a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json +++ b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json @@ -178,13 +178,13 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2024-03-05 11:22:57.346889", + "modified": "2024-03-27 13:06:33.664691", "modified_by": "Administrator", "module": "Assets", "name": "Asset Capitalization Stock Item", "owner": "Administrator", "permissions": [], - "sort_field": "modified", + "sort_field": "creation", "sort_order": "DESC", "states": [], "track_changes": 1 diff --git a/erpnext/assets/doctype/asset_category/asset_category.js b/erpnext/assets/doctype/asset_category/asset_category.js index 7dde14ea0e..046b62f244 100644 --- a/erpnext/assets/doctype/asset_category/asset_category.js +++ b/erpnext/assets/doctype/asset_category/asset_category.js @@ -1,56 +1,55 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Asset Category', { - onload: function(frm) { - frm.add_fetch('company_name', 'accumulated_depreciation_account', 'accumulated_depreciation_account'); - frm.add_fetch('company_name', 'depreciation_expense_account', 'depreciation_expense_account'); +frappe.ui.form.on("Asset Category", { + onload: function (frm) { + frm.add_fetch("company_name", "accumulated_depreciation_account", "accumulated_depreciation_account"); + frm.add_fetch("company_name", "depreciation_expense_account", "depreciation_expense_account"); - frm.set_query('fixed_asset_account', 'accounts', function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; + frm.set_query("fixed_asset_account", "accounts", function (doc, cdt, cdn) { + var d = locals[cdt][cdn]; return { - "filters": { - "account_type": "Fixed Asset", - "root_type": "Asset", - "is_group": 0, - "company": d.company_name - } + filters: { + account_type: "Fixed Asset", + root_type: "Asset", + is_group: 0, + company: d.company_name, + }, }; }); - frm.set_query('accumulated_depreciation_account', 'accounts', function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; + frm.set_query("accumulated_depreciation_account", "accounts", function (doc, cdt, cdn) { + var d = locals[cdt][cdn]; return { - "filters": { - "account_type": "Accumulated Depreciation", - "is_group": 0, - "company": d.company_name - } + filters: { + account_type: "Accumulated Depreciation", + is_group: 0, + company: d.company_name, + }, }; }); - frm.set_query('depreciation_expense_account', 'accounts', function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; + frm.set_query("depreciation_expense_account", "accounts", function (doc, cdt, cdn) { + var d = locals[cdt][cdn]; return { - "filters": { - "account_type": "Depreciation", - "root_type": ["in", ["Expense", "Income"]], - "is_group": 0, - "company": d.company_name - } + filters: { + account_type: "Depreciation", + root_type: ["in", ["Expense", "Income"]], + is_group: 0, + company: d.company_name, + }, }; }); - frm.set_query('capital_work_in_progress_account', 'accounts', function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; + frm.set_query("capital_work_in_progress_account", "accounts", function (doc, cdt, cdn) { + var d = locals[cdt][cdn]; return { - "filters": { - "account_type": "Capital Work in Progress", - "is_group": 0, - "company": d.company_name - } + filters: { + account_type: "Capital Work in Progress", + is_group: 0, + company: d.company_name, + }, }; }); - - } + }, }); diff --git a/erpnext/assets/doctype/asset_category/asset_category.json b/erpnext/assets/doctype/asset_category/asset_category.json index 0283f095ff..e58e9c0676 100644 --- a/erpnext/assets/doctype/asset_category/asset_category.json +++ b/erpnext/assets/doctype/asset_category/asset_category.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:asset_category_name", @@ -65,7 +66,7 @@ } ], "links": [], - "modified": "2021-01-22 12:31:14.425319", + "modified": "2024-03-27 13:06:33.840414", "modified_by": "Administrator", "module": "Assets", "name": "Asset Category", @@ -111,7 +112,8 @@ } ], "show_name_in_global_search": 1, - "sort_field": "modified", + "sort_field": "creation", "sort_order": "DESC", + "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/assets/doctype/asset_category/asset_category.py b/erpnext/assets/doctype/asset_category/asset_category.py index 9cf3bf35b3..8c2d301a89 100644 --- a/erpnext/assets/doctype/asset_category/asset_category.py +++ b/erpnext/assets/doctype/asset_category/asset_category.py @@ -9,6 +9,25 @@ from frappe.utils import cint, get_link_to_form class AssetCategory(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + from erpnext.assets.doctype.asset_category_account.asset_category_account import ( + AssetCategoryAccount, + ) + from erpnext.assets.doctype.asset_finance_book.asset_finance_book import AssetFinanceBook + + accounts: DF.Table[AssetCategoryAccount] + asset_category_name: DF.Data + enable_cwip_accounting: DF.Check + finance_books: DF.Table[AssetFinanceBook] + # end: auto-generated types + def validate(self): self.validate_finance_books() self.validate_account_types() @@ -38,7 +57,9 @@ class AssetCategory(Document): account_currency = frappe.get_value("Account", d.get(type_of_account), "account_currency") if account_currency != company_currency: invalid_accounts.append( - frappe._dict({"type": type_of_account, "idx": d.idx, "account": d.get(type_of_account)}) + frappe._dict( + {"type": type_of_account, "idx": d.idx, "account": d.get(type_of_account)} + ) ) for d in invalid_accounts: @@ -87,8 +108,7 @@ class AssetCategory(Document): missing_cwip_accounts_for_company.append(get_link_to_form("Company", d.company_name)) if missing_cwip_accounts_for_company: - msg = _("""To enable Capital Work in Progress Accounting,""") - msg += " " + msg = _("""To enable Capital Work in Progress Accounting,""") + " " msg += _("""you must select Capital Work in Progress Account in accounts table""") msg += "

" msg += _("You can also set default CWIP account in Company {}").format( diff --git a/erpnext/assets/doctype/asset_category/test_asset_category.py b/erpnext/assets/doctype/asset_category/test_asset_category.py index 31aeeec5d7..516e27e00f 100644 --- a/erpnext/assets/doctype/asset_category/test_asset_category.py +++ b/erpnext/assets/doctype/asset_category/test_asset_category.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt - import unittest import frappe @@ -27,14 +26,12 @@ class TestAssetCategory(unittest.TestCase): ) try: - asset_category.insert() + asset_category.insert(ignore_if_duplicate=True) except frappe.DuplicateEntryError: pass def test_cwip_accounting(self): - company_cwip_acc = frappe.db.get_value( - "Company", "_Test Company", "capital_work_in_progress_account" - ) + frappe.db.get_value("Company", "_Test Company", "capital_work_in_progress_account") frappe.db.set_value("Company", "_Test Company", "capital_work_in_progress_account", "") asset_category = frappe.new_doc("Asset Category") diff --git a/erpnext/assets/doctype/asset_category_account/asset_category_account.json b/erpnext/assets/doctype/asset_category_account/asset_category_account.json index b7df557552..8fa7488458 100644 --- a/erpnext/assets/doctype/asset_category_account/asset_category_account.json +++ b/erpnext/assets/doctype/asset_category_account/asset_category_account.json @@ -1,196 +1,71 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-03-07 15:55:18.806409", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2016-03-07 15:55:18.806409", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company_name", + "fixed_asset_account", + "accumulated_depreciation_account", + "depreciation_expense_account", + "capital_work_in_progress_account" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "company_name", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "company_name", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "remember_last_selected_value": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "fixed_asset_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Fixed Asset Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "fixed_asset_account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Fixed Asset Account", + "options": "Account", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "accumulated_depreciation_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Accumulated Depreciation Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "accumulated_depreciation_account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Accumulated Depreciation Account", + "options": "Account" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "depreciation_expense_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Depreciation Expense Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "depreciation_expense_account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Depreciation Expense Account", + "options": "Account" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "capital_work_in_progress_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Capital Work In Progress Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "columns": 2, + "fieldname": "capital_work_in_progress_account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Capital Work In Progress Account", + "options": "Account" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-05-10 17:06:48.839347", - "modified_by": "Administrator", - "module": "Assets", - "name": "Asset Category Account", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file + ], + "istable": 1, + "links": [], + "modified": "2024-03-27 13:06:34.012854", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Category Account", + "owner": "Administrator", + "permissions": [], + "sort_field": "creation", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} diff --git a/erpnext/assets/doctype/asset_category_account/asset_category_account.py b/erpnext/assets/doctype/asset_category_account/asset_category_account.py index cce22734f9..2798f1cd6b 100644 --- a/erpnext/assets/doctype/asset_category_account/asset_category_account.py +++ b/erpnext/assets/doctype/asset_category_account/asset_category_account.py @@ -2,9 +2,26 @@ # For license information, please see license.txt -import frappe from frappe.model.document import Document class AssetCategoryAccount(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + accumulated_depreciation_account: DF.Link | None + capital_work_in_progress_account: DF.Link | None + company_name: DF.Link + depreciation_expense_account: DF.Link | None + fixed_asset_account: DF.Link + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.js b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.js index 3d2dff179a..83b5c376ac 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.js +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.js @@ -2,50 +2,63 @@ // For license information, please see license.txt frappe.provide("erpnext.asset"); -frappe.ui.form.on('Asset Depreciation Schedule', { - onload: function(frm) { +frappe.ui.form.on("Asset Depreciation Schedule", { + onload: function (frm) { frm.events.make_schedules_editable(frm); }, - make_schedules_editable: function(frm) { - var is_editable = frm.doc.depreciation_method == "Manual" ? true : false; + make_schedules_editable: function (frm) { + var is_manual_hence_editable = frm.doc.depreciation_method === "Manual" ? true : false; + var is_shift_hence_editable = frm.doc.shift_based ? true : false; - frm.toggle_enable("depreciation_schedule", is_editable); - frm.fields_dict["depreciation_schedule"].grid.toggle_enable("schedule_date", is_editable); - frm.fields_dict["depreciation_schedule"].grid.toggle_enable("depreciation_amount", is_editable); - } + frm.toggle_enable("depreciation_schedule", is_manual_hence_editable || is_shift_hence_editable); + frm.fields_dict["depreciation_schedule"].grid.toggle_enable( + "schedule_date", + is_manual_hence_editable + ); + frm.fields_dict["depreciation_schedule"].grid.toggle_enable( + "depreciation_amount", + is_manual_hence_editable + ); + frm.fields_dict["depreciation_schedule"].grid.toggle_enable("shift", is_shift_hence_editable); + }, }); -frappe.ui.form.on('Depreciation Schedule', { - make_depreciation_entry: function(frm, cdt, cdn) { +frappe.ui.form.on("Depreciation Schedule", { + make_depreciation_entry: function (frm, cdt, cdn) { var row = locals[cdt][cdn]; if (!row.journal_entry) { frappe.call({ method: "erpnext.assets.doctype.asset.depreciation.make_depreciation_entry", args: { - "asset_depr_schedule_name": frm.doc.name, - "date": row.schedule_date + asset_depr_schedule_name: frm.doc.name, + date: row.schedule_date, }, - callback: function(r) { + callback: function (r) { frappe.model.sync(r.message); frm.refresh(); - } - }) + }, + }); } }, - depreciation_amount: function(frm, cdt, cdn) { + depreciation_amount: function (frm, cdt, cdn) { erpnext.asset.set_accumulated_depreciation(frm); - } + }, }); -erpnext.asset.set_accumulated_depreciation = function(frm) { - if(frm.doc.depreciation_method != "Manual") return; +erpnext.asset.set_accumulated_depreciation = function (frm) { + if (frm.doc.depreciation_method != "Manual") return; var accumulated_depreciation = flt(frm.doc.opening_accumulated_depreciation); - $.each(frm.doc.depreciation_schedule || [], function(i, row) { - accumulated_depreciation += flt(row.depreciation_amount); - frappe.model.set_value(row.doctype, row.name, "accumulated_depreciation_amount", accumulated_depreciation); - }) + $.each(frm.doc.depreciation_schedule || [], function (i, row) { + accumulated_depreciation += flt(row.depreciation_amount); + frappe.model.set_value( + row.doctype, + row.name, + "accumulated_depreciation_amount", + accumulated_depreciation + ); + }); }; diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json index 01c0ea7fc7..3a540d82d0 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json @@ -20,6 +20,8 @@ "depreciation_method", "total_number_of_depreciations", "rate_of_depreciation", + "daily_prorata_based", + "shift_based", "column_break_8", "frequency_of_depreciation", "expected_value_after_useful_life", @@ -177,11 +179,20 @@ "read_only": 1 }, { - "fetch_from": "asset.company", - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", + "default": "0", + "depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"", + "fieldname": "daily_prorata_based", + "fieldtype": "Check", + "label": "Depreciate based on daily pro-rata", + "print_hide": 1, + "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:doc.depreciation_method == \"Straight Line\"", + "fieldname": "shift_based", + "fieldtype": "Check", + "label": "Depreciate based on shifts", "read_only": 1 }, { @@ -196,7 +207,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-01-08 16:31:04.533928", + "modified": "2024-03-27 13:06:34.135004", "modified_by": "Administrator", "module": "Assets", "name": "Asset Depreciation Schedule", @@ -233,7 +244,7 @@ "write": 1 } ], - "sort_field": "modified", + "sort_field": "creation", "sort_order": "DESC", "states": [] } 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 0b07015391..eb0135033b 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -63,6 +63,7 @@ class AssetDepreciationSchedule(Document): self.prepare_draft_asset_depr_schedule_data_from_asset_name_and_fb_name( self.asset, self.finance_book ) + self.update_shift_depr_schedule() def validate(self): self.validate_another_asset_depr_schedule_does_not_exist() @@ -110,6 +111,16 @@ class AssetDepreciationSchedule(Document): def on_cancel(self): self.db_set("status", "Cancelled") + def update_shift_depr_schedule(self): + if not self.shift_based or self.docstatus != 0: + return + + asset_doc = frappe.get_doc("Asset", self.asset) + fb_row = asset_doc.finance_books[self.finance_book_id - 1] + + self.make_depr_schedule(asset_doc, fb_row) + self.set_accumulated_depreciation(asset_doc, fb_row) + def prepare_draft_asset_depr_schedule_data_from_asset_name_and_fb_name(self, asset_name, fb_name): asset_doc = frappe.get_doc("Asset", asset_name) @@ -190,13 +201,15 @@ class AssetDepreciationSchedule(Document): self.frequency_of_depreciation = row.frequency_of_depreciation self.rate_of_depreciation = row.rate_of_depreciation self.expected_value_after_useful_life = row.expected_value_after_useful_life + self.daily_prorata_based = row.daily_prorata_based + self.shift_based = row.shift_based self.status = "Draft" def make_depr_schedule( self, asset_doc, row, - date_of_disposal, + date_of_disposal=None, update_asset_finance_book_row=True, value_after_depreciation=None, ): @@ -217,6 +230,8 @@ class AssetDepreciationSchedule(Document): num_of_depreciations_completed = 0 depr_schedule = [] + self.schedules_before_clearing = self.get("depreciation_schedule") + for schedule in self.get("depreciation_schedule"): if schedule.journal_entry: num_of_depreciations_completed += 1 @@ -260,9 +275,7 @@ class AssetDepreciationSchedule(Document): row.depreciation_method in ("Written Down Value", "Double Declining Balance") and cint(row.frequency_of_depreciation) != 12 ): - has_wdv_or_dd_non_yearly_pro_rata = _check_is_pro_rata( - asset_doc, row, wdv_or_dd_non_yearly=True - ) + has_wdv_or_dd_non_yearly_pro_rata = _check_is_pro_rata(asset_doc, row, wdv_or_dd_non_yearly=True) skip_row = False should_get_last_day = is_last_day_of_the_month(row.depreciation_start_date) @@ -272,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: @@ -288,8 +302,7 @@ class AssetDepreciationSchedule(Document): prev_depreciation_amount = self.get("depreciation_schedule")[n - 1].depreciation_amount 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, @@ -299,8 +312,8 @@ 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 ): @@ -317,7 +330,6 @@ class AssetDepreciationSchedule(Document): getdate(asset_doc.available_for_use_date), (asset_doc.opening_number_of_booked_depreciations * row.frequency_of_depreciation), ) - if self.depreciation_schedule: from_date = self.depreciation_schedule[-1].schedule_date @@ -326,13 +338,11 @@ class AssetDepreciationSchedule(Document): depreciation_amount, from_date, date_of_disposal, + original_schedule_date=schedule_date, ) if depreciation_amount > 0: - self.add_depr_schedule_row( - date_of_disposal, - depreciation_amount, - ) + self.add_depr_schedule_row(date_of_disposal, depreciation_amount, n) break @@ -346,7 +356,6 @@ class AssetDepreciationSchedule(Document): from_date = add_days( asset_doc.available_for_use_date, -1 ) # needed to calc depr amount for available_for_use_date too - depreciation_amount, days, months = _get_pro_rata_amt( row, depreciation_amount, @@ -412,7 +421,7 @@ class AssetDepreciationSchedule(Document): depreciation_amount_without_pro_rata, depreciation_amount ) - schedule_date = asset_doc.to_date # @dokos + schedule_date = add_days(schedule_date, days) if not depreciation_amount: continue @@ -432,10 +441,7 @@ class AssetDepreciationSchedule(Document): skip_row = True if flt(depreciation_amount, asset_doc.precision("gross_purchase_amount")) > 0: - self.add_depr_schedule_row( - schedule_date, - depreciation_amount, - ) + self.add_depr_schedule_row(schedule_date, depreciation_amount, n) # to ensure that final accumulated depreciation amount is accurate def get_adjusted_depreciation_amount( @@ -457,16 +463,22 @@ class AssetDepreciationSchedule(Document): def get_depreciation_amount_for_first_row(self): return self.get("depreciation_schedule")[0].depreciation_amount - def add_depr_schedule_row( - self, - schedule_date, - depreciation_amount, - ): + def add_depr_schedule_row(self, schedule_date, depreciation_amount, schedule_idx): + if self.shift_based: + shift = ( + self.schedules_before_clearing[schedule_idx].shift + if self.schedules_before_clearing and len(self.schedules_before_clearing) > schedule_idx + else frappe.get_cached_value("Asset Shift Factor", {"default": 1}, "shift_name") + ) + else: + shift = None + self.append( "depreciation_schedule", { "schedule_date": schedule_date, "depreciation_amount": depreciation_amount, + "shift": shift, }, ) @@ -508,6 +520,7 @@ class AssetDepreciationSchedule(Document): and i == max(straight_line_idx) - 1 and not date_of_disposal and not date_of_return + and not row.shift_based ): depreciation_amount += flt( value_after_depreciation - flt(row.expected_value_after_useful_life), @@ -551,9 +564,6 @@ def _check_is_pro_rata(asset_doc, row, wdv_or_dd_non_yearly=False): if days < total_days: has_pro_rata = True - if row.depreciation_start_date == from_date: - has_pro_rata = False # @dokos - return has_pro_rata @@ -571,34 +581,38 @@ def _get_modified_available_for_use_date(asset_doc, row, wdv_or_dd_non_yearly=Fa def _get_pro_rata_amt( - row, depreciation_amount, from_date, to_date, has_wdv_or_dd_non_yearly_pro_rata=False + row, + depreciation_amount, + from_date, + to_date, + has_wdv_or_dd_non_yearly_pro_rata=False, + original_schedule_date=None, ): - days = date_difference(to_date, from_date) # @dokos + days = date_diff(to_date, from_date) months = month_diff(to_date, from_date) if has_wdv_or_dd_non_yearly_pro_rata: - total_days = get_total_days(to_date, 12) + total_days = get_total_days(original_schedule_date or to_date, 12) else: - total_days = get_total_days(to_date, row.frequency_of_depreciation) + total_days = get_total_days(original_schedule_date or to_date, row.frequency_of_depreciation) - return (depreciation_amount * flt(days)) / flt(total_days), days, months + depreciation_days = date_difference(to_date, from_date) # @dokos + return (depreciation_amount * flt(depreciation_days)) / flt(total_days), days, months -@erpnext.allow_regional +@erpnext.allow_regional # @dokos def get_total_days(date, frequency): period_start_date = add_months(date, cint(frequency) * -1) - if is_last_day_of_the_month(date): period_start_date = get_last_day(period_start_date) + return date_difference(date, period_start_date) # @dokos - return date_difference(date, period_start_date) - -@erpnext.allow_regional +@erpnext.allow_regional # @dokos def date_difference(to_date, from_date): return date_diff(to_date, from_date) -@erpnext.allow_regional +@erpnext.allow_regional # @dokos def get_depreciation_amount( asset_depr_schedule, asset, @@ -609,12 +623,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, fb_row, schedule_idx, number_of_pending_depreciations - ) - + asset_depr_schedule, asset, fb_row, schedule_idx, number_of_pending_depreciations + ), None else: return get_wdv_or_dd_depr_amount( asset, @@ -625,16 +639,20 @@ def get_depreciation_amount( prev_depreciation_amount, has_wdv_or_dd_non_yearly_pro_rata, asset_depr_schedule, + prev_per_day_depr, ) def get_straight_line_or_manual_depr_amount( - asset, row, schedule_idx, number_of_pending_depreciations + asset_depr_schedule, asset, row, schedule_idx, number_of_pending_depreciations ): + if row.shift_based: + return get_shift_depr_amount(asset_depr_schedule, asset, row, schedule_idx) + # if the Depreciation Schedule is being modified after Asset Repair due to increase in asset life and value if asset.flags.increase_in_asset_life: return (flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)) / ( - date_diff(asset.to_date, asset.available_for_use_date) / 365 + date_difference(asset.to_date, asset.available_for_use_date) / 365 # @dokos ) # if the Depreciation Schedule is being modified after Asset Repair due to increase in asset value elif asset.flags.increase_in_asset_value_due_to_repair: @@ -643,9 +661,20 @@ def get_straight_line_or_manual_depr_amount( ) # if the Depreciation Schedule is being modified after Asset Value Adjustment due to decrease in asset value elif asset.flags.decrease_in_asset_value_due_to_value_adjustment: - return ( - flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) - ) / number_of_pending_depreciations + if row.daily_prorata_based: + amount = flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) + + return get_daily_prorata_based_straight_line_depr( + asset, + row, + schedule_idx, + number_of_pending_depreciations, + amount, + ) + else: + return ( + flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) + ) / number_of_pending_depreciations # if the Depreciation Schedule is being prepared for the first time else: if row.daily_prorata_based: @@ -690,6 +719,32 @@ def get_shift_depr_amount(asset_depr_schedule, asset, row, schedule_idx): - flt(row.expected_value_after_useful_life) ) / flt(row.total_number_of_depreciations - asset.opening_number_of_booked_depreciations) + asset_shift_factors_map = get_asset_shift_factors_map() + shift = ( + asset_depr_schedule.schedules_before_clearing[schedule_idx].shift + if len(asset_depr_schedule.schedules_before_clearing) > schedule_idx + else None + ) + shift_factor = asset_shift_factors_map.get(shift) if shift else 0 + + shift_factors_sum = sum( + flt(asset_shift_factors_map.get(schedule.shift)) + for schedule in asset_depr_schedule.schedules_before_clearing + ) + + return ( + ( + flt(asset.gross_purchase_amount) + - flt(asset.opening_accumulated_depreciation) + - flt(row.expected_value_after_useful_life) + ) + / flt(shift_factors_sum) + ) * shift_factor + + +def get_asset_shift_factors_map(): + return dict(frappe.db.get_all("Asset Shift Factor", ["shift_name", "shift_factor"], as_list=True)) + @erpnext.allow_regional def get_wdv_or_dd_depr_amount( @@ -701,6 +756,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, @@ -710,6 +766,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, ) @@ -721,6 +778,39 @@ 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 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, + 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) @@ -747,6 +837,75 @@ 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 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: # Year changes + return get_monthly_depr_amount(fb_row, schedule_idx, depreciable_value) + else: + 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: # year changes + return get_monthly_depr_amount(fb_row, schedule_idx, depreciable_value) + else: + 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( + fb_row, + depreciable_value, + from_date, +): + 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 + + +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 from_date, date_diff(to_date, from_date) + 1 + + def make_draft_asset_depr_schedules_if_not_present(asset_doc): asset_depr_schedules_names = [] @@ -862,11 +1021,14 @@ def make_new_active_asset_depr_schedules_and_cancel_current_ones( def get_temp_asset_depr_schedule_doc( - asset_doc, row, date_of_disposal=None, date_of_return=None, update_asset_finance_book_row=False + asset_doc, + row, + date_of_disposal=None, + date_of_return=None, + update_asset_finance_book_row=False, + new_depr_schedule=None, ): - current_asset_depr_schedule_doc = get_asset_depr_schedule_doc( - asset_doc.name, "Active", row.finance_book - ) + current_asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset_doc.name, "Active", row.finance_book) if not current_asset_depr_schedule_doc: frappe.throw( @@ -877,6 +1039,21 @@ def get_temp_asset_depr_schedule_doc( temp_asset_depr_schedule_doc = frappe.copy_doc(current_asset_depr_schedule_doc) + if new_depr_schedule: + temp_asset_depr_schedule_doc.depreciation_schedule = [] + + for schedule in new_depr_schedule: + temp_asset_depr_schedule_doc.append( + "depreciation_schedule", + { + "schedule_date": schedule.schedule_date, + "depreciation_amount": schedule.depreciation_amount, + "accumulated_depreciation_amount": schedule.accumulated_depreciation_amount, + "journal_entry": schedule.journal_entry, + "shift": schedule.shift, + }, + ) + temp_asset_depr_schedule_doc.prepare_draft_asset_depr_schedule_data( asset_doc, row, @@ -898,15 +1075,14 @@ def get_depr_schedule(asset_name, status, finance_book=None): return asset_depr_schedule_doc.get("depreciation_schedule") +@frappe.whitelist() def get_asset_depr_schedule_doc(asset_name, status, finance_book=None): asset_depr_schedule = get_asset_depr_schedule_name(asset_name, status, finance_book) if not asset_depr_schedule: return - asset_depr_schedule_doc = frappe.get_doc( - "Asset Depreciation Schedule", asset_depr_schedule[0].name - ) + asset_depr_schedule_doc = frappe.get_doc("Asset Depreciation Schedule", asset_depr_schedule[0].name) return asset_depr_schedule_doc 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 ec0f40c842..6009ac1496 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 @@ -11,7 +11,7 @@ from erpnext.assets.doctype.asset.depreciation import ( 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 + get_depr_schedule, ) @@ -23,7 +23,7 @@ class TestAssetDepreciationSchedule(FrappeTestCase): asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, submit=1) first_asset_depr_schedule = get_asset_depr_schedule_doc(asset.name, "Active") - self.assertEquals(first_asset_depr_schedule.status, "Active") + self.assertEqual(first_asset_depr_schedule.status, "Active") second_asset_depr_schedule = frappe.get_doc( {"doctype": "Asset Depreciation Schedule", "asset": asset.name, "finance_book": None} diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json index a6a7d81de7..779749ee4e 100644 --- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json +++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json @@ -21,6 +21,7 @@ ], "fields": [ { + "columns": 2, "fieldname": "finance_book", "fieldtype": "Link", "in_list_view": 1, @@ -36,6 +37,7 @@ "reqd": 1 }, { + "columns": 2, "fieldname": "total_number_of_depreciations", "fieldtype": "Int", "in_list_view": 1, @@ -47,6 +49,7 @@ "fieldtype": "Column Break" }, { + "columns": 2, "fieldname": "frequency_of_depreciation", "fieldtype": "Int", "in_list_view": 1, @@ -61,6 +64,7 @@ "mandatory_depends_on": "eval:parent.doctype == 'Asset'" }, { + "columns": 1, "default": "0", "depends_on": "eval:parent.doctype == 'Asset'", "fieldname": "expected_value_after_useful_life", @@ -82,8 +86,7 @@ "description": "In Percentage", "fieldname": "rate_of_depreciation", "fieldtype": "Percent", - "label": "Rate of Depreciation", - "precision": "4" + "label": "Rate of Depreciation" }, { "fieldname": "salvage_value_percentage", @@ -121,7 +124,7 @@ "owner": "Administrator", "permissions": [], "quick_entry": 1, - "sort_field": "modified", + "sort_field": "creation", "sort_order": "DESC", "states": [], "track_changes": 1 diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.py b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.py index ab01956625..d06d6355ec 100644 --- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.py +++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.py @@ -2,7 +2,6 @@ # For license information, please see license.txt -import frappe from frappe.model.document import Document diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js index 5c03b98873..83dabab893 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js @@ -1,49 +1,47 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Asset Maintenance', { +frappe.ui.form.on("Asset Maintenance", { setup: (frm) => { - frm.set_query("assign_to", "asset_maintenance_tasks", function(doc) { + frm.set_query("assign_to", "asset_maintenance_tasks", function (doc) { return { query: "erpnext.assets.doctype.asset_maintenance.asset_maintenance.get_team_members", filters: { - maintenance_team: doc.maintenance_team - } + maintenance_team: doc.maintenance_team, + }, }; }); - frm.set_indicator_formatter('maintenance_status', - function(doc) { - let indicator = 'blue'; - if (doc.maintenance_status == 'Overdue') { - indicator = 'orange'; - } - if (doc.maintenance_status == 'Cancelled') { - indicator = 'red'; - } - return indicator; + frm.set_indicator_formatter("maintenance_status", function (doc) { + let indicator = "blue"; + if (doc.maintenance_status == "Overdue") { + indicator = "orange"; + } + if (doc.maintenance_status == "Cancelled") { + indicator = "red"; } - ); + return indicator; + }); }, refresh: (frm) => { - if(!frm.is_new()) { - frm.trigger('make_dashboard'); + if (!frm.is_new()) { + frm.trigger("make_dashboard"); } }, make_dashboard: (frm) => { - if(!frm.is_new()) { + if (!frm.is_new()) { frappe.call({ - method: 'erpnext.assets.doctype.asset_maintenance.asset_maintenance.get_maintenance_log', - args: {asset_name: frm.doc.asset_name}, + method: "erpnext.assets.doctype.asset_maintenance.asset_maintenance.get_maintenance_log", + args: { asset_name: frm.doc.asset_name }, callback: (r) => { - if(!r.message) { + if (!r.message) { return; } - const section = frm.dashboard.add_section('', __("Maintenance Log")); - var rows = $('
').appendTo(section); + const section = frm.dashboard.add_section("", __("Maintenance Log")); + var rows = $("
").appendTo(section); // show - (r.message || []).forEach(function(d) { + (r.message || []).forEach(function (d) { $(`
{ get_next_due_date(frm, cdt, cdn); }, - periodicity: (frm, cdt, cdn) => { + periodicity: (frm, cdt, cdn) => { get_next_due_date(frm, cdt, cdn); }, - last_completion_date: (frm, cdt, cdn) => { + last_completion_date: (frm, cdt, cdn) => { get_next_due_date(frm, cdt, cdn); }, - end_date: (frm, cdt, cdn) => { + end_date: (frm, cdt, cdn) => { get_next_due_date(frm, cdt, cdn); - } + }, }); var get_next_due_date = function (frm, cdt, cdn) { var d = locals[cdt][cdn]; if (d.start_date && d.periodicity) { return frappe.call({ - method: 'erpnext.assets.doctype.asset_maintenance.asset_maintenance.calculate_next_due_date', + method: "erpnext.assets.doctype.asset_maintenance.asset_maintenance.calculate_next_due_date", args: { start_date: d.start_date, periodicity: d.periodicity, end_date: d.end_date, last_completion_date: d.last_completion_date, - next_due_date: d.next_due_date + next_due_date: d.next_due_date, }, - callback: function(r) { + callback: function (r) { if (r.message) { frappe.model.set_value(cdt, cdn, "next_due_date", r.message); - } - else { + } else { frappe.model.set_value(cdt, cdn, "next_due_date", ""); } - } + }, }); } }; diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json index c8a504cad8..5287f643bd 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json @@ -1,140 +1,141 @@ { - "actions": [], - "autoname": "field:asset_name", - "creation": "2017-10-19 16:50:22.879545", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "asset_name", - "asset_category", - "company", - "column_break_3", - "item_code", - "item_name", - "section_break_6", - "maintenance_team", - "column_break_9", - "maintenance_manager", - "maintenance_manager_name", - "section_break_8", - "asset_maintenance_tasks" - ], - "fields": [ - { - "fieldname": "asset_name", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Asset Name", - "options": "Asset", - "reqd": 1, - "unique": 1 - }, - { - "fetch_from": "asset_name.asset_category", - "fieldname": "asset_category", - "fieldtype": "Read Only", - "label": "Asset Category" - }, - { - "fetch_from": "asset_name.item_code", - "fieldname": "item_code", - "fieldtype": "Read Only", - "label": "Item Code" - }, - { - "fetch_from": "asset_name.item_name", - "fieldname": "item_name", - "fieldtype": "Read Only", - "label": "Item Name" - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "reqd": 1 - }, - { - "fieldname": "section_break_6", - "fieldtype": "Section Break" - }, - { - "fieldname": "maintenance_team", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Maintenance Team", - "options": "Asset Maintenance Team", - "reqd": 1 - }, - { - "fieldname": "column_break_9", - "fieldtype": "Column Break" - }, - { - "fetch_from": "maintenance_team.maintenance_manager", - "fieldname": "maintenance_manager", - "fieldtype": "Data", - "hidden": 1, - "label": "Maintenance Manager", - "read_only": 1 - }, - { - "fetch_from": "maintenance_team.maintenance_manager_name", - "fieldname": "maintenance_manager_name", - "fieldtype": "Read Only", - "label": "Maintenance Manager Name" - }, - { - "fieldname": "section_break_8", - "fieldtype": "Section Break", - "label": "Tasks" - }, - { - "fieldname": "asset_maintenance_tasks", - "fieldtype": "Table", - "label": "Maintenance Tasks", - "options": "Asset Maintenance Task", - "reqd": 1 - } - ], - "links": [], - "modified": "2020-05-28 20:28:32.993823", - "modified_by": "Administrator", - "module": "Assets", - "name": "Asset Maintenance", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Quality Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Manufacturing User", - "share": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 - } \ No newline at end of file + "actions": [], + "autoname": "field:asset_name", + "creation": "2017-10-19 16:50:22.879545", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "asset_name", + "asset_category", + "company", + "column_break_3", + "item_code", + "item_name", + "section_break_6", + "maintenance_team", + "column_break_9", + "maintenance_manager", + "maintenance_manager_name", + "section_break_8", + "asset_maintenance_tasks" + ], + "fields": [ + { + "fieldname": "asset_name", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Asset Name", + "options": "Asset", + "reqd": 1, + "unique": 1 + }, + { + "fetch_from": "asset_name.asset_category", + "fieldname": "asset_category", + "fieldtype": "Read Only", + "label": "Asset Category" + }, + { + "fetch_from": "asset_name.item_code", + "fieldname": "item_code", + "fieldtype": "Read Only", + "label": "Item Code" + }, + { + "fetch_from": "asset_name.item_name", + "fieldname": "item_name", + "fieldtype": "Read Only", + "label": "Item Name" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "fieldname": "maintenance_team", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Maintenance Team", + "options": "Asset Maintenance Team", + "reqd": 1 + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, + { + "fetch_from": "maintenance_team.maintenance_manager", + "fieldname": "maintenance_manager", + "fieldtype": "Data", + "hidden": 1, + "label": "Maintenance Manager", + "read_only": 1 + }, + { + "fetch_from": "maintenance_team.maintenance_manager_name", + "fieldname": "maintenance_manager_name", + "fieldtype": "Read Only", + "label": "Maintenance Manager Name" + }, + { + "fieldname": "section_break_8", + "fieldtype": "Section Break", + "label": "Tasks" + }, + { + "fieldname": "asset_maintenance_tasks", + "fieldtype": "Table", + "label": "Maintenance Tasks", + "options": "Asset Maintenance Task", + "reqd": 1 + } + ], + "links": [], + "modified": "2024-03-27 13:06:34.491299", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Maintenance", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Quality Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "share": 1, + "write": 1 + } + ], + "sort_field": "creation", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py index 7cb826fe96..99829df28e 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py @@ -10,6 +10,29 @@ from frappe.utils import add_days, add_months, add_years, getdate, nowdate class AssetMaintenance(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + from erpnext.assets.doctype.asset_maintenance_task.asset_maintenance_task import ( + AssetMaintenanceTask, + ) + + asset_category: DF.ReadOnly | None + asset_maintenance_tasks: DF.Table[AssetMaintenanceTask] + asset_name: DF.Link + company: DF.Link + item_code: DF.ReadOnly | None + item_name: DF.ReadOnly | None + maintenance_manager: DF.Data | None + maintenance_manager_name: DF.ReadOnly | None + maintenance_team: DF.Link + # end: auto-generated types + def validate(self): for task in self.get("asset_maintenance_tasks"): if task.end_date and (getdate(task.start_date) >= getdate(task.end_date)): @@ -69,9 +92,7 @@ def calculate_next_due_date( if not start_date and not last_completion_date: start_date = frappe.utils.now() - if last_completion_date and ( - (start_date and last_completion_date > start_date) or not start_date - ): + if last_completion_date and ((start_date and last_completion_date > start_date) or not start_date): start_date = last_completion_date if periodicity == "Daily": next_due_date = add_days(start_date, 1) @@ -149,9 +170,9 @@ def get_team_members(doctype, txt, searchfield, start, page_len, filters): def get_maintenance_log(asset_name): return frappe.db.sql( """ - select maintenance_status, count(asset_name) as count, asset_name - from `tabAsset Maintenance Log` - where asset_name=%s group by maintenance_status""", + select maintenance_status, count(asset_name) as count, asset_name + from `tabAsset Maintenance Log` + where asset_name=%s group by maintenance_status""", (asset_name), as_dict=1, ) diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.js b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.js index c5db90ad37..47a2128c37 100644 --- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.js +++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.js @@ -1,15 +1,15 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Asset Maintenance Log', { +frappe.ui.form.on("Asset Maintenance Log", { asset_maintenance: (frm) => { - frm.set_query('task', function(doc) { + frm.set_query("task", function (doc) { return { query: "erpnext.assets.doctype.asset_maintenance_log.asset_maintenance_log.get_maintenance_tasks", filters: { - 'asset_maintenance': doc.asset_maintenance - } + asset_maintenance: doc.asset_maintenance, + }, }; }); - } -}); \ No newline at end of file + }, +}); diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json index 2619229980..c641d48075 100644 --- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json +++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json @@ -1,201 +1,203 @@ { - "actions": [], - "autoname": "naming_series:", - "creation": "2017-10-23 16:58:44.424309", - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "asset_maintenance", - "naming_series", - "asset_name", - "column_break_2", - "item_code", - "item_name", - "section_break_5", - "task", - "task_name", - "maintenance_type", - "periodicity", - "has_certificate", - "certificate_attachement", - "column_break_6", - "maintenance_status", - "assign_to_name", - "due_date", - "completion_date", - "description", - "column_break_9", - "actions_performed", - "amended_from" - ], - "fields": [ - { - "fieldname": "asset_maintenance", - "fieldtype": "Link", - "label": "Asset Maintenance", - "options": "Asset Maintenance" - }, - { - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Series", - "options": "ACC-AML-.YYYY.-", - "reqd": 1 - }, - { - "fetch_from": "asset_maintenance.asset_name", - "fieldname": "asset_name", - "fieldtype": "Read Only", - "label": "Asset Name" - }, - { - "fieldname": "column_break_2", - "fieldtype": "Column Break" - }, - { - "fetch_from": "asset_maintenance.item_code", - "fieldname": "item_code", - "fieldtype": "Read Only", - "label": "Item Code" - }, - { - "fetch_from": "asset_maintenance.item_name", - "fieldname": "item_name", - "fieldtype": "Read Only", - "label": "Item Name" - }, - { - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "label": "Maintenance Details" - }, - { - "fieldname": "task", - "fieldtype": "Link", - "label": "Task", - "options": "Asset Maintenance Task" - }, - { - "fetch_from": "task.maintenance_type", - "fieldname": "maintenance_type", - "fieldtype": "Read Only", - "label": "Maintenance Type" - }, - { - "fetch_from": "task.periodicity", - "fieldname": "periodicity", - "fieldtype": "Data", - "label": "Periodicity", - "read_only": 1 - }, - { - "fetch_from": "task.assign_to_name", - "fieldname": "assign_to_name", - "fieldtype": "Read Only", - "label": "Assign To" - }, - { - "fieldname": "column_break_6", - "fieldtype": "Column Break" - }, - { - "fetch_from": "task.next_due_date", - "fieldname": "due_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Due Date", - "read_only": 1 - }, - { - "fieldname": "completion_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Completion Date" - }, - { - "fieldname": "maintenance_status", - "fieldtype": "Select", - "in_standard_filter": 1, - "label": "Maintenance Status", - "options": "Planned\nCompleted\nCancelled\nOverdue", - "reqd": 1 - }, - { - "default": "0", - "fetch_from": "task.certificate_required", - "fieldname": "has_certificate", - "fieldtype": "Check", - "label": "Has Certificate " - }, - { - "depends_on": "eval:doc.has_certificate", - "fieldname": "certificate_attachement", - "fieldtype": "Attach", - "label": "Certificate" - }, - { - "fetch_from": "task.description", - "fieldname": "description", - "fieldtype": "Read Only", - "label": "Description", - "read_only": 1 - }, - { - "fieldname": "column_break_9", - "fieldtype": "Section Break" - }, - { - "allow_on_submit": 1, - "fieldname": "actions_performed", - "fieldtype": "Text Editor", - "label": "Actions performed" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Asset Maintenance Log", - "print_hide": 1, - "read_only": 1 - }, - { - "fetch_from": "task.maintenance_task", - "fieldname": "task_name", - "fieldtype": "Data", - "in_preview": 1, - "label": "Task Name", - "read_only": 1 - } - ], - "is_submittable": 1, - "links": [], - "modified": "2021-01-22 12:33:45.888124", - "modified_by": "Administrator", - "module": "Assets", - "name": "Asset Maintenance Log", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Manufacturing User", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 1 - } \ No newline at end of file + "actions": [], + "autoname": "naming_series:", + "creation": "2017-10-23 16:58:44.424309", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "asset_maintenance", + "naming_series", + "asset_name", + "column_break_2", + "item_code", + "item_name", + "section_break_5", + "task", + "task_name", + "maintenance_type", + "periodicity", + "has_certificate", + "certificate_attachement", + "column_break_6", + "maintenance_status", + "assign_to_name", + "due_date", + "completion_date", + "description", + "column_break_9", + "actions_performed", + "amended_from" + ], + "fields": [ + { + "fieldname": "asset_maintenance", + "fieldtype": "Link", + "label": "Asset Maintenance", + "options": "Asset Maintenance" + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "ACC-AML-.YYYY.-", + "reqd": 1 + }, + { + "fetch_from": "asset_maintenance.asset_name", + "fieldname": "asset_name", + "fieldtype": "Read Only", + "label": "Asset Name" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fetch_from": "asset_maintenance.item_code", + "fieldname": "item_code", + "fieldtype": "Read Only", + "label": "Item Code" + }, + { + "fetch_from": "asset_maintenance.item_name", + "fieldname": "item_name", + "fieldtype": "Read Only", + "label": "Item Name" + }, + { + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "label": "Maintenance Details" + }, + { + "fieldname": "task", + "fieldtype": "Link", + "label": "Task", + "options": "Asset Maintenance Task" + }, + { + "fetch_from": "task.maintenance_type", + "fieldname": "maintenance_type", + "fieldtype": "Read Only", + "label": "Maintenance Type" + }, + { + "fetch_from": "task.periodicity", + "fieldname": "periodicity", + "fieldtype": "Data", + "label": "Periodicity", + "read_only": 1 + }, + { + "fetch_from": "task.assign_to_name", + "fieldname": "assign_to_name", + "fieldtype": "Read Only", + "label": "Assign To" + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "fetch_from": "task.next_due_date", + "fieldname": "due_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Due Date", + "read_only": 1 + }, + { + "fieldname": "completion_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Completion Date" + }, + { + "fieldname": "maintenance_status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Maintenance Status", + "options": "Planned\nCompleted\nCancelled\nOverdue", + "reqd": 1 + }, + { + "default": "0", + "fetch_from": "task.certificate_required", + "fieldname": "has_certificate", + "fieldtype": "Check", + "label": "Has Certificate " + }, + { + "depends_on": "eval:doc.has_certificate", + "fieldname": "certificate_attachement", + "fieldtype": "Attach", + "label": "Certificate" + }, + { + "fetch_from": "task.description", + "fieldname": "description", + "fieldtype": "Read Only", + "label": "Description", + "read_only": 1 + }, + { + "fieldname": "column_break_9", + "fieldtype": "Section Break" + }, + { + "allow_on_submit": 1, + "fieldname": "actions_performed", + "fieldtype": "Text Editor", + "label": "Actions performed" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Asset Maintenance Log", + "print_hide": 1, + "read_only": 1 + }, + { + "fetch_from": "task.maintenance_task", + "fieldname": "task_name", + "fieldtype": "Data", + "in_preview": 1, + "label": "Task Name", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2024-03-27 13:06:34.654633", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Maintenance Log", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "creation", + "sort_order": "DESC", + "states": [], + "track_changes": 1, + "track_seen": 1 +} diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py index ff791b2754..009bcc3e69 100644 --- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py +++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.py @@ -11,6 +11,34 @@ from erpnext.assets.doctype.asset_maintenance.asset_maintenance import calculate class AssetMaintenanceLog(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + actions_performed: DF.TextEditor | None + amended_from: DF.Link | None + asset_maintenance: DF.Link | None + asset_name: DF.ReadOnly | None + assign_to_name: DF.ReadOnly | None + certificate_attachement: DF.Attach | None + completion_date: DF.Date | None + description: DF.ReadOnly | None + due_date: DF.Date | None + has_certificate: DF.Check + item_code: DF.ReadOnly | None + item_name: DF.ReadOnly | None + maintenance_status: DF.Literal["Planned", "Completed", "Cancelled", "Overdue"] + maintenance_type: DF.ReadOnly | None + naming_series: DF.Literal["ACC-AML-.YYYY.-"] + periodicity: DF.Data | None + task: DF.Link | None + task_name: DF.Data | None + # end: auto-generated types + def validate(self): if getdate(self.due_date) < getdate(nowdate()) and self.maintenance_status not in [ "Completed", diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log_calendar.js b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log_calendar.js index c804b31e90..7b7b508536 100644 --- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log_calendar.js +++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log_calendar.js @@ -3,20 +3,20 @@ frappe.views.calendar["Asset Maintenance Log"] = { field_map: { - "start": "due_date", - "end": "due_date", - "id": "name", - "title": "task", - "allDay": "allDay", - "progress": "progress" + start: "due_date", + end: "due_date", + id: "name", + title: "task", + allDay: "allDay", + progress: "progress", }, filters: [ { - "fieldtype": "Link", - "fieldname": "asset_name", - "options": "Asset Maintenance", - "label": __("Asset Maintenance") - } + fieldtype: "Link", + fieldname: "asset_name", + options: "Asset Maintenance", + label: __("Asset Maintenance"), + }, ], - get_events_method: "frappe.desk.calendar.get_events" + get_events_method: "frappe.desk.calendar.get_events", }; diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log_list.js b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log_list.js index 23100bc2ec..13f2444742 100644 --- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log_list.js +++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log_list.js @@ -1,14 +1,15 @@ -frappe.listview_settings['Asset Maintenance Log'] = { +frappe.listview_settings["Asset Maintenance Log"] = { add_fields: ["maintenance_status"], - get_indicator: function(doc) { - if (doc.maintenance_status=="Planned") { + has_indicator_for_draft: 1, + get_indicator: function (doc) { + if (doc.maintenance_status == "Planned") { return [__(doc.maintenance_status), "orange", "status,=," + doc.maintenance_status]; - } else if (doc.maintenance_status=="Completed") { + } else if (doc.maintenance_status == "Completed") { return [__(doc.maintenance_status), "green", "status,=," + doc.maintenance_status]; - } else if (doc.maintenance_status=="Cancelled") { + } else if (doc.maintenance_status == "Cancelled") { return [__(doc.maintenance_status), "red", "status,=," + doc.maintenance_status]; - } else if (doc.maintenance_status=="Overdue") { + } else if (doc.maintenance_status == "Overdue") { return [__(doc.maintenance_status), "red", "status,=," + doc.maintenance_status]; } - } + }, }; diff --git a/erpnext/assets/doctype/asset_maintenance_log/test_asset_maintenance_log.py b/erpnext/assets/doctype/asset_maintenance_log/test_asset_maintenance_log.py index 8766079934..9980ff31f6 100644 --- a/erpnext/assets/doctype/asset_maintenance_log/test_asset_maintenance_log.py +++ b/erpnext/assets/doctype/asset_maintenance_log/test_asset_maintenance_log.py @@ -1,7 +1,6 @@ # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt - import unittest diff --git a/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.json b/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.json index 80d90c6347..f8f9d4db29 100644 --- a/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.json +++ b/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.json @@ -144,13 +144,14 @@ ], "istable": 1, "links": [], - "modified": "2023-03-23 07:03:07.113452", + "modified": "2024-03-27 13:06:34.835687", "modified_by": "Administrator", "module": "Assets", "name": "Asset Maintenance Task", "owner": "Administrator", "permissions": [], "quick_entry": 1, - "sort_field": "modified", - "sort_order": "DESC" + "sort_field": "creation", + "sort_order": "DESC", + "states": [] } diff --git a/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py b/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py index 65b0e36171..f3a21b59ca 100644 --- a/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py +++ b/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py @@ -2,9 +2,35 @@ # For license information, please see license.txt -import frappe from frappe.model.document import Document class AssetMaintenanceTask(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + assign_to: DF.Link | None + assign_to_name: DF.ReadOnly | None + certificate_required: DF.Check + description: DF.TextEditor | None + end_date: DF.Date | None + last_completion_date: DF.Date | None + maintenance_status: DF.Literal["Planned", "Overdue", "Cancelled"] + maintenance_task: DF.Data + maintenance_type: DF.Literal["Preventive Maintenance", "Calibration"] + next_due_date: DF.Date | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + periodicity: DF.Literal[ + "", "Daily", "Weekly", "Monthly", "Quarterly", "Half-yearly", "Yearly", "2 Yearly", "3 Yearly" + ] + start_date: DF.Date + # end: auto-generated types + pass diff --git a/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.js b/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.js index c94e3dbc3c..8598765190 100644 --- a/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.js +++ b/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.js @@ -1,8 +1,6 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Asset Maintenance Team', { - refresh: function() { - - } +frappe.ui.form.on("Asset Maintenance Team", { + refresh: function () {}, }); diff --git a/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.json b/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.json index 6adc2de8b9..e2bfdc20dd 100644 --- a/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.json +++ b/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.json @@ -1,87 +1,88 @@ { - "actions": [], - "autoname": "field:maintenance_team_name", - "creation": "2017-10-20 11:43:47.712616", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "maintenance_team_name", - "maintenance_manager", - "maintenance_manager_name", - "column_break_2", - "company", - "section_break_2", - "maintenance_team_members" - ], - "fields": [ - { - "fieldname": "maintenance_team_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Maintenance Team Name", - "reqd": 1, - "unique": 1 - }, - { - "fieldname": "maintenance_manager", - "fieldtype": "Link", - "label": "Maintenance Manager", - "options": "User" - }, - { - "fetch_from": "maintenance_manager.full_name", - "fieldname": "maintenance_manager_name", - "fieldtype": "Read Only", - "label": "Maintenance Manager Name" - }, - { - "fieldname": "column_break_2", - "fieldtype": "Column Break" - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "reqd": 1 - }, - { - "fieldname": "section_break_2", - "fieldtype": "Section Break", - "label": "Team" - }, - { - "fieldname": "maintenance_team_members", - "fieldtype": "Table", - "label": "Maintenance Team Members", - "options": "Maintenance Team Member", - "reqd": 1 - } - ], - "index_web_pages_for_search": 1, - "links": [], - "modified": "2021-01-22 15:09:03.347345", - "modified_by": "Administrator", - "module": "Assets", - "name": "Asset Maintenance Team", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Manufacturing User", - "share": 1, - "write": 1 - } - ], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 - } \ No newline at end of file + "actions": [], + "autoname": "field:maintenance_team_name", + "creation": "2017-10-20 11:43:47.712616", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "maintenance_team_name", + "maintenance_manager", + "maintenance_manager_name", + "column_break_2", + "company", + "section_break_2", + "maintenance_team_members" + ], + "fields": [ + { + "fieldname": "maintenance_team_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Maintenance Team Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "maintenance_manager", + "fieldtype": "Link", + "label": "Maintenance Manager", + "options": "User" + }, + { + "fetch_from": "maintenance_manager.full_name", + "fieldname": "maintenance_manager_name", + "fieldtype": "Read Only", + "label": "Maintenance Manager Name" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fieldname": "section_break_2", + "fieldtype": "Section Break", + "label": "Team" + }, + { + "fieldname": "maintenance_team_members", + "fieldtype": "Table", + "label": "Maintenance Team Members", + "options": "Maintenance Team Member", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-03-27 13:06:34.976117", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Maintenance Team", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "creation", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} diff --git a/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.py b/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.py index 22015d63ed..b2219f0ae0 100644 --- a/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.py +++ b/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.py @@ -2,9 +2,27 @@ # For license information, please see license.txt -import frappe from frappe.model.document import Document class AssetMaintenanceTeam(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + from erpnext.assets.doctype.maintenance_team_member.maintenance_team_member import ( + MaintenanceTeamMember, + ) + + company: DF.Link + maintenance_manager: DF.Link | None + maintenance_manager_name: DF.ReadOnly | None + maintenance_team_members: DF.Table[MaintenanceTeamMember] + maintenance_team_name: DF.Data + # end: auto-generated types + pass diff --git a/erpnext/assets/doctype/asset_maintenance_team/test_asset_maintenance_team.py b/erpnext/assets/doctype/asset_maintenance_team/test_asset_maintenance_team.py index 173864f6a5..732ab4ae48 100644 --- a/erpnext/assets/doctype/asset_maintenance_team/test_asset_maintenance_team.py +++ b/erpnext/assets/doctype/asset_maintenance_team/test_asset_maintenance_team.py @@ -1,7 +1,6 @@ # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt - import unittest diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.js b/erpnext/assets/doctype/asset_movement/asset_movement.js index 1482639f4c..e445c90f30 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.js +++ b/erpnext/assets/doctype/asset_movement/asset_movement.js @@ -1,104 +1,107 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Asset Movement', { +frappe.ui.form.on("Asset Movement", { setup: (frm) => { frm.set_query("to_employee", "assets", (doc) => { return { filters: { - company: doc.company - } + company: doc.company, + }, }; - }) + }); frm.set_query("from_employee", "assets", (doc) => { return { filters: { - company: doc.company - } + company: doc.company, + }, }; - }) + }); frm.set_query("reference_name", (doc) => { return { filters: { company: doc.company, - docstatus: 1 - } + docstatus: 1, + }, }; - }) + }); frm.set_query("reference_doctype", () => { return { filters: { - name: ["in", ["Purchase Receipt", "Purchase Invoice"]] - } + name: ["in", ["Purchase Receipt", "Purchase Invoice"]], + }, }; }), - frm.set_query("asset", "assets", () => { - return { - filters: { - status: ["not in", ["Draft"]] - } - } - }) + frm.set_query("asset", "assets", () => { + return { + filters: { + status: ["not in", ["Draft"]], + }, + }; + }); }, onload: (frm) => { - frm.trigger('set_required_fields'); + frm.trigger("set_required_fields"); }, purpose: (frm) => { - frm.trigger('set_required_fields'); + frm.trigger("set_required_fields"); }, set_required_fields: (frm, cdt, cdn) => { let fieldnames_to_be_altered; - if (frm.doc.purpose === 'Transfer') { + if (frm.doc.purpose === "Transfer") { fieldnames_to_be_altered = { target_location: { read_only: 0, reqd: 1 }, - source_location: { read_only: 1, reqd: 0 }, + source_location: { read_only: 1, reqd: 1 }, from_employee: { read_only: 1, reqd: 0 }, - to_employee: { read_only: 1, reqd: 0 } + to_employee: { read_only: 1, reqd: 0 }, }; - } - else if (frm.doc.purpose === 'Receipt') { + } else if (frm.doc.purpose === "Receipt") { fieldnames_to_be_altered = { target_location: { read_only: 0, reqd: 1 }, source_location: { read_only: 1, reqd: 0 }, from_employee: { read_only: 0, reqd: 0 }, - to_employee: { read_only: 1, reqd: 0 } + to_employee: { read_only: 1, reqd: 0 }, }; - } - else if (frm.doc.purpose === 'Issue') { + } else if (frm.doc.purpose === "Issue") { fieldnames_to_be_altered = { target_location: { read_only: 1, reqd: 0 }, - source_location: { read_only: 1, reqd: 1 }, + source_location: { read_only: 1, reqd: 0 }, from_employee: { read_only: 1, reqd: 0 }, - to_employee: { read_only: 0, reqd: 1 } + to_employee: { read_only: 0, reqd: 1 }, }; } if (fieldnames_to_be_altered) { - Object.keys(fieldnames_to_be_altered).forEach(fieldname => { + Object.keys(fieldnames_to_be_altered).forEach((fieldname) => { let property_to_be_altered = fieldnames_to_be_altered[fieldname]; - Object.keys(property_to_be_altered).forEach(property => { + Object.keys(property_to_be_altered).forEach((property) => { let value = property_to_be_altered[property]; - frm.fields_dict['assets'].grid.update_docfield_property(fieldname, property, value); + frm.fields_dict["assets"].grid.update_docfield_property(fieldname, property, value); }); }); - frm.refresh_field('assets'); + frm.refresh_field("assets"); } - } + }, }); -frappe.ui.form.on('Asset Movement Item', { - asset: function(frm, cdt, cdn) { +frappe.ui.form.on("Asset Movement Item", { + asset: function (frm, cdt, cdn) { // on manual entry of an asset auto sets their source location / employee const asset_name = locals[cdt][cdn].asset; - if (asset_name){ - frappe.db.get_doc('Asset', asset_name).then((asset_doc) => { - if(asset_doc.location) frappe.model.set_value(cdt, cdn, 'source_location', asset_doc.location); - if(asset_doc.custodian) frappe.model.set_value(cdt, cdn, 'from_employee', asset_doc.custodian); - }).catch((err) => { - console.log(err); // eslint-disable-line - }); + if (asset_name) { + frappe.db + .get_doc("Asset", asset_name) + .then((asset_doc) => { + if (asset_doc.location) + frappe.model.set_value(cdt, cdn, "source_location", asset_doc.location); + if (asset_doc.custodian) + frappe.model.set_value(cdt, cdn, "from_employee", asset_doc.custodian); + }) + .catch((err) => { + console.log(err); // eslint-disable-line + }); } - } -}); \ No newline at end of file + }, +}); diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.json b/erpnext/assets/doctype/asset_movement/asset_movement.json index 7515dc03c5..6e0dee6c5d 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.json +++ b/erpnext/assets/doctype/asset_movement/asset_movement.json @@ -96,7 +96,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-06-28 16:54:26.571083", + "modified": "2024-03-27 13:06:35.116228", "modified_by": "Administrator", "module": "Assets", "name": "Asset Movement", @@ -149,7 +149,7 @@ "write": 1 } ], - "sort_field": "modified", + "sort_field": "creation", "sort_order": "DESC", "states": [] } diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.py b/erpnext/assets/doctype/asset_movement/asset_movement.py index 267fe6f259..29c8b3c39e 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.py +++ b/erpnext/assets/doctype/asset_movement/asset_movement.py @@ -11,6 +11,25 @@ from erpnext.assets.doctype.asset_activity.asset_activity import add_asset_activ class AssetMovement(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + from erpnext.assets.doctype.asset_movement_item.asset_movement_item import AssetMovementItem + + amended_from: DF.Link | None + assets: DF.Table[AssetMovementItem] + company: DF.Link + purpose: DF.Literal["", "Issue", "Receipt", "Transfer"] + reference_doctype: DF.Link | None + reference_name: DF.DynamicLink | None + transaction_date: DF.Datetime + # end: auto-generated types + def validate(self): self.validate_asset() self.validate_location() @@ -35,7 +54,9 @@ class AssetMovement(Document): if d.source_location: if current_location != d.source_location: frappe.throw( - _("Asset {0} does not belongs to the location {1}").format(d.asset, d.source_location) + _("Asset {0} does not belongs to the location {1}").format( + d.asset, d.source_location + ) ) else: d.source_location = current_location @@ -60,19 +81,25 @@ class AssetMovement(Document): title=_("Incorrect Movement Purpose"), ) if not d.target_location: - frappe.throw(_("Target Location is required while transferring Asset {0}").format(d.asset)) + frappe.throw( + _("Target Location is required while transferring Asset {0}").format(d.asset) + ) if d.source_location == d.target_location: frappe.throw(_("Source and Target Location cannot be same")) if self.purpose == "Receipt": if not (d.source_location) and not d.target_location and not d.to_employee: frappe.throw( - _("Target Location or To Employee is required while receiving Asset {0}").format(d.asset) + _("Target Location or To Employee is required while receiving Asset {0}").format( + d.asset + ) ) elif d.source_location: if d.from_employee and not d.target_location: frappe.throw( - _("Target Location is required while receiving Asset {0} from an employee").format(d.asset) + _( + "Target Location is required while receiving Asset {0} from an employee" + ).format(d.asset) ) elif d.to_employee and d.target_location: frappe.throw( @@ -112,19 +139,17 @@ class AssetMovement(Document): # latest entry corresponds to current document's location, employee when transaction date > previous dates # In case of cancellation it corresponds to previous latest document's location, employee latest_movement_entry = frappe.db.sql( - """ + f""" SELECT asm_item.target_location, asm_item.to_employee FROM `tabAsset Movement Item` asm_item, `tabAsset Movement` asm WHERE asm_item.parent=asm.name and asm_item.asset=%(asset)s and asm.company=%(company)s and - asm.docstatus=1 and {0} + asm.docstatus=1 and {cond} ORDER BY asm.transaction_date desc limit 1 - """.format( - cond - ), + """, args, ) if latest_movement_entry: @@ -145,7 +170,9 @@ class AssetMovement(Document): elif current_location: add_asset_activity( d.asset, - _("Asset transferred to Location {0}").format(get_link_to_form("Location", current_location)), + _("Asset transferred to Location {0}").format( + get_link_to_form("Location", current_location) + ), ) elif current_employee: add_asset_activity( diff --git a/erpnext/assets/doctype/asset_movement/test_asset_movement.py b/erpnext/assets/doctype/asset_movement/test_asset_movement.py index 27e7e557f1..52590d2ba8 100644 --- a/erpnext/assets/doctype/asset_movement/test_asset_movement.py +++ b/erpnext/assets/doctype/asset_movement/test_asset_movement.py @@ -20,9 +20,7 @@ class TestAssetMovement(unittest.TestCase): make_location() def test_movement(self): - pr = make_purchase_receipt( - item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location" - ) + pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location") asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name") asset = frappe.get_doc("Asset", asset_name) @@ -51,7 +49,11 @@ class TestAssetMovement(unittest.TestCase): purpose="Transfer", company=asset.company, assets=[ - {"asset": asset.name, "source_location": "Test Location", "target_location": "Test Location 2"} + { + "asset": asset.name, + "source_location": "Test Location", + "target_location": "Test Location 2", + } ], reference_doctype="Purchase Receipt", reference_name=pr.name, @@ -62,7 +64,11 @@ class TestAssetMovement(unittest.TestCase): purpose="Transfer", company=asset.company, assets=[ - {"asset": asset.name, "source_location": "Test Location 2", "target_location": "Test Location"} + { + "asset": asset.name, + "source_location": "Test Location 2", + "target_location": "Test Location", + } ], reference_doctype="Purchase Receipt", reference_name=pr.name, @@ -97,9 +103,7 @@ class TestAssetMovement(unittest.TestCase): self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location") def test_last_movement_cancellation(self): - pr = make_purchase_receipt( - item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location" - ) + pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location") asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name") asset = frappe.get_doc("Asset", asset_name) @@ -129,7 +133,11 @@ class TestAssetMovement(unittest.TestCase): purpose="Transfer", company=asset.company, assets=[ - {"asset": asset.name, "source_location": "Test Location", "target_location": "Test Location 2"} + { + "asset": asset.name, + "source_location": "Test Location", + "target_location": "Test Location 2", + } ], reference_doctype="Purchase Receipt", reference_name=pr.name, @@ -167,6 +175,4 @@ def create_asset_movement(**args): def make_location(): for location in ["Pune", "Mumbai", "Nagpur"]: if not frappe.db.exists("Location", location): - frappe.get_doc({"doctype": "Location", "location_name": location}).insert( - ignore_permissions=True - ) + frappe.get_doc({"doctype": "Location", "location_name": location}).insert(ignore_permissions=True) diff --git a/erpnext/assets/doctype/asset_movement_item/asset_movement_item.json b/erpnext/assets/doctype/asset_movement_item/asset_movement_item.json index 994c3c0989..084b736f8a 100644 --- a/erpnext/assets/doctype/asset_movement_item/asset_movement_item.json +++ b/erpnext/assets/doctype/asset_movement_item/asset_movement_item.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2019-10-07 18:49:00.737806", "doctype": "DocType", "editable_grid": 1, @@ -73,14 +74,16 @@ } ], "istable": 1, - "modified": "2019-10-09 15:59:08.265141", + "links": [], + "modified": "2024-03-27 13:06:35.272015", "modified_by": "Administrator", "module": "Assets", "name": "Asset Movement Item", "owner": "Administrator", "permissions": [], "quick_entry": 1, - "sort_field": "modified", + "sort_field": "creation", "sort_order": "DESC", + "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/assets/doctype/asset_movement_item/asset_movement_item.py b/erpnext/assets/doctype/asset_movement_item/asset_movement_item.py index e25226d580..1a5302b9d8 100644 --- a/erpnext/assets/doctype/asset_movement_item/asset_movement_item.py +++ b/erpnext/assets/doctype/asset_movement_item/asset_movement_item.py @@ -7,4 +7,24 @@ from frappe.model.document import Document class AssetMovementItem(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + asset: DF.Link + asset_name: DF.Data | None + company: DF.Link | None + from_employee: DF.Link | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + source_location: DF.Link | None + target_location: DF.Link | None + to_employee: DF.Link | None + # end: auto-generated types + pass diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js index 03afcb9442..27a4eb6e99 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.js +++ b/erpnext/assets/doctype/asset_repair/asset_repair.js @@ -1,31 +1,31 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Asset Repair', { - setup: function(frm) { - frm.fields_dict.cost_center.get_query = function(doc) { +frappe.ui.form.on("Asset Repair", { + setup: function (frm) { + frm.fields_dict.cost_center.get_query = function (doc) { return { filters: { - 'is_group': 0, - 'company': doc.company - } + is_group: 0, + company: doc.company, + }, }; }; - frm.fields_dict.project.get_query = function(doc) { + frm.fields_dict.project.get_query = function (doc) { return { filters: { - 'company': doc.company - } + company: doc.company, + }, }; }; - frm.fields_dict.warehouse.get_query = function(doc) { + frm.fields_dict.warehouse.get_query = function (doc) { return { filters: { - 'is_group': 0, - 'company': doc.company - } + is_group: 0, + company: doc.company, + }, }; }; @@ -33,87 +33,87 @@ frappe.ui.form.on('Asset Repair', { let row = locals[cdt][cdn]; return { filters: { - 'item_code': row.item_code, - 'voucher_type': doc.doctype, - 'voucher_no': ["in", [doc.name, ""]], - 'is_cancelled': 0, - } - } + item_code: row.item_code, + voucher_type: doc.doctype, + voucher_no: ["in", [doc.name, ""]], + is_cancelled: 0, + }, + }; }); }, - refresh: function(frm) { + refresh: function (frm) { if (frm.doc.docstatus) { - frm.add_custom_button(__("View General Ledger"), function() { + frm.add_custom_button(__("View General Ledger"), function () { frappe.route_options = { - "voucher_no": frm.doc.name + voucher_no: frm.doc.name, }; frappe.set_route("query-report", "General Ledger"); }); } - let sbb_field = frm.get_docfield('stock_items', 'serial_and_batch_bundle'); + let sbb_field = frm.get_docfield("stock_items", "serial_and_batch_bundle"); if (sbb_field) { sbb_field.get_route_options_for_new_doc = (row) => { return { - 'item_code': row.doc.item_code, - 'voucher_type': frm.doc.doctype, - } + item_code: row.doc.item_code, + voucher_type: frm.doc.doctype, + }; }; } }, repair_status: (frm) => { if (frm.doc.completion_date && frm.doc.repair_status == "Completed") { - frappe.call ({ + frappe.call({ method: "erpnext.assets.doctype.asset_repair.asset_repair.get_downtime", args: { - "failure_date":frm.doc.failure_date, - "completion_date":frm.doc.completion_date + failure_date: frm.doc.failure_date, + completion_date: frm.doc.completion_date, }, - callback: function(r) { - if(r.message) { + callback: function (r) { + if (r.message) { frm.set_value("downtime", r.message + " Hrs"); } - } + }, }); } if (frm.doc.repair_status == "Completed") { - frm.set_value('completion_date', frappe.datetime.now_datetime()); + frm.set_value("completion_date", frappe.datetime.now_datetime()); } }, stock_items_on_form_rendered() { erpnext.setup_serial_or_batch_no(); - } + }, }); -frappe.ui.form.on('Asset Repair Consumed Item', { - item_code: function(frm, cdt, cdn) { +frappe.ui.form.on("Asset Repair Consumed Item", { + item_code: function (frm, cdt, cdn) { var item = locals[cdt][cdn]; let item_args = { - 'item_code': item.item_code, - 'warehouse': frm.doc.warehouse, - 'qty': item.consumed_quantity, - 'serial_no': item.serial_no, - 'company': frm.doc.company, + item_code: item.item_code, + warehouse: frm.doc.warehouse, + qty: item.consumed_quantity, + serial_no: item.serial_no, + company: frm.doc.company, }; frappe.call({ - method: 'erpnext.stock.utils.get_incoming_rate', + method: "erpnext.stock.utils.get_incoming_rate", args: { - args: item_args + args: item_args, + }, + callback: function (r) { + frappe.model.set_value(cdt, cdn, "valuation_rate", r.message); }, - callback: function(r) { - frappe.model.set_value(cdt, cdn, 'valuation_rate', r.message); - } }); }, - consumed_quantity: function(frm, cdt, cdn) { + consumed_quantity: function (frm, cdt, cdn) { var row = locals[cdt][cdn]; - frappe.model.set_value(cdt, cdn, 'total_value', row.consumed_quantity * row.valuation_rate); + frappe.model.set_value(cdt, cdn, "total_value", row.consumed_quantity * row.valuation_rate); }, }); diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.json b/erpnext/assets/doctype/asset_repair/asset_repair.json index accb5bf54b..218ff45ce7 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.json +++ b/erpnext/assets/doctype/asset_repair/asset_repair.json @@ -264,7 +264,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-08-16 15:55:25.023471", + "modified": "2024-03-27 13:06:35.397626", "modified_by": "Administrator", "module": "Assets", "name": "Asset Repair", @@ -302,10 +302,10 @@ "write": 1 } ], - "sort_field": "modified", + "sort_field": "creation", "sort_order": "DESC", "states": [], "title_field": "asset_name", "track_changes": 1, "track_seen": 1 -} \ No newline at end of file +} diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 0b8450614c..ccde836fe0 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -17,6 +17,42 @@ from erpnext.controllers.accounts_controller import AccountsController class AssetRepair(AccountsController): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + from erpnext.assets.doctype.asset_repair_consumed_item.asset_repair_consumed_item import ( + AssetRepairConsumedItem, + ) + + actions_performed: DF.LongText | None + amended_from: DF.Link | None + asset: DF.Link + asset_name: DF.ReadOnly | None + capitalize_repair_cost: DF.Check + company: DF.Link | None + completion_date: DF.Datetime | None + cost_center: DF.Link | None + description: DF.LongText | None + downtime: DF.Data | None + failure_date: DF.Datetime + increase_in_asset_life: DF.Int + naming_series: DF.Literal["ACC-ASR-.YYYY.-"] + project: DF.Link | None + purchase_invoice: DF.Link | None + repair_cost: DF.Currency + repair_status: DF.Literal["Pending", "Completed", "Cancelled"] + stock_consumption: DF.Check + stock_entry: DF.Link | None + stock_items: DF.Table[AssetRepairConsumedItem] + total_repair_cost: DF.Currency + warehouse: DF.Link | None + # end: auto-generated types + def validate(self): self.asset_doc = frappe.get_doc("Asset", self.asset) self.update_status() @@ -133,9 +169,7 @@ class AssetRepair(AccountsController): def check_for_stock_items_and_warehouse(self): if not self.get("stock_items"): - frappe.throw( - _("Please enter Stock Items consumed during the Repair."), title=_("Missing Items") - ) + frappe.throw(_("Please enter Stock Items consumed during the Repair."), title=_("Missing Items")) if not self.warehouse: frappe.throw( _("Please enter Warehouse from which Stock Items consumed during the Repair were taken."), @@ -227,9 +261,7 @@ class AssetRepair(AccountsController): def get_gl_entries(self): gl_entries = [] - fixed_asset_account = get_asset_account( - "fixed_asset_account", asset=self.asset, company=self.company - ) + fixed_asset_account = get_asset_account("fixed_asset_account", asset=self.asset, company=self.company) self.get_gl_entries_for_repair_cost(gl_entries, fixed_asset_account) self.get_gl_entries_for_consumed_items(gl_entries, fixed_asset_account) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair_list.js b/erpnext/assets/doctype/asset_repair/asset_repair_list.js index 86376f4004..633c39ba77 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair_list.js +++ b/erpnext/assets/doctype/asset_repair/asset_repair_list.js @@ -1,12 +1,12 @@ -frappe.listview_settings['Asset Repair'] = { +frappe.listview_settings["Asset Repair"] = { add_fields: ["repair_status"], - get_indicator: function(doc) { - if(doc.repair_status=="Pending") { + get_indicator: function (doc) { + if (doc.repair_status == "Pending") { return [__("Pending"), "orange"]; - } else if(doc.repair_status=="Completed") { + } else if (doc.repair_status == "Completed") { return [__("Completed"), "green"]; - } else if(doc.repair_status=="Cancelled") { + } else if (doc.repair_status == "Cancelled") { return [__("Cancelled"), "red"]; } - } + }, }; diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.py b/erpnext/assets/doctype/asset_repair/test_asset_repair.py index 6d75871a35..d59c057df9 100644 --- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py @@ -199,9 +199,9 @@ class TestAssetRepair(unittest.TestCase): self.assertEqual(expected_values[d.account][1], d.credit) def test_gl_entries_with_periodical_inventory(self): - frappe.db.set_value( - "Company", "_Test Company", "default_expense_account", "Cost of Goods Sold - _TC" - ) + frappe.db.set_value("Company", "_Test Company", "default_expense_account", "Cost of Goods Sold - _TC") + frappe.db.set_value("Company", "_Test Company", "stock_adjustment_account", None) # @dokos + asset_repair = create_asset_repair( capitalize_repair_cost=1, stock_consumption=1, @@ -244,7 +244,7 @@ class TestAssetRepair(unittest.TestCase): asset = create_asset(calculate_depreciation=1, submit=1) first_asset_depr_schedule = get_asset_depr_schedule_doc(asset.name, "Active") - self.assertEquals(first_asset_depr_schedule.status, "Active") + self.assertEqual(first_asset_depr_schedule.status, "Active") initial_num_of_depreciations = num_of_depreciations(asset) create_asset_repair(asset=asset, capitalize_repair_cost=1, submit=1) @@ -253,8 +253,8 @@ class TestAssetRepair(unittest.TestCase): first_asset_depr_schedule.load_from_db() second_asset_depr_schedule = get_asset_depr_schedule_doc(asset.name, "Active") - self.assertEquals(second_asset_depr_schedule.status, "Active") - self.assertEquals(first_asset_depr_schedule.status, "Cancelled") + self.assertEqual(second_asset_depr_schedule.status, "Active") + self.assertEqual(first_asset_depr_schedule.status, "Cancelled") self.assertEqual((initial_num_of_depreciations + 1), num_of_depreciations(asset)) self.assertEqual( @@ -291,9 +291,7 @@ def create_asset_repair(**args): if args.stock_consumption: asset_repair.stock_consumption = 1 - asset_repair.warehouse = args.warehouse or create_warehouse( - "Test Warehouse", company=asset.company - ) + asset_repair.warehouse = args.warehouse or create_warehouse("Test Warehouse", company=asset.company) bundle = None if args.serial_no: diff --git a/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json index 5fed16888c..f15b5a8667 100644 --- a/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json +++ b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json @@ -56,13 +56,13 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-04-06 02:24:20.375870", + "modified": "2024-03-27 13:06:35.608355", "modified_by": "Administrator", "module": "Assets", "name": "Asset Repair Consumed Item", "owner": "Administrator", "permissions": [], - "sort_field": "modified", + "sort_field": "creation", "sort_order": "DESC", "states": [], "track_changes": 1 diff --git a/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.py b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.py index 2a8d64ef49..ab43cfe62a 100644 --- a/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.py +++ b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.py @@ -6,4 +6,23 @@ from frappe.model.document import Document class AssetRepairConsumedItem(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + consumed_quantity: DF.Data | None + item_code: DF.Link | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + serial_and_batch_bundle: DF.Link | None + serial_no: DF.SmallText | None + total_value: DF.Currency + valuation_rate: DF.Currency + # end: auto-generated types + pass diff --git a/erpnext/assets/doctype/asset_shift_allocation/__init__.py b/erpnext/assets/doctype/asset_shift_allocation/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/assets/doctype/asset_shift_allocation/asset_shift_allocation.js b/erpnext/assets/doctype/asset_shift_allocation/asset_shift_allocation.js new file mode 100644 index 0000000000..c22feb0545 --- /dev/null +++ b/erpnext/assets/doctype/asset_shift_allocation/asset_shift_allocation.js @@ -0,0 +1,14 @@ +// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +frappe.ui.form.on("Asset Shift Allocation", { + onload: function (frm) { + frm.events.make_schedules_editable(frm); + }, + + make_schedules_editable: function (frm) { + frm.toggle_enable("depreciation_schedule", true); + frm.fields_dict["depreciation_schedule"].grid.toggle_enable("schedule_date", false); + frm.fields_dict["depreciation_schedule"].grid.toggle_enable("depreciation_amount", false); + frm.fields_dict["depreciation_schedule"].grid.toggle_enable("shift", true); + }, +}); diff --git a/erpnext/assets/doctype/asset_shift_allocation/asset_shift_allocation.json b/erpnext/assets/doctype/asset_shift_allocation/asset_shift_allocation.json new file mode 100644 index 0000000000..925f3b4b63 --- /dev/null +++ b/erpnext/assets/doctype/asset_shift_allocation/asset_shift_allocation.json @@ -0,0 +1,111 @@ +{ + "actions": [], + "autoname": "naming_series:", + "creation": "2023-11-24 15:07:44.652133", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "section_break_esaa", + "asset", + "naming_series", + "column_break_tdae", + "finance_book", + "amended_from", + "depreciation_schedule_section", + "depreciation_schedule" + ], + "fields": [ + { + "fieldname": "section_break_esaa", + "fieldtype": "Section Break" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Asset Shift Allocation", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "asset", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Asset", + "options": "Asset", + "reqd": 1 + }, + { + "fieldname": "column_break_tdae", + "fieldtype": "Column Break" + }, + { + "fieldname": "finance_book", + "fieldtype": "Link", + "label": "Finance Book", + "options": "Finance Book" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "depreciation_schedule_section", + "fieldtype": "Section Break", + "label": "Depreciation Schedule" + }, + { + "fieldname": "depreciation_schedule", + "fieldtype": "Table", + "label": "Depreciation Schedule", + "options": "Depreciation Schedule" + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "options": "ACC-ASA-.YYYY.-", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2024-03-27 13:06:35.732191", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Shift Allocation", + "naming_rule": "By \"Naming Series\" field", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "creation", + "sort_order": "DESC", + "states": [] +} diff --git a/erpnext/assets/doctype/asset_shift_allocation/asset_shift_allocation.py b/erpnext/assets/doctype/asset_shift_allocation/asset_shift_allocation.py new file mode 100644 index 0000000000..323cb73fb8 --- /dev/null +++ b/erpnext/assets/doctype/asset_shift_allocation/asset_shift_allocation.py @@ -0,0 +1,273 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import ( + add_months, + cint, + flt, + get_last_day, + get_link_to_form, + is_last_day_of_the_month, +) + +from erpnext.assets.doctype.asset_activity.asset_activity import add_asset_activity +from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import ( + get_asset_depr_schedule_doc, + get_asset_shift_factors_map, + get_temp_asset_depr_schedule_doc, +) + + +class AssetShiftAllocation(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + from erpnext.assets.doctype.depreciation_schedule.depreciation_schedule import ( + DepreciationSchedule, + ) + + amended_from: DF.Link | None + asset: DF.Link + depreciation_schedule: DF.Table[DepreciationSchedule] + finance_book: DF.Link | None + naming_series: DF.Literal["ACC-ASA-.YYYY.-"] + # end: auto-generated types + + def after_insert(self): + self.fetch_and_set_depr_schedule() + + def validate(self): + self.asset_depr_schedule_doc = get_asset_depr_schedule_doc(self.asset, "Active", self.finance_book) + + self.validate_invalid_shift_change() + self.update_depr_schedule() + + def on_submit(self): + self.create_new_asset_depr_schedule() + + def fetch_and_set_depr_schedule(self): + if self.asset_depr_schedule_doc: + if self.asset_depr_schedule_doc.shift_based: + for schedule in self.asset_depr_schedule_doc.get("depreciation_schedule"): + self.append( + "depreciation_schedule", + { + "schedule_date": schedule.schedule_date, + "depreciation_amount": schedule.depreciation_amount, + "accumulated_depreciation_amount": schedule.accumulated_depreciation_amount, + "journal_entry": schedule.journal_entry, + "shift": schedule.shift, + }, + ) + + self.flags.ignore_validate = True + self.save() + else: + frappe.throw( + _( + "Asset Depreciation Schedule for Asset {0} and Finance Book {1} is not using shift based depreciation" + ).format(self.asset, self.finance_book) + ) + else: + frappe.throw( + _("Asset Depreciation Schedule not found for Asset {0} and Finance Book {1}").format( + self.asset, self.finance_book + ) + ) + + def validate_invalid_shift_change(self): + if not self.get("depreciation_schedule") or self.docstatus == 1: + return + + for i, sch in enumerate(self.depreciation_schedule): + if sch.journal_entry and self.asset_depr_schedule_doc.depreciation_schedule[i].shift != sch.shift: + frappe.throw( + _( + "Row {0}: Shift cannot be changed since the depreciation has already been processed" + ).format(i) + ) + + def update_depr_schedule(self): + if not self.get("depreciation_schedule") or self.docstatus == 1: + return + + self.allocate_shift_diff_in_depr_schedule() + + asset_doc = frappe.get_doc("Asset", self.asset) + fb_row = asset_doc.finance_books[self.asset_depr_schedule_doc.finance_book_id - 1] + + asset_doc.flags.shift_allocation = True + + temp_depr_schedule = get_temp_asset_depr_schedule_doc( + asset_doc, fb_row, new_depr_schedule=self.depreciation_schedule + ).get("depreciation_schedule") + + self.depreciation_schedule = [] + + for schedule in temp_depr_schedule: + self.append( + "depreciation_schedule", + { + "schedule_date": schedule.schedule_date, + "depreciation_amount": schedule.depreciation_amount, + "accumulated_depreciation_amount": schedule.accumulated_depreciation_amount, + "journal_entry": schedule.journal_entry, + "shift": schedule.shift, + }, + ) + + def allocate_shift_diff_in_depr_schedule(self): + asset_shift_factors_map = get_asset_shift_factors_map() + reverse_asset_shift_factors_map = {asset_shift_factors_map[k]: k for k in asset_shift_factors_map} + + original_shift_factors_sum = sum( + flt(asset_shift_factors_map.get(schedule.shift)) + for schedule in self.asset_depr_schedule_doc.depreciation_schedule + ) + + new_shift_factors_sum = sum( + flt(asset_shift_factors_map.get(schedule.shift)) for schedule in self.depreciation_schedule + ) + + diff = new_shift_factors_sum - original_shift_factors_sum + + if diff > 0: + for i, schedule in reversed(list(enumerate(self.depreciation_schedule))): + if diff <= 0: + break + + shift_factor = flt(asset_shift_factors_map.get(schedule.shift)) + + if shift_factor <= diff: + self.depreciation_schedule.pop() + diff -= shift_factor + else: + try: + self.depreciation_schedule[i].shift = reverse_asset_shift_factors_map.get( + shift_factor - diff + ) + diff = 0 + except Exception: + frappe.throw( + _("Could not auto update shifts. Shift with shift factor {0} needed.") + ).format(shift_factor - diff) + elif diff < 0: + shift_factors = list(asset_shift_factors_map.values()) + desc_shift_factors = sorted(shift_factors, reverse=True) + depr_schedule_len_diff = self.asset_depr_schedule_doc.total_number_of_depreciations - len( + self.depreciation_schedule + ) + subsets_result = [] + + if depr_schedule_len_diff > 0: + num_rows_to_add = depr_schedule_len_diff + + while not subsets_result and num_rows_to_add > 0: + find_subsets_with_sum(shift_factors, num_rows_to_add, abs(diff), [], subsets_result) + if subsets_result: + break + num_rows_to_add -= 1 + + if subsets_result: + for i in range(num_rows_to_add): + schedule_date = add_months( + self.depreciation_schedule[-1].schedule_date, + cint(self.asset_depr_schedule_doc.frequency_of_depreciation), + ) + + if is_last_day_of_the_month(self.depreciation_schedule[-1].schedule_date): + schedule_date = get_last_day(schedule_date) + + self.append( + "depreciation_schedule", + { + "schedule_date": schedule_date, + "shift": reverse_asset_shift_factors_map.get(subsets_result[0][i]), + }, + ) + + if depr_schedule_len_diff <= 0 or not subsets_result: + for i, schedule in reversed(list(enumerate(self.depreciation_schedule))): + diff = abs(diff) + + if diff <= 0: + break + + shift_factor = flt(asset_shift_factors_map.get(schedule.shift)) + + if shift_factor <= diff: + for sf in desc_shift_factors: + if sf - shift_factor <= diff: + self.depreciation_schedule[i].shift = reverse_asset_shift_factors_map.get(sf) + diff -= sf - shift_factor + break + else: + try: + self.depreciation_schedule[i].shift = reverse_asset_shift_factors_map.get( + shift_factor + diff + ) + diff = 0 + except Exception: + frappe.throw( + _("Could not auto update shifts. Shift with shift factor {0} needed.") + ).format(shift_factor + diff) + + def create_new_asset_depr_schedule(self): + new_asset_depr_schedule_doc = frappe.copy_doc(self.asset_depr_schedule_doc) + + new_asset_depr_schedule_doc.depreciation_schedule = [] + + for schedule in self.depreciation_schedule: + new_asset_depr_schedule_doc.append( + "depreciation_schedule", + { + "schedule_date": schedule.schedule_date, + "depreciation_amount": schedule.depreciation_amount, + "accumulated_depreciation_amount": schedule.accumulated_depreciation_amount, + "journal_entry": schedule.journal_entry, + "shift": schedule.shift, + }, + ) + + notes = _( + "This schedule was created when Asset {0}'s shifts were adjusted through Asset Shift Allocation {1}." + ).format( + get_link_to_form("Asset", self.asset), + get_link_to_form(self.doctype, self.name), + ) + + new_asset_depr_schedule_doc.notes = notes + + self.asset_depr_schedule_doc.flags.should_not_cancel_depreciation_entries = True + self.asset_depr_schedule_doc.cancel() + + new_asset_depr_schedule_doc.submit() + + add_asset_activity( + self.asset, + _("Asset's depreciation schedule updated after Asset Shift Allocation {0}").format( + get_link_to_form(self.doctype, self.name) + ), + ) + + +def find_subsets_with_sum(numbers, k, target_sum, current_subset, result): + if k == 0 and target_sum == 0: + result.append(current_subset.copy()) + return + if k <= 0 or target_sum <= 0 or not numbers: + return + + # Include the current number in the subset + find_subsets_with_sum(numbers, k - 1, target_sum - numbers[0], [*current_subset, numbers[0]], result) + + # Exclude the current number from the subset + find_subsets_with_sum(numbers[1:], k, target_sum, current_subset, result) diff --git a/erpnext/assets/doctype/asset_shift_allocation/test_asset_shift_allocation.py b/erpnext/assets/doctype/asset_shift_allocation/test_asset_shift_allocation.py new file mode 100644 index 0000000000..8d00a24f6b --- /dev/null +++ b/erpnext/assets/doctype/asset_shift_allocation/test_asset_shift_allocation.py @@ -0,0 +1,113 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import cstr + +from erpnext.assets.doctype.asset.test_asset import create_asset +from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import ( + get_depr_schedule, +) + + +class TestAssetShiftAllocation(FrappeTestCase): + @classmethod + def setUpClass(cls): + create_asset_shift_factors() + + @classmethod + def tearDownClass(cls): + frappe.db.rollback() + + def test_asset_shift_allocation(self): + asset = create_asset( + calculate_depreciation=1, + available_for_use_date="2023-01-01", + purchase_date="2023-01-01", + gross_purchase_amount=120000, + depreciation_start_date="2023-01-31", + total_number_of_depreciations=12, + frequency_of_depreciation=1, + shift_based=1, + submit=1, + ) + + expected_schedules = [ + ["2023-01-31", 10000.0, 10000.0, "Single"], + ["2023-02-28", 10000.0, 20000.0, "Single"], + ["2023-03-31", 10000.0, 30000.0, "Single"], + ["2023-04-30", 10000.0, 40000.0, "Single"], + ["2023-05-31", 10000.0, 50000.0, "Single"], + ["2023-06-30", 10000.0, 60000.0, "Single"], + ["2023-07-31", 10000.0, 70000.0, "Single"], + ["2023-08-31", 10000.0, 80000.0, "Single"], + ["2023-09-30", 10000.0, 90000.0, "Single"], + ["2023-10-31", 10000.0, 100000.0, "Single"], + ["2023-11-30", 10000.0, 110000.0, "Single"], + ["2023-12-31", 10000.0, 120000.0, "Single"], + ] + + schedules = [ + [cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount, d.shift] + for d in get_depr_schedule(asset.name, "Active") + ] + + self.assertEqual(schedules, expected_schedules) + + asset_shift_allocation = frappe.get_doc( + {"doctype": "Asset Shift Allocation", "asset": asset.name} + ).insert() + + schedules = [ + [cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount, d.shift] + for d in asset_shift_allocation.get("depreciation_schedule") + ] + + self.assertEqual(schedules, expected_schedules) + + asset_shift_allocation = frappe.get_doc("Asset Shift Allocation", asset_shift_allocation.name) + asset_shift_allocation.depreciation_schedule[4].shift = "Triple" + asset_shift_allocation.save() + + schedules = [ + [cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount, d.shift] + for d in asset_shift_allocation.get("depreciation_schedule") + ] + + expected_schedules = [ + ["2023-01-31", 10000.0, 10000.0, "Single"], + ["2023-02-28", 10000.0, 20000.0, "Single"], + ["2023-03-31", 10000.0, 30000.0, "Single"], + ["2023-04-30", 10000.0, 40000.0, "Single"], + ["2023-05-31", 20000.0, 60000.0, "Triple"], + ["2023-06-30", 10000.0, 70000.0, "Single"], + ["2023-07-31", 10000.0, 80000.0, "Single"], + ["2023-08-31", 10000.0, 90000.0, "Single"], + ["2023-09-30", 10000.0, 100000.0, "Single"], + ["2023-10-31", 10000.0, 110000.0, "Single"], + ["2023-11-30", 10000.0, 120000.0, "Single"], + ] + + self.assertEqual(schedules, expected_schedules) + + asset_shift_allocation.submit() + + schedules = [ + [cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount, d.shift] + for d in get_depr_schedule(asset.name, "Active") + ] + + self.assertEqual(schedules, expected_schedules) + + +def create_asset_shift_factors(): + shifts = [ + {"doctype": "Asset Shift Factor", "shift_name": "Half", "shift_factor": 0.5, "default": 0}, + {"doctype": "Asset Shift Factor", "shift_name": "Single", "shift_factor": 1, "default": 1}, + {"doctype": "Asset Shift Factor", "shift_name": "Double", "shift_factor": 1.5, "default": 0}, + {"doctype": "Asset Shift Factor", "shift_name": "Triple", "shift_factor": 2, "default": 0}, + ] + + for s in shifts: + frappe.get_doc(s).insert() diff --git a/erpnext/assets/doctype/asset_shift_factor/__init__.py b/erpnext/assets/doctype/asset_shift_factor/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/assets/doctype/asset_shift_factor/asset_shift_factor.js b/erpnext/assets/doctype/asset_shift_factor/asset_shift_factor.js new file mode 100644 index 0000000000..88887fea87 --- /dev/null +++ b/erpnext/assets/doctype/asset_shift_factor/asset_shift_factor.js @@ -0,0 +1,8 @@ +// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Asset Shift Factor", { +// refresh(frm) { + +// }, +// }); diff --git a/erpnext/assets/doctype/asset_shift_factor/asset_shift_factor.json b/erpnext/assets/doctype/asset_shift_factor/asset_shift_factor.json new file mode 100644 index 0000000000..98757fa66b --- /dev/null +++ b/erpnext/assets/doctype/asset_shift_factor/asset_shift_factor.json @@ -0,0 +1,74 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:shift_name", + "creation": "2023-11-27 18:16:03.980086", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "shift_name", + "shift_factor", + "default" + ], + "fields": [ + { + "fieldname": "shift_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Shift Name", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "shift_factor", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Shift Factor", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "default", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Default" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-03-27 13:06:35.869900", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Shift Factor", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "write": 1 + } + ], + "sort_field": "creation", + "sort_order": "DESC", + "states": [] +} diff --git a/erpnext/assets/doctype/asset_shift_factor/asset_shift_factor.py b/erpnext/assets/doctype/asset_shift_factor/asset_shift_factor.py new file mode 100644 index 0000000000..4af7067f28 --- /dev/null +++ b/erpnext/assets/doctype/asset_shift_factor/asset_shift_factor.py @@ -0,0 +1,35 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe import _ +from frappe.model.document import Document + + +class AssetShiftFactor(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + default: DF.Check + shift_factor: DF.Float + shift_name: DF.Data + # end: auto-generated types + + def validate(self): + self.validate_default() + + def validate_default(self): + if self.default: + existing_default_shift_factor = frappe.db.get_value("Asset Shift Factor", {"default": 1}, "name") + + if existing_default_shift_factor: + frappe.throw( + _("Asset Shift Factor {0} is set as default currently. Please change it first.").format( + frappe.bold(existing_default_shift_factor) + ) + ) diff --git a/erpnext/assets/doctype/asset_shift_factor/test_asset_shift_factor.py b/erpnext/assets/doctype/asset_shift_factor/test_asset_shift_factor.py new file mode 100644 index 0000000000..75073673c0 --- /dev/null +++ b/erpnext/assets/doctype/asset_shift_factor/test_asset_shift_factor.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestAssetShiftFactor(FrappeTestCase): + pass diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js index 3620049ba0..917c678be3 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js +++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js @@ -3,57 +3,57 @@ frappe.provide("erpnext.accounts.dimensions"); -frappe.ui.form.on('Asset Value Adjustment', { - setup: function(frm) { - frm.add_fetch('company', 'cost_center', 'cost_center'); - frm.set_query('cost_center', function() { +frappe.ui.form.on("Asset Value Adjustment", { + setup: function (frm) { + frm.add_fetch("company", "cost_center", "cost_center"); + frm.set_query("cost_center", function () { return { filters: { company: frm.doc.company, - is_group: 0 - } - } + is_group: 0, + }, + }; }); - frm.set_query('asset', function() { + frm.set_query("asset", function () { return { filters: { calculate_depreciation: 1, - docstatus: 1 - } + docstatus: 1, + }, }; }); }, - onload: function(frm) { - if(frm.is_new() && frm.doc.asset) { + onload: function (frm) { + if (frm.is_new() && frm.doc.asset) { frm.trigger("set_current_asset_value"); } erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, - company: function(frm) { + company: function (frm) { erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, - asset: function(frm) { + asset: function (frm) { frm.trigger("set_current_asset_value"); }, - finance_book: function(frm) { + finance_book: function (frm) { frm.trigger("set_current_asset_value"); }, - set_current_asset_value: function(frm) { + set_current_asset_value: function (frm) { if (frm.doc.asset) { frm.call({ method: "erpnext.assets.doctype.asset.asset.get_asset_value_after_depreciation", args: { asset_name: frm.doc.asset, - finance_book: frm.doc.finance_book + finance_book: frm.doc.finance_book, }, - callback: function(r) { + callback: function (r) { if (r.message) { - frm.set_value('current_asset_value', r.message); + frm.set_value("current_asset_value", r.message); } - } + }, }); } - } + }, }); diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.json b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.json index a255825a7f..743e2a61c8 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.json +++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.json @@ -1,185 +1,186 @@ { - "actions": [], - "creation": "2018-05-11 00:22:43.695151", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "company", - "asset", - "asset_category", - "column_break_4", - "date", - "finance_book", - "amended_from", - "value_details_section", - "current_asset_value", - "new_asset_value", - "column_break_11", - "difference_amount", - "journal_entry", - "accounting_dimensions_section", - "cost_center", - "dimension_col_break" - ], - "fields": [ - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company" - }, - { - "fieldname": "asset", - "fieldtype": "Link", - "label": "Asset", - "options": "Asset", - "reqd": 1 - }, - { - "fetch_from": "asset.asset_category", - "fieldname": "asset_category", - "fieldtype": "Read Only", - "label": "Asset Category" - }, - { - "fieldname": "finance_book", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Finance Book", - "options": "Finance Book" - }, - { - "fieldname": "journal_entry", - "fieldtype": "Link", - "label": "Journal Entry", - "options": "Journal Entry", - "read_only": 1 - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "fieldname": "date", - "fieldtype": "Date", - "label": "Date", - "reqd": 1 - }, - { - "fieldname": "current_asset_value", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Current Asset Value", - "read_only": 1, - "reqd": 1 - }, - { - "fieldname": "new_asset_value", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "New Asset Value", - "reqd": 1 - }, - { - "fieldname": "difference_amount", - "fieldtype": "Currency", - "label": "Difference Amount", - "no_copy": 1, - "read_only": 1 - }, - { - "fieldname": "cost_center", - "fieldtype": "Link", - "label": "Cost Center", - "options": "Cost Center" - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Asset Value Adjustment", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "accounting_dimensions_section", - "fieldtype": "Section Break", - "label": "Accounting Dimensions" - }, - { - "fieldname": "dimension_col_break", - "fieldtype": "Column Break" - }, - { - "fieldname": "value_details_section", - "fieldtype": "Section Break", - "label": "Value Details" - }, - { - "fieldname": "column_break_11", - "fieldtype": "Column Break" - } - ], - "index_web_pages_for_search": 1, - "is_submittable": 1, - "links": [], - "modified": "2021-01-22 14:10:23.085181", - "modified_by": "Administrator", - "module": "Assets", - "name": "Asset Value Adjustment", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "asset", - "track_changes": 1 - } \ No newline at end of file + "actions": [], + "creation": "2018-05-11 00:22:43.695151", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company", + "asset", + "asset_category", + "column_break_4", + "date", + "finance_book", + "amended_from", + "value_details_section", + "current_asset_value", + "new_asset_value", + "column_break_11", + "difference_amount", + "journal_entry", + "accounting_dimensions_section", + "cost_center", + "dimension_col_break" + ], + "fields": [ + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + }, + { + "fieldname": "asset", + "fieldtype": "Link", + "label": "Asset", + "options": "Asset", + "reqd": 1 + }, + { + "fetch_from": "asset.asset_category", + "fieldname": "asset_category", + "fieldtype": "Read Only", + "label": "Asset Category" + }, + { + "fieldname": "finance_book", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Finance Book", + "options": "Finance Book" + }, + { + "fieldname": "journal_entry", + "fieldtype": "Link", + "label": "Journal Entry", + "options": "Journal Entry", + "read_only": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "date", + "fieldtype": "Date", + "label": "Date", + "reqd": 1 + }, + { + "fieldname": "current_asset_value", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Current Asset Value", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "new_asset_value", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "New Asset Value", + "reqd": 1 + }, + { + "fieldname": "difference_amount", + "fieldtype": "Currency", + "label": "Difference Amount", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Asset Value Adjustment", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "value_details_section", + "fieldtype": "Section Break", + "label": "Value Details" + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2024-03-27 13:06:36.004049", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Value Adjustment", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "creation", + "sort_order": "DESC", + "states": [], + "title_field": "asset", + "track_changes": 1 +} diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py index 9be7243602..eaf7ef3332 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py +++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py @@ -77,7 +77,9 @@ class AssetValueAdjustment(Document): je.naming_series = depreciation_series je.posting_date = self.date je.company = self.company - je.remark = "Depreciation Entry against {0} worth {1}".format(self.asset, self.difference_amount) + je.remark = frappe._("Depreciation Entry against {} worth {}").format( + self.asset, self.difference_amount + ) je.finance_book = self.finance_book credit_entry = { diff --git a/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.py index 2c97baece5..634ed41377 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.py +++ b/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.py @@ -23,9 +23,7 @@ class TestAssetValueAdjustment(unittest.TestCase): ) def test_current_asset_value(self): - pr = make_purchase_receipt( - item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location" - ) + pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location") asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name") asset_doc = frappe.get_doc("Asset", asset_name) @@ -52,9 +50,7 @@ class TestAssetValueAdjustment(unittest.TestCase): self.assertEqual(current_value, 100000.0) def test_asset_depreciation_value_adjustment(self): - pr = make_purchase_receipt( - item_code="Macbook Pro", qty=1, rate=120000.0, location="Test Location" - ) + pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=120000.0, location="Test Location") asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name") asset_doc = frappe.get_doc("Asset", asset_name) @@ -75,7 +71,7 @@ class TestAssetValueAdjustment(unittest.TestCase): asset_doc.submit() first_asset_depr_schedule = get_asset_depr_schedule_doc(asset_doc.name, "Active") - self.assertEquals(first_asset_depr_schedule.status, "Active") + self.assertEqual(first_asset_depr_schedule.status, "Active") post_depreciation_entries(getdate("2023-08-21")) @@ -92,8 +88,8 @@ class TestAssetValueAdjustment(unittest.TestCase): first_asset_depr_schedule.load_from_db() second_asset_depr_schedule = get_asset_depr_schedule_doc(asset_doc.name, "Active") - self.assertEquals(second_asset_depr_schedule.status, "Active") - self.assertEquals(first_asset_depr_schedule.status, "Cancelled") + self.assertEqual(second_asset_depr_schedule.status, "Active") + self.assertEqual(first_asset_depr_schedule.status, "Cancelled") expected_gle = ( ("_Test Accumulated Depreciations - _TC", 0.0, 4625.29), diff --git a/erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.json b/erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.json index 3c5e5ced35..ad09c36948 100644 --- a/erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.json +++ b/erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.json @@ -12,6 +12,7 @@ "column_break_3", "accumulated_depreciation_amount", "journal_entry", + "shift", "make_depreciation_entry" ], "fields": [ @@ -57,18 +58,24 @@ "fieldname": "make_depreciation_entry", "fieldtype": "Button", "label": "Make Depreciation Entry" + }, + { + "fieldname": "shift", + "fieldtype": "Link", + "label": "Shift", + "options": "Asset Shift Factor" } ], "istable": 1, "links": [], - "modified": "2023-07-26 12:56:48.718736", + "modified": "2024-03-27 13:06:51.227001", "modified_by": "Administrator", "module": "Assets", "name": "Depreciation Schedule", "owner": "Administrator", "permissions": [], "quick_entry": 1, - "sort_field": "modified", + "sort_field": "creation", "sort_order": "DESC", "states": [] } diff --git a/erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.py b/erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.py index b597c58752..41aade6591 100644 --- a/erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.py +++ b/erpnext/assets/doctype/depreciation_schedule/depreciation_schedule.py @@ -6,4 +6,22 @@ from frappe.model.document import Document class DepreciationSchedule(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + accumulated_depreciation_amount: DF.Currency + depreciation_amount: DF.Currency + journal_entry: DF.Link | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + schedule_date: DF.Date + shift: DF.Link | None + # end: auto-generated types + pass diff --git a/erpnext/assets/doctype/linked_location/linked_location.json b/erpnext/assets/doctype/linked_location/linked_location.json index f04a79e593..455ef61629 100644 --- a/erpnext/assets/doctype/linked_location/linked_location.json +++ b/erpnext/assets/doctype/linked_location/linked_location.json @@ -1,74 +1,33 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-11-22 14:34:59.461273", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2017-11-22 14:34:59.461273", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "location" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "location", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Location", - "length": 0, - "no_copy": 0, - "options": "Location", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "location", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Location", + "options": "Location", + "reqd": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-06-20 04:35:59.514281", - "modified_by": "Administrator", - "module": "Assets", - "name": "Linked Location", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file + ], + "istable": 1, + "links": [], + "modified": "2024-03-27 13:10:02.464102", + "modified_by": "Administrator", + "module": "Assets", + "name": "Linked Location", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "creation", + "sort_order": "DESC", + "states": [], + "track_changes": 1 +} diff --git a/erpnext/assets/doctype/linked_location/linked_location.py b/erpnext/assets/doctype/linked_location/linked_location.py index 5acba70505..35e6d8b41d 100644 --- a/erpnext/assets/doctype/linked_location/linked_location.py +++ b/erpnext/assets/doctype/linked_location/linked_location.py @@ -2,9 +2,22 @@ # For license information, please see license.txt -import frappe from frappe.model.document import Document class LinkedLocation(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + location: DF.Link + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + pass diff --git a/erpnext/assets/doctype/location/location.js b/erpnext/assets/doctype/location/location.js index 0f069b2fd8..97d7239158 100644 --- a/erpnext/assets/doctype/location/location.js +++ b/erpnext/assets/doctype/location/location.js @@ -1,13 +1,13 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Location', { +frappe.ui.form.on("Location", { setup: function (frm) { frm.set_query("parent_location", function () { return { - "filters": { - "is_group": 1 - } + filters: { + is_group: 1, + }, }; }); }, @@ -15,10 +15,9 @@ frappe.ui.form.on('Location', { onload_post_render(frm) { if (!frm.doc.location && frm.doc.latitude && frm.doc.longitude) { frm.fields_dict.location.map.setView([frm.doc.latitude, frm.doc.longitude], 13); - } - else { - frm.doc.latitude = frm.fields_dict.location.map.getCenter()['lat']; - frm.doc.longitude = frm.fields_dict.location.map.getCenter()['lng']; + } else { + frm.doc.latitude = frm.fields_dict.location.map.getCenter()["lat"]; + frm.doc.longitude = frm.fields_dict.location.map.getCenter()["lng"]; } }, }); diff --git a/erpnext/assets/doctype/location/location.json b/erpnext/assets/doctype/location/location.json index 47dd7784c7..64fc4f52e4 100644 --- a/erpnext/assets/doctype/location/location.json +++ b/erpnext/assets/doctype/location/location.json @@ -142,7 +142,7 @@ ], "is_tree": 1, "links": [], - "modified": "2023-08-29 12:49:33.290527", + "modified": "2024-03-27 13:10:02.572355", "modified_by": "Administrator", "module": "Assets", "name": "Location", @@ -197,11 +197,33 @@ "role": "Stock Manager", "share": 1, "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Agriculture Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Agriculture User", + "share": 1, + "write": 1 } ], "quick_entry": 1, "show_name_in_global_search": 1, - "sort_field": "modified", + "sort_field": "creation", "sort_order": "DESC", "states": [], "track_changes": 1 diff --git a/erpnext/assets/doctype/location/location.py b/erpnext/assets/doctype/location/location.py index 5bff3dd8c9..03d0980b03 100644 --- a/erpnext/assets/doctype/location/location.py +++ b/erpnext/assets/doctype/location/location.py @@ -13,6 +13,27 @@ EARTH_RADIUS = 6378137 class Location(NestedSet): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + area: DF.Float + area_uom: DF.Link | None + is_container: DF.Check + is_group: DF.Check + latitude: DF.Float + lft: DF.Int + location_name: DF.Data + longitude: DF.Float + old_parent: DF.Data | None + parent_location: DF.Link | None + rgt: DF.Int + # end: auto-generated types + nsm_parent_field = "parent_location" def validate(self): @@ -195,17 +216,15 @@ def get_children(doctype, parent=None, location=None, is_root=False): parent = "" return frappe.db.sql( - """ + f""" select name as value, is_group as expandable from `tabLocation` comp where - ifnull(parent_location, "")={parent} - """.format( - parent=frappe.db.escape(parent) - ), + ifnull(parent_location, "")={frappe.db.escape(parent)} + """, as_dict=1, ) diff --git a/erpnext/assets/doctype/location/location_tree.js b/erpnext/assets/doctype/location/location_tree.js index b405afd1dd..c3484c2469 100644 --- a/erpnext/assets/doctype/location/location_tree.js +++ b/erpnext/assets/doctype/location/location_tree.js @@ -1,7 +1,7 @@ frappe.treeview_settings["Location"] = { ignore_fields: ["parent_location"], - get_tree_nodes: 'erpnext.assets.doctype.location.location.get_children', - add_tree_node: 'erpnext.assets.doctype.location.location.add_node', + get_tree_nodes: "erpnext.assets.doctype.location.location.get_children", + add_tree_node: "erpnext.assets.doctype.location.location.add_node", filters: [ { fieldname: "location", @@ -10,9 +10,9 @@ frappe.treeview_settings["Location"] = { label: __("Location"), get_query: function () { return { - filters: [["Location", "is_group", "=", 1]] + filters: [["Location", "is_group", "=", 1]], }; - } + }, }, ], breadcrumb: "Assets", @@ -24,10 +24,10 @@ frappe.treeview_settings["Location"] = { action: function () { frappe.new_doc("Location", true); }, - condition: 'frappe.boot.user.can_create.indexOf("Location") !== -1' - } + condition: 'frappe.boot.user.can_create.indexOf("Location") !== -1', + }, ], onload: function (treeview) { treeview.make_tree(); - } -}; \ No newline at end of file + }, +}; diff --git a/erpnext/assets/doctype/location/test_location.py b/erpnext/assets/doctype/location/test_location.py index 27ba6345fb..3b5af61fd4 100644 --- a/erpnext/assets/doctype/location/test_location.py +++ b/erpnext/assets/doctype/location/test_location.py @@ -1,7 +1,6 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt - import json import unittest @@ -32,9 +31,7 @@ class TestLocation(unittest.TestCase): ordered_test_location_features = sorted( test_location_features, key=lambda x: x["properties"]["feature_of"] ) - ordered_formatted_locations = sorted( - formatted_locations, key=lambda x: x["properties"]["feature_of"] - ) + ordered_formatted_locations = sorted(formatted_locations, key=lambda x: x["properties"]["feature_of"]) self.assertEqual(ordered_formatted_locations, ordered_test_location_features) self.assertEqual(area, test_location.get("area")) diff --git a/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.js b/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.js index 2db712546c..6f3521ffd8 100644 --- a/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.js +++ b/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.js @@ -1,8 +1,6 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Maintenance Team Member', { - refresh: function() { - - } +frappe.ui.form.on("Maintenance Team Member", { + refresh: function () {}, }); diff --git a/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.json b/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.json index 5155b0b426..f13bbbbf17 100644 --- a/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.json +++ b/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.json @@ -1,140 +1,57 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:team_member", - "beta": 0, - "creation": "2016-10-26 10:56:04.534717", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "field:team_member", + "creation": "2016-10-26 10:56:04.534717", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "team_member", + "full_name", + "maintenance_role" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "team_member", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Team Member", - "length": 0, - "no_copy": 0, - "options": "User", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "team_member", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Team Member", + "options": "User", + "reqd": 1, + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", "fetch_from": "team_member.full_name", - "fieldname": "full_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Full Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "full_name", + "fieldtype": "Data", + "in_filter": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Full Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "maintenance_role", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Maintenance Role", - "length": 0, - "no_copy": 0, - "options": "Role", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "maintenance_role", + "fieldtype": "Link", + "in_filter": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Maintenance Role", + "options": "Role", + "reqd": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-05-16 22:43:15.255185", - "modified_by": "Administrator", - "module": "Assets", - "name": "Maintenance Team Member", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 -} \ No newline at end of file + ], + "istable": 1, + "links": [], + "modified": "2024-03-27 13:10:04.127108", + "modified_by": "Administrator", + "module": "Assets", + "name": "Maintenance Team Member", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "creation", + "sort_order": "DESC", + "states": [] +} diff --git a/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.py b/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.py index 670731fb55..80a6a563b8 100644 --- a/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.py +++ b/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.py @@ -2,9 +2,24 @@ # For license information, please see license.txt -import frappe from frappe.model.document import Document class MaintenanceTeamMember(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + full_name: DF.Data | None + maintenance_role: DF.Link + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + team_member: DF.Link + # end: auto-generated types + pass diff --git a/erpnext/assets/doctype/maintenance_team_member/test_maintenance_team_member.py b/erpnext/assets/doctype/maintenance_team_member/test_maintenance_team_member.py index a955b38567..911a6544e9 100644 --- a/erpnext/assets/doctype/maintenance_team_member/test_maintenance_team_member.py +++ b/erpnext/assets/doctype/maintenance_team_member/test_maintenance_team_member.py @@ -1,7 +1,6 @@ # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt - import unittest diff --git a/erpnext/regional/france/assets.py b/erpnext/regional/france/assets.py index 2a17b42877..74215922fc 100644 --- a/erpnext/regional/france/assets.py +++ b/erpnext/regional/france/assets.py @@ -1,25 +1,34 @@ from frappe.utils import add_months, cint, date_diff, flt, get_last_day, getdate +from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import ( + get_straight_line_or_manual_depr_amount, +) + def get_depreciation_amount( - asset_depr_schedule, asset, depreciable_value, yearly_opening_wdv, row, *args, **kwargs + asset_depr_schedule, + asset, + depreciable_value, + yearly_opening_wdv, + fb_row, + schedule_idx=0, + prev_depreciation_amount=0, + has_wdv_or_dd_non_yearly_pro_rata=False, + number_of_pending_depreciations=0, + prev_per_day_depr=0, ): - if row.depreciation_method in ("Straight Line", "Manual"): + if fb_row.depreciation_method in ("Straight Line", "Manual"): # if the Depreciation Schedule is being prepared for the first time if not asset.flags.increase_in_asset_life: depreciation_amount = ( - flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life) - ) / flt(row.total_number_of_depreciations) + flt(asset.gross_purchase_amount) - flt(fb_row.expected_value_after_useful_life) + ) / flt(fb_row.total_number_of_depreciations) - # if the Depreciation Schedule is being modified after Asset Repair - else: - depreciation_amount = ( - flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) - ) / (date_diff(asset.to_date, asset.available_for_use_date) / 365) - else: - depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100)) + return depreciation_amount, None - return depreciation_amount + return get_straight_line_or_manual_depr_amount( + asset_depr_schedule, asset, fb_row, schedule_idx, number_of_pending_depreciations + ), None def get_total_days(date, frequency): -- GitLab