detail for each mission in career
This commit is contained in:
parent
1e8f55fe09
commit
45c1e9613e
@ -11,7 +11,7 @@ Addons for Odoo 16.
|
||||
| gn_cc | 16.0.0.0.2 | Configuration for French convention collective, with data for IDCC3442 |
|
||||
| gn_holidays | 16.0.0.0.1 | French configuration for Publics Holidays and Leave Management |
|
||||
| gn_contract | 16.0.0.0.1 | Amendements to Hr Contracts |
|
||||
| gn_career | 16.0.0.0.1 | Fiche de poste et évolution de carrière |
|
||||
| gn_career | 16.0.0.0.2 | Fiche de poste et évolution de carrière |
|
||||
|
||||
|
||||
## ToDo
|
||||
|
||||
@ -12,6 +12,8 @@ Module de gestion des fiches de postes et des évolutions de carrière
|
||||
|
||||
- v16.0.0.0.1 (2024/03/02):
|
||||
- Création du module
|
||||
- v16.0.0.0.2 (2024/03/08):
|
||||
- Add detail of Missions for each Career
|
||||
|
||||
|
||||
## Issues
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "France - Fiche de poste",
|
||||
"version": "16.0.0.0.1",
|
||||
"version": "16.0.0.0.2",
|
||||
"category": "HR",
|
||||
"summary": "Configuration de la fiche de poste et de son évolution conventionnelle",
|
||||
"author": "Le Garage Numérique",
|
||||
@ -14,6 +14,7 @@
|
||||
],
|
||||
"data": [
|
||||
"views/gn_career.xml",
|
||||
"views/gn_career_detail.xml",
|
||||
"views/gn_contract.xml",
|
||||
"data/gn_career_menus.xml",
|
||||
"security/ir.model.access.csv",
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models, api
|
||||
from odoo.exceptions import ValidationError
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
@ -11,6 +12,7 @@ class GnCareer(models.Model):
|
||||
_description = "Analyse du poste"
|
||||
_order = 'start_date'
|
||||
|
||||
name = fields.Char("Nom", compute='_compute_name')
|
||||
start_date = fields.Date('From', required=True, default=lambda self: fields.Date.today())
|
||||
contract_id = fields.Many2one('hr.contract', string="Contrat ou Avenant associé")
|
||||
|
||||
@ -23,13 +25,46 @@ class GnCareer(models.Model):
|
||||
('active', 'Active'),
|
||||
], string="Statut", default='draft')
|
||||
|
||||
mission_ids = fields.Many2many('gn_career.mission', string="Missions effectuées")
|
||||
mission_rel_ids = fields.One2many('gn_career.mission_career_rel', 'career_id', string="Mission Relations")
|
||||
mission_ids = fields.Many2many('gn_career.mission', 'career_ids', compute='_compute_mission_ids', string="Missions effectuées")
|
||||
mission_detail_ids = fields.One2many('gn_career.mission.detail', 'career_id', string="Détail de la mission")
|
||||
|
||||
class GnCareerMissionCareerRel(models.Model):
|
||||
_name = 'gn_career.mission_career_rel'
|
||||
_description = "Répartition des missions au sein d'une analyse de poste"
|
||||
total_percentage = fields.Float(compute='_compute_total_percentage', string="Total Percentage")
|
||||
|
||||
mission_id = fields.Many2one('gn_career.mission', string="Mission")
|
||||
@api.depends('mission_detail_ids.percentage')
|
||||
def _compute_total_percentage(self):
|
||||
for record in self:
|
||||
record.total_percentage = sum(mission.percentage for mission in record.mission_detail_ids)
|
||||
|
||||
@api.constrains('mission_detail_ids', 'total_percentage')
|
||||
def _check_total_percentage(self):
|
||||
for record in self:
|
||||
if record.total_percentage != 100:
|
||||
raise ValidationError("Le pourcentage total des missions au sein de la fiche de poste doit atteindre 100%. Veuillez ajuster la répartition de la mission.")
|
||||
|
||||
@api.depends('mission_detail_ids.mission_id')
|
||||
def _compute_mission_ids(self):
|
||||
for record in self:
|
||||
mission_ids_set = set()
|
||||
for mission_detail in record.mission_detail_ids:
|
||||
mission_ids_set.add(mission_detail.mission_id.id)
|
||||
record.mission_ids = [(6, 0, list(mission_ids_set))]
|
||||
|
||||
@api.depends('start_date')
|
||||
def _compute_name(self):
|
||||
for record in self:
|
||||
if record.start_date:
|
||||
# Use an f-string for formatting
|
||||
record.name = f"Fiche de poste du {record.start_date}"
|
||||
else:
|
||||
# Provide a default or handle the case where start_date isn't set
|
||||
record.name = "Fiche de poste sans date"
|
||||
|
||||
class GnCareerMissionDetail(models.Model):
|
||||
_name = 'gn_career.mission.detail'
|
||||
_description = "Détails d'une mission pour une fiche de poste"
|
||||
|
||||
name = fields.Char(related='mission_id.name')
|
||||
career_id = fields.Many2one('gn_career.career', string="Fiche de poste")
|
||||
percentage = fields.Float("Temps occupé par la mission en %")
|
||||
mission_id = fields.Many2one('gn_career.mission', string="Mission")
|
||||
percentage = fields.Integer("Temps occupé par la mission en %", default="100")
|
||||
task_ids = fields.Many2many('gn_career.task', 'mission_detail', string="Tâches incluses dans la mission")
|
||||
@ -2,6 +2,9 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models, api
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class GnCareerContract(models.Model):
|
||||
_inherit = "hr.contract"
|
||||
@ -9,9 +12,46 @@ class GnCareerContract(models.Model):
|
||||
career_ids = fields.One2many("gn_career.career", 'contract_id',
|
||||
string="Analyses du poste")
|
||||
active_career_id = fields.Many2one('gn_career.career', string="Fiche de poste en cours", compute='_compute_active_career', store=True)
|
||||
has_active_career = fields.Integer(compute='_has_active_career')
|
||||
|
||||
@api.depends('career_ids')
|
||||
def _compute_active_career(self):
|
||||
for record in self:
|
||||
active_careers = record.career_ids.filtered(lambda c: c.status == 'active').sorted(key=lambda c: c.start_date, reverse=True)
|
||||
record.active_career_id = active_careers[:1].id if active_careers else False
|
||||
record.active_career_id = active_careers[0] if active_careers else False
|
||||
|
||||
@api.depends('active_career_id')
|
||||
def _has_active_career(self):
|
||||
for record in self:
|
||||
record.has_active_career = True if record.active_career_id else False
|
||||
|
||||
#@api.model
|
||||
def open_associated_careers(self):
|
||||
_logger.warning("Open Active Career called on records: %s", self)
|
||||
self.ensure_one() # Ensure that the method is called on a single record
|
||||
|
||||
associated_contract_ids = [self.id,]
|
||||
if self.previous_contract_id:
|
||||
previous_contract = self.previous_contract_id
|
||||
while previous_contract:
|
||||
associated_contract_ids.append(previous_contract.id)
|
||||
previous_contract = previous_contract.previous_contract_id
|
||||
|
||||
action = {
|
||||
'type': 'ir.actions.act_window',
|
||||
"views": [[False, "tree"], [False, "form"]],
|
||||
'res_model': 'gn_career.career',
|
||||
'target': 'current',
|
||||
'domain': [('contract_id', 'in', associated_contract_ids)],
|
||||
}
|
||||
return action
|
||||
|
||||
def open_active_career(self):
|
||||
action = {
|
||||
'type': 'ir.actions.act_window',
|
||||
"views": [[False, "form"]],
|
||||
'res_model': 'gn_career.career',
|
||||
'res_id': self.active_career_id.id, # ID of the career to open
|
||||
'target': 'current',
|
||||
}
|
||||
return action
|
||||
@ -9,8 +9,9 @@ class GnCareerTask(models.Model):
|
||||
|
||||
name = fields.Char("Nom de la tâche")
|
||||
description = fields.Text("Description de la tâche")
|
||||
mission_ids = fields.Many2many('gn_career.mission', string="Missions impliquant cette tâche")
|
||||
|
||||
possible_mission_ids = fields.Many2many('gn_career.mission', string="Missions pouvant inclure cette tâche")
|
||||
effective_mission_ids = fields.Many2many('gn_career.mission.detail', relation="effective_missions", string="Missions définies impliquant cette tâche")
|
||||
active_mission_ids = fields.Many2many('gn_career.mission.detail', relation="active_missions", string="Missions actives incluant cette tâche")
|
||||
|
||||
class GnCareerMission(models.Model):
|
||||
_name = 'gn_career.mission'
|
||||
@ -20,5 +21,5 @@ class GnCareerMission(models.Model):
|
||||
description = fields.Text("Description de la mission")
|
||||
task_ids = fields.Many2many('gn_career.task', string="Tâches pouvant être incluses dans la mission")
|
||||
|
||||
career_ids = fields.Many2many('gn_career.career', string="Fiches de poste incluant cette mission")
|
||||
career_mission_ids = fields.One2many('gn_career.mission_career_rel', 'mission_id', string="Répartition des missions au sein du poste")
|
||||
career_ids = fields.Many2many('gn_career.career', 'mission_ids', string="Fiches de poste incluant cette mission")
|
||||
career_mission_ids = fields.One2many('gn_career.mission.detail', 'mission_id', string="Répartition des missions au sein du poste")
|
||||
@ -1,9 +1,9 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_gn_career_career_user,gn_career_career_user,model_gn_career_career,base.group_user,1,0,0,0
|
||||
access_gn_career_career_admin,gn_career_career_admin,model_gn_career_career,hr_contract.group_hr_contract_manager,1,1,1,1
|
||||
access_gn_career_mission_career_rel_user,gn_career_mission_career_rel_user,model_gn_career_mission_career_rel,base.group_user,1,0,0,0
|
||||
access_gn_career_mission_career_rel_admin,gn_career_mission_career_rel_admin,model_gn_career_mission_career_rel,hr_contract.group_hr_contract_manager,1,1,1,1
|
||||
access_gn_career_task_user,gn_career_task_user,model_gn_career_task,base.group_user,1,0,0,0
|
||||
access_gn_career_task_admin,gn_career_task_admin,model_gn_career_task,hr_contract.group_hr_contract_manager,1,1,1,1
|
||||
access_gn_career_mission_user,gn_career_mission_user,model_gn_career_mission,base.group_user,1,0,0,0
|
||||
access_gn_career_mission_admin,gn_career_mission_admin,model_gn_career_mission,hr_contract.group_hr_contract_manager,1,1,1,1
|
||||
access_gn_career_mission_detail_user,gn_career_mission_detail_user,model_gn_career_mission_detail,base.group_user,1,0,0,0
|
||||
access_gn_career_mission_detail_admin,gn_career_mission_detail_admin,model_gn_career_mission_detail,hr_contract.group_hr_contract_manager,1,1,1,1
|
||||
|
37
gn_career/views/gn_career_detail.xml
Normal file
37
gn_career/views/gn_career_detail.xml
Normal file
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="gn_career_mission_detail_form_view" model="ir.ui.view">
|
||||
<field name="name">gn_career.mission.detail.form</field>
|
||||
<field name="model">gn_career.mission.detail</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Détail des missions">
|
||||
<sheet string="Détail de la mission">
|
||||
<group>
|
||||
<group>
|
||||
<field name="career_id"/>
|
||||
<field name="mission_id"/>
|
||||
<field name="task_ids"/>
|
||||
<field name="percentage"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="gn_career_mission_detail_tree_view" model="ir.ui.view">
|
||||
<field name="name">gn_career.mission.detail.tree</field>
|
||||
<field name="model">gn_career.mission.detail</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Mission Details">
|
||||
<field name="mission_id"/>
|
||||
<field name="percentage"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="gn_career_mission_detail_action" model="ir.actions.act_window">
|
||||
<field name="name">Détails des missions</field>
|
||||
<field name="res_model">gn_career.mission.detail</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_ids" eval="[(5, 0, 0), (0, 0, {'view_mode': 'tree', 'view_id': ref('gn_career_mission_detail_tree_view')}), (0, 0, {'view_mode': 'form', 'view_id': ref('gn_career_mission_detail_form_view')})]"/>
|
||||
</record>
|
||||
</odoo>
|
||||
@ -5,15 +5,30 @@
|
||||
<field name="model">hr.contract</field>
|
||||
<field name="priority" eval="30"/>
|
||||
<field name="inherit_id" ref="hr_contract.hr_contract_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//div[@name='button_box']" position="inside">
|
||||
<button name="open_associated_careers" type='object' class="oe_stat_button" icon="fa-money" groups="hr_contract.group_hr_contract_manager">
|
||||
<field name="has_active_career" widget="statinfo" string="Fiche de poste"/>
|
||||
</button>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
<record id="gn_career.hr_contract_career" model="ir.ui.view">
|
||||
<field name="name">hr.contract.form.gncareer.career</field>
|
||||
<field name="model">hr.contract</field>
|
||||
<field name="priority" eval="30"/>
|
||||
<field name="inherit_id" ref="hr_contract.hr_contract_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//page[@name='information']" position="after">
|
||||
<page string="Fiche de poste" name='career'>
|
||||
<group name="missions">
|
||||
<field name="active_career_id" widget='many2one' options="{'no_open': True, 'no_create': True}" domain="[('id','=',active_career_id)]"/>
|
||||
<!-- Assuming mission_ids is a field in gn_career.career, display related missions -->
|
||||
<!--<field name="active_career_id" string="Missions" widget="many2many_tags" options="{'no_create': True}" domain="[('id', 'in', active_career_id.mission_ids)]"/>-->
|
||||
</group>
|
||||
<page string="Fiche de poste">
|
||||
<field name="active_career_id" invisible="1"/>
|
||||
<group>
|
||||
<field name="active_career_id" options="{'no_open': False}" string="Active Career"/>
|
||||
<button name="open_active_career" type="object" class="oe_highlight" string="See Active Career"/>
|
||||
</group>
|
||||
</page>
|
||||
</xpath>
|
||||
</data>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user