diff --git a/account_asset_management/__manifest__.py b/account_asset_management/__manifest__.py index ba089185..99891053 100644 --- a/account_asset_management/__manifest__.py +++ b/account_asset_management/__manifest__.py @@ -28,5 +28,6 @@ "views/menuitem.xml", "data/cron.xml", "wizard/wiz_account_asset_report.xml", + "wizard/wiz_asset_move_reverse.xml", ], } diff --git a/account_asset_management/models/account_asset_line.py b/account_asset_management/models/account_asset_line.py index 1d478482..e51e6a35 100644 --- a/account_asset_management/models/account_asset_line.py +++ b/account_asset_management/models/account_asset_line.py @@ -296,16 +296,38 @@ class AccountAssetLine(models.Model): "context": self.env.context, } + def update_asset_line_after_unlink_move(self): + self.write({"move_id": False}) + if self.parent_state == "close": + self.asset_id.write({"state": "open"}) + elif self.parent_state == "removed" and self.type == "remove": + self.asset_id.write({"state": "close", "date_remove": False}) + self.unlink() + def unlink_move(self): for line in self: - move = line.move_id - move.button_draft() - move.with_context(force_delete=True, unlink_from_asset=True).unlink() - # trigger store function - line.with_context(unlink_from_asset=True).write({"move_id": False}) - if line.parent_state == "close": - line.asset_id.write({"state": "open"}) - elif line.parent_state == "removed" and line.type == "remove": - line.asset_id.write({"state": "close", "date_remove": False}) - line.unlink() + if line.asset_id.profile_id.allow_reversal: + context = dict(self._context or {}) + context.update( + { + "active_model": self._name, + "active_ids": line.ids, + "active_id": line.id, + } + ) + return { + "name": _("Reverse Move"), + "view_mode": "form", + "res_model": "wiz.asset.move.reverse", + "target": "new", + "type": "ir.actions.act_window", + "context": context, + } + else: + move = line.move_id + move.button_draft() + move.with_context(force_delete=True, unlink_from_asset=True).unlink() + line.with_context( + unlink_from_asset=True + ).update_asset_line_after_unlink_move() return True diff --git a/account_asset_management/models/account_asset_profile.py b/account_asset_management/models/account_asset_profile.py index 11830f7d..c132ea07 100644 --- a/account_asset_management/models/account_asset_profile.py +++ b/account_asset_management/models/account_asset_profile.py @@ -163,6 +163,12 @@ class AccountAssetProfile(models.Model): "product item. So, there will be an asset by product item.", ) active = fields.Boolean(default=True) + allow_reversal = fields.Boolean( + "Allow Reversal of journal entries", + help="If set, when pressing the Delete/Reverse Move button in a " + "posted depreciation line will prompt the option to reverse the " + "journal entry, instead of deleting them.", + ) @api.model def _default_company_id(self): diff --git a/account_asset_management/models/account_move.py b/account_asset_management/models/account_move.py index 15ff6de8..505899ec 100644 --- a/account_asset_management/models/account_move.py +++ b/account_asset_management/models/account_move.py @@ -130,8 +130,15 @@ class AccountMove(models.Model): for line_command in move_vals.get("line_ids", []): line_vals = line_command[2] # (0, 0, {...}) asset = self.env["account.asset"].browse(line_vals["asset_id"]) - asset.unlink() - line_vals.update(asset_profile_id=False, asset_id=False) + # We remove the asset if we recognize that we are reversing + # the asset creation + if asset: + asset_line = self.env["account.asset.line"].search( + [("asset_id", "=", asset.id), ("type", "=", "create")], limit=1 + ) + if asset_line and asset_line.move_id == self: + asset.unlink() + line_vals.update(asset_profile_id=False, asset_id=False) return move_vals def action_view_assets(self): diff --git a/account_asset_management/readme/CONTRIBUTORS.rst b/account_asset_management/readme/CONTRIBUTORS.rst index 585785e1..99153bcc 100644 --- a/account_asset_management/readme/CONTRIBUTORS.rst +++ b/account_asset_management/readme/CONTRIBUTORS.rst @@ -15,3 +15,8 @@ * Pedro M. Baeza * João Marques * Víctor Martínez + +* `ForgeFlow `_: + + * Jordi Ballester + * Miquel Raïch diff --git a/account_asset_management/readme/HISTORY.rst b/account_asset_management/readme/HISTORY.rst index e15948e1..9dd4bf99 100644 --- a/account_asset_management/readme/HISTORY.rst +++ b/account_asset_management/readme/HISTORY.rst @@ -3,6 +3,12 @@ * [BREAKING] Removed all functionality associated with `account.fiscal.year` +13.0.3.0.0 (2021-07-06) +~~~~~~~~~~~~~~~~~~~~~~~ + +* Allow to reverse the posting of a depreciation line instead of deleting the + journal entry. + 13.0.2.0.0 (2021-02-19) ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/account_asset_management/security/ir.model.access.csv b/account_asset_management/security/ir.model.access.csv index 148fa11e..9e23ff43 100644 --- a/account_asset_management/security/ir.model.access.csv +++ b/account_asset_management/security/ir.model.access.csv @@ -16,3 +16,4 @@ access_account_asset_group_manager,account.asset.group,model_account_asset_group access_account_asset_remove_user,account.asset.remove,model_account_asset_remove,account.group_account_user,1,1,1,1 access_account_asset_compute_user,account.asset.compute,model_account_asset_compute,account.group_account_user,1,1,1,1 access_wiz_account_asset_report,wiz.account.asset.report,model_wiz_account_asset_report,account.group_account_readonly,1,1,1,0 +access_wiz_asset_move_reverse_user,wiz.asset.move.reverse,model_wiz_asset_move_reverse,account.group_account_user,1,1,1,1 diff --git a/account_asset_management/tests/test_account_asset_management.py b/account_asset_management/tests/test_account_asset_management.py index a06a7e8e..00eb73f9 100644 --- a/account_asset_management/tests/test_account_asset_management.py +++ b/account_asset_management/tests/test_account_asset_management.py @@ -803,6 +803,88 @@ class TestAssetManagement(AccountTestInvoicingCommon): # In the last month the small deviations are compensated self.assertAlmostEqual(d_lines[12].amount, 416.63, places=2) + def test_18_reverse_entries(self): + """Test that cancelling a posted entry creates a reversal.""" + ict0 = self.asset_model.create( + { + "state": "draft", + "method_time": "year", + "method_number": 3, + "method_period": "year", + "name": "Laptop", + "code": "PI00101", + "purchase_value": 1500.0, + "profile_id": self.ict3Y.id, + "date_start": time.strftime("%Y-01-01"), + } + ) + ict0.profile_id.allow_reversal = True + # compute the depreciation boards + ict0.compute_depreciation_board() + ict0.refresh() + # post the first depreciation line + ict0.validate() + ict0.depreciation_line_ids[1].create_move() + original_move = ict0.depreciation_line_ids[1].move_id + ict0.refresh() + self.assertEqual(ict0.state, "open") + self.assertEqual(ict0.value_depreciated, 500) + self.assertEqual(ict0.value_residual, 1000) + depreciation_line = ict0.depreciation_line_ids[1] + wiz_res = depreciation_line.unlink_move() + self.assertTrue( + "res_model" in wiz_res and wiz_res["res_model"] == "wiz.asset.move.reverse" + ) + wiz = Form( + self.env["wiz.asset.move.reverse"].with_context( + { + "active_model": depreciation_line._name, + "active_id": depreciation_line.id, + "active_ids": [depreciation_line.id], + } + ) + ) + reverse_wizard = wiz.save() + reverse_wizard.reverse_move() + ict0.refresh() + self.assertEqual(ict0.value_depreciated, 0) + self.assertEqual(ict0.value_residual, 1500) + self.assertEqual(len(original_move.reversal_move_id), 1) + + def test_19_unlink_entries(self): + """Test that cancelling a posted entry creates a reversal, if the + journal entry has the inalterability hash.""" + ict0 = self.asset_model.create( + { + "state": "draft", + "method_time": "year", + "method_number": 3, + "method_period": "year", + "name": "Laptop", + "code": "PI00101", + "purchase_value": 1500.0, + "profile_id": self.ict3Y.id, + "date_start": time.strftime("%Y-01-01"), + } + ) + # compute the depreciation boards + ict0.compute_depreciation_board() + ict0.refresh() + # post the first depreciation line + ict0.validate() + ict0.depreciation_line_ids[1].create_move() + original_move_id = ict0.depreciation_line_ids[1].move_id.id + ict0.refresh() + self.assertEqual(ict0.state, "open") + self.assertEqual(ict0.value_depreciated, 500) + self.assertEqual(ict0.value_residual, 1000) + ict0.depreciation_line_ids[1].unlink_move() + ict0.refresh() + self.assertEqual(ict0.value_depreciated, 0) + self.assertEqual(ict0.value_residual, 1500) + move = self.env["account.move"].search([("id", "=", original_move_id)]) + self.assertFalse(move) + def test_20_asset_removal_with_value_residual(self): """Asset removal with value residual""" asset = self.asset_model.create( diff --git a/account_asset_management/views/account_asset.xml b/account_asset_management/views/account_asset.xml index d6f6d4c6..99f6e26c 100644 --- a/account_asset_management/views/account_asset.xml +++ b/account_asset_management/views/account_asset.xml @@ -204,7 +204,7 @@