gn_job_sheet

This commit is contained in:
Florian du Garage Num 2024-04-07 00:51:01 +02:00
parent 52da9fc847
commit 69b6db9dea
9 changed files with 273 additions and 10 deletions

View File

@ -14,7 +14,7 @@ Addons for Odoo 16.
| gn_contract | 16.0.0.0.7 | Amendements to Hr Contracts |
| gn_contract_amendment | 16.0.0.0.1 | Amendments to Hr Contract with Prototype Inheritance |
| gn_contract_validation | 16.0.0.0.1 | Simple class inheritance from HrContract to add some states |
| gn_job_sheet | 16.0.0.0.1 | Fiche de poste |
| gn_job_sheet | 16.0.0.0.4 | Fiche de poste |
| gn_contract_situation | 16.0.0.0.1 | Multiple Delegation Inheritance to associate original contract, amendments, job sheets |
## ToDo

View File

@ -5,6 +5,10 @@ This module creates a detailed job description on a job sheet (Fiche de poste)
# Changelog
- v16.0.0.0.4 (2024/04/07)
- clear management of states and dates
- v16.0.0.0.3 (2024/04/04)
- link to contract
- v16.0.0.0.2 (2024/03/24)
- add state field and security csv
- v16.0.0.0.1 (2024/03/23)

View File

