diff --git a/fusion_accounting_assets/__manifest__.py b/fusion_accounting_assets/__manifest__.py index 4f77df8d..b71d07e5 100644 --- a/fusion_accounting_assets/__manifest__.py +++ b/fusion_accounting_assets/__manifest__.py @@ -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': [ diff --git a/fusion_accounting_assets/security/ir.model.access.csv b/fusion_accounting_assets/security/ir.model.access.csv index 1782a255..7448bb30 100644 --- a/fusion_accounting_assets/security/ir.model.access.csv +++ b/fusion_accounting_assets/security/ir.model.access.csv @@ -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 diff --git a/fusion_accounting_assets/tests/__init__.py b/fusion_accounting_assets/tests/__init__.py index 44936b8e..0a56cdc0 100644 --- a/fusion_accounting_assets/tests/__init__.py +++ b/fusion_accounting_assets/tests/__init__.py @@ -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 diff --git a/fusion_accounting_assets/tests/test_disposal_wizard.py b/fusion_accounting_assets/tests/test_disposal_wizard.py new file mode 100644 index 00000000..23258ce8 --- /dev/null +++ b/fusion_accounting_assets/tests/test_disposal_wizard.py @@ -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, + ) diff --git a/fusion_accounting_assets/wizards/__init__.py b/fusion_accounting_assets/wizards/__init__.py index 02131f82..a4f73d87 100644 --- a/fusion_accounting_assets/wizards/__init__.py +++ b/fusion_accounting_assets/wizards/__init__.py @@ -1 +1,2 @@ from . import create_asset_wizard +from . import disposal_wizard diff --git a/fusion_accounting_assets/wizards/disposal_wizard.py b/fusion_accounting_assets/wizards/disposal_wizard.py new file mode 100644 index 00000000..6444f1b7 --- /dev/null +++ b/fusion_accounting_assets/wizards/disposal_wizard.py @@ -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', + } diff --git a/fusion_accounting_assets/wizards/disposal_wizard_views.xml b/fusion_accounting_assets/wizards/disposal_wizard_views.xml new file mode 100644 index 00000000..91b8125a --- /dev/null +++ b/fusion_accounting_assets/wizards/disposal_wizard_views.xml @@ -0,0 +1,39 @@ + + + + fusion.disposal.wizard.form + fusion.disposal.wizard + +
+ + + + + + + + + + + + + + +
+
+
+
+
+ + + Dispose Asset + fusion.disposal.wizard + form + new + + form,list + +