From 8a42bfd7305d3b3ee41bcf6bf94b6cdc64c572e0 Mon Sep 17 00:00:00 2001 From: Simone Rubino Date: Wed, 10 Jul 2024 12:26:28 +0200 Subject: [PATCH] [IMP] l10n_it_asset_management: Recharge asset --- l10n_it_asset_management/README.rst | 13 +- .../models/account_move.py | 1 + .../models/asset_accounting_info.py | 1 + .../models/asset_depreciation.py | 2 +- .../models/asset_depreciation_line.py | 20 ++- l10n_it_asset_management/readme/USAGE.md | 6 + .../report/asset_journal.py | 4 +- .../report/asset_previsional.py | 8 +- .../static/description/index.html | 11 +- l10n_it_asset_management/tests/common.py | 21 +++ .../tests/test_assets_management.py | 131 ++++++++++++++ .../views/account_move.xml | 12 +- .../views/asset_depreciation.xml | 16 +- .../wizard/account_move_manage_asset.py | 170 +++++++++++++++++- .../wizard/account_move_manage_asset_view.xml | 39 ++++ 15 files changed, 435 insertions(+), 20 deletions(-) diff --git a/l10n_it_asset_management/README.rst b/l10n_it_asset_management/README.rst index 85c7af87ccd3..698730a2cefa 100644 --- a/l10n_it_asset_management/README.rst +++ b/l10n_it_asset_management/README.rst @@ -50,6 +50,11 @@ Depreciations can be generated by using the related wizard found in Assets -> Assets Management -> Generate Depreciations, or by triggering the same wizard from a single asset form view. +When an asset is returned, it is possible to recharge its purchase and +fund amounts choosing "Partial Recharge" in the "Link to Asset" wizard +of the credit note. The wizard will allow to link to the credit note +only the assets of the refunded invoice. + **Italiano** È possibile creare e gestire cespiti dalla sezione contabilità di Odoo. @@ -63,6 +68,11 @@ contabili. Gli ammortamenti possono essere generati utilizzando l'apposito wizard in Cespiti -> Gestione Cestpiti -> Genera Ammortamenti, o aprendo quello stesso wizard dalla scheda del cespite. +Quando un cespite viene restituito, è possibile ricaricare gli importi +di acquisto e di fondo scegliendo "Ricarica parziale" nella procedura +"Collega a cespite" della nota di credito. La procedura consentirà di +collegare alla nota di credito solo i cespiti della fattura rimborsata. + Bug Tracker =========== @@ -99,8 +109,7 @@ Contributors - Nextev Srl Base icon made by `surang `__ -from -[`www.flaticon.com](https://www.flaticon.com/) `__. +from `www.flaticon.com `__. Maintainers ----------- diff --git a/l10n_it_asset_management/models/account_move.py b/l10n_it_asset_management/models/account_move.py index 6f13756a8516..048384155e8f 100644 --- a/l10n_it_asset_management/models/account_move.py +++ b/l10n_it_asset_management/models/account_move.py @@ -107,6 +107,7 @@ def open_wizard_manage_asset(self): { "default_company_id": self.company_id.id, "default_dismiss_date": self.invoice_date or self.invoice_date_due, + "default_recharge_date": self.invoice_date or self.invoice_date_due, "default_move_ids": [Command.set(self.ids)], "default_move_line_ids": [Command.set(lines.ids)], "default_purchase_date": self.invoice_date or self.invoice_date_due, diff --git a/l10n_it_asset_management/models/asset_accounting_info.py b/l10n_it_asset_management/models/asset_accounting_info.py index 94e3a7c03bfa..bd0d26a710e0 100644 --- a/l10n_it_asset_management/models/asset_accounting_info.py +++ b/l10n_it_asset_management/models/asset_accounting_info.py @@ -51,6 +51,7 @@ class AssetAccountingInfo(models.Model): relation_type = fields.Selection( [ ("create", "Asset Creation"), + ("partial_recharge", "Partial Recharge"), ("update", "Asset Update"), ("partial_dismiss", "Asset Partial Dismissal"), ("dismiss", "Asset Dismissal"), diff --git a/l10n_it_asset_management/models/asset_depreciation.py b/l10n_it_asset_management/models/asset_depreciation.py index 78ef4ebbb9ed..ddaf11370e31 100644 --- a/l10n_it_asset_management/models/asset_depreciation.py +++ b/l10n_it_asset_management/models/asset_depreciation.py @@ -297,7 +297,7 @@ def _compute_last_depreciation_date(self): for dep in self: dep_lines = dep.line_ids.filtered( lambda line: line.move_type == "depreciated" - and not line.partial_dismissal + and not (line.partial_dismissal or line.partial_recharge) ) if dep_lines: dep.last_depreciation_date = max(dep_lines.mapped("date")) diff --git a/l10n_it_asset_management/models/asset_depreciation_line.py b/l10n_it_asset_management/models/asset_depreciation_line.py index 9f13c11eb15c..95623661a4a5 100644 --- a/l10n_it_asset_management/models/asset_depreciation_line.py +++ b/l10n_it_asset_management/models/asset_depreciation_line.py @@ -100,6 +100,7 @@ class AssetDepreciationLine(models.Model): ) partial_dismissal = fields.Boolean() + partial_recharge = fields.Boolean() percentage = fields.Float(string="%") @@ -265,9 +266,8 @@ def get_update_move_types(self): def is_depreciation_nr_required(self): """Defines if a line requires to be numbered""" self.ensure_one() - return ( - self.move_type in self.get_numbered_move_types() - and not self.partial_dismissal + return self.move_type in self.get_numbered_move_types() and not ( + self.partial_dismissal or self.partial_recharge ) def make_name(self): @@ -398,15 +398,22 @@ def get_depreciated_account_move_line_vals(self): self.ensure_one() # Asset depreciation - if not self.partial_dismissal: + if not (self.partial_dismissal or self.partial_recharge): credit_account_id = self.asset_id.category_id.fund_account_id.id debit_account_id = self.depreciation_id.depreciation_account_id.id - # Asset partial dismissal else: + # Asset partial dismissal debit_account_id = self.asset_id.category_id.fund_account_id.id credit_account_id = self.asset_id.category_id.asset_account_id.id + # Asset partial recharge + if self.partial_recharge: + credit_account_id, debit_account_id = ( + debit_account_id, + credit_account_id, + ) + amt = abs(self.amount) credit_line_vals = { "account_id": credit_account_id, @@ -499,3 +506,6 @@ def post_partial_dismiss_asset(self): ) if to_create_move: to_create_move.generate_account_move() + + def post_partial_recharge_asset(self): + return self.post_partial_dismiss_asset() diff --git a/l10n_it_asset_management/readme/USAGE.md b/l10n_it_asset_management/readme/USAGE.md index 75a337b11f92..809dc498e223 100644 --- a/l10n_it_asset_management/readme/USAGE.md +++ b/l10n_it_asset_management/readme/USAGE.md @@ -10,6 +10,9 @@ Depreciations can be generated by using the related wizard found in Assets -\> Assets Management -\> Generate Depreciations, or by triggering the same wizard from a single asset form view. +When an asset is returned, it is possible to recharge its purchase and fund amounts choosing "Partial Recharge" in the "Link to Asset" wizard of the credit note. +The wizard will allow to link to the credit note only the assets of the refunded invoice. + **Italiano** È possibile creare e gestire cespiti dalla sezione contabilità di Odoo. @@ -22,3 +25,6 @@ I cespiti possono essere creati manualmente o da fatture e registrazioni contabili. Gli ammortamenti possono essere generati utilizzando l'apposito wizard in Cespiti -\> Gestione Cestpiti -\> Genera Ammortamenti, o aprendo quello stesso wizard dalla scheda del cespite. + +Quando un cespite viene restituito, è possibile ricaricare gli importi di acquisto e di fondo scegliendo "Ricarica parziale" nella procedura "Collega a cespite" della nota di credito. +La procedura consentirà di collegare alla nota di credito solo i cespiti della fattura rimborsata. diff --git a/l10n_it_asset_management/report/asset_journal.py b/l10n_it_asset_management/report/asset_journal.py index f00a7ece325a..fa9abe8fbfd0 100644 --- a/l10n_it_asset_management/report/asset_journal.py +++ b/l10n_it_asset_management/report/asset_journal.py @@ -728,7 +728,7 @@ def get_report_dep_line_year_data(self): line.amount for line in self.dep_line_ids.filtered( lambda line: line.move_type == "depreciated" - and not line.partial_dismissal + and not (line.partial_dismissal or line.partial_recharge) ) ] ) @@ -737,7 +737,7 @@ def get_report_dep_line_year_data(self): line.amount for line in self.dep_line_ids.filtered( lambda line: line.move_type == "depreciated" - and line.partial_dismissal + and (line.partial_dismissal or line.partial_recharge) ) ] ) diff --git a/l10n_it_asset_management/report/asset_previsional.py b/l10n_it_asset_management/report/asset_previsional.py index efe2d0e9aabf..a2cb7b52f564 100644 --- a/l10n_it_asset_management/report/asset_previsional.py +++ b/l10n_it_asset_management/report/asset_previsional.py @@ -245,7 +245,9 @@ def generate_structure(self): if fyear.date_to >= dep.date_start: prev = not lines or not any( line.move_type == "depreciated" - and not line.partial_dismissal + and not ( + line.partial_dismissal or line.partial_recharge + ) for line in lines ) sequence += 1 @@ -797,7 +799,7 @@ def get_report_dep_line_year_data(self): line.amount for line in self.dep_line_ids.filtered( lambda line: line.move_type == "depreciated" - and not line.partial_dismissal + and not (line.partial_dismissal or line.partial_recharge) ) ] ) @@ -806,7 +808,7 @@ def get_report_dep_line_year_data(self): line.amount for line in self.dep_line_ids.filtered( lambda line: line.move_type == "depreciated" - and line.partial_dismissal + and (line.partial_dismissal or line.partial_recharge) ) ] ) diff --git a/l10n_it_asset_management/static/description/index.html b/l10n_it_asset_management/static/description/index.html index bb6ce773a1f5..e4c0c4685749 100644 --- a/l10n_it_asset_management/static/description/index.html +++ b/l10n_it_asset_management/static/description/index.html @@ -394,6 +394,10 @@

