gn_job_sheet
This commit is contained in:
parent
52da9fc847
commit
69b6db9dea
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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",
|
||||
|
||||
10
gn_job_sheet/data/gn_job_sheet.xml
Normal file
10
gn_job_sheet/data/gn_job_sheet.xml
Normal 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>
|
||||
@ -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
|
||||
|
||||
39
gn_job_sheet/models/gn_contract.py
Normal file
39
gn_job_sheet/models/gn_contract.py
Normal 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)
|
||||
@ -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)
|
||||
15
gn_job_sheet/views/gn_contract.xml
Normal file
15
gn_job_sheet/views/gn_contract.xml
Normal 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>
|
||||
62
gn_job_sheet/views/gn_job_sheet.xml
Normal file
62
gn_job_sheet/views/gn_job_sheet.xml
Normal 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>
|
||||
Loading…
x
Reference in New Issue
Block a user