diff --git a/extra-addons/approvals/__manifest__.py b/extra-addons/approvals/__manifest__.py
index c516d2d04..92fbca0ac 100644
--- a/extra-addons/approvals/__manifest__.py
+++ b/extra-addons/approvals/__manifest__.py
@@ -33,6 +33,7 @@ creates next activities for the related approvers.
'report/approval_request_report.xml',
'views/approval_request_views.xml',
'views/res_users_views.xml',
+ 'views/approval_condition_views.xml'
],
'demo':[
'data/approval_demo.xml',
diff --git a/extra-addons/approvals/models/__init__.py b/extra-addons/approvals/models/__init__.py
index b6876aff2..7ac1bd6ae 100644
--- a/extra-addons/approvals/models/__init__.py
+++ b/extra-addons/approvals/models/__init__.py
@@ -7,3 +7,4 @@ from . import approval_product_line
from . import approval_request
from . import mail_activity
from . import ir_attachment
+from . import approval_condition
diff --git a/extra-addons/approvals/models/approval_category.py b/extra-addons/approvals/models/approval_category.py
index 48aec27c9..c9d2bad1e 100644
--- a/extra-addons/approvals/models/approval_category.py
+++ b/extra-addons/approvals/models/approval_category.py
@@ -95,6 +95,8 @@ class ApprovalCategory(models.Model):
copy=False, check_company=True)
model_id = fields.Many2one('ir.model', 'Model',copy=False)
+
+ conditions_ids = fields.Many2many('approval.condition', 'approval_category_condition_rel', 'category_id', 'condition_id')
# ---------------------------------------- Methods -----------------------------------------
def _compute_request_to_validate_count(self):
diff --git a/extra-addons/approvals/models/approval_condition.py b/extra-addons/approvals/models/approval_condition.py
new file mode 100644
index 000000000..f39de8204
--- /dev/null
+++ b/extra-addons/approvals/models/approval_condition.py
@@ -0,0 +1,77 @@
+from odoo import api, fields, models
+
+class ApprovalConditions(models.Model):
+ _name = 'approval.condition'
+ _description = 'Approval Condition'
+ _order = 'sequence, id'
+
+ name = fields.Char(string="Name", required=True)
+ description = fields.Text(string="Description", required=True)
+
+ filter_pre_condition = fields.Char(
+ string='Condition',
+ compute='_compute_filter_pre_condition',
+ readonly=False, store=True,
+ help="If present, this condition must be satisfied before the update of the record. "
+ "Not checked on record creation.")
+
+ trigger = fields.Selection(
+ [
+ ('on_stage_set', "Stage is set to"),
+ ('on_user_set', "User is set"),
+ ('on_tag_set', "Tag is added"),
+ ('on_state_set', "State is set to"),
+ ('on_priority_set', "Priority is set to"),
+ ('on_archive', "On archived"),
+ ('on_unarchive', "On unarchived"),
+ ('on_create_or_write', "On save"),
+ ('on_create', "On creation"), # deprecated, use 'on_create_or_write' instead
+ ('on_write', "On update"), # deprecated, use 'on_create_or_write' instead
+ ('on_unlink', "On deletion"),
+ ('on_change', "On UI change"),
+ ('on_time', "Based on date field"),
+ ('on_time_created', "After creation"),
+ ('on_time_updated', "After last update"),
+ ("on_message_received", "On incoming message"),
+ ("on_message_sent", "On outgoing message"),
+ ('on_webhook', "On webhook"),
+ ], string='Trigger',
+ compute='_compute_trigger', readonly=False, store=True, required=True
+ )
+
+ trigger_field_ids = fields.Many2many(
+ 'ir.model.fields', string='Trigger Fields',
+ compute='_compute_trigger_field_ids', readonly=False, store=True,
+ help="The automation rule will be triggered if and only if one of these fields is updated."
+ "If empty, all fields are watched.")
+
+ trg_field_ref = fields.Reference(
+ selection=[('ir.model.fields', 'Field Reference')],
+ string='Trigger Reference',
+ readonly=False,
+ store=True,
+ help="Some triggers need a reference to another field. This field is used to store it."
+ )
+
+ sequence = fields.Boolean('Approvers Sequence?', help="If checked, the approvers have to approve in sequence.")
+ approval_minimum = fields.Integer(string="Minimum Approval", default=1, required=True)
+ user_ids = fields.Many2many('res.users', compute='_compute_user_ids', string="Approver Users")
+ approver_ids = fields.One2many('approval.category.approver', 'category_id', string="Approvers")
+ @api.depends('approver_ids')
+ def _compute_user_ids(self):
+ for record in self:
+ record.user_ids = record.approver_ids.user_id
+ @api.depends('trigger', 'trigger_field_ids', 'trg_field_ref')
+ def _compute_filter_pre_condition(self):
+ for automation in self:
+ if automation.trigger != 'on_tag_set' or len(automation.trigger_field_ids) != 1:
+ automation.filter_pre_condition = False
+ else:
+ field = automation.trigger_field_ids.name
+ value = automation.trg_field_ref
+ automation.filter_pre_condition = f"[('{field}', 'not in', [{value}])]" if value else False
+
+ def delete_condition(self):
+ for record in self:
+ record.unlink()
+
diff --git a/extra-addons/approvals/security/ir.model.access.csv b/extra-addons/approvals/security/ir.model.access.csv
index 837145bcd..aea42a037 100644
--- a/extra-addons/approvals/security/ir.model.access.csv
+++ b/extra-addons/approvals/security/ir.model.access.csv
@@ -13,3 +13,5 @@ access_approval_approver_user,access_approval_approver,model_approval_approver,g
access_approval_approver_manager,access_approval_approver,model_approval_approver,group_approval_manager,1,1,1,1
access_approval_product_line_user,access_approval_product_line,model_approval_product_line,base.group_user,1,1,1,1
access_approval_product_line_manager,access_approval_product_line,model_approval_product_line,group_approval_manager,1,1,1,1
+access_approval_condition,access_approval_condition,model_approval_condition,base.group_user,1,1,1,1
+
diff --git a/extra-addons/approvals/static/src/views/kanban/approvals_category_kanban.scss b/extra-addons/approvals/static/src/views/kanban/approvals_category_kanban.scss
new file mode 100644
index 000000000..0be126336
--- /dev/null
+++ b/extra-addons/approvals/static/src/views/kanban/approvals_category_kanban.scss
@@ -0,0 +1,52 @@
+.o_approvals_category_actions_field,
+.o_approvals_category_kanban_view {
+ .o_kanban_ungrouped {
+ padding: 0;
+
+ .o_kanban_record {
+ width: 30%;
+ margin: 0;
+
+ &.o_kanban_ghost {
+ display: none;
+ }
+ }
+ }
+}
+
+.o_approvals_category_actions_field .o_kanban_ghost {
+ display: none;
+}
+
+.o_approvals_category_kanban_view {
+ .o_kanban_grouped .row {
+ flex-direction: column !important;
+ gap: 0.5rem !important;
+
+ > * {
+ width: 100% !important;
+
+ > * {
+ margin: 0 0.5rem !important;
+ }
+ }
+ }
+
+ .o_kanban_ungrouped .o_kanban_record .oe_kanban_global_click {
+ border-top: 0;
+ display: flex;
+ align-items: center;
+
+ .o_widget_web_ribbon {
+ align-self: flex-start;
+ }
+ }
+}
+.o_form_view .o_group .o_field_widget, .o_form_view .o_inner_group .o_field_widget {
+ width: 50%;
+}
+.o_approvals_category_error {
+ pre {
+ max-height: 25vh !important;
+ }
+}
diff --git a/extra-addons/approvals/views/approval_category_views.xml b/extra-addons/approvals/views/approval_category_views.xml
index 60ea973ee..e5a2159f5 100644
--- a/extra-addons/approvals/views/approval_category_views.xml
+++ b/extra-addons/approvals/views/approval_category_views.xml
@@ -76,39 +76,54 @@
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
diff --git a/extra-addons/approvals/views/approval_condition_views.xml b/extra-addons/approvals/views/approval_condition_views.xml
new file mode 100644
index 000000000..93d17c4be
--- /dev/null
+++ b/extra-addons/approvals/views/approval_condition_views.xml
@@ -0,0 +1,44 @@
+
+
+
+ approval.condition.form
+ approval.condition
+
+
+
+
+
+
+ approval.condition.list
+ approval.condition
+
+
+
+
+
+
+
+
+
+
+
diff --git a/extra-addons/hr_promote/models/hr_promote.py b/extra-addons/hr_promote/models/hr_promote.py
index e6acd7ce4..7f36fa5cd 100644
--- a/extra-addons/hr_promote/models/hr_promote.py
+++ b/extra-addons/hr_promote/models/hr_promote.py
@@ -1,5 +1,5 @@
from odoo.exceptions import ValidationError,UserError
-from odoo import api, fields, models
+from odoo import api, fields, models, exceptions
from datetime import date
class HRPromote(models.Model):
# ----------------------------------- Private Attributes ---------------------------------
@@ -71,23 +71,30 @@ class HRPromote(models.Model):
else:
record.name = "Promotion"
@api.model
- def create(self, vals):
- vals['state'] = 'approval_needed'
- if 'employee_id' in vals and not vals.get('job_id'):
- employee = self.env['hr.employee'].browse(vals['employee_id'])
- vals['job_id'] = employee.job_id.id if employee.job_id else False
+ def create(self, vals_list):
+ # Ensure vals_list is always a list
+ if isinstance(vals_list, dict):
+ vals_list = [vals_list]
- if not vals.get('job_id'):
- raise ValueError("The employee does not have a current job position.")
- promotion_record = super().create(vals)
- # Create the approval request related to the promotion
- self.env['approval.request'].create({
- 'employee_id': vals['employee_id'],
- 'promotion_record_id': promotion_record.id,
- 'state': 'draft', # You can adjust this based on your workflow
- 'approval_type': 'promotion', # Custom field for the type of approval
- })
- return promotion_record
+ promotion_records = []
+ for vals in vals_list:
+ # Set default state
+ vals['state'] = 'approval_needed'
+
+ # Handle employee's job_id logic
+ if 'employee_id' in vals and not vals.get('job_id'):
+ employee = self.env['hr.employee'].browse(vals['employee_id'])
+ vals['job_id'] = employee.job_id.id if employee.job_id else False
+
+ # Validate job_id existence
+ if not vals.get('job_id'):
+ raise exceptions.ValidationError("The employee does not have a current job position.")
+
+ # Create the promotion record
+ promotion_record = super(HRPromote, self).create(vals)
+ promotion_records.append(promotion_record)
+
+ return self.browse([record.id for record in promotion_records])
def action_save_and_close(self):
return {
'type': 'ir.actions.act_window',