Usage

Depreciations can be generated by using the related wizard found in Assets -> Assets Management -> Generate Depreciations, or by triggering the same wizard from a single asset form view.

+

When an asset is returned, it is possible to recharge its purchase and +fund amounts choosing “Partial Recharge” in the “Link to Asset” wizard +of the credit note. The wizard will allow to link to the credit note +only the assets of the refunded invoice.

Italiano

È possibile creare e gestire cespiti dalla sezione contabilità di Odoo.

La configurazione dei cespiti dev’essere fatta andando in Cespiti -> @@ -403,6 +407,10 @@

Usage

contabili. Gli ammortamenti possono essere generati utilizzando l’apposito wizard in Cespiti -> Gestione Cestpiti -> Genera Ammortamenti, o aprendo quello stesso wizard dalla scheda del cespite.

+

Quando un cespite viene restituito, è possibile ricaricare gli importi +di acquisto e di fondo scegliendo “Ricarica parziale” nella procedura +“Collega a cespite” della nota di credito. La procedura consentirà di +collegare alla nota di credito solo i cespiti della fattura rimborsata.

Bug Tracker

@@ -438,8 +446,7 @@

Contributors

  • Nextev Srl <odoo@nextev.it>
  • Base icon made by surang -from -[www.flaticon.com](https://www.flaticon.com/).

    +from www.flaticon.com.

    Maintainers

    diff --git a/l10n_it_asset_management/tests/common.py b/l10n_it_asset_management/tests/common.py index b307a145b18d..3d49912f452f 100644 --- a/l10n_it_asset_management/tests/common.py +++ b/l10n_it_asset_management/tests/common.py @@ -292,6 +292,27 @@ def _create_entry(self, account, amount, post=True): self.assertEqual(entry.move_type, "entry") return entry + def _refund_move(self, move, method="cancel", ref_date=None): + reverse_context = { + "active_model": move._name, + "active_ids": move.ids, + } + refund_wizard_form = Form( + self.env["account.move.reversal"].with_context(**reverse_context) + ) + refund_wizard_form.reason = "test" + if ref_date: + refund_wizard_form.date_mode = "custom" + refund_wizard_form.date = ref_date + refund_wizard_form.refund_method = method + refund_wizard = refund_wizard_form.save() + + refund_action = refund_wizard.reverse_moves() + refund_move = self.env[refund_action["res_model"]].browse( + refund_action["res_id"] + ) + return refund_move + def _civil_depreciate_asset(self, asset): # Keep only one civil depreciation civil_depreciation_type = self.env.ref( diff --git a/l10n_it_asset_management/tests/test_assets_management.py b/l10n_it_asset_management/tests/test_assets_management.py index 41f80ac93cc9..15ff636de23b 100644 --- a/l10n_it_asset_management/tests/test_assets_management.py +++ b/l10n_it_asset_management/tests/test_assets_management.py @@ -2,6 +2,8 @@ # Copyright 2022 Simone Rubino - TAKOBI # Copyright 2023 Simone Rubino - Aion Tech # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import datetime from datetime import date from odoo import fields @@ -731,3 +733,132 @@ def test_same_asset_report_residual_partial_depreciation(self): self.assertEqual( asset_report_depreciation_line.amount_residual, expected_residual_amount ) + + def test_purchase_sale_refund_recharge(self): + """A sale refund can be used to restore asset value.""" + # Create with purchase + purchase_amount = 2500 + purchase_invoice = self._create_purchase_invoice( + datetime.date(2020, month=1, day=1), amount=purchase_amount + ) + asset = self._link_asset_move( + purchase_invoice, + "create", + { + "category_id": self.asset_category_1, + "name": "Test recharge asset", + }, + ) + civ_depreciation = asset.depreciation_ids.filtered( + lambda x: x.type_id + == self.env.ref("l10n_it_asset_management.ad_type_civilistico") + ) + self.assertEqual(civ_depreciation.amount_depreciable_updated, purchase_amount) + + # Partial dismiss with sale + asset_account_amount = asset_fund_amount = 1000 + sale_invoice = self._create_sale_invoice( + asset, amount=8000, invoice_date=datetime.date(2020, month=3, day=1) + ) + self._link_asset_move( + sale_invoice, + "partial_dismiss", + wiz_values={ + "asset_id": asset, + "depreciated_fund_amount": asset_account_amount, + "asset_purchase_amount": asset_fund_amount, + }, + ) + civ_depreciation_lines = civ_depreciation.line_ids + self.assertRecordValues( + civ_depreciation_lines.sorted("move_type"), + [ + { + "move_type": "depreciated", + "amount": -1000.0, + }, + { + "move_type": "gain", + "amount": 8000.0, + }, + { + "move_type": "out", + "amount": 1000.0, + }, + ], + ) + civ_depreciation_move_lines = civ_depreciation_lines.filtered( + lambda cdl: cdl.move_type == "depreciated" + ).move_id.line_ids + self.assertRecordValues( + civ_depreciation_move_lines.sorted("balance"), + [ + { + "account_id": asset.category_id.asset_account_id.id, + "balance": -1000, + }, + { + "account_id": asset.category_id.fund_account_id.id, + "balance": 1000, + }, + ], + ) + self.assertEqual( + civ_depreciation.amount_depreciable_updated, + purchase_amount - asset_account_amount, + ) + + # Refund and recharge + sale_refund = self._refund_move( + sale_invoice, ref_date=datetime.date(2020, month=7, day=1) + ) + recharge_purchase_amount = recharge_fund_amount = 1000 + wizard = self._get_move_asset_wizard( + sale_refund, + "partial_recharge", + wiz_values={ + "recharge_purchase_amount": recharge_purchase_amount, + "recharge_fund_amount": recharge_fund_amount, + }, + ) + wizard.link_asset() + self.assertEqual(wizard.asset_id, asset) + self.assertEqual(wizard.allowed_asset_ids, asset) + civ_depreciation_lines = civ_depreciation.line_ids - civ_depreciation_lines + self.assertRecordValues( + civ_depreciation_lines.sorted("move_type"), + [ + { + "move_type": "depreciated", + "amount": 1000.0, + }, + { + "move_type": "in", + "amount": 1000.0, + }, + { + "move_type": "loss", + "amount": 8000.0, + }, + ], + ) + civ_depreciation_move_lines = civ_depreciation_lines.filtered( + lambda cdl: cdl.move_type == "depreciated" + ).move_id.line_ids + self.assertRecordValues( + civ_depreciation_move_lines.sorted("balance"), + [ + { + "account_id": asset.category_id.fund_account_id.id, + "balance": -1000, + }, + { + "account_id": asset.category_id.asset_account_id.id, + "balance": 1000, + }, + ], + ) + self.assertEqual( + civ_depreciation.amount_depreciable_updated, + purchase_amount, + ) diff --git a/l10n_it_asset_management/views/account_move.xml b/l10n_it_asset_management/views/account_move.xml index 0d68c8cf8e5e..7703007bfaeb 100644 --- a/l10n_it_asset_management/views/account_move.xml +++ b/l10n_it_asset_management/views/account_move.xml @@ -100,9 +100,19 @@ /> + diff --git a/l10n_it_asset_management/views/asset_depreciation.xml b/l10n_it_asset_management/views/asset_depreciation.xml index 7f6f73bf097e..2562712cbb82 100644 --- a/l10n_it_asset_management/views/asset_depreciation.xml +++ b/l10n_it_asset_management/views/asset_depreciation.xml @@ -111,10 +111,22 @@ /> + 0 else "loss", + "name": name, + "partial_recharge": True, + } + dep_vals["line_ids"].append(Command.create(loss_gain_vals)) + + vals["depreciation_ids"].append(Command.update(dep.id, dep_vals)) + return vals + + def partial_recharge_asset(self): + """Recharge asset partially and return it.""" + self.ensure_one() + self.check_pre_partial_recharge_asset() + old_dep_lines = self.asset_id.mapped("depreciation_ids.line_ids") + self.asset_id.write(self.get_partial_recharge_asset_vals()) + + for dep in self.asset_id.depreciation_ids: + (dep.line_ids - old_dep_lines).post_partial_recharge_asset() + + return self.asset_id diff --git a/l10n_it_asset_management/wizard/account_move_manage_asset_view.xml b/l10n_it_asset_management/wizard/account_move_manage_asset_view.xml index 732600f9e4ee..ce5e3242e470 100644 --- a/l10n_it_asset_management/wizard/account_move_manage_asset_view.xml +++ b/l10n_it_asset_management/wizard/account_move_manage_asset_view.xml @@ -55,6 +55,7 @@ placeholder="Asset Name" attrs="{'invisible': [('management_type', '!=', 'create')], 'required': [('management_type', '=', 'create')]}" /> + + + + + + + + +