odex30_standard/odex30_base/system_dashboard_classic/models/config.py

440 lines
17 KiB
Python

# -*- coding: utf-8 -*-
from odoo import fields, models, api, _
from odoo.exceptions import ValidationError
class BaseDashboard(models.Model):
_name = 'base.dashbord'
_description = 'Dashboard Builder'
_order = 'sequence'
sequence = fields.Integer()
name = fields.Char(
string='Card name',
translate=True
)
model_name = fields.Char(string='Model Name')
model_id = fields.Many2one(
string='Model',
comodel_name='ir.model'
)
line_ids = fields.One2many(
string='Lines',
comodel_name='base.dashbord.line',
inverse_name='board_id'
)
icon_type = fields.Selection(
[('image', 'Image'), ('icon', 'Icon Library')],
string='Icon Type',
default='image',
required=True
)
icon_name = fields.Char(
string='Icon Class',
help="FontAwesome class (e.g., 'fa-plane', 'fa-users'). See https://fontawesome.com/v4/icons/"
)
icon_preview_html = fields.Html(
compute='_compute_icon_preview',
string="Icon/Image Preview"
)
card_image = fields.Binary(string='Card Image')
is_self_service = fields.Boolean(string='Self Service?')
is_financial_impact = fields.Boolean(string='Without Financial Impact?')
form_view_id = fields.Many2one('ir.ui.view', string='Form View')
list_view_id = fields.Many2one('ir.ui.view', string='List View')
action_id = fields.Many2one('ir.actions.act_window', string='Action')
field_id = fields.Many2one('ir.model.fields', string='Fields')
is_button = fields.Boolean(string='Is Button')
is_stage = fields.Boolean(string='Is Stage', compute='_compute_field', store=True)
is_double = fields.Boolean(string='Is Double', compute='_compute_field', store=True)
is_state = fields.Boolean(string='Is State', compute='_compute_field', store=True)
action_domain = fields.Char(
string='Action Domain',
compute='_compute_action_domain',
store=True
)
action_context = fields.Char(
string='Action Context',
compute='_compute_action_domain',
store=True
)
relation = fields.Char(string='Relation')
search_field = fields.Char(
string='Search Field',
required=True,
default='employee_id.user_id'
)
@api.depends('icon_type', 'icon_name', 'card_image')
def _compute_icon_preview(self):
for record in self:
if record.icon_type == 'icon' and record.icon_name:
record.icon_preview_html = f'<div class="dashboard-icon-preview-box icon-mode"><i class="fa {record.icon_name}"></i></div>'
elif record.icon_type == 'image' and record.card_image:
try:
img_rec = record.with_context(bin_size=False)
image_data = img_rec.card_image.decode('utf-8') if isinstance(img_rec.card_image, bytes) else img_rec.card_image
record.icon_preview_html = f'<div class="dashboard-icon-preview-box image-mode"><img src="data:image/png;base64,{image_data}"/></div>'
except Exception:
record.icon_preview_html = '<div class="dashboard-icon-preview-box error-mode"><i class="fa fa-exclamation-triangle"></i></div>'
else:
record.icon_preview_html = '<div class="dashboard-icon-preview-box default-mode"><i class="fa fa-th-large"></i></div>'
def unlink_nodes(self):
for rec in self:
rec.is_button = False
nodes = self.env['node.state'].sudo().search([
('model_id', '=', rec.model_id.id),
('is_workflow', '=', False)
])
nodes.sudo().unlink()
def unlink(self):
for rec in self:
rec.unlink_nodes()
return super(BaseDashboard, self).unlink()
@api.constrains('action_id')
def _check_action_id(self):
for record in self:
is_record = self.sudo().search_count([('action_id', '=', record.action_id.id)])
if is_record > 1:
raise ValidationError(_('There is already a record for this action.'))
@api.depends('action_id')
def _compute_action_domain(self):
for record in self:
record.action_domain = record.action_id.domain if record.action_id else False
record.action_context = record.action_id.context if record.action_id else False
@api.onchange('model_id')
def _get_stage_value(self):
for rec in self:
if rec.model_id:
rec.model_name = rec.model_id.model
@api.depends('model_name')
def _compute_field(self):
for rec in self:
rec.is_stage = False
rec.is_double = False
rec.is_state = False
if rec.model_id and rec.model_name and rec.model_name in self.env:
model = self.env[rec.model_name]
# hr.holidays has special case (can have both state and stage)
if rec.model_name in ('hr.holidays', 'hr.leave'):
rec.is_double = True
elif 'state' in model._fields:
rec.is_state = True
elif 'stage_id' in model._fields:
rec.is_stage = True
@api.depends('name', 'model_id')
def _compute_display_name(self):
for record in self:
if record.name:
record.display_name = record.name
elif record.model_id:
record.display_name = record.model_id.name
else:
record.display_name = _('Dashboard Card')
def _get_stage(self, rel):
"""Get stages from relation model and create them in intermediate table"""
for rec in self:
current_model = self.env['stage.stage'].sudo().search([('model_id', '=', rec.model_id.id)])
stage_ids = self.env[rel].sudo().search([])
if not current_model:
for stage in stage_ids:
value = stage.with_context(lang=self.env.user.lang).name
self.env['stage.stage'].sudo().create({
'model_id': rec.model_id.id,
'form_view_id': rec.form_view_id.id,
'list_view_id': rec.list_view_id.id,
'stage_id': stage.id,
'name': stage.name,
'value': value
})
else:
self.update_selection()
def update_selection(self):
"""Update states/stages when dynamic workflow changes"""
odoo_dynamic_workflow = self.env['ir.module.module'].sudo().search([
('name', '=', 'odoo_dynamic_workflow')
])
for rec in self:
if odoo_dynamic_workflow and odoo_dynamic_workflow.state == 'installed':
if rec.model_name and rec.model_name in self.env:
model = self.env[rec.model_name]
work_folow_active = self.env['odoo.workflow'].sudo().search([
('model_id', '=', rec.model_id.id),
('active', '=', True)
])
state = self.env['node.state'].sudo().search([('is_workflow', '=', True)])
work_folow_name = work_folow_active.node_ids.filtered(
lambda r: r.code_node == False and r.active == True
).mapped("node_name")
state_name = state.mapped('state')
if not rec.is_stage and rec.model_name not in ('hr.holidays', 'hr.leave'):
for line in work_folow_active.node_ids:
if not line.code_node and line.active:
if not self.env['node.state'].sudo().search([('state', '=', line.node_name)]):
self.env['node.state'].create({
'model_id': rec.model_id.id,
'form_view_id': rec.form_view_id.id,
'list_view_id': rec.list_view_id.id,
'action_id': rec.action_id.id,
'state': line.node_name,
'name': line.name,
'is_workflow': True
})
diffs = list(set(state_name) - set(work_folow_name))
self.env['node.state'].sudo().search([('state', 'in', diffs)]).unlink()
# Handle stage updates
if rec.is_stage:
rel = self.env['ir.model.fields'].sudo().search([
('model_id', '=', rec.model_id.id),
('name', '=', 'stage_id')
])
current_model = self.env['stage.stage'].sudo().search([('model_id', '=', rec.model_id.id)]).ids
if rel:
rel_ids = self.env[rel.relation].sudo().search([])
for r in rel_ids:
if r.id not in current_model:
self.env['stage.stage'].create({
'model_id': rec.model_id.id,
'form_view_id': rec.form_view_id.id,
'list_view_id': rec.list_view_id.id,
'stage_id': r.id,
'name': r.name
})
def compute_selection(self):
"""Compute states or stages depending on chosen model"""
for rec in self:
rec.is_button = True
if not rec.model_name or rec.model_name not in self.env:
raise ValidationError(_('Please select a valid model first.'))
model = self.env[rec.model_name]
current_model = self.env['node.state'].sudo().search([('model_id', '=', rec.model_id.id)])
# Handle hr.holidays/hr.leave (can have both states and stages)
if rec.model_name in ('hr.holidays', 'hr.leave'):
rec.is_double = True
if not current_model:
if 'state' in model._fields:
nodes = model._fields.get('state')._description_selection(self.env)
for node in nodes:
self.env['node.state'].create({
'model_id': rec.model_id.id,
'form_view_id': rec.form_view_id.id,
'list_view_id': rec.list_view_id.id,
'action_id': rec.action_id.id,
'state': node[0],
'name': node[1]
})
rel = self.env['ir.model.fields'].sudo().search([
('model_id', '=', rec.model_id.id),
('name', '=', 'stage_id')
])
if rel:
rel_ids = self.env[rel.relation].sudo().search([])
for r in rel_ids:
if hasattr(r, 'state') and r.state == 'approved':
self.env['node.state'].create({
'model_id': rec.model_id.id,
'form_view_id': rec.form_view_id.id,
'list_view_id': rec.list_view_id.id,
'action_id': rec.action_id.id,
'stage_id': r.id,
'name': r.name,
'is_holiday_workflow': True
})
else:
self.update_selection()
# Handle state-based models
elif 'state' in model._fields:
if not current_model:
nodes = model._fields.get('state')._description_selection(self.env)
for node in nodes:
self.env['node.state'].create({
'model_id': rec.model_id.id,
'form_view_id': rec.form_view_id.id,
'list_view_id': rec.list_view_id.id,
'action_id': rec.action_id.id,
'state': node[0],
'name': node[1]
})
else:
self.update_selection()
# Handle stage-based models
elif 'stage_id' in model._fields:
rel = self.env['ir.model.fields'].sudo().search([
('model_id', '=', rec.model_id.id),
('name', '=', 'stage_id')
])
if rel:
self._get_stage(rel.relation)
else:
raise ValidationError(_('This model has no states nor stages.'))
class BaseDashboardLine(models.Model):
_name = 'base.dashbord.line'
_description = 'Dashboard Builder Line'
name = fields.Char(string='Name')
group_ids = fields.Many2many(
string='Groups',
comodel_name='res.groups'
)
board_id = fields.Many2one(
string='Dashboard',
comodel_name='base.dashbord',
ondelete='cascade'
)
state_id = fields.Many2one(
string='State',
comodel_name='node.state'
)
stage_id = fields.Many2one(
string='Stage',
comodel_name='stage.stage'
)
model_id = fields.Many2one(
string='Model',
comodel_name='ir.model'
)
model_name = fields.Char(string='Model Name')
sequence = fields.Integer(string='Sequence')
@api.onchange('state_id')
def onchange_state_id(self):
if self.state_id:
state_ids = [stat.state_id.id for stat in self.board_id.line_ids]
if state_ids.count(self.state_id.id) > 2:
raise ValidationError(_('This state is already selected.'))
@api.onchange('stage_id')
def onchange_stage_id(self):
if self.stage_id:
stage_ids = [stat.stage_id.id for stat in self.board_id.line_ids]
if stage_ids.count(self.stage_id.id) > 2:
raise ValidationError(_('This stage is already selected.'))
class NodeState(models.Model):
_name = 'node.state'
_description = 'Dashboard Node State'
name = fields.Char(string='Name', translate=True)
state = fields.Char(string='State', translate=True)
stage_id = fields.Char(string='Stage', readonly=True)
is_workflow = fields.Boolean(string='Is Workflow', readonly=True)
is_holiday_workflow = fields.Boolean(string='Is Holiday Workflow', readonly=True)
model_id = fields.Many2one(
string='Model',
comodel_name='ir.model',
readonly=True
)
form_view_id = fields.Many2one(
'ir.ui.view',
string='Form View',
readonly=True
)
list_view_id = fields.Many2one(
'ir.ui.view',
string='List View',
readonly=True
)
action_id = fields.Many2one(
'ir.actions.act_window',
string='Action',
readonly=True
)
@api.depends('name', 'state')
def _compute_display_name(self):
for record in self:
if self.env.user.lang == 'en_US':
record.display_name = record.state or record.name or ''
else:
record.display_name = record.name or record.state or ''
class StageStage(models.Model):
_name = 'stage.stage'
_description = 'Dashboard Stage'
name = fields.Char(string='Name', translate=True, readonly=True)
stage_id = fields.Char(string='Stage', readonly=True)
value = fields.Char(string='Value', readonly=True)
model_id = fields.Many2one(
string='Model',
comodel_name='ir.model',
readonly=True
)
form_view_id = fields.Many2one(
'ir.ui.view',
string='Form View',
readonly=True
)
list_view_id = fields.Many2one(
'ir.ui.view',
string='List View',
readonly=True
)
action_id = fields.Many2one(
'ir.actions.act_window',
string='Action',
readonly=True
)
@api.depends('name', 'value')
def _compute_display_name(self):
for record in self:
if self.env.user.lang == 'en_US':
record.display_name = record.name or ''
else:
record.display_name = record.value or record.name or ''