hr_efficiency_dynamic_field.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. from odoo import models, fields, api, _
  2. from odoo.exceptions import ValidationError
  3. class HrEfficiencyDynamicField(models.Model):
  4. _name = 'hr.efficiency.dynamic.field'
  5. _description = 'Dynamic Field Configuration'
  6. _order = 'sequence, name'
  7. name = fields.Char('Field Name', required=True, help='Technical field name (e.g., planning_efficiency)')
  8. label = fields.Char('Field Label', required=True, help='Display name (e.g., Planning Efficiency)')
  9. indicator_id = fields.Many2one('hr.efficiency.indicator', 'Related Indicator', required=True, ondelete='cascade')
  10. # Display settings
  11. sequence = fields.Integer('Sequence', default=10, help='Order in which fields appear')
  12. active = fields.Boolean('Active', default=True, help='Whether this field is visible')
  13. # View settings
  14. show_in_list = fields.Boolean('Show in List View', default=True)
  15. show_in_form = fields.Boolean('Show in Form View', default=True)
  16. show_in_search = fields.Boolean('Show in Search View', default=False)
  17. # Formatting
  18. widget = fields.Selection([
  19. ('percentage', 'Percentage'),
  20. ('float_time', 'Float Time'),
  21. ('number', 'Number'),
  22. ('text', 'Text'),
  23. ('monetary', 'Monetary'),
  24. ], string='Widget', default='percentage', help='How to display the field')
  25. # Styling
  26. decoration_success = fields.Float('Success Threshold', help='Value above which to show green')
  27. decoration_warning = fields.Float('Warning Threshold', help='Value above which to show yellow')
  28. decoration_danger = fields.Float('Danger Threshold', help='Value below which to show red')
  29. priority_background_color = fields.Char('Priority Background Color', help='Color de fondo de la columna basado en la prioridad del indicador')
  30. # Computed fields
  31. field_technical_name = fields.Char('Technical Field Name', compute='_compute_technical_name', store=True)
  32. @api.depends('name')
  33. def _compute_technical_name(self):
  34. for record in self:
  35. if record.name:
  36. # Convert to valid field name
  37. field_name = record.name.lower()
  38. field_name = field_name.replace(' ', '_').replace('-', '_').replace('(', '').replace(')', '')
  39. field_name = field_name.replace('í', 'i').replace('á', 'a').replace('é', 'e').replace('ó', 'o').replace('ú', 'u')
  40. field_name = field_name.replace('ñ', 'n')
  41. # Ensure it starts with a letter
  42. if not field_name[0].isalpha():
  43. field_name = 'indicator_' + field_name
  44. record.field_technical_name = field_name
  45. @api.constrains('name')
  46. def _check_name_unique(self):
  47. for record in self:
  48. if self.search_count([('name', '=', record.name), ('id', '!=', record.id)]) > 0:
  49. raise ValidationError(_('Field name must be unique'))
  50. @api.model_create_multi
  51. def create(self, vals_list):
  52. """Create dynamic fields and update model"""
  53. records = super().create(vals_list)
  54. self._update_model_fields()
  55. return records
  56. def write(self, vals):
  57. """Update dynamic field and update model"""
  58. result = super().write(vals)
  59. self._update_model_fields()
  60. return result
  61. def unlink(self):
  62. """Delete dynamic fields and update model"""
  63. result = super().unlink()
  64. self._update_model_fields()
  65. return result
  66. @api.model
  67. def _update_model_fields(self):
  68. """Update the hr.efficiency model with dynamic fields"""
  69. efficiency_model = self.env['hr.efficiency']
  70. # Get all active dynamic fields
  71. dynamic_fields = self.search([('active', '=', True)], order='sequence')
  72. # Clear existing dynamic fields from model (only those starting with 'indicator_')
  73. for field_name in list(efficiency_model._fields.keys()):
  74. if field_name.startswith('indicator_'):
  75. if hasattr(efficiency_model.__class__, field_name):
  76. try:
  77. delattr(efficiency_model.__class__, field_name)
  78. except AttributeError:
  79. pass # Field might not exist, ignore
  80. # Add new dynamic fields
  81. for dynamic_field in dynamic_fields:
  82. field_name = dynamic_field.field_technical_name
  83. # Create field with appropriate widget
  84. field_kwargs = {
  85. 'string': dynamic_field.label,
  86. 'compute': '_compute_indicators',
  87. 'store': True,
  88. 'help': f'Dynamic indicator: {dynamic_field.indicator_id.name}',
  89. }
  90. # Add widget if specified
  91. if dynamic_field.widget:
  92. field_kwargs['widget'] = dynamic_field.widget
  93. # Create the field
  94. field = fields.Float(**field_kwargs)
  95. setattr(efficiency_model.__class__, field_name, field)
  96. def action_toggle_visibility(self):
  97. """Toggle field visibility"""
  98. for record in self:
  99. record.active = not record.active
  100. self._update_model_fields()
  101. def action_move_up(self):
  102. """Move field up in sequence"""
  103. for record in self:
  104. prev_record = self.search([
  105. ('sequence', '<', record.sequence),
  106. ('active', '=', True)
  107. ], order='sequence desc', limit=1)
  108. if prev_record:
  109. record.sequence, prev_record.sequence = prev_record.sequence, record.sequence
  110. def action_move_down(self):
  111. """Move field down in sequence"""
  112. for record in self:
  113. next_record = self.search([
  114. ('sequence', '>', record.sequence),
  115. ('active', '=', True)
  116. ], order='sequence', limit=1)
  117. if next_record:
  118. record.sequence, next_record.sequence = next_record.sequence, record.sequence