143 lines
5.1 KiB
Python
143 lines
5.1 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
import re
|
|
from markupsafe import Markup
|
|
|
|
from odoo import api, fields, models, _
|
|
from odoo.exceptions import UserError, ValidationError
|
|
|
|
|
|
class BudgetAnalytic(models.Model):
|
|
_name = 'budget.analytic'
|
|
_description = "Budget"
|
|
_inherit = ['mail.thread', 'mail.activity.mixin']
|
|
|
|
name = fields.Char('Budget Name', required=True)
|
|
parent_id = fields.Many2one(
|
|
string="Revision Of",
|
|
comodel_name='budget.analytic',
|
|
index=True,
|
|
ondelete='cascade',
|
|
)
|
|
children_ids = fields.One2many(
|
|
string="Revisions",
|
|
comodel_name='budget.analytic',
|
|
inverse_name='parent_id',
|
|
)
|
|
user_id = fields.Many2one('res.users', 'Responsible', default=lambda self: self.env.user)
|
|
date_from = fields.Date('Start Date', required=True)
|
|
date_to = fields.Date('End Date', required=True)
|
|
state = fields.Selection(
|
|
string="Status",
|
|
selection=[
|
|
('draft', "Draft"),
|
|
('confirmed', "Open"),
|
|
('revised', "Revised"),
|
|
('done', "Done"),
|
|
('canceled', "Canceled")
|
|
],
|
|
required=True, default='draft',
|
|
readonly=True,
|
|
copy=False,
|
|
tracking=True,
|
|
)
|
|
budget_type = fields.Selection(
|
|
string="Budget Type",
|
|
selection=[
|
|
('revenue', "Revenue"),
|
|
('expense', "Expense"),
|
|
('both', "Both"),
|
|
],
|
|
required=True, default='expense',
|
|
copy=False,
|
|
)
|
|
budget_line_ids = fields.One2many('budget.line', 'budget_analytic_id', 'Budget Lines', copy=True)
|
|
company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env.company)
|
|
|
|
@api.constrains('date_from', 'date_to')
|
|
def _check_dates(self):
|
|
for record in self:
|
|
if record.date_from > record.date_to:
|
|
raise ValidationError(_("Budget end date may not be before the starting date."))
|
|
|
|
@api.constrains('parent_id')
|
|
def _check_parent_id(self):
|
|
for budget in self:
|
|
if budget._has_cycle():
|
|
raise ValidationError(_('You cannot create recursive revision of budget.'))
|
|
|
|
@api.ondelete(at_uninstall=False)
|
|
def _unlink_except_draft_or_cancel(self):
|
|
if any(budget.state not in ('draft', 'canceled') for budget in self):
|
|
raise UserError(_("Deletion is only allowed in the Draft and Canceled stages."))
|
|
|
|
def action_budget_confirm(self):
|
|
self.parent_id.filtered(lambda b: b.state == 'confirmed').state = 'revised'
|
|
for budget in self:
|
|
budget.state = 'revised' if budget.children_ids else 'confirmed'
|
|
|
|
def action_budget_draft(self):
|
|
self.state = 'draft'
|
|
|
|
def action_budget_cancel(self):
|
|
self.state = 'canceled'
|
|
|
|
def action_budget_done(self):
|
|
self.state = 'done'
|
|
|
|
def create_revised_budget(self):
|
|
revised = self.browse()
|
|
for budget in self:
|
|
revised_budget = budget.copy(default={'name': budget._get_revised_budget_name(), 'parent_id': budget.id, 'budget_type': budget.budget_type})
|
|
revised += revised_budget
|
|
budget.message_post(
|
|
body=Markup("%s: <a href='#' data-oe-model='budget.analytic' data-oe-id='%s'>%s</a>") % (
|
|
_("New revision"),
|
|
revised_budget.id,
|
|
revised_budget.name,
|
|
))
|
|
return revised._get_records_action()
|
|
|
|
def _get_revised_budget_name(self):
|
|
"""
|
|
Generate revised budget name with "REV(datetime)" format
|
|
|
|
:return: Updated budget name.
|
|
"""
|
|
self.ensure_one()
|
|
current_time = fields.Datetime.context_timestamp(self, fields.Datetime.now())
|
|
datetime_str = current_time.strftime("%Y-%m-%d %H:%M")
|
|
|
|
# Extract existing revision pattern and base name
|
|
match = re.search(r'(.*) - REV\([^)]+\)$', self.name.strip())
|
|
base_name = match.group(1).strip() if match else self.name.strip()
|
|
return f"{base_name} - REV({datetime_str})"
|
|
|
|
def action_open_budget_lines(self):
|
|
context = dict(self.env.context)
|
|
if len(self) == 1:
|
|
context['default_budget_analytic_id'] = self.id
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': _('Budget Lines'),
|
|
'res_model': 'budget.line',
|
|
'view_mode': 'list,pivot,graph',
|
|
'domain': [('budget_analytic_id', 'in', self.ids)],
|
|
'context': context,
|
|
}
|
|
|
|
def action_open_budget_report(self):
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': _('Budget Report'),
|
|
'res_model': 'budget.report',
|
|
'view_mode': 'pivot,list,graph',
|
|
'context': {
|
|
'search_default_budget_analytic_id': self.id,
|
|
'search_default_group_by_budget_line_id': 1,
|
|
},
|
|
}
|
|
|
|
def _get_view(self, view_id=None, view_type='form', **options):
|
|
arch, view = super()._get_view(view_id, view_type, **options)
|
|
return self.env['analytic.plan.fields.mixin']._patch_view(arch, view, view_type) # patch the budget lines list view
|