helpdesk_rating_five_stars/models/rating_rating.py
2025-11-26 10:39:26 +07:00

144 lines
5.1 KiB
Python

# -*- coding: utf-8 -*-
from odoo import api, fields, models
from odoo.exceptions import ValidationError
import logging
_logger = logging.getLogger(__name__)
class Rating(models.Model):
_inherit = 'rating.rating'
_description = 'Rating with 5-star support'
# Enable audit logging for rating changes
_log_access = True
# Override rating field to support 0-5 range
rating = fields.Float(
string='Rating Value',
required=True,
help='Rating value: 0 (no rating), 1-5 (stars)',
aggregator="avg",
tracking=True # Track changes to rating value
)
# Computed fields for star display
rating_stars_filled = fields.Integer(
compute='_compute_rating_stars',
string='Filled Stars',
help='Number of filled stars to display'
)
rating_stars_empty = fields.Integer(
compute='_compute_rating_stars',
string='Empty Stars',
help='Number of empty stars to display'
)
# Audit fields - track who submitted/modified the rating
feedback = fields.Text(
string='Feedback',
tracking=True # Track changes to feedback
)
consumed = fields.Boolean(
string='Rating Submitted',
tracking=True # Track when rating is consumed
)
@api.constrains('rating')
def _check_rating_value(self):
"""Validate rating is between 0 and 5"""
for record in self:
if record.rating < 0 or record.rating > 5:
raise ValidationError(
'Rating must be between 0 and 5 stars. '
'Received value: %s' % record.rating
)
# Allow 0 (no rating) or values between 1-5
if record.rating > 0 and record.rating < 1:
raise ValidationError(
'Rating must be 0 (no rating) or between 1 and 5 stars. '
'Received value: %s' % record.rating
)
@api.depends('rating')
def _compute_rating_stars(self):
"""Compute the number of filled and empty stars"""
for record in self:
# Round rating to nearest integer for display
rating_int = round(record.rating)
record.rating_stars_filled = rating_int
record.rating_stars_empty = 5 - rating_int
def _get_rating_stars_html(self):
"""Generate HTML for star display"""
self.ensure_one()
filled_stars = self.rating_stars_filled
empty_stars = self.rating_stars_empty
# Unicode star characters
filled_star = '' # U+2605 BLACK STAR
empty_star = '' # U+2606 WHITE STAR
# Generate HTML with stars
html = '<span class="o_rating_stars">'
html += '<span class="o_rating_stars_filled">' + (filled_star * filled_stars) + '</span>'
html += '<span class="o_rating_stars_empty">' + (empty_star * empty_stars) + '</span>'
html += '</span>'
return html
def write(self, vals):
"""Override write to add audit logging for rating changes"""
# Log rating changes for audit trail
for record in self:
if 'rating' in vals and vals['rating'] != record.rating:
old_value = record.rating
new_value = vals['rating']
_logger.info(
'Rating modified: ID=%s, Model=%s, ResID=%s, OldValue=%s, NewValue=%s, User=%s',
record.id,
record.res_model,
record.res_id,
old_value,
new_value,
self.env.user.login
)
# Post message to chatter if available
if record.res_model and record.res_id:
try:
resource = self.env[record.res_model].browse(record.res_id)
if resource.exists() and hasattr(resource, 'message_post'):
resource.message_post(
body=f'Rating updated from {int(old_value)} to {int(new_value)} stars',
subject='Rating Updated',
message_type='notification',
subtype_xmlid='mail.mt_note'
)
except Exception as e:
_logger.warning('Could not post rating change to chatter: %s', str(e))
return super(Rating, self).write(vals)
@api.model_create_multi
def create(self, vals_list):
"""Override create to add audit logging for new ratings"""
records = super(Rating, self).create(vals_list)
# Log new ratings for audit trail
for record in records:
if record.rating > 0:
_logger.info(
'Rating created: ID=%s, Model=%s, ResID=%s, Value=%s, User=%s',
record.id,
record.res_model,
record.res_id,
record.rating,
self.env.user.login
)
return records