feat(fusion_accounting_assets): disposal wizard

Made-with: Cursor
This commit is contained in:
gsinghpal
2026-04-19 20:04:03 -04:00
parent a6ef7e0c2a
commit 892c37e2b0
7 changed files with 159 additions and 1 deletions

View File

@@ -1,6 +1,6 @@
{
'name': 'Fusion Accounting Assets',
'version': '19.0.1.0.27',
'version': '19.0.1.0.28',
'category': 'Accounting/Accounting',
'summary': 'AI-augmented asset management with depreciation schedules.',
'description': """
@@ -35,6 +35,7 @@ menu hides; the engine + AI tools remain available for the chat.
'security/ir.model.access.csv',
'data/cron.xml',
'wizards/create_asset_wizard_views.xml',
'wizards/disposal_wizard_views.xml',
],
'assets': {
'web.assets_backend': [

View File

@@ -10,3 +10,4 @@ access_fusion_asset_disposal_admin,fusion.asset.disposal.admin,model_fusion_asse
access_fusion_asset_anomaly_user,fusion.asset.anomaly.user,model_fusion_asset_anomaly,base.group_user,1,0,0,0
access_fusion_asset_anomaly_admin,fusion.asset.anomaly.admin,model_fusion_asset_anomaly,fusion_accounting_core.group_fusion_accounting_admin,1,1,1,1
access_fusion_create_asset_wizard_user,fusion.create.asset.wizard.user,model_fusion_create_asset_wizard,base.group_user,1,1,1,0
access_fusion_disposal_wizard_user,fusion.disposal.wizard.user,model_fusion_disposal_wizard,base.group_user,1,1,1,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
10 access_fusion_asset_anomaly_user fusion.asset.anomaly.user model_fusion_asset_anomaly base.group_user 1 0 0 0
11 access_fusion_asset_anomaly_admin fusion.asset.anomaly.admin model_fusion_asset_anomaly fusion_accounting_core.group_fusion_accounting_admin 1 1 1 1
12 access_fusion_create_asset_wizard_user fusion.create.asset.wizard.user model_fusion_create_asset_wizard base.group_user 1 1 1 0
13 access_fusion_disposal_wizard_user fusion.disposal.wizard.user model_fusion_disposal_wizard base.group_user 1 1 1 0

View File

@@ -20,3 +20,4 @@ from . import test_method_integration
from . import test_asset_book_values_mv
from . import test_performance_benchmarks
from . import test_create_asset_wizard
from . import test_disposal_wizard

View File

@@ -0,0 +1,50 @@
from datetime import date
from odoo.tests import tagged
from odoo.tests.common import TransactionCase
@tagged('post_install', '-at_install')
class TestDisposalWizard(TransactionCase):
def setUp(self):
super().setUp()
self.asset = self.env['fusion.asset'].create({
'name': 'Disposal Test Asset',
'cost': 6000,
'acquisition_date': date(2026, 1, 1),
'in_service_date': date(2026, 1, 1),
'method': 'straight_line', 'useful_life_years': 3,
})
self.env['fusion.asset.engine'].compute_depreciation_schedule(self.asset)
self.asset.action_set_running()
def test_default_loads_active_asset(self):
wizard = self.env['fusion.disposal.wizard'].with_context(
active_model='fusion.asset', active_id=self.asset.id,
).create({})
self.assertEqual(wizard.asset_id, self.asset)
def test_action_dispose_marks_asset_disposed(self):
wizard = self.env['fusion.disposal.wizard'].create({
'asset_id': self.asset.id,
'disposal_type': 'sale',
'sale_amount': 4000,
'disposal_date': date(2026, 6, 1),
})
wizard.action_dispose()
self.asset.invalidate_recordset(['state'])
self.assertEqual(self.asset.state, 'disposed')
def test_compute_gain_loss_sale(self):
wizard = self.env['fusion.disposal.wizard'].create({
'asset_id': self.asset.id,
'disposal_type': 'sale',
'sale_amount': 7000,
})
wizard._compute_gain_loss()
self.assertAlmostEqual(
wizard.estimated_gain_loss,
7000 - self.asset.book_value,
places=2,
)

View File

@@ -1 +1,2 @@
from . import create_asset_wizard
from . import disposal_wizard

View File

@@ -0,0 +1,65 @@
"""Asset disposal wizard (sale, scrap, donation, lost)."""
from odoo import _, api, fields, models
from odoo.exceptions import UserError
class FusionDisposalWizard(models.TransientModel):
_name = "fusion.disposal.wizard"
_description = "Asset Disposal Wizard"
asset_id = fields.Many2one(
'fusion.asset', required=True,
default=lambda self: self._default_asset(),
)
company_id = fields.Many2one(related='asset_id.company_id')
currency_id = fields.Many2one(related='asset_id.currency_id')
book_value = fields.Monetary(related='asset_id.book_value', readonly=True)
disposal_type = fields.Selection([
('sale', 'Sale'),
('scrap', 'Scrap'),
('donation', 'Donation'),
('lost', 'Lost / Stolen'),
], required=True, default='sale')
disposal_date = fields.Date(required=True, default=fields.Date.today)
sale_amount = fields.Monetary(default=0.0)
sale_partner_id = fields.Many2one('res.partner')
notes = fields.Text()
estimated_gain_loss = fields.Monetary(compute='_compute_gain_loss')
@api.model
def _default_asset(self):
ctx = self.env.context
if ctx.get('active_model') == 'fusion.asset':
return ctx.get('active_id')
return False
@api.depends('sale_amount', 'book_value', 'disposal_type')
def _compute_gain_loss(self):
for w in self:
if w.disposal_type == 'sale':
w.estimated_gain_loss = w.sale_amount - w.book_value
else:
w.estimated_gain_loss = -w.book_value
def action_dispose(self):
self.ensure_one()
if self.asset_id.state == 'disposed':
raise UserError(_("Asset already disposed."))
partner = self.sale_partner_id if self.disposal_type == 'sale' else None
self.env['fusion.asset.engine'].dispose_asset(
self.asset_id,
sale_amount=self.sale_amount if self.disposal_type == 'sale' else 0,
sale_date=self.disposal_date,
sale_partner=partner,
disposal_type=self.disposal_type,
)
return {
'type': 'ir.actions.act_window',
'res_model': 'fusion.asset',
'res_id': self.asset_id.id,
'view_mode': 'form',
'target': 'current',
}

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_fusion_disposal_wizard_form" model="ir.ui.view">
<field name="name">fusion.disposal.wizard.form</field>
<field name="model">fusion.disposal.wizard</field>
<field name="arch" type="xml">
<form string="Dispose Asset">
<group>
<field name="asset_id" options="{'no_create': True}" readonly="1"/>
<field name="book_value" readonly="1"/>
<field name="company_id" invisible="1"/>
<field name="currency_id" invisible="1"/>
</group>
<group>
<field name="disposal_type"/>
<field name="disposal_date"/>
<field name="sale_amount" invisible="disposal_type != 'sale'"/>
<field name="sale_partner_id" invisible="disposal_type != 'sale'"/>
<field name="estimated_gain_loss" readonly="1"/>
<field name="notes"/>
</group>
<footer>
<button name="action_dispose" type="object"
string="Confirm Disposal" class="btn-primary"/>
<button special="cancel" string="Cancel"/>
</footer>
</form>
</field>
</record>
<record id="action_fusion_disposal_wizard" model="ir.actions.act_window">
<field name="name">Dispose Asset</field>
<field name="res_model">fusion.disposal.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="binding_model_id" ref="model_fusion_asset"/>
<field name="binding_view_types">form,list</field>
</record>
</odoo>