Compare commits

...

20 Commits

@ -2,4 +2,35 @@
Addons for Odoo 16.
Starting with association loi 1901 chart of accounts.
## List of modules
| Name | Version | Description |
|-------------------------|--------------|----------------------------------------------------------|
| gn_l10n_fr_pcg_asso | 16.0.0.0.3 | French chart of account and fiscal position for NGO's |
| 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 |
## ToDo
## gn_payroll
### Major
- [x] Créer une structure d'avenants au contrat (commit 6efa7429)
- [x] Déterminer l'ancienneté du salarié (commit a86dd2de )
- [x] Création d'un modèle pour les entretiens
- [x] Création automatique des entretiens légaux
- [x] Lien entre entretiens et events
- [] gestion de la valorisation professionnelle
- [] création d'une action de création d'avenants
- [] Faire la fiche de poste
- [] Create report view for each entretien_type
- [] refactor in several modules
### Minor
- [] In gn_payroll_contract.py, class Entretien, need compute rule for "absence" start_point in entretien.type model (cf issue #3)
- [] compute contract.cc_id from contract.company_id.cc (cf issue #4)
- [x] debug entretien start date on creation (cf issue #5)
- [x] debug numérotation des entretiens par salarié on creation (cf issue #6)
- [] better handling of event creation from entretien (cf issue #7)

@ -0,0 +1,24 @@
# GN CC
Module de gestion des conventions collectives par Le Garage Numérique.
## How to install
1. Clone this repository in your odoo's extra-modules folder (i.e. /mnt/extra-addons)
2. Refresh list of Applications in UI
3. Search for 'gn_cc' and click on **Install**
## Changelog
- v16.0.0.0.2 (2024/03/01):
- Add 'l10n_fr_oca' as dependency
- Add Readme
- v16.0.0.0.1 (2024/02/29):
- Creation of module
# Feature requests
- [] Add Convention Collective for "Organisme de Formation"
- [] Add Convention Collective for "Centres Sociaux"
- [] Add Convention Collective for "Prévention Spécialisée"

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

@ -0,0 +1,21 @@
{
"name": "France - Conventions Collective",
"version": "16.0.0.0.2",
"category": "France",
"summary": "Configuration des accords de branche pour le France: Conventions Collectives",
"author": "Le Garage Numérique",
"maintainers": ["makayabou"],
"website": "https://odoo.legaragenumerique.fr",
"depends": [
"hr",
"hr_contract",
"l10n_fr_oca",
],
"data": [
"views/gn_cc_company.xml",
"views/gn_cc_cc.xml",
"data/gn_cc_cc.xml",
"security/ir.model.access.csv"
],
"license": "LGPL-3",
}

@ -0,0 +1,359 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="gn_cc.cc_3442" model="gn_cc.cc">
<field name="name">Convention Eclat</field>
<field name="idcc">3442</field>
<field name="groups">A;B;C;D;E;F;G;H;I;J;K</field>
<field name="criterias">Autonomie;Responsabilité;Technicité</field>
<field name="point_cat">V1;V2</field>
</record>
<record id="gn_cc.cc_3442_A_coeff_20220101" model="gn_cc.cc.group.coeff">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">A</field>
<field name="coeff_min" eval="247"/>
</record>
<record id="gn_cc.cc_3442_A_coeff_20220501" model="gn_cc.cc.group.coeff">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-05-01</field>
<field name="group">A</field>
<field name="coeff_min" eval="250"/>
</record>
<record id="gn_cc.cc_3442_A_coeff_20231101" model="gn_cc.cc.group.coeff">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2023-11-01</field>
<field name="group">A</field>
<field name="coeff_min" eval="257"/>
</record>
<record id="gn_cc.cc_3442_A_autonomie_2021" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2021-01-01</field>
<field name="group">A</field>
<field name="criteria">Autonomie</field>
<field name="description">Ancienne définition du groupe A autonomie.</field>
</record>
<record id="gn_cc.cc_3442_A_autonomie" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">A</field>
<field name="criteria">Autonomie</field>
<field name="description">Les consignes et processus sont mis en œuvre. Le contrôle est permanent.</field>
</record>
<record id="gn_cc.cc_3442_A_responsabilite" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">A</field>
<field name="criteria">Responsabilité</field>
<field name="description">Responsabilité des biens (matériel, outils, salle éventuellement) confiés au salarié et/ou des personnes extérieures dont le salarié a la charge (public accueilli). Peut avoir la gestion dun fond de caisse.</field>
</record>
<record id="gn_cc.cc_3442_A_technicite" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">A</field>
<field name="criteria">Technicité</field>
<field name="description">Compétences élémentaires impliquant la mise en œuvre de procédures simples</field>
</record>
<record id="gn_cc.cc_3442_B_coeff_20220101" model="gn_cc.cc.group.coeff">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">B</field>
<field name="coeff_min" eval="257"/>
</record>
<record id="gn_cc.cc_3442_B_coeff_20220501" model="gn_cc.cc.group.coeff">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-05-01</field>
<field name="group">B</field>
<field name="coeff_min" eval="260"/>
</record>
<record id="gn_cc.cc_3442_B_coeff_20240101" model="gn_cc.cc.group.coeff">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2024-01-01</field>
<field name="group">B</field>
<field name="coeff_min" eval="265"/>
</record>
<record id="gn_cc.cc_3442_B_autonomie" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2024-01-01</field>
<field name="group">B</field>
<field name="criteria">Autonomie</field>
<field name="description">Les consignes et processus sont mis en œuvre. Le contrôle est permanent.</field>
</record>
<record id="gn_cc.cc_3442_B_responsabilite" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2024-01-01</field>
<field name="group">B</field>
<field name="criteria">Responsabilité</field>
<field name="description">Responsabilité des biens (matériel, outils, salle éventuellement) confiés au salarié et/ou des personnes extérieures dont le salarié a la charge (public accueilli). Peut avoir la gestion dun fond de caisse.</field>
</record>
<record id="gn_cc.cc_3442_B_technicite" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2024-01-01</field>
<field name="group">B</field>
<field name="criteria">Technicité</field>
<field name="description">Compétences professionnelles pratiques dans le cadre dune activité généralement simple.</field>
</record>
<record id="gn_cc.cc_3442_C_coeff_20240101" model="gn_cc.cc.group.coeff">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2024-01-01</field>
<field name="group">C</field>
<field name="coeff_min" eval="285"/>
</record>
<record id="gn_cc.cc_3442_C_autonomie" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">C</field>
<field name="criteria">Autonomie</field>
<field name="description">Le salarié peut interpréter et adapter les processus. Le contrôle est périodique.</field>
</record>
<record id="gn_cc.cc_3442_C_responsabilite" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">C</field>
<field name="criteria">Responsabilité</field>
<field name="description">Responsabilité dun budget prescrit. Peut assurer la coordination/le conseil dautres salariés</field>
</record>
<record id="gn_cc.cc_3442_C_technicite" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">C</field>
<field name="criteria">Technicité</field>
<field name="description">Compétences techniques et relationnelles nécessaires à la maîtrise dun domaine dactivité</field>
</record>
<record id="gn_cc.cc_3442_D_coeff_20240101" model="gn_cc.cc.group.coeff">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2024-01-01</field>
<field name="group">D</field>
<field name="coeff_min" eval="305"/>
</record>
<record id="gn_cc.cc_3442_D_autonomie" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">D</field>
<field name="criteria">Autonomie</field>
<field name="description">Le salarié peut interpréter et adapter les processus. Le contrôle est périodique.</field>
</record>
<record id="gn_cc.cc_3442_D_responsabilite" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">D</field>
<field name="criteria">Responsabilité</field>
<field name="description">Responsabilité dun budget prescrit. Participe à lélaboration des procédures de léquipe/du service. Implique des fonctions de coordination et de « contrôle » dautres salariés.</field>
</record>
<record id="gn_cc.cc_3442_D_technicite" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">D</field>
<field name="criteria">Technicité</field>
<field name="description">Compétences techniques et relationnelles nécessaires à la maîtrise dun domaine dactivité</field>
</record>
<record id="gn_cc.cc_3442_E_coeff_20220101" model="gn_cc.cc.group.coeff">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">E</field>
<field name="coeff_min" eval="325"/>
</record>
<record id="gn_cc.cc_3442_E_autonomie" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">E</field>
<field name="criteria">Autonomie</field>
<field name="description">Le salarié interprète et adapte les processus et leur mise en œuvre sous le contrôle ponctuel de son responsable hiérarchique </field>
</record>
<record id="gn_cc.cc_3442_E_responsabilite" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">E</field>
<field name="criteria">Responsabilité</field>
<field name="description">Responsabilité dun budget prescrit. Participe à lélaboration des procédures de léquipe/du service. Implique des fonctions de coordination et de « contrôle » dautres salariés.</field>
</record>
<record id="gn_cc.cc_3442_E_technicite" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">E</field>
<field name="criteria">Technicité</field>
<field name="description">Compétences techniques et relationnelles nécessaires à la maîtrise dun domaine dactivité</field>
</record>
<record id="gn_cc.cc_3442_F_coeff_20220101" model="gn_cc.cc.group.coeff">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">F</field>
<field name="coeff_min" eval="350"/>
</record>
<record id="gn_cc.cc_3442_F_autonomie" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">F</field>
<field name="criteria">Autonomie</field>
<field name="description">Le salarié peut créer lui-même ses processus. Le salarié rend compte au terme de sa mission et est évalué sur les écarts entre les attendus et le réalisé. Le contrôle seffectue a posteriori.</field>
</record>
<record id="gn_cc.cc_3442_F_responsabilite" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">F</field>
<field name="criteria">Responsabilité</field>
<field name="description">Participe à lélaboration des directives et/ou dun budget limité à son périmètre daction et est responsable de son exécution. Il peut assurer la responsabilité hiérarchique dautres salariés dans le cadre dune délégation de responsabilité.</field>
</record>
<record id="gn_cc.cc_3442_F_technicite" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">F</field>
<field name="criteria">Technicité</field>
<field name="description">Compétences élargies impliquant une très bonne maîtrise dun ou plusieurs domaines dintervention</field>
</record>
<record id="gn_cc.cc_3442_G_coeff_20220101" model="gn_cc.cc.group.coeff">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">G</field>
<field name="coeff_min" eval="375"/>
</record>
<record id="gn_cc.cc_3442_G_autonomie" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">G</field>
<field name="criteria">Autonomie</field>
<field name="description">Le salarié peut créer lui-même ses processus. Le salarié rend compte au terme de sa mission et est évalué sur les écarts entre les attendus et le réalisé. Le contrôle seffectue a posteriori.</field>
</record>
<record id="gn_cc.cc_3442_G_responsabilite" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">G</field>
<field name="criteria">Responsabilité</field>
<field name="description">Participe à lélaboration des directives et/ou dun budget limité à son périmètre daction et est responsable de son exécution. Il peut assurer la responsabilité hiérarchique dautres salariés dans le cadre dune délégation de responsabilité.</field>
</record>
<record id="gn_cc.cc_3442_G_technicite" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">G</field>
<field name="criteria">Technicité</field>
<field name="description">Compétences élargies impliquant une très bonne maîtrise dun ou plusieurs domaines dintervention</field>
</record>
<record id="gn_cc.cc_3442_H_coeff_20220101" model="gn_cc.cc.group.coeff">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">H</field>
<field name="coeff_min" eval="400"/>
</record>
<record id="gn_cc.cc_3442_H_autonomie" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">H</field>
<field name="criteria">Autonomie</field>
<field name="description">Lautonomie est inhérente au statut de cadre. Elle est définie en fonction du poste occupé dans la structure. Le salarié rend compte au terme de sa mission et est évalué sur les écarts entre les attendus et le réalisé. Le contrôle seffectue obligatoirement a posteriori.</field>
</record>
<record id="gn_cc.cc_3442_H_responsabilite" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">H</field>
<field name="criteria">Responsabilité</field>
<field name="description">Définit et assume la politique économique de la structure et/ou assume la responsabilité hiérarchique et disciplinaire et/ou assume la responsabilité juridique de lactivité mise en œuvre. Assure la représentation de la structure dans tout ou partie de ces compétences.</field>
</record>
<record id="gn_cc.cc_3442_H_technicite" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">H</field>
<field name="criteria">Technicité</field>
<field name="description">Expertise dans un ou plusieurs domaines de compétences. Au groupe H, peut assurer des missions de développement sur un secteur dactivité ou ponctuellement sur plusieurs secteurs dactivités.</field>
</record>
<record id="gn_cc.cc_3442_I_coeff_20220101" model="gn_cc.cc.group.coeff">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">I</field>
<field name="coeff_min" eval="450"/>
</record>
<record id="gn_cc.cc_3442_I_autonomie" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">I</field>
<field name="criteria">Autonomie</field>
<field name="description">Lautonomie est inhérente au statut de cadre. Elle est définie en fonction du poste occupé dans la structure. Le salarié rend compte au terme de sa mission et est évalué sur les écarts entre les attendus et le réalisé. Le contrôle seffectue obligatoirement a posteriori.</field>
</record>
<record id="gn_cc.cc_3442_I_responsabilite" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">I</field>
<field name="criteria">Responsabilité</field>
<field name="description">Définit et assume la politique économique de la structure et/ou assume la responsabilité hiérarchique et disciplinaire et/ou assume la responsabilité juridique de lactivité mise en œuvre. Assure la représentation de la structure dans tout ou partie de ces compétences.</field>
</record>
<record id="gn_cc.cc_3442_I_technicite" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">I</field>
<field name="criteria">Technicité</field>
<field name="description">Expertise dans un ou plusieurs domaines de compétences. Au groupe I, il détermine et pilote la stratégie de développement.</field>
</record>
<record id="gn_cc.cc_3442_J_coeff_20220101" model="gn_cc.cc.group.coeff">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">J</field>
<field name="coeff_min" eval="500"/>
</record>
<record id="gn_cc.cc_3442_J_autonomie" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">J</field>
<field name="criteria">Autonomie</field>
<field name="description">Salariés qui détiennent la responsabilité de la définition des objectifs de la structure et de lorganisation du travail. Il doit détenir une délégation permanente de pouvoirs émanant dun cadre dun niveau supérieur ou des instances statutaires</field>
</record>
<record id="gn_cc.cc_3442_K_autonomie" model="gn_cc.cc.group.description">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="group">K</field>
<field name="criteria">Autonomie</field>
<field name="description">Salarié répondant à la définition légale et jurisprudentielle de cadre dirigeant </field>
</record>
<record id="gn_cc.cc_3442_V1_20220101" model="gn_cc.cc.point_value">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="name">V1</field>
<field name="value">6.45</field>
</record>
<record id="gn_cc.cc_3442_V1_20220501" model="gn_cc.cc.point_value">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-05-01</field>
<field name="name">V1</field>
<field name="value">6.61</field>
</record>
<record id="gn_cc.cc_3442_V1_20230101" model="gn_cc.cc.point_value">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2023-01-01</field>
<field name="name">V1</field>
<field name="value">6.85</field>
</record>
<record id="gn_cc.cc_3442_V1_20240101" model="gn_cc.cc.point_value">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2024-01-01</field>
<field name="name">V1</field>
<field name="value">7.01</field>
</record>
<record id="gn_cc.cc_3442_V2_20220101" model="gn_cc.cc.point_value">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2022-01-01</field>
<field name="name">V2</field>
<field name="value">6.37</field>
</record>
<record id="gn_cc.cc_3442_V2_20230101" model="gn_cc.cc.point_value">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2023-01-01</field>
<field name="name">V2</field>
<field name="value">6.50</field>
</record>
<record id="gn_cc.cc_3442_V2_20240101" model="gn_cc.cc.point_value">
<field name="cc" ref="gn_cc.cc_3442"/>
<field name="start_date">2024-01-01</field>
<field name="name">V2</field>
<field name="value">6.60</field>
</record>
<menuitem
id="gn_cc.cc_configuration_menu"
name="Conventions Collectives"
parent="hr.menu_human_resources_configuration"
sequence="200"
groups="hr.group_hr_manager"/>
<menuitem
id="gn_cc.cc_configuration_menu_cc"
name="Conventions"
parent="gn_cc.cc_configuration_menu"
sequence="10"
action="gn_cc.cc_configuration"
groups="hr.group_hr_manager"/>
</odoo>

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

@ -0,0 +1,187 @@
# -*- coding: utf-8 -*-
# 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 ConventionCollective(models.Model):
_name = "gn_cc.cc"
_description = "Convention Collective"
_order = 'idcc, name'
name = fields.Char(string='Nom complet de la Convention Collective', required=True)
associated_companies = fields.One2many('res.company', string="Sociétés utilisant cette convention", inverse_name="cc")
active = fields.Boolean(default=True)
idcc = fields.Char(string="IDCC", default="3442")
groups = fields.Text(string="Groupes d'emploi")
criterias = fields.Text(string="Critères de description des groupes d'emploi")
point_cat = fields.Text(string="Catégories de points")
point_values = fields.One2many('gn_cc.cc.point_value', string="Valeurs du point", inverse_name="cc")
descriptions = fields.One2many('gn_cc.cc.group.description', string="Description de groupes associés à la convention", inverse_name="cc")
actual_values = fields.One2many('gn_cc.cc.point_value', string="Valeurs du point en vigueur", compute='_compute_actual_values')
actual_descriptions = fields.One2many('gn_cc.cc.group.description', compute='_compute_actual_descriptions')
coeffs = fields.One2many('gn_cc.cc.group.coeff', string="Coefficient minimum des groupes", inverse_name="cc")
actual_coeffs = fields.One2many('gn_cc.cc.group.coeff', compute='_compute_actual_coeffs')
@api.depends('point_values')
def _compute_actual_values(self):
for cc in self:
# Reset the value first
cc.actual_values = [(5,)]
# This dict will hold the actual point_value ID for each 'name'
actual_records = {}
# create a default comparison_date so we can use a context var to call this method in other modules (payslip)
comparison_date = self.env.context.get('comparison_date', fields.Date.today())
# Iterate over all point_values to find the latest but not in the future
for pv in cc.point_values.sorted(key='start_date', reverse=True):
# If we haven't added a record for this 'name' yet, do it
if pv.name not in actual_records and pv.start_date <= comparison_date:
actual_records[pv.name] = pv
# Now, set the actual_values field to the IDs of the actual records
actual_ids = [rec.id for rec in actual_records.values()]
cc.actual_values = [(6, 0, actual_ids)]
@api.depends('descriptions')
def _compute_actual_descriptions(self):
for cc in self:
# Reset the value first
cc.actual_descriptions = [(5,)]
# This dict will hold the actual point_value ID for each 'name'
actual_records = {}
# create a default comparison_date so we can use a context var to call this method in other modules (payslip)
comparison_date = self.env.context.get('comparison_date', fields.Date.today())
# Iterate over all point_values to find the latest but not in the future
for desc in cc.descriptions.sorted(key='start_date', reverse=True):
key = (desc.group, desc.criteria)
# If we haven't added a record for this 'name' yet, do it
if key not in actual_records and desc.start_date <= comparison_date:
actual_records[key] = desc
# Now, set the actual_values field to the IDs of the actual records
actual_ids = [rec.id for rec in actual_records.values()]
cc.actual_descriptions = [(6, 0, actual_ids)]
@api.depends('coeffs')
def _compute_actual_coeffs(self):
for cc in self:
# Reset the value first
cc.actual_coeffs = [(5,)]
# This dict will hold the actual point_value ID for each 'name'
actual_records = {}
# create a default comparison_date so we can use a context var to call this method in other modules (payslip)
comparison_date = self.env.context.get('comparison_date', fields.Date.today())
# Iterate over all point_values to find the latest but not in the future
for coeff in cc.coeffs.sorted(key='start_date', reverse=True):
# If we haven't added a record for this 'name' yet, do it
if coeff.group not in actual_records and coeff.start_date <= comparison_date:
actual_records[coeff.group] = coeff
# Now, set the actual_values field to the IDs of the actual records
actual_ids = [rec.id for rec in actual_records.values()]
cc.actual_coeffs = [(6, 0, actual_ids)]
@api.model
def create(self, vals):
new_record = super(ConventionCollective, self).create(vals)
self.env.user.company_id.write({'cc': new_record})
return new_record
class ConventionCollectiveCoeffMin(models.Model):
_name = "gn_cc.cc.group.coeff"
_description = "Coefficient minimum pour le groupe d'emploi"
_order = 'start_date'
@api.model
def _default_cc(self):
return self.env.user.company_id.cc
cc = fields.Many2one('gn_cc.cc', required=True, default='_default_cc', string="Convention collective associée")
@api.model
def _get_groups_selection(self):
_logger.info("Current compagnie: %s, Current CC: %s, Groups: %s", self.env.user.company_id.name, self.env.user.company_id.cc.idcc, self.env.user.company_id.cc.groups)
if self.env.user.company_id.cc and self.env.user.company_id.cc.groups:
groups_list = [(group.strip(), group.strip()) for group in self.env.user.company_id.cc.groups.split(';')]
return groups_list
else:
return []
start_date = fields.Date('From', required=True, default=lambda self: fields.Date.today())
group = fields.Selection(selection='_get_groups_selection', required=True, string="Groupe associé au coefficient")
coeff_min = fields.Integer(string="Coefficient Minimum", required=True)
class ConventionCollectiveGroupDescription(models.Model):
_name = "gn_cc.cc.group.description"
_description = "Descriptions des groupes d'emploi"
_order = 'start_date'
@api.model
def _default_cc(self):
return self.env.user.company_id.cc
@api.model
def _get_groups_selection(self):
if self.env.user.company_id.cc and self.env.user.company_id.cc.groups:
groups_list = [(group.strip(), group.strip()) for group in self.env.user.company_id.cc.groups.split(';')]
return groups_list
else:
return []
@api.model
def _get_criterias_selection(self):
if self.env.user.company_id.cc and self.env.user.company_id.cc.criterias:
criterias_list = [(criteria.strip(), criteria.strip()) for criteria in self.env.user.company_id.cc.criterias.split(';')]
return criterias_list
else:
return []
cc = fields.Many2one('gn_cc.cc', required=True, default=_default_cc, string="Convention collective associée")
start_date = fields.Date('From', required=True, default=lambda self: fields.Date.today())
group = fields.Selection(selection='_get_groups_selection', required=True, string="Groupe")
criteria = fields.Selection(selection='_get_criterias_selection', required=True, string="Critère de classification")
description = fields.Text(required=True)
class ConventionCollectivePointValue(models.Model):
_name = "gn_cc.cc.point_value"
_description = "Valeur du point"
_order = 'start_date, cc'
@api.model
def _default_cc(self):
return self.env.user.company_id.cc
@api.model
def _get_point_cat_selection(self):
if self.env.user.company_id.cc and self.env.user.company_id.cc.point_cat:
point_cat_list = [(cat.strip(), cat.strip()) for cat in self.env.user.company_id.cc.point_cat.split(';')]
return point_cat_list
else:
return []
start_date = fields.Date('From', required=True)
cc = fields.Many2one('gn_cc.cc', required=True, default=_default_cc, string="Convention collective associée")
name = fields.Selection(selection='_get_point_cat_selection', required=True, string="Catégorie de point")
value = fields.Monetary(string='Valeur du Point', help='Valeur du point défini par les partenaires sociaux', required=True)
currency_id = fields.Many2one(
'res.currency',
string='Currency',
required=True,
default=lambda self: self.env.user.company_id.currency_id.id,
help="Currency"
)

@ -7,5 +7,6 @@ from odoo import fields, models
class ResCompany(models.Model):
_inherit = 'res.company'
cc = fields.Many2one('gn_payroll.cc', "Convention Collective appliquée")
cc = fields.Many2one('gn_cc.cc', string="Convention Collective appliquée")

@ -0,0 +1,9 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_gn_cc_cc_user,gn_cc_cc_user,model_gn_cc_cc,base.group_user,1,0,0,0
access_gn_cc_cc_admin,gn_cc_cc_admin,model_gn_cc_cc,hr_contract.group_hr_contract_manager,1,1,1,1
access_gn_cc_cc_group_coeff_user,gn_cc_cc_group_coeff_user,model_gn_cc_cc_group_coeff,base.group_user,1,0,0,0
access_gn_cc_cc_group_coeff_admin,gn_cc_cc_group_coeff_admin,model_gn_cc_cc_group_coeff,hr_contract.group_hr_contract_manager,1,1,1,1
access_gn_cc_cc_group_description_user,gn_cc_cc_group_description_user,model_gn_cc_cc_group_description,base.group_user,1,0,0,0
access_gn_cc_cc_group_description_admin,gn_cc_cc_group_description_admin,model_gn_cc_cc_group_description,hr_contract.group_hr_contract_manager,1,1,1,1
access_gn_cc_cc_point_value_user,gn_cc_cc_point_value_user,model_gn_cc_cc_point_value,base.group_user,1,0,0,0
access_gn_cc_cc_point_value_admin,gn_cc_cc_point_value_admin,model_gn_cc_cc_point_value,hr_contract.group_hr_contract_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_gn_cc_cc_user gn_cc_cc_user model_gn_cc_cc base.group_user 1 0 0 0
3 access_gn_cc_cc_admin gn_cc_cc_admin model_gn_cc_cc hr_contract.group_hr_contract_manager 1 1 1 1
4 access_gn_cc_cc_group_coeff_user gn_cc_cc_group_coeff_user model_gn_cc_cc_group_coeff base.group_user 1 0 0 0
5 access_gn_cc_cc_group_coeff_admin gn_cc_cc_group_coeff_admin model_gn_cc_cc_group_coeff hr_contract.group_hr_contract_manager 1 1 1 1
6 access_gn_cc_cc_group_description_user gn_cc_cc_group_description_user model_gn_cc_cc_group_description base.group_user 1 0 0 0
7 access_gn_cc_cc_group_description_admin gn_cc_cc_group_description_admin model_gn_cc_cc_group_description hr_contract.group_hr_contract_manager 1 1 1 1
8 access_gn_cc_cc_point_value_user gn_cc_cc_point_value_user model_gn_cc_cc_point_value base.group_user 1 0 0 0
9 access_gn_cc_cc_point_value_admin gn_cc_cc_point_value_admin model_gn_cc_cc_point_value hr_contract.group_hr_contract_manager 1 1 1 1

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="gn_cc_form_view" model="ir.ui.view">
<field name="name">gn_cc.cc.form</field>
<field name="model">gn_cc.cc</field>
<field name="arch" type="xml">
<form string="Conventions Collectives">
<sheet string="Convention Collective">
<div class="oe_title">
<h1>
<field name="name" placeholder="Nom complet de la convention collective" />
</h1>
</div>
<group>
<group>
<field name="idcc" />
<field name="groups" placeholder="Liste des groupes, séparés par un point-virgule"/>
<field name="criterias" placeholder="Liste des critères de classement des groupes, séparés par un retour à la ligne"/>
<field name="point_cat" placeholder="Liste des catégories de points, séparés par un retour à la ligne"/>
<field name="actual_values" context="{'default_cc': active_id}" options="{'create': True, 'delete': True}">
<tree editable="bottom">
<field name="name" help="catégorie de point"/>
<field name="start_date"/>
<field name="value"/>
</tree>
</field>
<field name="actual_descriptions" context="{'default_cc': active_id}" widget="one2many_list" options="{'create': True, 'delete': True}">
<tree editable="bottom">
<field name="start_date"/>
<field name="group"/>
<field name="criteria"/>
<field name="description"/>
</tree>
</field>
<field name="actual_coeffs" context="{'default_cc': active_id}" widget="one2many_list" options="{'create': True, 'delete': True}">
<tree editable="bottom">
<field name="start_date"/>
<field name="group"/>
<field name="coeff_min"/>
</tree>
</field>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record id="gn_cc.cc_configuration" model="ir.actions.act_window">
<field name="name">Conventions Collectives</field>
<field name="res_model">gn_cc.cc</field>
<field name="view_mode">tree,form</field>
</record>
<record id="gn_cc.cc_group_coeff_configuration" model="ir.actions.act_window">
<field name="name">Coefficient minimum</field>
<field name="res_model">gn_cc.cc.group.coeff</field>
<field name="view_mode">tree,form</field>
</record>
<record id="gn_cc.cc_group_description_configuration" model="ir.actions.act_window">
<field name="name">Description du groupe</field>
<field name="res_model">gn_cc.cc.group.description</field>
<field name="view_mode">tree,form</field>
</record>
<record id="gn_cc.cc_point_value_configuration" model="ir.actions.act_window">
<field name="name">Valeur du point</field>
<field name="res_model">gn_cc.cc.point_value</field>
<field name="view_mode">tree,form</field>
</record>
</odoo>

@ -3,8 +3,8 @@
<record id="res_company_form_gn_payroll" model="ir.ui.view">
<field name="name">res.company.form.gnpayroll.fr</field>
<field name="model">res.company</field>
<field name="priority">30</field>
<field name="inherit_id" ref="account.view_company_form"/>
<field name="priority" eval="30"/>
<field name="inherit_id" ref="base.view_company_form"/>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='company_registry']" position="after">

@ -0,0 +1,19 @@
# GN Holidays
Module de gestion des congés et absences des employés
## How to install
1. Clone this repository in your odoo's extra-modules folder (i.e. /mnt/extra-addons)
2. Refresh list of Applications in UI
3. Search for 'gn_holidays' and click on **Install**
## Changelog
- v16.0.0.0.1 (2024/03/01):
- Création du module
- Ajouts des jours feriés pour 2024
- Ajout des types d'absence
## Issues
- [] Need prevention mechanism when CP or Compensatory Day is asked on a Public Holiday (cf issue #8)

@ -0,0 +1,22 @@
{
"name": "France - Congés",
"version": "16.0.0.0.1",
"category": "HR",
"summary": "Configuration des congés et absences",
"author": "Le Garage Numérique",
"maintainers": ["makayabou"],
"website": "https://odoo.legaragenumerique.fr",
"depends": [
"hr",
"hr_contract",
"l10n_fr_oca",
"hr_work_entry",
"hr_work_entry_holidays",
"hr_holidays_public",
],
"data": [
"data/gn_holidays_public.xml",
"data/gn_holidays.xml",
],
"license": "LGPL-3",
}

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data noupdate="0">
<!-- Work Entry Type -->
<record id="hr_work_entry_contract.work_entry_type_compensatory" model="hr.work.entry.type">
<field name="display_name">Congé compensatoire (récup')</field>
<field name="code">RTT</field>
<field name="color">15</field>
<field name="is_leave" eval="True"/>
</record>
<record id="hr_work_entry_contract.work_entry_type_home_working" model="hr.work.entry.type">
<field name="name">Télé-travail</field>
<field name="code">TT</field>
<field name="color">1</field>
<field name="is_leave" eval="True"/>
</record>
<record id="hr_work_entry_contract.work_entry_type_unpaid_leave" model="hr.work.entry.type">
<field name="name">Absence non-rémunérée</field>
<field name="color">2</field>
<field name="code">ABS</field>
<field name="is_leave" eval="True"/>
</record>
<record id="hr_work_entry_contract.work_entry_type_sick_leave" model="hr.work.entry.type">
<field name="name">Congé Maladie</field>
<field name="code">AM</field>
<field name="is_leave" eval="True"/>
<field name="color">9</field>
</record>
<record id="hr_work_entry_contract.work_entry_type_legal_leave" model="hr.work.entry.type">
<field name="name">Congé payé</field>
<field name="code">CP</field>
<field name="is_leave" eval="True"/>
<field name="color">6</field>
</record>
<!--Congés payés-->
<record id="hr_holidays.holiday_status_cl" model="hr.leave.type">
<field name="name">Paid Time Off</field>
<field name="requires_allocation">yes</field>
<field name="employee_requests">no</field>
<field name="leave_validation_type">hr</field>
<field name="allocation_validation_type">officer</field>
<field name="leave_notif_subtype_id" ref="hr_holidays.mt_leave"/>
<field name="allocation_notif_subtype_id" ref="hr_holidays.mt_leave_allocation"/>
<field name="responsible_id" ref="base.user_admin"/>
<field name="request_unit">day</field>
<field name="support_document" eval="False"/>
<field name="time_type">leave</field>
<field name="exclude_public_holidays" eval="True"/>
<field name="work_entry_type_id" ref="hr_work_entry_contract.work_entry_type_legal_leave"/>
<field name="icon_id" ref="hr_holidays.icon_14"/>
<field name="color">2</field>
<field name="company_id" eval="False"/> <!-- Explicitely set to False for it to be available to all companies -->
<field name="sequence">1</field>
</record>
<!-- Arrêt maladie -->
<record id="hr_holidays.holiday_status_sl" model="hr.leave.type">
<field name="name">Sick Time Off</field>
<field name="requires_allocation">no</field>
<field name="color_name">red</field>
<field name="leave_validation_type">no_validation</field>
<field name="leave_notif_subtype_id" ref="hr_holidays.mt_leave_sick"/>
<field name="responsible_id" ref="base.user_admin"/>
<field name="request_unit">day</field>
<field name="support_document" eval="True"/>
<field name="time_type">leave</field>
<field name="exclude_public_holidays" eval="False"/>
<field name="work_entry_type_id" ref="hr_work_entry_contract.work_entry_type_sick_leave"/>
<field name="icon_id" ref="hr_holidays.icon_22"/>
<field name="color">3</field>
<field name="company_id" eval="False"/> <!-- Explicitely set to False for it to be available to all companies -->
<field name="sequence">2</field>
</record>
<!-- Compensatory Days -->
<record id="hr_holidays.holiday_status_comp" model="hr.leave.type">
<field name="name">Compensatory Days</field>
<field name="requires_allocation">yes</field>
<field name="employee_requests">yes</field>
<field name="leave_validation_type">hr</field>
<field name="allocation_validation_type">officer</field>
<field name="request_unit">hour</field>
<field name="support_document" eval="True"/>
<field name="time_type">leave</field>
<field name="work_entry_type_id" ref="hr_work_entry_contract.work_entry_type_compensatory"/>
<field name="exclude_public_holidays" eval="True"/>
<field name="leave_notif_subtype_id" ref="hr_holidays.mt_leave"/>
<field name="responsible_id" ref="base.user_admin"/>
<field name="icon_id" ref="hr_holidays.icon_4"/>
<field name="color">4</field>
<field name="company_id" eval="False"/> <!-- Explicitely set to False for it to be available to all companies -->
<field name="sequence">4</field>
</record>
<!--Absence non-rémunérée -->
<record id="hr_holidays.holiday_status_unpaid" model="hr.leave.type">
<field name="name">Unpaid</field>
<field name="requires_allocation">no</field>
<field name="leave_validation_type">hr</field>
<field name="allocation_validation_type">officer</field>
<field name="request_unit">hour</field>
<field name="unpaid" eval="True"/>
<field name="time_type">leave</field>
<field name="support_document" eval="True"/>
<field name="exclude_public_holidays" eval="True"/>
<field name="work_entry_type_id" ref="hr_work_entry_contract.work_entry_type_unpaid_leave"/>
<field name="leave_notif_subtype_id" ref="hr_holidays.mt_leave_unpaid"/>
<field name="responsible_id" ref="base.user_admin"/>
<field name="icon_id" ref="hr_holidays.icon_28"/>
<field name="color">5</field>
<field name="company_id" eval="False"/> <!-- Explicitely set to False for it to be available to all companies -->
<field name="sequence">3</field>
</record>
<!-- Plan de cumul des congés payés-->
<record id="cumul_cp" model="hr.leave.accrual.plan">
<field name="name">Cumul des congés payés</field>
<field name="time_off_type_id" ref="hr_holidays.holiday_status_cl"/>
</record>
<!-- Régle de cumul des Congés payés-->
<record id="cumul_standard" model="hr.leave.accrual.level">
<field name="start_count">0</field>
<field name="start_type">month</field>
<field name="is_based_on_worked_time" eval="True"/>
<field name="added_value">2.5</field>
<field name="added_value_type">days</field>
<field name="frequency">monthly</field>
<field name="maximum_leave">90</field>
<field name="action_with_unused_accruals">postponed</field>
<field name="postpone_max_days">60</field>
<field name="accrual_plan_id" ref="gn_holidays.cumul_cp"/>
</record>
</data>
<delete model="hr.work.entry.type" id="hr_work_entry_contract.work_entry_type_leave"/>
</odoo>

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data noupdate="0">
<!-- Jour fériés 2024 -->
<record id="jours_feries_2024" model="hr.holidays.public">
<field name="year">2024</field>
<field name="country_id" ref="base.fr"/>
</record>
<record id="jour_ferie_jour_de_l_an" model="hr.holidays.public.line">
<field name="name">Jour de l'an</field>
<field name="date" eval="'2024-01-01'"/>
<field name="variable_date" eval="False"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
<record id="jour_ferie_lundi_de_paques" model="hr.holidays.public.line">
<field name="name">Lundi de Pâques</field>
<field name="date" eval="'2024-04-01'"/>
<field name="variable_date" eval="True"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
<record id="jour_ferie_fete_du_travail" model="hr.holidays.public.line">
<field name="name">Fête du Travail</field>
<field name="date" eval="'2024-05-01'"/>
<field name="variable_date" eval="False"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
<record id="jour_ferie_victoire_1945" model="hr.holidays.public.line">
<field name="name">Victoire 1945</field>
<field name="date" eval="'2024-05-08'"/>
<field name="variable_date" eval="False"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
<record id="jour_ferie_ascension" model="hr.holidays.public.line">
<field name="name">Ascension</field>
<field name="date" eval="'2024-05-09'"/>
<field name="variable_date" eval="True"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
<record id="jour_ferie_pentecote" model="hr.holidays.public.line">
<field name="name">Pentecôte</field>
<field name="date" eval="'2024-05-20'"/>
<field name="variable_date" eval="True"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
<record id="jour_ferie_fete_nationale" model="hr.holidays.public.line">
<field name="name">Fête Nationale</field>
<field name="date" eval="'2024-07-14'"/>
<field name="variable_date" eval="False"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
<record id="jour_ferie_assomption" model="hr.holidays.public.line">
<field name="name">Assomption</field>
<field name="date" eval="'2024-08-15'"/>
<field name="variable_date" eval="False"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
<record id="jour_ferie_toussaint" model="hr.holidays.public.line">
<field name="name">Toussaint</field>
<field name="date" eval="'2024-11-01'"/>
<field name="variable_date" eval="False"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
<record id="jour_ferie_armistrice_1918" model="hr.holidays.public.line">
<field name="name">Armistrice 1918</field>
<field name="date" eval="'2024-11-11'"/>
<field name="variable_date" eval="False"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
<record id="jour_ferie_noel" model="hr.holidays.public.line">
<field name="name">Jour de Noël</field>
<field name="date" eval="'2024-12-25'"/>
<field name="variable_date" eval="False"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
</data>
</odoo>

@ -1,6 +1,6 @@
{
"name": "France - Paye",
"version": "16.0.0.0.2",
"version": "16.0.0.0.4",
"category": "Payroll",
"summary": "Configuration de la paie",
"author": "Le Garage Numérique",
@ -8,17 +8,24 @@
"website": "https://odoo.legaragenumerique.fr",
"depends": [
"payroll",
"hr_contract"
"hr_contract",
"hr_work_entry",
"hr_work_entry_holidays",
"hr_holidays_public",
"contacts"
],
"data": [
"data/gn_payroll_cc.xml",
"views/gn_payroll_company.xml",
"views/gn_payroll_contract.xml",
"views/gn_payroll_calendar.xml",
"data/gn_payroll_contract.xml",
"data/gn_payroll_holidays.xml",
"data/gn_payroll_holidays_public.xml",
"data/gn_payroll_salary.xml",
"data/gn_payroll_sickness.xml",
#"data/gn_payroll_sickness.xml",
"data/gn_payroll_time.xml",
"data/gn_payroll_universal.xml",
"data/gn_payroll_structure.xml",
"views/gn_payroll_company.xml",
#"data/gn_payroll_structure.xml",
"security/ir.model.access.csv"
],
"license": "LGPL-3",

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="gn_payroll.cc_1418" model="gn_payroll.cc">
<field name="name">Convention Eclat</field>
<field name="idcc">1418</field>
</record>
</odoo>

@ -43,4 +43,64 @@
<record id="gn_payroll_contract_internship" model="hr.contract.type">
<field name="name">Stage</field>
</record>
<record id="gn_payroll_contract_entretien_type_annuel" model="gn_payroll.hr.contract.entretien.type">
<field name="name">Entretien annuel d'évaluation</field>
<field name="month_delay">12</field>
<field name="start_point">endofyear</field>
<field name="auto">True</field>
<field name="cc_ids" eval="[(6, 0, [ref('gn_payroll.cc_3442')])]"></field>
</record>
<record id="gn_payroll_contract_entretien_type_periodique" model="gn_payroll.hr.contract.entretien.type">
<field name="name">Entretien professionnel périodique</field>
</record>
<record id="gn_payroll_contract_entretien_type_periodique_retour" model="gn_payroll.hr.contract.entretien.type">
<field name="name">Entretien professionnel périodique de retour de congé long</field>
<field name="parent_id" ref="gn_payroll_contract_entretien_type_periodique"/>
<field name="month_delay">0</field>
<field name="cc_ids" eval="[(6, 0, [ref('gn_payroll.cc_3442')])]"/>
</record>
<record id="gn_payroll_contract_entretien_type_periodique_initial" model="gn_payroll.hr.contract.entretien.type">
<field name="name">Entretien professionnel périodique des 2 ans</field>
<field name="parent_id" ref="gn_payroll_contract_entretien_type_periodique"/>
<field name="start_point">contract</field>
<field name="month_delay">24</field>
<field name="auto">True</field>
<field name="cc_ids" eval="[(6, 0, [ref('gn_payroll.cc_3442')])]"/>
</record>
<record id="gn_payroll_contract_entretien_type_periodique_bilan" model="gn_payroll.hr.contract.entretien.type">
<field name="name">Entretien professionnel bilan des 6 ans</field>
<field name="parent_id" ref="gn_payroll_contract_entretien_type_periodique"/>
<field name="start_point">contract</field>
<field name="month_delay">72</field>
<field name="auto">True</field>
<field name="cc_ids" eval="[(6, 0, [ref('gn_payroll.cc_3442')])]"/>
</record>
<record id="gn_payroll_contract_entretien_type_valorisation" model="gn_payroll.hr.contract.entretien.type">
<field name="name">Entretien de valorisation des acquis professionnels</field>
<field name="start_point">contract</field>
<field name="month_delay">48</field>
<field name="generate_subcontract" eval="True"/>
<field name="auto">True</field>
<field name="cc_ids" eval="[(6, 0, [ref('gn_payroll.cc_3442')])]"/>
</record>
<record id="gn_payroll_contract_entretien_type_modif_cc" model="gn_payroll.hr.contract.entretien.type">
<field name="name">Modification conventionnelle</field>
</record>
<data noupdate="1">
<!-- Define the sequence for the Entretien model -->
<record id="seq_entretien" model="ir.sequence">
<field name="name">Entretien Sequence</field>
<field name="code">gn_payroll.hr.contract.entretien</field>
<field name="prefix">ENT</field>
<field name="padding">5</field>
<field name="number_increment">1</field>
</record>
</data>
<menuitem
id="menu_human_resources_configuration_contract_entretien"
name="Entretiens"
parent="hr.menu_hr_employee_payroll"
sequence="35"
action="gn_payroll.hr_contract_entretien"
groups="hr.group_hr_manager"/>
</odoo>

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data noupdate="0">
<!-- Work Entry Type -->
<record id="hr_work_entry_contract.work_entry_type_compensatory" model="hr.work.entry.type">
<field name="display_name">Congé compensatoire (récup')</field>
<field name="code">RTT</field>
<field name="color">15</field>
<field name="is_leave" eval="True"/>
</record>
<record id="hr_work_entry_contract.work_entry_type_home_working" model="hr.work.entry.type">
<field name="name">Télé-travail</field>
<field name="code">TT</field>
<field name="color">1</field>
<field name="is_leave" eval="True"/>
</record>
<record id="hr_work_entry_contract.work_entry_type_unpaid_leave" model="hr.work.entry.type">
<field name="name">Absence non-rémunérée</field>
<field name="color">2</field>
<field name="code">ABS</field>
<field name="is_leave" eval="True"/>
</record>
<record id="hr_work_entry_contract.work_entry_type_sick_leave" model="hr.work.entry.type">
<field name="name">Congé Maladie</field>
<field name="code">AM</field>
<field name="is_leave" eval="True"/>
<field name="color">9</field>
</record>
<record id="hr_work_entry_contract.work_entry_type_legal_leave" model="hr.work.entry.type">
<field name="name">Congé payé</field>
<field name="code">CP</field>
<field name="is_leave" eval="True"/>
<field name="color">6</field>
</record>
<!--Congés payés-->
<record id="hr_holidays.holiday_status_cl" model="hr.leave.type">
<field name="name">Paid Time Off</field>
<field name="requires_allocation">yes</field>
<field name="employee_requests">no</field>
<field name="leave_validation_type">hr</field>
<field name="allocation_validation_type">officer</field>
<field name="leave_notif_subtype_id" ref="hr_holidays.mt_leave"/>
<field name="allocation_notif_subtype_id" ref="hr_holidays.mt_leave_allocation"/>
<field name="responsible_id" ref="base.user_admin"/>
<field name="request_unit">day</field>
<field name="support_document" eval="False"/>
<field name="time_type">leave</field>
<field name="exclude_public_holidays" eval="True"/>
<field name="work_entry_type_id" ref="hr_work_entry_contract.work_entry_type_legal_leave"/>
<field name="icon_id" ref="hr_holidays.icon_14"/>
<field name="color">2</field>
<field name="company_id" eval="False"/> <!-- Explicitely set to False for it to be available to all companies -->
<field name="sequence">1</field>
</record>
<!-- Arrêt maladie -->
<record id="hr_holidays.holiday_status_sl" model="hr.leave.type">
<field name="name">Sick Time Off</field>
<field name="requires_allocation">no</field>
<field name="color_name">red</field>
<field name="leave_validation_type">no_validation</field>
<field name="leave_notif_subtype_id" ref="hr_holidays.mt_leave_sick"/>
<field name="responsible_id" ref="base.user_admin"/>
<field name="request_unit">day</field>
<field name="support_document" eval="True"/>
<field name="time_type">leave</field>
<field name="exclude_public_holidays" eval="False"/>
<field name="work_entry_type_id" ref="hr_work_entry_contract.work_entry_type_sick_leave"/>
<field name="icon_id" ref="hr_holidays.icon_22"/>
<field name="color">3</field>
<field name="company_id" eval="False"/> <!-- Explicitely set to False for it to be available to all companies -->
<field name="sequence">2</field>
</record>
<!-- Compensatory Days -->
<record id="hr_holidays.holiday_status_comp" model="hr.leave.type">
<field name="name">Compensatory Days</field>
<field name="requires_allocation">yes</field>
<field name="employee_requests">yes</field>
<field name="leave_validation_type">hr</field>
<field name="allocation_validation_type">officer</field>
<field name="request_unit">hour</field>
<field name="support_document" eval="True"/>
<field name="time_type">leave</field>
<field name="work_entry_type_id" ref="hr_work_entry_contract.work_entry_type_compensatory"/>
<field name="exclude_public_holidays" eval="True"/>
<field name="leave_notif_subtype_id" ref="hr_holidays.mt_leave"/>
<field name="responsible_id" ref="base.user_admin"/>
<field name="icon_id" ref="hr_holidays.icon_4"/>
<field name="color">4</field>
<field name="company_id" eval="False"/> <!-- Explicitely set to False for it to be available to all companies -->
<field name="sequence">4</field>
</record>
<!--Absence non-rémunérée -->
<record id="hr_holidays.holiday_status_unpaid" model="hr.leave.type">
<field name="name">Unpaid</field>
<field name="requires_allocation">no</field>
<field name="leave_validation_type">hr</field>
<field name="allocation_validation_type">officer</field>
<field name="request_unit">hour</field>
<field name="unpaid" eval="True"/>
<field name="time_type">leave</field>
<field name="support_document" eval="True"/>
<field name="exclude_public_holidays" eval="True"/>
<field name="work_entry_type_id" ref="hr_work_entry_contract.work_entry_type_unpaid_leave"/>
<field name="leave_notif_subtype_id" ref="hr_holidays.mt_leave_unpaid"/>
<field name="responsible_id" ref="base.user_admin"/>
<field name="icon_id" ref="hr_holidays.icon_28"/>
<field name="color">5</field>
<field name="company_id" eval="False"/> <!-- Explicitely set to False for it to be available to all companies -->
<field name="sequence">3</field>
</record>
<!-- Plan de cumul des congés payés-->
<record id="cumul_cp" model="hr.leave.accrual.plan">
<field name="name">Cumul des congés payés</field>
<field name="time_off_type_id" ref="hr_holidays.holiday_status_cl"/>
</record>
<!-- Régle de cumul des Congés payés-->
<record id="cumul_standard" model="hr.leave.accrual.level">
<field name="start_count">0</field>
<field name="start_type">month</field>
<field name="is_based_on_worked_time" eval="True"/>
<field name="added_value">2.5</field>
<field name="added_value_type">days</field>
<field name="frequency">monthly</field>
<field name="maximum_leave">90</field>
<field name="action_with_unused_accruals">postponed</field>
<field name="postpone_max_days">60</field>
<field name="accrual_plan_id" ref="gn_payroll.cumul_cp"/>
</record>
</data>
<delete model="hr.work.entry.type" id="hr_work_entry_contract.work_entry_type_leave"/>
</odoo>

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data noupdate="0">
<!-- Jour fériés 2024 -->
<record id="jours_feries_2024" model="hr.holidays.public">
<field name="year">2024</field>
<field name="country_id" ref="base.fr"/>
</record>
<record id="jour_ferie_jour_de_l_an" model="hr.holidays.public.line">
<field name="name">Jour de l'an</field>
<field name="date" eval="'2024-01-01'"/>
<field name="variable_date" eval="False"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
<record id="jour_ferie_lundi_de_paques" model="hr.holidays.public.line">
<field name="name">Lundi de Pâques</field>
<field name="date" eval="'2024-04-01'"/>
<field name="variable_date" eval="True"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
<record id="jour_ferie_fete_du_travail" model="hr.holidays.public.line">
<field name="name">Fête du Travail</field>
<field name="date" eval="'2024-05-01'"/>
<field name="variable_date" eval="False"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
<record id="jour_ferie_victoire_1945" model="hr.holidays.public.line">
<field name="name">Victoire 1945</field>
<field name="date" eval="'2024-05-08'"/>
<field name="variable_date" eval="False"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
<record id="jour_ferie_ascension" model="hr.holidays.public.line">
<field name="name">Ascension</field>
<field name="date" eval="'2024-05-09'"/>
<field name="variable_date" eval="True"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
<record id="jour_ferie_pentecote" model="hr.holidays.public.line">
<field name="name">Pentecôte</field>
<field name="date" eval="'2024-05-20'"/>
<field name="variable_date" eval="True"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
<record id="jour_ferie_fete_nationale" model="hr.holidays.public.line">
<field name="name">Fête Nationale</field>
<field name="date" eval="'2024-07-14'"/>
<field name="variable_date" eval="False"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
<record id="jour_ferie_assomption" model="hr.holidays.public.line">
<field name="name">Assomption</field>
<field name="date" eval="'2024-08-15'"/>
<field name="variable_date" eval="False"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
<record id="jour_ferie_toussaint" model="hr.holidays.public.line">
<field name="name">Toussaint</field>
<field name="date" eval="'2024-11-01'"/>
<field name="variable_date" eval="False"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
<record id="jour_ferie_armistrice_1918" model="hr.holidays.public.line">
<field name="name">Armistrice 1918</field>
<field name="date" eval="'2024-11-11'"/>
<field name="variable_date" eval="False"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
<record id="jour_ferie_noel" model="hr.holidays.public.line">
<field name="name">Jour de Noël</field>
<field name="date" eval="'2024-12-25'"/>
<field name="variable_date" eval="False"/>
<field name="year_id" ref="jours_feries_2024"/>
</record>
</data>
</odoo>

@ -4,6 +4,71 @@
<field name="name">Rémunération</field>
<field name="code">REMUNERATION</field>
</record>
<record id="CONTRACT_WAGE" model="hr.salary.rule">
<field name="name">Salaire contractuel</field>
<field name="code">CONTRACT_WAGE</field>
<field name="sequence" eval="100"/>
<field name="category_id" ref="gn_payroll.REMUNERATION"/>
<field name="condition_select">none</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">
result = contract.wage
</field>
</record>
<record id="WAGEV1" model="hr.salary.rule">
<field name="name">Rémunération des points de class V1</field>
<field name="code">WAGEV1</field>
<field name="sequence" eval="100"/>
<field name="category_id" ref="gn_payroll.REMUNERATION"/>
<field name="condition_select">python</field>
<field name="condition_python">
if payslip.company_id.cc.idcc == "3442":
result = True
</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">
payslip_date_from = payslip.date_from
cc = payslip.company_id.cc.with_context(comparison_date=payslip_date_from)
cc_id = cc.id
actual_v1 = 0
for value in cc.actual_values:
if value['name'] == "V1":
actual_v1 = value.value
break
actual_gpA = 0
for coeff in cc.actual_coeffs:
if coeff['group'] == "A":
actual_gpA = coeff['coeff_min']
break
result_qty = actual_gpA
result = actual_v1
</field>
</record>
<record id="WAGEV2" model="hr.salary.rule">
<field name="name">Rémunération des points de class V2</field>
<field name="code">WAGEV2</field>
<field name="sequence" eval="100"/>
<field name="category_id" ref="gn_payroll.REMUNERATION"/>
<field name="condition_select">python</field>
<field name="condition_python">
if payslip.company_id.cc.idcc == "3442":
result = True
</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">
payslip_date_from = payslip.date_from
cc = payslip.company_id.cc.with_context(comparison_date=payslip_date_from)
cc_id = cc.id
actual_v1 = 0
for value in cc.actual_values:
if value['name'] == "V2":
actual_v2 = value.value
break
result_qty = 173
result = actual_v2
</field>
</record>
<record id="BRUT" model="hr.salary.rule">
<field name="name">Salaire Brut</field>
<field name="code">BRUT</field>
@ -17,22 +82,10 @@ if inputs.BRUT and inputs.BRUT.amount > 0:
elif inputs.BRUT and inputs.BRUT.amount == -1 :
result = 0
else:
hours_month = 0
dic_days = {0: 0, 1:0, 2: 0, 3: 0, 4:0}
for x in contract.resource_calendar_id.attendance_ids:
dic_days[int(x.dayofweek)] += (x.hour_to - x.hour_from)
# Calculating hours workable for the month
for x in range(1, int(JOURSMOIS) + 1):
day = payslip.date_from.replace(day = x).weekday()
if day in dic_days:
hours_month += dic_days[day]
hours_std = sum([(x.hour_to - x.hour_from) for x in contract.resource_calendar_id.attendance_ids]) * 52 /12
result = round( HOURS * contract.wage / hours_month , 2) + categories.ALW + categories.INDEMNCOT
#result = round( HOURS * contract.wage / hours_std , 2) + categories.ALW + categories.INDEMNCOT
retenue = 0
if worked_days.ABS and worked_days.ABS.hours:
retenue = round( worked_days.ABS.hours * contract.wage / worked_days.WORK100.hours , 2)
result = contract.wage + retenue + categories.ALW + categories.INDEMNCOT
</field>
</record>
<record id="BRUTSC" model="hr.salary.rule">
@ -43,10 +96,15 @@ else:
<field name="condition_select">none</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">
if inputs.BRUT and inputs.BRUT.amount > 0:
result = inputs.BRUT.amount
else:
hours_std = sum([( x.hour_to - x.hour_from ) for x in contract.resource_calendar_id.attendance_ids]) * 52 / 12
if inputs.BRUT and inputs.BRUT.amount > 0:
result = inputs.BRUT.amount + categories.ALW + categories.INDEMNCOT
elif inputs.BRUT and inputs.BRUT.amount == -1 :
result = 0
else:
retenue = 0
if worked_days.ABS and worked_days.ABS.hours:
retenue = round( worked_days.ABS.hours * contract.wage / worked_days.WORK100.hours , 2)
result = retenue + categories.ALW + categories.INDEMNCOT
result = round( HOURS * TXSERVICECIVIQUE / hours_std , 2)
</field>
</record>

@ -4,6 +4,11 @@
<field name="name">Gestion des arrêts maladie</field>
<field name="code">SICKNESS</field>
</record>
<record id="work_entry_type_sickness" model="hr.work.entry.type">
<field name="name">Sickness</field>
<field name="color">0</field>
<field name="code">SICKNESSTYPE</field>
</record>
<record id ="CALENDAR_SICKNESS_DAYS" model="hr.salary.rule">
<field name="name">Arrêt maladie (Jours Calendaires)</field>
<field name="code">CALENDAR_SICKNESS_DAYS</field>

@ -5,18 +5,20 @@
<field name="company_id" eval="1"/>
<field name="rule_ids" eval="[
(6, 0, [
ref('USUAL_HOURS'),
ref('EXPECTED_HOURS'),
ref('CALENDAR_SICKNESS_DAYS'),
ref('NON_WORKED_SICKNESS_DAYS'),
ref('NON_WORKED_SICKNESS_DAYS'),
ref('WAITING_SICKNESS_DAYS'),
ref('IJSS_SICKNESS'),
ref('HOURS'),
ref('BRUT'),
ref('JOURSMOIS'),
ref('SMICH'),
ref('PMSS'),
ref('WAGE'),
ref('TXREMBTRANS')
ref('TXREMBTRANS'),
ref('COEFF'),
ref('BRUT'),
])
]"/>
</record>

@ -4,84 +4,15 @@
<field name="name">Calcul des heures</field>
<field name="code">TIME</field>
</record>
<record id="HOURS" model="hr.salary.rule">
<record id="PAYED_HOURS" model="hr.salary.rule">
<field name="name">Heures rémunérées</field>
<field name="code">HOURS</field>
<field name="sequence" eval="100" />
<field name="category_id" ref="gn_payroll.TIME" />
<field name="code">PAYED_HOURS</field>
<field name="sequence" eval="100"/>
<field name="category_id" ref="gn_payroll.TIME"/>
<field name="condition_select">none</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">
day = 0
hours = 0
hours_month = 0
hours_std = sum([(x.hour_to - x.hour_from) for x in contract.resource_calendar_id.attendance_ids]) * 52 /12
# calculation for Service civique
if contract.struct_id.code == "VOLSERVCIV":
if payslip.date_from.day > 1:
result = hours_std * (31 - payslip.date_from.day) / 30
elif JOURSMOIS > payslip.date_to.day:
result = hours_std * payslip.date_to.day / 30
else:
result = hours_std
# other calculations
else:
dic_days = {0: 0, 1:0, 2: 0, 3: 0, 4:0}
for x in contract.resource_calendar_id.attendance_ids:
if ((not x.date_from) or ( payslip.date_from >= x.date_from )) and \
((not x.date_to) or ( x.date_to >= payslip.date_to )):
dic_days[int(x.dayofweek)] += (x.hour_to - x.hour_from)
# Calculating missed hours when payslip doesn't start / end at the beginning/ end of the month
if payslip.date_from.day > 1:
for x in range(payslip.date_from.day, 1, -1):
day = payslip.date_from.replace(day = x).weekday()
if day in dic_days:
hours += dic_days[day]
if JOURSMOIS > payslip.date_to.day:
for x in range(payslip.date_to.day + 1, int(JOURSMOIS)):
day = payslip.date_to.replace(day = x).weekday()
if day in dic_days:
hours += dic_days[day]
hours_missed = hours
# Calculating missed hours when absence or sickday
maladie = inputs.MALADIE.amount if inputs.MALADIE and inputs.MALADIE.amount > 0.0 else 0
absences = inputs.ABSENCE.amount if inputs.ABSENCE and inputs.ABSENCE.amount > 0.0 else 0
calendaires = inputs.CALENDAIRE.amount if inputs.CALENDAIRE and inputs.CALENDAIRE.amount > 0.0 else 0
if ( maladie != 0 or absences != 0):
if calendaires + absences == JOURSMOIS:
hours_std = 0
else:
days_off = absences + maladie
hours_off = days_off * contract.resource_calendar_id.hours_per_day
hours_missed += hours_off
# Final calculations
if worked_days.WORK100 and worked_days.WORK100.number_of_hours:
hours_done = worked_days.WORK100.number_of_hours
else:
hours_done = hours_std
####result = hours_done - hours_missed
#result = hours_done * hours_std / (hours_missed + hours_done)
#result = hours_std * (hours_done - hours_missed) / hours_done
result = hours_done - hours_missed
####result = hours_std - hours_missed
result = worked_days.WORK100.number_of_hours + (worked_days.ABS.number_of_hours if worked_days.ABS else 0)
</field>
</record>
<record id="ABSENCE" model="hr.rule.input">
<field name="name">Absences non rémunérées ( hors arrêt maladie)</field>
<field name="code">ABSENCE</field>
<field name="input_id" ref="gn_payroll.HOURS" />
</record>
</odoo>

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import gn_payroll_cc
from . import gn_payroll_company
from . import gn_payroll_employee
from . import gn_payroll_contract
from . import gn_payroll_employee
#from . import gn_payroll_payslip

@ -1,17 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class ConventionCollective(models.Model):
_name = "gn_payroll.cc"
_description = "Convention Collective"
_order = 'idcc, name'
def copy(self, default=None):
raise UserError(_('Duplicating a company is not allowed. Please create a new company instead.'))
name = fields.Char(string='Nom complet de la Convention Collective', required=True, store=True, readonly=False)
active = fields.Boolean(default=True)
idcc = fields.Integer(string="IDCC", help='Used to order Conventions Collectives in the switcher', default=10)

@ -0,0 +1,226 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models, api
from dateutil.relativedelta import relativedelta
from datetime import date, datetime
from odoo.exceptions import ValidationError
import logging
_logger = logging.getLogger(__name__)
class GnCalendarEvent(models.Model):
_inherit = "calendar.event"
is_entretien = fields.Boolean(string="Concerne un entretien professionnel", default=False)
entretien_id = fields.Many2one("gn_payroll.hr.contract.entretien", string="Entretien professionnel associé")
entretien_ids = fields.Many2one("gn_payroll.hr.contract.entretien", string="Entretiens professionnels associés")
class GnHrContract(models.Model):
_inherit = "hr.contract"
previous_contract_id = fields.Many2one("hr.contract", string="Contrat précédent (en cas d'avenant, de renouvellement ou de changement de poste)", default=False)
contract_lineage_sequence_number = fields.Integer(string="Numéro de séquence dans l'historique du salarié", default=0)
computed_wage = fields.Integer(string="Salaire négocié (en fonction des éléments de la fiche de poste)", compute='_compute_wage', readonly=True, store=True)
#entretien_type_ids = fields.Many2many('gn_payroll.hr.contract.entretien.type', string="Catégories d'entretiens prévues au contrat")
#entretien_sequence_number = fields.Integer(string="Entretien Sequence Number", default=1)
entretien_ids = fields.One2many('gn_payroll.hr.contract.entretien', inverse_name='main_contract_id', string="Entretiens liés au contrat")
career_ids = fields.One2many('gn_payroll.hr.contract.career', readonly=True, inverse_name='contract_id', string="Fiche de poste associée")
# def update_entretiens_names(self):
# for contract in self:
# sequence_number = 1
# for entretien in contract.entretien_ids.sorted('start'):
# entretien.name = 'Entretien n°{}: {}'.format(sequence_number, entretien.type_id.name )
# sequence_number += 1
# @api.model
# def _get_groups_selection(self):
# if self.env.user.company_id.cc and self.env.user.company_id.cc.groups:
# groups_list = [(group.strip(), group.strip()) for group in self.env.user.company_id.cc.groups.split(';')]
# return groups_list
# else:
# return []
# Pas à supprimer complètement, mais à reprendre
@api.depends('indice', 'classification_group')
def _compute_wage(self):
for contract in self:
contract.computed_wage = contract.wage
return True
if contract.company_id.cc and contract.company_id.cc.idcc =="3442":
group_coeff = cc.actual_coeffs.filtered(lambda r: r.group == contract.classification_group).ensure_one()['coeff_min']
if group_coeff > employee.contract_id.indice:
raise UserError("L'indice minimum conventionnel pour le poste est %s, veuillez ajuster l'indice du contract en conséquence.", group_coeff)
@api.model
def create(self, vals):
contract = super(GnHrContract, self).create(vals)
if not contract.parent_contract_id:
cc_id = contract.company_id.cc.id
entretien_types = self.env['gn_payroll.hr.contract.entretien.type'].search([
('cc_ids', 'in', [cc_id]),
('auto', '=', True)
])
for entretien_type in entretien_types:
self.env['gn_payroll.hr.contract.entretien'].create({
'employee_id': contract.employee_id.user_partner_id.id,
'main_contract_id': contract.id,
'type_id': entretien_type.id,
})
#contract.entretien_sequence_number += 1
contract_lineage_sequence_number += 1
return contract
class EntretienType(models.Model):
_name='gn_payroll.hr.contract.entretien.type'
_description = "Type d'entretien d'évaluation ou de modification du contrat"
cc_ids = fields.Many2many('gn_payroll.cc', string="Conventions collectives rattachées")
name = fields.Char(string="Type de l'entretien", required = True)
auto = fields.Boolean("Est généré automatiquement à la création du contrat")
start_point = fields.Selection(string="Point de départ des entretiens", selection=[('endofyear', "En fin d'année"), ('contract', "À la date anniversaire du contrat"), ('absence', "Au retour d'un congé long")])
month_delay = fields.Integer(string="Nombre de mois entre chaque entretien")
generate_subcontract = fields.Boolean("Génère la création d'un avenant au contrat")
parent_id = fields.Many2one('gn_payroll.hr.contract.entretien.type', string="Type d'entretien parent", ondelete="cascade")
child_ids = fields.One2many('gn_payroll.hr.contract.entretien.type', 'parent_id', string="Types d'entretien enfants")
@api.constrains('parent_id')
def _check_parent_id(self):
for record in self:
if record.parent_id == record:
raise ValidationError("A record cannot select itself as a parent.")
if record.parent_id and record.parent_id.parent_id:
raise ValidationError("You cannot select a parent type which already has a parent.")
@api.onchange('parent_id')
def _onchange_parent_id(self):
if self.parent_id:
if self.parent_id == self:
self.parent_id = False
return {
'warning': {
'title': "Invalid Parent Selection",
'message': "A record cannot select itself as a parent.",
},
'domain': {'parent_id': domain}
}
if self.parent_id.parent_id:
self.parent_id = False
return {
'warning': {
'title': "Invalid Parent Selection",
'message': "You cannot select a parent type which already has a parent.",
}
}
class Entretien(models.Model):
_name = 'gn_payroll.hr.contract.entretien'
_description = "Entretien légal d'évaluation ou de modification du contrat"
ref = fields.Char(string="Référence", readonly=True)
name = fields.Char(string="Titre", readonly=True)
employee_id = fields.Many2one('res.partner', string="Employé", required=True, domain=[("employee_ids", "!=", False)], relation="contract_entretien_employee_rel")
other_participant_ids = fields.Many2many('res.partner', string="Autres participants à l'entretien (délégué salariale, etc.)", relation="contract_entretien_other_participant_rel")
interviewer_id = fields.Many2one('res.partner', domain=[("employee_ids", "!=", False)], string="Interviewer", relation="contract_entretien_interviewer_rel")
other_interviewer_ids = fields.Many2many('res.partner', domain=[("employee_ids", "!=", False)], string="Participants à l'entretien", relation="contract_entretien_other_interviewer_rel")
main_contract_id = fields.Many2one('hr.contract', string = "Contrat concerné", required=True)
running_contract_id = fields.Many2one('hr.contract', string="Avenant concerné")
type_id = fields.Many2one('gn_payroll.hr.contract.entretien.type', string="Type d'entretien", required=True)
first_date = fields.Date(string="Premier entretien", compute='_compute_first_date', store=True)
next_date = fields.Date(string="Date du prochain entretien", compute='_compute_next_entretien_date', store=True)
report = fields.Html(string="Compte-rendu")
event_ids = fields.One2many('calendar.event', 'entretien_ids', string="Rendez-vous programmés")
event_id = fields.One2many('calendar.event', 'entretien_id', string="Rendez-vous programmé", compute='_get_date_from_event', store=True)
start = fields.Datetime("Date et heure du rendez-vous", compute='_get_date_from_event', store=True)
generate_subcontract = fields.Boolean(
string="Génére la création d'un avenant",
default=lambda self: self._get_default_generate_subcontract()
)
generation_start = fields.Datetime(string="Date de prise en compte des aménagements")
generated_contract_id = fields.Many2one('hr.contract', string="Avenant généré")
generated_career_id = fields.Many2one('gn_payroll.hr.contract.career', string="Fiche de poste")
def _get_default_generate_subcontract(self):
return self.type_id.generate_subcontract if self.type_id else False
def update_main_contract(self):
# Updates entretien's num by date for employee,
# Called in _get_date_from_event on event_ids access
for entretien in self:
if entretien['main_contract_id']:
contract = self.env['hr.contract'].browse(entretien['main_contract_id'].id)
contract.update_entretiens_names()
@api.depends('type_id.start_point', 'type_id.month_delay')
def _compute_first_date(self):
for entretien in self:
if entretien.type_id and entretien.type_id.start_point:
start_point = entretien.type_id.start_point
if start_point == "contract" and entretien.main_contract_id.date_start and entretien.type_id.month_delay:
contract_start_date = fields.Date.from_string(entretien.main_contract_id.date_start)
entretien.first_date = contract_start_date + relativedelta(months=+entretien.type_id.month_delay)
elif start_point == "endofyear":
today = fields.Date.today()
entretien.first_date = date(today.year, 12, today.day)
elif start_point == "absence":
entretien.first_date = fields.Date.today()
@api.depends('event_ids')
def _get_date_from_event(self):
for entretien in self:
try:
if len(entretien.event_ids) > 0 and entretien.event_ids[0].start:
#entretien.event_id = self.env['calendar.event'].browse(entretien.event_ids[0].id)
entretien.event_id = entretien.event_ids[0]
_logger.info("Entretien has event_id: %s", entretien.event_id)
entretien.start = datetime.combine(entretien.event_ids[0].start, datetime.min.time())
else:
entretien.start = datetime.combine(entretien.first_date, datetime.min.time()) if entretien.first_date else datetime.now()
except Exception as e:
_logger.error("Error occurred while setting start date: %s", e)
entretien.start = datetime.combine(fields.Date.today(), datetime.min.time())
_logger.info("Date defined as start in entretien: %s", entretien.start)
self.update_main_contract()
@api.depends('first_date', 'type_id.month_delay')
def _compute_next_entretien_date(self):
for entretien in self:
if entretien.first_date and entretien.type_id.month_delay:
next_entretien_date = fields.Date.from_string(entretien.first_date) + relativedelta(months=entretien.type_id.month_delay)
entretien.next_date = fields.Date.to_string(next_entretien_date)
@api.model
def create(self, vals):
result = super(Entretien, self).create(vals)
employee_type = self.env['res.partner'].browse(vals['employee_id'])
_logger.info("Employee id is : %s", employee_type.company_type)
# Generate a sequence number for the name field
if not vals.get('ref'):
vals['ref'] = self.env['ir.sequence'].next_by_code('gn_payroll.hr.contract.entretien') or "New"
_logger.info("Ref for entretien : %s", vals['ref'])
# Fetch contract to regenerated associated entretiens names
if vals.get('main_contract_id'):
contract = self.env['hr.contract'].browse(vals['main_contract_id'])
contract.update_entretiens_names()
if vals.get('event_ids'):
existing_entretien = self.search([('event_ids', 'in', vals['event_id'])], limit=1)
if existing_entretien:
raise ValidationError("An entretien is already associated with this event.")
return result

@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
# 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 ContractCareer(models.Model):
_name = "gn_payroll.career"
_description = "Analyse du poste"
_order = 'start_date, employee_id'
start_date = fields.Date('From', required=True, default=lambda self: fields.Date.today())
contract_id = fields.Many2one('gn_payroll.hr.contract', 'career_id', required=True, string="Contrat ou Avenant Associé")
main_contract_id = fields.Many2one('gn_payroll.hr.contract', compute='_compute_main_contract', string="Contract principal associé")
entretien_id = fields.Many2one('gn_payroll.hr.contract.entretien', string="Entretien professionnel associé")
classification_group = fields.Selection(selection='_get_groups_selection', string="Groupe associé au coefficient")
indice = fields.Integer(string="indice du salaire négocié (nombre de points)")
@api.constrains('entretien_id')
def _check_entretien_uniqueness(self):
for record in self:
if record.entretien_id:
# Check if any other career records have the same entretien_id
existing = self.search([
('id', '!=', record.id),
('entretien_id', '=', record.entretien_id.id),
])
if existing:
raise ValidationError("An entretien can only be associated with one career.")
@api.constrains('entretien_id')
def _check_contract_uniqueness(self):
for record in self:
if record.contract_id:
# Check if any other career records have the same entretien_id
existing = self.search([
('id', '!=', record.id),
('contract_id', '=', record.contract_id.id),
])
if existing:
raise ValidationError("An contract can only be associated with one career.")
@api.model
def create(self, vals):
career = super(ContractCareer, self).create(vals)
contract_id = vals.get('contract_id')
if contract_id:
contract = self.env['gn_payroll.hr.contract'].browse(contract_id)
if contract.parent_contract_id:
career.write({'main_contract_id': contract.parent_contract_id.id})
return career

@ -1,4 +1,4 @@
from odoo import models, fields
from odoo import models, fields, api
class GnPayrollHrEmployee(models.Model):
_inherit = 'hr.employee'
@ -9,4 +9,17 @@ class GnPayrollHrEmployee(models.Model):
('volunteer', 'Volontaire'),
('benevolent', 'Bénévole'),
], string='Employee Type', default='employee', required=True,
help="The employee type. Although the primary purpose may seem to categorize employees, this field has also an impact in the Contract History. Only Employee type is supposed to be under contract and will have a Contract History.")
help="The employee type. Although the primary purpose may seem to categorize employees, this field has also an impact in the Contract History. Only Employee type is supposed to be under contract and will have a Contract History.")
anciennete_start_date = fields.Date(compute='_compute_anciennete_start_date', groups='hr_group.hr_user', store=True)
@api.depends('contract_ids.state', 'contract_ids.date_start')
def _compute_anciennete_start_date(self):
for employee in self:
if employee.contract_id:
if employee.contract_id.parent_contract_id:
employee.anciennete_start_date = employee.contract_id.parent_contract_id.date_start
else:
employee.anciennete_start_date = employee.contract_id.date_start
else:
employee.anciennete_start_date = False

@ -0,0 +1,59 @@
from odoo import models, api
from datetime import datetime, time
class GnPayrollHrPayslip(models.Model):
_inherit = 'hr.payslip'
def _compute_sickness_days(self, contract, day_from, day_to):
"""
Worked days computation
@return: returns a list containing the total worked_days for the period
of the payslip. This returns the FULL work days expected for the resource
calendar selected for the employee (it don't substract leaves by default).
"""
work_data = contract.employee_id._get_work_days_data_batch(
day_from,
day_to,
calendar=contract.resource_calendar_id,
compute_leaves=False,
)
return {
"name": "Sickness days",
"sequence": 1,
"code": "SICKNESS",
"number_of_days": work_data[contract.employee_id.id]["days"],
"number_of_hours": work_data[contract.employee_id.id]["hours"],
"contract_id": contract.id,
}
@api.model
def get_worked_day_lines(self, contracts, date_from, date_to):
"""
@param contracts: Browse record of contracts
@return: returns a list of dict containing the input that should be
applied for the given contract between date_from and date_to
"""
res = []
for contract in contracts.filtered(
lambda contract: contract.resource_calendar_id
):
day_from = datetime.combine(date_from, time.min)
day_to = datetime.combine(date_to, time.max)
day_contract_start = datetime.combine(contract.date_start, time.min)
# Support for the hr_public_holidays module.
contract = contract.with_context(
employee_id=self.employee_id.id, exclude_public_holidays=True
)
# only use payslip day_from if it's greather than contract start date
if day_from < day_contract_start:
day_from = day_contract_start
# == compute leave days == #
leaves = self._compute_leave_days(contract, day_from, day_to)
res.extend(leaves)
# == compute worked days == #
attendances = self._compute_worked_days(contract, day_from, day_to)
res.append(attendances)
# == compute sicknss days == #
sickness = self._compute_sickness_days(contract, day_from, day_to)
res.append(sickness)
return res

@ -1,3 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_gn_payroll_cc_user,gn_payroll_cc_user,model_gn_payroll_cc,base.group_user,1,0,0,0
acces_gn_payroll_cc_admin,gn_payroll_cc_admin,model_gn_payroll_cc,hr_contract.group_hr_contract_manager,1,1,0,0
access_gn_payroll_hr_contract_entretien_user,gn_payroll_hr_contract_entretien_user,model_gn_payroll_hr_contract_entretien,base.group_user,1,0,0,0
access_gn_payroll_hr_contract_entretien_admin,gn_payroll_hr_contract_entretien_admin,model_gn_payroll_hr_contract_entretien,hr_contract.group_hr_contract_manager,1,1,1,1
access_gn_payroll_hr_contract_entretien_type_user,gn_payroll_hr_contract_entretien_type_user,model_gn_payroll_hr_contract_entretien_type,base.group_user,1,0,0,0
access_gn_payroll_hr_contract_entretien_type_admin,gn_payroll_hr_contract_entretien_type_admin,model_gn_payroll_hr_contract_entretien_type,hr_contract.group_hr_contract_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_gn_payroll_cc_user access_gn_payroll_hr_contract_entretien_user gn_payroll_cc_user gn_payroll_hr_contract_entretien_user model_gn_payroll_cc model_gn_payroll_hr_contract_entretien base.group_user 1 0 0 0
3 acces_gn_payroll_cc_admin access_gn_payroll_hr_contract_entretien_admin gn_payroll_cc_admin gn_payroll_hr_contract_entretien_admin model_gn_payroll_cc model_gn_payroll_hr_contract_entretien hr_contract.group_hr_contract_manager 1 1 0 1 0 1
4 access_gn_payroll_hr_contract_entretien_type_user gn_payroll_hr_contract_entretien_type_user model_gn_payroll_hr_contract_entretien_type base.group_user 1 0 0 0
5 access_gn_payroll_hr_contract_entretien_type_admin gn_payroll_hr_contract_entretien_type_admin model_gn_payroll_hr_contract_entretien_type hr_contract.group_hr_contract_manager 1 1 1 1

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="gn_view_calendar_event_form" model="ir.ui.view">
<field name="name">gncalendar.event.form</field>
<field name="model">calendar.event</field>
<field name="inherit_id" ref="calendar.view_calendar_event_form"/>
<field name="priority" eval="1"/>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='categ_ids']" position="after">
<field name="is_entretien"/>
<field name="entretien_ids" domain="[('event_id', '=', False)]" attrs="{'invisible': [('is_entretien', '=', False)]}"/>
</xpath>
</data>
</field>
</record>
</odoo>

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="gn_payroll_entretien_form_view" model="ir.ui.view">
<field name="name">gn_payroll.hr.contract.entretien.form</field>
<field name="model">gn_payroll.hr.contract.entretien</field>
<field name="arch" type="xml">
<form string="Entretiens">
<sheet string="Entretien">
<div class="oe_title">
<h1>
<field name="name" placeholder="Entretien" />
</h1>
</div>
<group>
<field name="ref"/>
<field name="employee_id"/>
<field name="interviewer_id"/>
<field name="other_interviewer_ids"/>
<field name="other_participant_ids"/>
<field name="event_ids"/>
<field name="event_id"/>
<field name="start"/>
<field name="first_date"/>
<field name="next_date"/>
<field name="main_contract_id"/>
<field name="running_contract_id"/>
<field name="generated_contract_id"/>
<field name="type_id"/>
<field name="report"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="gn_payroll.hr_contract_entretien" model="ir.actions.act_window">
<field name="name">Entretiens</field>
<field name="res_model">gn_payroll.hr.contract.entretien</field>
<field name="view_mode">tree,form</field>
</record>
<record id="gn_payroll.hr_contract_entretien_type" model="ir.actions.act_window">
<field name="name">Types d'Entretiens</field>
<field name="res_model">gn_payroll.hr.contract.entretien.type</field>
<field name="view_mode">tree,form</field>
</record>
</odoo>
Loading…
Cancel
Save