diff --git a/fusion_accounting_assets/__manifest__.py b/fusion_accounting_assets/__manifest__.py index ac94d55a..e30f2273 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.29', + 'version': '19.0.1.0.30', 'category': 'Accounting/Accounting', 'summary': 'AI-augmented asset management with depreciation schedules.', 'description': """ @@ -37,6 +37,7 @@ menu hides; the engine + AI tools remain available for the chat. 'wizards/create_asset_wizard_views.xml', 'wizards/disposal_wizard_views.xml', 'wizards/partial_sale_wizard_views.xml', + 'wizards/depreciation_run_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 14b5dcd5..eaade7de 100644 --- a/fusion_accounting_assets/security/ir.model.access.csv +++ b/fusion_accounting_assets/security/ir.model.access.csv @@ -12,3 +12,4 @@ access_fusion_asset_anomaly_admin,fusion.asset.anomaly.admin,model_fusion_asset_ 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 access_fusion_partial_sale_wizard_user,fusion.partial.sale.wizard.user,model_fusion_partial_sale_wizard,base.group_user,1,1,1,0 +access_fusion_depreciation_run_wizard_user,fusion.depreciation.run.wizard.user,model_fusion_depreciation_run_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 ee130246..d5544e90 100644 --- a/fusion_accounting_assets/tests/__init__.py +++ b/fusion_accounting_assets/tests/__init__.py @@ -22,3 +22,4 @@ from . import test_performance_benchmarks from . import test_create_asset_wizard from . import test_disposal_wizard from . import test_partial_sale_wizard +from . import test_depreciation_run_wizard diff --git a/fusion_accounting_assets/tests/test_depreciation_run_wizard.py b/fusion_accounting_assets/tests/test_depreciation_run_wizard.py new file mode 100644 index 00000000..6a55d7b4 --- /dev/null +++ b/fusion_accounting_assets/tests/test_depreciation_run_wizard.py @@ -0,0 +1,43 @@ +from datetime import date + +from odoo.tests import tagged +from odoo.tests.common import TransactionCase + + +@tagged('post_install', '-at_install') +class TestDepreciationRunWizard(TransactionCase): + + def test_run_all_running_posts_due_periods(self): + for amt in [3000, 5000]: + asset = self.env['fusion.asset'].create({ + 'name': f'Run Test {amt}', 'cost': amt, + '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(asset) + asset.action_set_running() + wizard = self.env['fusion.depreciation.run.wizard'].create({ + 'period_date': date(2030, 12, 31), + 'state_filter': 'all_running', + }) + wizard.action_run() + self.assertEqual(wizard.state, 'done') + self.assertGreater(wizard.posted_count, 0) + + def test_run_selected_posts_only_selected(self): + asset = self.env['fusion.asset'].create({ + 'name': 'Selected Test', 'cost': 1000, + '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(asset) + asset.action_set_running() + wizard = self.env['fusion.depreciation.run.wizard'].create({ + 'period_date': date(2030, 12, 31), + 'state_filter': 'selected', + 'asset_ids': [(6, 0, [asset.id])], + }) + wizard.action_run() + self.assertEqual(wizard.state, 'done') diff --git a/fusion_accounting_assets/wizards/__init__.py b/fusion_accounting_assets/wizards/__init__.py index f55dc348..dbad0030 100644 --- a/fusion_accounting_assets/wizards/__init__.py +++ b/fusion_accounting_assets/wizards/__init__.py @@ -1,3 +1,4 @@ from . import create_asset_wizard from . import disposal_wizard from . import partial_sale_wizard +from . import depreciation_run_wizard diff --git a/fusion_accounting_assets/wizards/depreciation_run_wizard.py b/fusion_accounting_assets/wizards/depreciation_run_wizard.py new file mode 100644 index 00000000..6c463384 --- /dev/null +++ b/fusion_accounting_assets/wizards/depreciation_run_wizard.py @@ -0,0 +1,72 @@ +"""Manual depreciation run wizard. + +Operator picks a period_date and the wizard posts all running assets' +unposted lines whose scheduled_date <= period_date.""" + +from odoo import fields, models + + +class FusionDepreciationRunWizard(models.TransientModel): + _name = "fusion.depreciation.run.wizard" + _description = "Manual Depreciation Run Wizard" + + period_date = fields.Date( + required=True, default=fields.Date.today, + help="Post all unposted lines whose scheduled_date is on or before this date.", + ) + state_filter = fields.Selection([ + ('all_running', 'All Running Assets'), + ('selected', 'Selected Asset(s) Only'), + ], default='all_running', required=True) + asset_ids = fields.Many2many( + 'fusion.asset', domain=[('state', '=', 'running')], + ) + + state = fields.Selection( + [('draft', 'Draft'), ('done', 'Done')], default='draft', + ) + posted_count = fields.Integer(readonly=True) + skipped_count = fields.Integer(readonly=True) + error_count = fields.Integer(readonly=True) + summary = fields.Text(readonly=True) + + def action_run(self): + self.ensure_one() + if self.state_filter == 'all_running': + assets = self.env['fusion.asset'].search([ + ('state', '=', 'running'), + ('company_id', '=', self.env.company.id), + ]) + else: + assets = self.asset_ids + + engine = self.env['fusion.asset.engine'] + posted = 0 + skipped = 0 + errors = [] + for asset in assets: + try: + with self.env.cr.savepoint(): + result = engine.post_depreciation_entry( + asset, period_date=self.period_date, + ) + posted += result.get('posted_count', 0) + if result.get('posted_count', 0) == 0: + skipped += 1 + except Exception as e: # noqa: BLE001 + errors.append(f"{asset.name}: {e}") + + self.write({ + 'state': 'done', + 'posted_count': posted, + 'skipped_count': skipped, + 'error_count': len(errors), + 'summary': '\n'.join(errors[:20]) if errors else False, + }) + return { + 'type': 'ir.actions.act_window', + 'res_model': self._name, + 'res_id': self.id, + 'view_mode': 'form', + 'target': 'new', + } diff --git a/fusion_accounting_assets/wizards/depreciation_run_wizard_views.xml b/fusion_accounting_assets/wizards/depreciation_run_wizard_views.xml new file mode 100644 index 00000000..bc01e874 --- /dev/null +++ b/fusion_accounting_assets/wizards/depreciation_run_wizard_views.xml @@ -0,0 +1,36 @@ + + + + fusion.depreciation.run.wizard.form + fusion.depreciation.run.wizard + +
+ + + + + + + + + + + + +
+
+ +
+
+ + + Run Depreciation + fusion.depreciation.run.wizard + form + new + +