feat(fusion_accounting_assets): depreciation run wizard
Made-with: Cursor
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
'name': 'Fusion Accounting Assets',
|
'name': 'Fusion Accounting Assets',
|
||||||
'version': '19.0.1.0.29',
|
'version': '19.0.1.0.30',
|
||||||
'category': 'Accounting/Accounting',
|
'category': 'Accounting/Accounting',
|
||||||
'summary': 'AI-augmented asset management with depreciation schedules.',
|
'summary': 'AI-augmented asset management with depreciation schedules.',
|
||||||
'description': """
|
'description': """
|
||||||
@@ -37,6 +37,7 @@ menu hides; the engine + AI tools remain available for the chat.
|
|||||||
'wizards/create_asset_wizard_views.xml',
|
'wizards/create_asset_wizard_views.xml',
|
||||||
'wizards/disposal_wizard_views.xml',
|
'wizards/disposal_wizard_views.xml',
|
||||||
'wizards/partial_sale_wizard_views.xml',
|
'wizards/partial_sale_wizard_views.xml',
|
||||||
|
'wizards/depreciation_run_wizard_views.xml',
|
||||||
],
|
],
|
||||||
'assets': {
|
'assets': {
|
||||||
'web.assets_backend': [
|
'web.assets_backend': [
|
||||||
|
|||||||
@@ -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_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_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_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
|
||||||
|
|||||||
|
@@ -22,3 +22,4 @@ from . import test_performance_benchmarks
|
|||||||
from . import test_create_asset_wizard
|
from . import test_create_asset_wizard
|
||||||
from . import test_disposal_wizard
|
from . import test_disposal_wizard
|
||||||
from . import test_partial_sale_wizard
|
from . import test_partial_sale_wizard
|
||||||
|
from . import test_depreciation_run_wizard
|
||||||
|
|||||||
@@ -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')
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
from . import create_asset_wizard
|
from . import create_asset_wizard
|
||||||
from . import disposal_wizard
|
from . import disposal_wizard
|
||||||
from . import partial_sale_wizard
|
from . import partial_sale_wizard
|
||||||
|
from . import depreciation_run_wizard
|
||||||
|
|||||||
72
fusion_accounting_assets/wizards/depreciation_run_wizard.py
Normal file
72
fusion_accounting_assets/wizards/depreciation_run_wizard.py
Normal file
@@ -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',
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_fusion_depreciation_run_wizard_form" model="ir.ui.view">
|
||||||
|
<field name="name">fusion.depreciation.run.wizard.form</field>
|
||||||
|
<field name="model">fusion.depreciation.run.wizard</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Run Depreciation">
|
||||||
|
<group invisible="state == 'done'">
|
||||||
|
<field name="period_date"/>
|
||||||
|
<field name="state_filter" widget="radio"/>
|
||||||
|
<field name="asset_ids" widget="many2many_tags"
|
||||||
|
invisible="state_filter != 'selected'"/>
|
||||||
|
</group>
|
||||||
|
<group invisible="state != 'done'" string="Results">
|
||||||
|
<field name="posted_count"/>
|
||||||
|
<field name="skipped_count"/>
|
||||||
|
<field name="error_count"/>
|
||||||
|
<field name="summary"/>
|
||||||
|
</group>
|
||||||
|
<field name="state" invisible="1"/>
|
||||||
|
<footer>
|
||||||
|
<button name="action_run" type="object" string="Run"
|
||||||
|
class="btn-primary" invisible="state == 'done'"/>
|
||||||
|
<button special="cancel" string="Close"/>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_fusion_depreciation_run_wizard" model="ir.actions.act_window">
|
||||||
|
<field name="name">Run Depreciation</field>
|
||||||
|
<field name="res_model">fusion.depreciation.run.wizard</field>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="target">new</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
Reference in New Issue
Block a user