@ -1,6 +1,6 @@
{
"name": "Gestion des Contrats: Fiches de poste",
"version": "16.0.0.0.1",
"version": "16.0.0.0.4",
"category": "HR",
"summary": "Détaille les missions et tâches pour l'exécution d'un contrat",
"author": "Le Garage Numérique",
@ -8,8 +8,12 @@
"website": "https://odoo.legaragenumerique.fr",
"depends": [
"hr",
"hr_contract",
],
"data": [
"views/gn_job_sheet.xml",
"views/gn_contract.xml",
"data/gn_job_sheet.xml",
"security/ir.model.access.csv",
],
"license": "LGPL-3",

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<menuitem
id="gn_job_sheet.sheets_menu"
name="Fiches de poste"
parent="hr.menu_hr_employee_payroll"
sequence="300"
action="gn_job_sheet"
groups="hr.group_hr_manager"/>
</odoo>

View File

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import gn_job_sheet
from . import gn_job_sheet
from . import gn_contract

View File

@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
# 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__)
class GnJobSheetContract(models.Model):
_inherit = 'hr.contract'
sheet_ids = fields.One2many('hr.job.sheet', 'contract_id', string="Fiches de postes associées")
active_sheet_id = fields.Many2one('hr.job.sheet', string="Fiche de poste en vigueur", compute='_compute_active_sheet_id')
pending_sheet_id = fields.Many2one('hr.job.sheet', string="Fiche de poste en préparation", compute='_compute_pending_sheet_id')
@api.depends('sheet_ids', 'sheet_ids.state')
def _compute_active_sheet_id(self):
for contract in self:
active_sheet_id = contract.sheet_ids.filtered(lambda s: s.state == 'active')
if len(active_sheet_id) == 0:
self.active_sheet_id = False
elif len(active_sheet_id) == 1:
self.active_sheet_id = active_sheet_id
elif len(active_sheet_id) > 1:
raise ValidationError("Problem with sheets for contract %s, too many active sheets (ids %s)", self.name, active_sheet_id)
@api.depends('sheet_ids', 'sheet_ids.state')
def _compute_pending_sheet_id(self):
for contract in self:
pending_sheet_id = contract.sheet_ids.filtered(lambda s: s.state not in ['active', 'expired'])
if len(pending_sheet_id) == 0:
self.pending_sheet_id = False
elif len(pending_sheet_id) == 1:
self.pending_sheet_id = pending_sheet_id
elif len(pending_sheet_id) > 1:
raise ValidationError("Problem with sheets for contract %s, too many pending sheets (ids %s)", self.name, pending_sheet_id)

View File

@ -1,11 +1,8 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import timedelta
from odoo import fields, models, api, _
from odoo.exceptions import ValidationError
from odoo.osv import expression
from pytz import timezone
import logging
@ -14,15 +11,146 @@ _logger = logging.getLogger(__name__)
class GnHrJobSheet(models.Model):
_name = 'hr.job.sheet'
situation_ids = fields.One2many('hr.contract.situation', string="Conditions d'éxecution", readonly=True)
contract_id = fields.Many2one('hr.contract', required=True, auto_join=True, string="Contrat lié", ondelete="cascade")
state = fields.Selection([
('draft', 'Brouillon'),
('wait_manager_approval', "En attente de validation du manager"),
('wait_director_approval', "En attente de validation par la direction"),
('wait_employee_approval', "En attente de signature par le salarié"),
('wait_employee_reception', "En attente de remise au salarié"),
('ready', 'Prête'),
('active', 'Active'),
('expired', 'Expiré'),
], string="État", default='draft', required=True)
date_start = fields.Date(string="Date de début")
date_end = fields.Date(string="Date de début")
date_start = fields.Date(string="Date de début", default=fields.Date.today())
date_end = fields.Date(string="Date de fin")
name = fields.Char(string="Nom", compute='_compute_name')
is_default = fields.Boolean(default=False, string="Fiche de poste par défaut")
previous_sheet_id = fields.Many2one("hr.job.sheet", string="Fiche de poste précédente", readonly=True)
lineage_sequence_number = fields.Integer(string="Numéro de séquence dans l'historique du salarié", default=1, compute='_compute_lineage_sequence_number', store=True)
@api.depends('date_start')
def _compute_name(self):
for sheet in self:
if sheet.date_start:
sheet.name = "Fiche de poste du " + sheet.date_start.strftime('%d/%m/%Y')
else:
sheet.name = "Fiche de poste sans date"
@api.depends('previous_sheet_id')
def _compute_lineage_sequence_number(self):
for sheet in self:
if sheet.previous_sheet_id:
previous_number = sheet.previous_sheet_id.lineage_sequence_number
sheet.lineage_sequence_number = previous_number + 1
## Checks for start_date
def start_date_before_contract(self):
if self.contract_id and self.contract_id.date_start and self.date_start:
if self.date_start < self.contract_id.date_start:
raise ValidationError("The start date cannot be before the contract's start date.")
def start_date_after_contract(self):
if self.contract_id and self.contract_id.date_end and self.date_start:
if self.date_start > self.contract_id.date_end:
raise ValidationError("The start date cannot be after the contract's end date.")
@api.constrains('date_start')
def _limit_date_start(self):
for sheet in self:
sheet.start_date_before_contract()
sheet.start_date_after_contract()
@api.onchange('date_start')
def _onchange_date_start(self):
self.start_date_before_contract()
self.start_date_after_contract()
## Checks for end_date
def end_date_before_start(self):
if self.date_end and self.date_end < self.date_start:
raise ValidationError("The end date cannot be before the start_date")
if not self.date_start:
raise ValidationError("You must define the start date")
def end_date_before_contract(self):
if self.date_end and self.date_end < self.contract_id.date_start:
raise ValidationError("The end date cannot be before the contract's start date.")
def end_date_after_contract(self):
contract_end_date = self.contract_id.date_end
if contract_end_date and self.date_end and self.date_end > contract_end_date:
raise ValidationError("The end date cannot be after the contract's end date.")
def end_date_not_defined_in_fixed_term_contract(self):
if not self.date_end and self.contract_id and self.contract_id.date_end:
raise ValidationError(_("The end date must be defined before or the same day than the contract's end on %s", self.contract_id.date_end))
@api.constrains('date_end')
def _constrains_date_end(self):
self.end_date_before_start()
self.end_date_before_contract()
self.end_date_after_contract()
self.end_date_not_defined_in_fixed_term_contract()
@api.onchange('date_end')
def _onchange_date_end(self):
self.end_date_before_start()
self.end_date_before_contract()
self.end_date_after_contract()
self.end_date_not_defined_in_fixed_term_contract()
## Checks for state
def _check_active_state_unicity(self, current_id, sheet_ids):
already_active = sheet_ids.filtered(lambda s: s.state == 'active' and s.id != current_id)
if len(already_active) > 0:
message = (_("Il ne peut y avoir qu'une fiche de poste active à la fois, la fiche de poste déjà active est la suivante: %s, id %s", already_active, already_active.ids))
return message
elif len(already_active) == 0:
return False
def _check_pending_state_unicity(self, current_id, sheet_ids):
already_pending = sheet_ids.filtered(lambda s: s.state not in ['active', 'expired'] and s.id != current_id)
if len(already_pending) > 0:
message = (_("Il ne peut y avoir qu'une fiche de poste en préparation à la fois, la fiche de poste déjà en préparation est la suivante: %s, id %s", already_pending, already_pending.ids))
return message
elif len(already_pending) == 0:
self.contract_id.pending_sheet_id = current_id
return False
def _check_state(self):
current_id = self._origin.id if self._origin else self.id
sheet_ids = self.contract_id.sheet_ids
if self.state == 'active':
already_active = self._check_active_state_unicity(current_id, sheet_ids)
return already_active
elif self.state not in ['active', 'expired']:
already_pending = self._check_pending_state_unicity(current_id, sheet_ids)
return already_pending
@api.onchange('state')
def _onchange_state(self):
_logger.warning("enter on change")
message = self._check_state()
if self.state and not message:
pass
else:
_logger.warning("raise Error in on change")
raise ValidationError(message)
@api.constrains('state')
def _constrains_state(self):
for record in self:
message = record._check_state()
if message:
_logger.warning("raise Error in constrains")
raise ValidationError(message)

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="gnsheet_hr_contract" model="ir.ui.view">
<field name="name">hr.contract.form.sheet</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">
<xpath expr="//field[@name='hr_responsible_id']" position="after">
<field name="active_sheet_id" attrs="{'readonly': True, 'invisible': [('active_sheet_id', '=', False)]}"/>
<field name="pending_sheet_id" attrs="{'readonly': True, 'invisible': [('pending_sheet_id', '=', False)]}"/>
</xpath>
</field>
</record>
</odoo>

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="gn_job_sheet_tree" model="ir.ui.view">
<field name="name">hr.job.sheet.tree</field>
<field name="model">hr.job.sheet</field>
<field name="arch" type="xml">
<tree string="Fiches de poste">
<field name="contract_id"/>
<field name="name"/>
<field name="date_start"/>
<field name="date_end"/>
<field name="state" widget="label_selection" options="{'classes': {'new': 'default', 'draft': 'default', 'wait_manager_approval': 'default', 'wait_director_approval': 'default', 'wait_employee_reception': 'default', 'ready': 'success', 'active': 'success', 'expired': 'danger'}}"/>
</tree>
</field>
</record>
<record id="gn_job_sheet_form" model="ir.ui.view">
<field name="name">hr.job.sheet.form</field>
<field name="model">hr.job.sheet</field>
<field name="arch" type="xml">
<form string="Fiche de poste">
<header>
<!--<button name="action_define_sheet" type="object"
groups="hr_contract.group_hr_contract_manager"
string="Définir la fiche de poste" class="oe_highlight"
attrs="{'invisible': [('sheet_id', '=', 'False')]}"/>
<button name="action_define_sheet" type="object"
groups="hr_contract.group_hr_contract_manager"
string="Modifier la fiche de poste" class="oe_highlight"
attrs="{'invisible': [('sheet_id', '!=', 'False')]}"/>-->
<div name="state_group">
<field name="state" groups="!hr_contract.group_hr_contract_manager" widget="statusbar"/>
<field name="state" groups="hr_contract.group_hr_contract_manager" widget="statusbar" options="{'clickable': '1'}"/>
</div>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
</div>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
<h1>
<field name="name" placeholder="Name"/>
</h1>
</div>
<field name="contract_id" string="Contrat" options="{'form_view_ref': 'gn_job_sheet.gnsheet_hr_contract'}"/>
<group name="dates">
<label for="date_start" string="Period"/>
<div>
<field name="date_start" widget="daterange" class="oe_inline" options="{'related_end_date': 'date_end'}"/>
-
<field name="date_end" widget="daterange" class="oe_inline" options="{'related_start_date': 'date_start'}"/>
</div>
</group>
</sheet>
</form>
</field>
</record>
<record id="gn_job_sheet" model="ir.actions.act_window">
<field name="name">Fiches de postes</field>
<field name="res_model">hr.job.sheet</field>
<field name="view_mode">tree,form</field>
</record>
</odoo>