update code : promote - approval flow works (need more testing)
This commit is contained in:
parent
a6b9292857
commit
bb344d53fb
@ -2,10 +2,11 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import base64
|
||||
|
||||
import logging
|
||||
from odoo import api, fields, models, tools, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
CATEGORY_SELECTION = [
|
||||
('required', 'Required'),
|
||||
@ -97,25 +98,28 @@ class ApprovalCategory(models.Model):
|
||||
model_id = fields.Many2one('ir.model', string='Model', store=True)
|
||||
model_name = fields.Char(string='Model Name', related='model_id.model', store=True)
|
||||
|
||||
invalid_minimum = fields.Boolean("Invalid Minimum", compute="_compute_invalid_minimum", store=True, readonly=False)
|
||||
invalid_minimum_warning = fields.Boolean("Invalid minimum Warning",compute="_compute_invalid_minimum_warning")
|
||||
domain = fields.Char(compute='_compute_domain')
|
||||
# invalid_minimum = fields.Boolean("Invalid Minimum", compute="_compute_invalid_minimum", store=True, readonly=False)
|
||||
# invalid_minimum_warning = fields.Boolean("Invalid minimum Warning",compute="_compute_invalid_minimum_warning")
|
||||
# domain = fields.Char(string='Conditions')
|
||||
|
||||
|
||||
|
||||
# ---------------------------------------- Methods -----------------------------------------
|
||||
|
||||
def _compute_domain(self):
|
||||
for record in self:
|
||||
record.domain = record.conditions_ids.domain if record.conditions_ids else '[]'
|
||||
@api.depends('conditions_ids')
|
||||
def _compute_invalid_minimum(self):
|
||||
for record in self.filtered('conditions_ids'):
|
||||
record.invalid_minimum = record.conditions_ids.invalid_minimum
|
||||
@api.depends('conditions_ids')
|
||||
def _compute_invalid_minimum_warning(self):
|
||||
for record in self.filtered('conditions_ids'):
|
||||
record.invalid_minimum_warning = record.conditions_ids.invalid_minimum_warning
|
||||
|
||||
|
||||
# @api.depends('conditions_ids')
|
||||
# def _compute_invalid_minimum(self):
|
||||
# for record in self:
|
||||
# # If you expect to compute the invalid_minimum for each condition, loop through them
|
||||
# invalid_minimums = record.conditions_ids.mapped('invalid_minimum')
|
||||
# if invalid_minimums:
|
||||
# # Handle the logic for determining invalid_minimum (example)
|
||||
# record.invalid_minimum = any(invalid_minimums)
|
||||
# @api.depends('conditions_ids')
|
||||
# def _compute_invalid_minimum_warning(self):
|
||||
# for record in self.filtered('conditions_ids'):
|
||||
# record.invalid_minimum_warning = record.conditions_ids.invalid_minimum_warning
|
||||
def _compute_request_to_validate_count(self):
|
||||
domain = [('request_status', '=', 'pending'), ('approver_ids.user_id', '=', self.env.user.id)]
|
||||
requests_data = self.env['approval.request']._read_group(domain, ['category_id'], ['__count'])
|
||||
@ -168,7 +172,8 @@ class ApprovalCategory(models.Model):
|
||||
'company_id': vals.get('company_id'),
|
||||
})
|
||||
vals['sequence_id'] = sequence.id
|
||||
return super().create(vals_list)
|
||||
res = super().create(vals_list)
|
||||
return res
|
||||
|
||||
def write(self, vals):
|
||||
if 'sequence_code' in vals:
|
||||
@ -188,7 +193,9 @@ class ApprovalCategory(models.Model):
|
||||
for approval_category in self:
|
||||
if approval_category.sequence_id:
|
||||
approval_category.sequence_id.company_id = vals.get('company_id')
|
||||
return super().write(vals)
|
||||
|
||||
res = super().write(vals)
|
||||
return res
|
||||
|
||||
def create_request(self):
|
||||
self.ensure_one()
|
||||
|
@ -12,8 +12,9 @@ class ApprovalCategoryApprover(models.Model):
|
||||
_rec_name = 'user_id'
|
||||
_order = 'sequence'
|
||||
sequence = fields.Integer('Sequence', default=10)
|
||||
category_id = fields.Many2one('approval.category', string='Approval Type', required=True)
|
||||
company_id = fields.Many2one('res.company', related='category_id.company_id')
|
||||
category_id = fields.Many2one('approval.category', string='Approval Type')
|
||||
condition_id = fields.Many2one('approval.category.condition', string='Approval Condition')
|
||||
company_id = fields.Many2one('res.company', related='condition_id.company_id')
|
||||
user_id = fields.Many2one('res.users', string='User', ondelete='cascade', required=True,
|
||||
check_company=True, domain="[('company_ids', 'in', company_id), ('id', 'not in', existing_user_ids)]")
|
||||
job_title = fields.Char(string='Job Position', compute='_compute_job_title', store=True)
|
||||
@ -33,7 +34,7 @@ class ApprovalCategoryApprover(models.Model):
|
||||
record.job_title = employee.job_title if employee else ''
|
||||
else:
|
||||
record.job_title = ''
|
||||
@api.depends('category_id')
|
||||
@api.depends('condition_id')
|
||||
def _compute_existing_user_ids(self):
|
||||
for record in self:
|
||||
record.existing_user_ids = record.category_id.approver_ids.user_id
|
||||
record.existing_user_ids = record.condition_id.approver_ids.user_id
|
||||
|
@ -8,12 +8,16 @@ class ApprovalConditions(models.Model):
|
||||
|
||||
sequence = fields.Integer(default=1)
|
||||
description = fields.Text(string="Description")
|
||||
company_id = fields.Many2one(
|
||||
'res.company', 'Company', copy=False,
|
||||
required=True, index=True, default=lambda s: s.env.company)
|
||||
approver_sequence = fields.Boolean('Approvers Sequence?', help="If checked, the approvers have to approve in sequence.")
|
||||
approval_minimum = fields.Integer(string="Minimum Approval", default=5, required=True)
|
||||
invalid_minimum = fields.Boolean(compute='_compute_invalid_minimum')
|
||||
invalid_minimum_warning = fields.Char(compute='_compute_invalid_minimum')
|
||||
approver_ids = fields.One2many('approval.category.approver', 'category_id', string="Approvers")
|
||||
category_id = fields.Many2one('approval.category', string="Approval Category", required=True,ondelete="cascade")
|
||||
approver_ids = fields.One2many('approval.category.approver', 'condition_id', string="Approvers")
|
||||
user_ids = fields.Many2many('res.users', compute='_compute_user_ids', string="Approver Users")
|
||||
category_id = fields.Many2one('approval.category', string="Approval Category")
|
||||
model_id = fields.Many2one('ir.model', string='Model', related='category_id.model_id', store=True, readonly=True)
|
||||
model_name = fields.Char(
|
||||
string='Model Name',
|
||||
@ -21,7 +25,7 @@ class ApprovalConditions(models.Model):
|
||||
store=True,
|
||||
readonly=True
|
||||
)
|
||||
domain = fields.Char(string="Conditions", compute='_compute_domain', readonly=False, store=True)
|
||||
domain = fields.Char(string="Conditions", readonly=False, store=True)
|
||||
|
||||
# @api.depends('category_id.model_id')
|
||||
# def _compute_model_id(self):
|
||||
@ -29,11 +33,10 @@ class ApprovalConditions(models.Model):
|
||||
# for record in self:
|
||||
# record.model_id = record.category_id.model_id
|
||||
|
||||
|
||||
@api.depends('model_id')
|
||||
def _compute_domain(self):
|
||||
@api.depends('approver_ids')
|
||||
def _compute_user_ids(self):
|
||||
for record in self:
|
||||
record.domain = '[]'
|
||||
record.user_ids = record.approver_ids.user_id
|
||||
|
||||
@api.depends('approval_minimum', 'approver_ids')
|
||||
def _compute_invalid_minimum(self):
|
||||
@ -54,6 +57,15 @@ class ApprovalConditions(models.Model):
|
||||
# for record in self:
|
||||
# record.filter_id = False
|
||||
|
||||
@api.constrains('approver_ids')
|
||||
def _constrains_approver_ids(self):
|
||||
# There seems to be a problem with how the database is updated which doesn't let use to an sql constraint for this
|
||||
# Issue is: records seem to be created before others are saved, meaning that if you originally have only user a
|
||||
# change user a to user b and add a new line with user a, the second line will be created and will trigger the constraint
|
||||
# before the first line will be updated which wouldn't trigger a ValidationError
|
||||
for record in self:
|
||||
if len(record.approver_ids) != len(record.approver_ids.user_id):
|
||||
raise ValidationError(_('An user may not be in the approver list multiple times.'))
|
||||
@api.constrains('approval_minimum', 'approver_ids')
|
||||
def _constrains_approval_minimum(self):
|
||||
for record in self:
|
||||
|
@ -21,7 +21,7 @@ class ApprovalPromotionLine(models.Model):
|
||||
current_job = fields.Char(
|
||||
"Current Job", required=True,
|
||||
compute="_compute_promotion", store=True, readonly=True, precompute=True)
|
||||
designation_job = fields.Char(
|
||||
new_designation = fields.Char(
|
||||
"New Designation", required=True,
|
||||
compute="_compute_promotion", store=True, readonly=True, precompute=True)
|
||||
@api.depends('promote_id')
|
||||
@ -29,5 +29,4 @@ class ApprovalPromotionLine(models.Model):
|
||||
for line in self:
|
||||
line.description = line.promote_id.description or line.promote_id.display_name
|
||||
line.current_job = line.promote_id.job_id.name or ''
|
||||
line.designation_job = line.promote_id.designation_id.name or ''
|
||||
|
||||
line.new_designation = line.promote_id.designation_id.name or ''
|
@ -1,9 +1,9 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
from odoo import api, Command, fields, models, _
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
|
||||
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
_logger = logging.getLogger(__name__)
|
||||
class ApprovalRequest(models.Model):
|
||||
_name = 'approval.request'
|
||||
_description = 'Approval Request'
|
||||
@ -68,7 +68,6 @@ class ApprovalRequest(models.Model):
|
||||
has_location = fields.Selection(related="category_id.has_location")
|
||||
has_product = fields.Selection(related="category_id.has_product")
|
||||
has_promotion = fields.Selection(related="category_id.has_promotion")
|
||||
|
||||
requirer_document = fields.Selection(related="category_id.requirer_document")
|
||||
approval_minimum = fields.Integer(related="category_id.approval_minimum")
|
||||
approval_type = fields.Selection(related="category_id.approval_type")
|
||||
@ -99,30 +98,81 @@ class ApprovalRequest(models.Model):
|
||||
for request in self:
|
||||
if request.date_start and request.date_end and request.date_start > request.date_end:
|
||||
raise ValidationError(_("Start date should precede the end date."))
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
category = 'category_id' in vals and self.env['approval.category'].browse(vals['category_id'])
|
||||
if category and category.automated_sequence:
|
||||
vals['name'] = category.sequence_id.next_by_id()
|
||||
self._check_conditions_promotion(vals)
|
||||
created_requests = super().create(vals_list)
|
||||
self._check_conditions(created_requests)
|
||||
for request in created_requests:
|
||||
request.message_subscribe(partner_ids=request.request_owner_id.partner_id.ids)
|
||||
return created_requests
|
||||
|
||||
def _check_conditions_promotion(self,vals):
|
||||
if vals.get('has_promotion'):
|
||||
promotion_lines = vals.get('promotion_line_ids', [])
|
||||
approval_conditions_to_add = []
|
||||
if promotion_lines:
|
||||
for promotion_line in promotion_lines:
|
||||
promotion_line_record = self.env['approval.promotion.line'].browse(promotion_line)
|
||||
if promotion_line_record and promotion_line_record.promote_id:
|
||||
def _check_conditions(self,request):
|
||||
data = request
|
||||
if isinstance(request, bool):
|
||||
_logger.error(f"Expected 'vals' to be a list, got {type(request)}")
|
||||
data = self
|
||||
for request in data:
|
||||
if request.has_promotion:
|
||||
promotion_lines = request.promotion_line_ids
|
||||
if promotion_lines:
|
||||
conditions = request.category_id.conditions_ids
|
||||
for condition in conditions:
|
||||
satisfy_condition = False
|
||||
domain = condition.domain if condition.domain else []
|
||||
|
||||
return
|
||||
return
|
||||
# Ensure domain is in list or tuple format
|
||||
if isinstance(domain, str):
|
||||
try:
|
||||
domain = safe_eval(domain)
|
||||
except Exception as e:
|
||||
_logger.error(f"Failed to evaluate domain: {e}")
|
||||
domain = []
|
||||
elif not isinstance(domain, (list, tuple)):
|
||||
domain = []
|
||||
|
||||
_logger.debug(f"Evaluating condition: {condition}, Domain: {domain}")
|
||||
|
||||
for promotion_line in promotion_lines:
|
||||
if not promotion_line.promote_id or not promotion_line.promote_id.employee_id:
|
||||
_logger.warning(f"No promote_id or employee_id found for promotion_line: {promotion_line.id}")
|
||||
continue
|
||||
|
||||
model_name = request.category_id.model_name
|
||||
# if not hasattr(self.env, model_name):
|
||||
# _logger.error(f"Invalid model name: {model_name}")
|
||||
# continue
|
||||
|
||||
result = self.env[model_name].search(domain)
|
||||
# Compare promotion_line with result
|
||||
for res in result:
|
||||
if promotion_line.promote_id.employee_id.id == res.id:
|
||||
satisfy_condition = True
|
||||
break
|
||||
if satisfy_condition:
|
||||
request._add_approvers(condition)
|
||||
else:
|
||||
_logger.info('Condition not satisfied')
|
||||
|
||||
def _add_approvers(self, condition):
|
||||
for request in self:
|
||||
approvers = condition.approver_ids
|
||||
for approver in approvers:
|
||||
existing_approver = request.approver_ids.filtered(lambda x: x.user_id == approver.user_id)
|
||||
if existing_approver:
|
||||
existing_approver.write({
|
||||
'status': 'pending',
|
||||
})
|
||||
else:
|
||||
self.env['approval.approver'].create({
|
||||
'user_id': approver.user_id.id,
|
||||
'request_id': request.id,
|
||||
'required':approver.required,
|
||||
'status': 'pending'
|
||||
})
|
||||
|
||||
@api.ondelete(at_uninstall=False)
|
||||
def unlink_attachments(self):
|
||||
attachment_ids = self.env['ir.attachment'].search([
|
||||
@ -168,7 +218,6 @@ class ApprovalRequest(models.Model):
|
||||
approvers = approvers[0] if approvers and approvers[0].status != 'pending' else self.env['approval.approver']
|
||||
else:
|
||||
approvers = approvers.filtered(lambda a: a.status == 'new')
|
||||
|
||||
approvers._create_activity()
|
||||
approvers.sudo().write({'status': 'pending'})
|
||||
self.sudo().write({'date_confirmed': fields.Datetime.now()})
|
||||
@ -230,7 +279,17 @@ class ApprovalRequest(models.Model):
|
||||
subject=subject,
|
||||
partner_ids=approval.request_owner_id.partner_id.ids,
|
||||
)
|
||||
|
||||
if self.has_promotion:
|
||||
all_confirm = True
|
||||
for approver in self.approver_ids:
|
||||
if approver.required and approver.status != 'approved':
|
||||
all_confirm = False
|
||||
if all_confirm:
|
||||
for promote in self.promotion_line_ids:
|
||||
promote_records = self.env['hr.promote'].search([('id', '=', promote.promote_id.id)],limit=1)
|
||||
promote_records.write({
|
||||
'state': 'confirmed'
|
||||
})
|
||||
self.sudo()._update_next_approvers('pending', approver, only_next_approver=True)
|
||||
self.sudo()._get_user_approval_activities(user=self.env.user).action_feedback()
|
||||
|
||||
@ -360,9 +419,8 @@ class ApprovalRequest(models.Model):
|
||||
if 'request_owner_id' in vals:
|
||||
for approval in self:
|
||||
approval.message_unsubscribe(partner_ids=approval.request_owner_id.partner_id.ids)
|
||||
|
||||
res = super().write(vals)
|
||||
|
||||
self._check_conditions(res)
|
||||
if 'request_owner_id' in vals:
|
||||
for approval in self:
|
||||
approval.message_subscribe(partner_ids=approval.request_owner_id.partner_id.ids)
|
||||
@ -417,7 +475,6 @@ class ApprovalApprover(models.Model):
|
||||
category_approver = fields.Boolean(compute='_compute_category_approver')
|
||||
can_edit = fields.Boolean(compute='_compute_can_edit')
|
||||
can_edit_user_id = fields.Boolean(compute='_compute_can_edit', help="Simple users should not be able to remove themselves as approvers because they will lose access to the record if they misclick.")
|
||||
|
||||
def action_approve(self):
|
||||
self.request_id.action_approve(self)
|
||||
|
||||
|
@ -79,7 +79,7 @@
|
||||
<notebook>
|
||||
<page string="Conditions">
|
||||
<group>
|
||||
<field name="conditions_ids" nolabel="1"/>
|
||||
<field name="conditions_ids" widget="one2many_list" nolabel="1"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Options">
|
||||
|
@ -8,7 +8,7 @@
|
||||
<field name="company_id" column_invisible="True"/>
|
||||
<field name="promote_id"/>
|
||||
<field name="current_job" />
|
||||
<field name="designation_job"/>
|
||||
<field name="new_designation"/>
|
||||
<field name="description"/>
|
||||
</list>
|
||||
</field>
|
||||
@ -25,7 +25,7 @@
|
||||
<field name="promote_id"/>
|
||||
<field name="description"/>
|
||||
<!-- <field name="current_job" options="{'no_create': True, 'no_open': True}"/>
|
||||
<field name="designation_job" options="{'no_create': True, 'no_open': True}"/> -->
|
||||
<field name="new_designation" options="{'no_create': True, 'no_open': True}"/> -->
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
@ -41,7 +41,7 @@
|
||||
<templates>
|
||||
<t t-name="card" class="d-flex justify-content-between">
|
||||
<field name="promote_id" class="fw-bolder fs-5"/>
|
||||
<field name="designation_job" class="fw-bolder fs-5"/>
|
||||
<field name="new_designation" class="fw-bolder fs-5"/>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
|
@ -103,8 +103,20 @@ class HRPromote(models.Model):
|
||||
raise exceptions.ValidationError("The employee does not have a current job position.")
|
||||
promotion_record = super(HRPromote, self).create(vals)
|
||||
promotion_records.append(promotion_record)
|
||||
# Create an Approval request
|
||||
|
||||
category = self.env['approval.category'].search([("has_promotion", "=", "required")], limit=1)
|
||||
if not category:
|
||||
category = self.env['approval.category'].search([], limit=1)
|
||||
# Create the approval request
|
||||
self.env['approval.request'].create({
|
||||
'category_id': category.id,
|
||||
'date': fields.Datetime.now(),
|
||||
'company_id': self.env.company.id,
|
||||
'name': 'Automatic',
|
||||
'promotion_line_ids': [(0, 0, {
|
||||
'company_id': self.env.company.id,
|
||||
'promote_id': promotion_record.id,
|
||||
})],
|
||||
})
|
||||
return self.browse([record.id for record in promotion_records])
|
||||
def action_save_and_close(self):
|
||||
return {
|
||||
|
Loading…
Reference in New Issue
Block a user