2
0

[IMP] account_asset_management: Allow to reverse the posting of a depreciation line

instead of deleting the journal entry.
This will be done when the company has activated the inalterability hash
on the original journal entry.
This commit is contained in:
Jordi Ballester 2021-07-06 17:34:48 +02:00 committed by Rodrigo
parent c62bab7c9e
commit 92e7f1cee5
13 changed files with 231 additions and 13 deletions

View File

@ -28,5 +28,6 @@
"views/menuitem.xml", "views/menuitem.xml",
"data/cron.xml", "data/cron.xml",
"wizard/wiz_account_asset_report.xml", "wizard/wiz_account_asset_report.xml",
"wizard/wiz_asset_move_reverse.xml",
], ],
} }

View File

@ -296,16 +296,38 @@ class AccountAssetLine(models.Model):
"context": self.env.context, "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): def unlink_move(self):
for line in self: for line in self:
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 = line.move_id
move.button_draft() move.button_draft()
move.with_context(force_delete=True, unlink_from_asset=True).unlink() move.with_context(force_delete=True, unlink_from_asset=True).unlink()
# trigger store function line.with_context(
line.with_context(unlink_from_asset=True).write({"move_id": False}) unlink_from_asset=True
if line.parent_state == "close": ).update_asset_line_after_unlink_move()
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()
return True return True

View File

@ -163,6 +163,12 @@ class AccountAssetProfile(models.Model):
"product item. So, there will be an asset by product item.", "product item. So, there will be an asset by product item.",
) )
active = fields.Boolean(default=True) 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 @api.model
def _default_company_id(self): def _default_company_id(self):

View File

@ -130,6 +130,13 @@ class AccountMove(models.Model):
for line_command in move_vals.get("line_ids", []): for line_command in move_vals.get("line_ids", []):
line_vals = line_command[2] # (0, 0, {...}) line_vals = line_command[2] # (0, 0, {...})
asset = self.env["account.asset"].browse(line_vals["asset_id"]) asset = self.env["account.asset"].browse(line_vals["asset_id"])
# 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() asset.unlink()
line_vals.update(asset_profile_id=False, asset_id=False) line_vals.update(asset_profile_id=False, asset_id=False)
return move_vals return move_vals

View File

@ -15,3 +15,8 @@
* Pedro M. Baeza * Pedro M. Baeza
* João Marques * João Marques
* Víctor Martínez * Víctor Martínez
* `ForgeFlow <https://www.forgeflow.com>`_:
* Jordi Ballester <jordi.ballester@forgeflow.com>
* Miquel Raïch <miquel.raich@forgeflow.com>

View File

@ -3,6 +3,12 @@
* [BREAKING] Removed all functionality associated with `account.fiscal.year` * [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) 13.0.2.0.0 (2021-02-19)
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -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_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_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_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

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
16 access_account_asset_remove_user account.asset.remove model_account_asset_remove account.group_account_user 1 1 1 1
17 access_account_asset_compute_user account.asset.compute model_account_asset_compute account.group_account_user 1 1 1 1
18 access_wiz_account_asset_report wiz.account.asset.report model_wiz_account_asset_report account.group_account_readonly 1 1 1 0
19 access_wiz_asset_move_reverse_user wiz.asset.move.reverse model_wiz_asset_move_reverse account.group_account_user 1 1 1 1

View File

@ -803,6 +803,88 @@ class TestAssetManagement(AccountTestInvoicingCommon):
# In the last month the small deviations are compensated # In the last month the small deviations are compensated
self.assertAlmostEqual(d_lines[12].amount, 416.63, places=2) 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): def test_20_asset_removal_with_value_residual(self):
"""Asset removal with value residual""" """Asset removal with value residual"""
asset = self.asset_model.create( asset = self.asset_model.create(

View File

@ -204,7 +204,7 @@
<button <button
name="unlink_move" name="unlink_move"
icon="fa-times" icon="fa-times"
string="Delete Move" string="Delete/Reverse Move"
type="object" type="object"
confirm="Are you sure ?" confirm="Are you sure ?"
groups="account.group_account_manager" groups="account.group_account_manager"

View File

@ -38,6 +38,7 @@
<field name="account_plus_value_id" /> <field name="account_plus_value_id" />
<field name="account_min_value_id" /> <field name="account_min_value_id" />
<field name="account_residual_value_id" /> <field name="account_residual_value_id" />
<field name="allow_reversal" />
</group> </group>
<group string="Depreciation Dates"> <group string="Depreciation Dates">
<field name="method_time" /> <field name="method_time" />

View File

@ -1,3 +1,4 @@
from . import account_asset_compute from . import account_asset_compute
from . import account_asset_remove from . import account_asset_remove
from . import wiz_account_asset_report from . import wiz_account_asset_report
from . import wiz_asset_move_reverse

View File

@ -0,0 +1,59 @@
# Copyright 2021 ForgeFlow, S.L.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
class WizAssetMoveReverse(models.TransientModel):
_name = "wiz.asset.move.reverse"
_description = "Reverse posted journal entry on depreciation line"
line_id = fields.Many2one(
comodel_name="account.asset.line",
string="Asset Line",
readonly=True,
required=True,
)
date_reversal = fields.Date(
string="Reversal date",
required=True,
default=fields.Date.context_today,
)
reason = fields.Char(string="Reason")
journal_id = fields.Many2one(
"account.journal",
string="Use Specific Journal",
help="If empty, uses the journal of the journal entry to be reversed.",
)
@api.model
def default_get(self, fields):
res = super(WizAssetMoveReverse, self).default_get(fields)
line_ids = (
self.env["account.asset.line"].browse(self.env.context["active_ids"])
if self.env.context.get("active_model") == "account.asset.line"
else self.env["account.asset.line"]
)
res["line_id"] = line_ids[0].id if line_ids else False
return res
def reverse_move(self):
move = self.line_id.move_id
move_reversal = (
self.env["account.move.reversal"]
.with_context(active_model="account.move", active_ids=move.ids)
.create(
{
"date": fields.Date.today(),
"reason": self.reason,
"refund_method": "refund",
"journal_id": self.journal_id.id,
}
)
)
reversal = move_reversal.with_context(allow_asset=True).reverse_moves()
reverse_move = self.env["account.move"].browse(reversal["res_id"])
reverse_move.action_post()
self.line_id.with_context(
unlink_from_asset=True
).update_asset_line_after_unlink_move()
return True

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="wiz_asset_move_reverse_view_form" model="ir.ui.view">
<field name="name">wiz.asset.move.reverse.form</field>
<field name="model">wiz.asset.move.reverse</field>
<field name="arch" type="xml">
<form string="Reverse Journal Entry">
<group>
<field name="line_id" invisible="True" />
<field name="date_reversal" />
<field name="journal_id" />
<field name="reason" />
</group>
<newline />
<footer>
<button
string="Confirm"
name="reverse_move"
type="object"
class="oe_highlight"
/>
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
</odoo>