Initial commit: Hr packages

This commit is contained in:
Ernad Husremovic 2025-08-29 15:20:50 +02:00
commit 62531cd146
2820 changed files with 1432848 additions and 0 deletions

View file

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

View file

@ -0,0 +1,137 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, tools, _
from collections import defaultdict
class ContractHistory(models.Model):
_name = 'hr.contract.history'
_description = 'Contract history'
_auto = False
_order = 'is_under_contract'
# Even though it would have been obvious to use the reference contract's id as the id of the
# hr.contract.history model, it turned out it was a bad idea as this id could change (for instance if a
# new contract is created with a later start date). The hr.contract.history is instead closely linked
# to the employee. That's why we will use this id (employee_id) as the id of the hr.contract.history.
contract_id = fields.Many2one('hr.contract', readonly=True)
display_name = fields.Char(compute='_compute_display_name')
name = fields.Char('Contract Name', readonly=True)
date_hired = fields.Date('Hire Date', readonly=True)
date_start = fields.Date('Start Date', readonly=True)
date_end = fields.Date('End Date', readonly=True)
employee_id = fields.Many2one('hr.employee', string='Employee', readonly=True)
active_employee = fields.Boolean('Active Employee', readonly=True)
is_under_contract = fields.Boolean('Is Currently Under Contract', readonly=True)
department_id = fields.Many2one('hr.department', string='Department', readonly=True)
structure_type_id = fields.Many2one('hr.payroll.structure.type', string='Salary Structure Type', readonly=True)
hr_responsible_id = fields.Many2one('res.users', string='HR Responsible', readonly=True)
job_id = fields.Many2one('hr.job', string='Job Position', readonly=True)
state = fields.Selection([
('draft', 'New'),
('open', 'Running'),
('close', 'Expired'),
('cancel', 'Cancelled')
], string='Status', readonly=True)
resource_calendar_id = fields.Many2one('resource.calendar', string="Working Schedule", readonly=True)
wage = fields.Monetary('Wage', help="Employee's monthly gross wage.", readonly=True, group_operator="avg")
company_id = fields.Many2one('res.company', string='Company', readonly=True)
company_country_id = fields.Many2one('res.country', string="Company country", related='company_id.country_id', readonly=True)
country_code = fields.Char(related='company_country_id.code', depends=['company_country_id'], readonly=True)
currency_id = fields.Many2one(string='Currency', related='company_id.currency_id', readonly=True)
contract_type_id = fields.Many2one('hr.contract.type', 'Contract Type', readonly=True)
contract_ids = fields.One2many('hr.contract', string='Contracts', compute='_compute_contract_ids', readonly=True, compute_sudo=True)
contract_count = fields.Integer(compute='_compute_contract_count', string="# Contracts")
under_contract_state = fields.Selection([
('done', 'Under Contract'),
('blocked', 'Not Under Contract')
], string='Contractual Status', compute='_compute_under_contract_state')
activity_state = fields.Selection(related='contract_id.activity_state')
@api.depends('contract_ids')
def _compute_contract_count(self):
for history in self:
history.contract_count = len(history.contract_ids)
@api.depends('is_under_contract')
def _compute_under_contract_state(self):
for history in self:
history.under_contract_state = 'done' if history.is_under_contract else 'blocked'
@api.depends('employee_id.name')
def _compute_display_name(self):
for history in self:
history.display_name = _("%s's Contracts History", history.employee_id.name)
@api.model
def _get_fields(self):
return ','.join('contract.%s' % name for name, field in self._fields.items()
if field.store
and field.type not in ['many2many', 'one2many', 'related']
and field.name not in ['id', 'contract_id', 'employee_id', 'date_hired', 'is_under_contract', 'active_employee'])
def init(self):
tools.drop_view_if_exists(self.env.cr, self._table)
# Reference contract is the one with the latest start_date.
self.env.cr.execute("""CREATE or REPLACE VIEW %s AS (
WITH contract_information AS (
SELECT DISTINCT employee_id,
company_id,
FIRST_VALUE(id) OVER w_partition AS id,
MAX(CASE
WHEN state='open' THEN 1
WHEN state='draft' AND kanban_state='done' THEN 1
ELSE 0 END) OVER w_partition AS is_under_contract
FROM hr_contract AS contract
WHERE contract.active = true
WINDOW w_partition AS (
PARTITION BY contract.employee_id, contract.company_id
ORDER BY
CASE
WHEN contract.state = 'open' THEN 0
WHEN contract.state = 'draft' THEN 1
WHEN contract.state = 'close' THEN 2
WHEN contract.state = 'cancel' THEN 3
ELSE 4 END,
contract.date_start DESC
RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
)
)
SELECT DISTINCT employee.id AS id,
employee.id AS employee_id,
employee.active AS active_employee,
contract.id AS contract_id,
contract_information.is_under_contract::bool AS is_under_contract,
employee.first_contract_date AS date_hired,
%s
FROM hr_contract AS contract
INNER JOIN contract_information ON contract.id = contract_information.id
RIGHT JOIN hr_employee AS employee
ON contract_information.employee_id = employee.id
AND contract.company_id = employee.company_id
WHERE employee.employee_type IN ('employee', 'student', 'trainee')
)""" % (self._table, self._get_fields()))
@api.depends('employee_id.contract_ids')
def _compute_contract_ids(self):
sorted_contracts = self.mapped('employee_id.contract_ids').sorted('date_start', reverse=True)
mapped_employee_contracts = defaultdict(lambda: self.env['hr.contract'])
for contract in sorted_contracts:
mapped_employee_contracts[contract.employee_id] |= contract
for history in self:
history.contract_ids = mapped_employee_contracts[history.employee_id]
def hr_contract_view_form_new_action(self):
self.ensure_one()
action = self.env['ir.actions.actions']._for_xml_id('hr_contract.action_hr_contract')
action.update({
'context': {'default_employee_id': self.employee_id.id},
'view_mode': 'form',
'view_id': self.env.ref('hr_contract.hr_contract_view_form').id,
'views': [(self.env.ref('hr_contract.hr_contract_view_form').id, 'form')],
})
return action

View file

@ -0,0 +1,215 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="hr_contract_history_view_search" model="ir.ui.view">
<field name="name">hr.contract.history.search</field>
<field name="model">hr.contract.history</field>
<field name="arch" type="xml">
<search string="Search Reference Contracts">
<field name="name"/>
<field name="employee_id"/>
<field name="job_id"/>
<field name="department_id" operator="child_of"/>
<field name="resource_calendar_id"/>
<field name="state"/>
<field name="is_under_contract"/>
<filter string="Running Contracts" name="open_contracts" domain="[('state', '=', 'open')]"/>
<filter string="Contracts to Review" name="contract_to_review" domain="['|', ('state', 'in', ['draft', 'close', 'cancel']), ('is_under_contract', '!=', True)]"/>
<filter string="No Contracts" name="no_contracts" domain="[('contract_id', '=', False)]"/>
<filter string="Currently Under Contract" name="currently_under_contract" domain="[('is_under_contract', '=', True)]"/>
<filter string="Active Employees" name="active_employees" domain="[('active_employee', '=', True)]"/>
<group expand="0" string="Group By">
<filter string="Job Position" name="job" domain="[]" context="{'group_by': 'job_id'}"/>
<filter string="Status" name='group_by_state' domain="[]" context="{'group_by': 'state'}"/>
<filter string="Reference Working Time" name="group_by_resource_calendar_id" domain="[]" context="{'group_by': 'resource_calendar_id'}"/>
<filter string="Salary Structure Type" name="group_by_structure_type_id" domain="[]" context="{'group_by': 'structure_type_id'}"/>
</group>
</search>
</field>
</record>
<record id="hr_contract_history_view_form_action" model="ir.actions.act_window">
<field name="name">Contracts</field>
<field name="res_model">hr.contract.history</field>
<field name="view_mode">form</field>
<field name="context">{'search_default_active_employees': 1}</field>
</record>
<record id="hr_contract_history_view_form" model="ir.ui.view">
<field name="name">hr.contract.history.form</field>
<field name="model">hr.contract.history</field>
<field name="arch" type="xml">
<form string="Contract History"
create="false"
edit="false"
delete="false"
duplicate="false"
import="false">
<header>
<button name="hr_contract_view_form_new_action" string="Create" type="object" groups="hr_contract.group_hr_contract_manager" class="btn-primary"/>
</header>
<sheet>
<div class="oe_button_box" name="button_box"/>
<h1>
<div class="d-flex justify-content-start">
<div>
<field name="display_name"/>
</div>
<div class="ps-3">
<field name="under_contract_state" widget="state_selection" readonly="1"/>
</div>
</div>
</h1>
<h2>
<field name="employee_id"/>
</h2>
<group>
<group>
<field name="contract_id" invisible="1"/>
<field name="company_country_id" invisible="1"/>
<field name="country_code" invisible="1"/>
<field name="structure_type_id"/>
<field name="resource_calendar_id"/>
<field name="currency_id" invisible="1"/>
<field name="wage" invisible="1"/>
</group>
<group>
<field name="department_id"/>
<field name="job_id"/>
</group>
</group>
<notebook>
<page string="Contract History" name="contract_history">
<field name="contract_ids" widget="one2many" readonly="0">
<tree string="Current Contracts"
decoration-primary="state == 'open'"
decoration-muted="state == 'close'"
decoration-bf="id == parent.contract_id"
default_order = "date_start desc, state desc"
editable="bottom"
no_open="1"
create="0" delete="0">
<button name="action_open_contract_form" type="object" icon="fa-external-link" title="Open Contract"/>
<field name="id" invisible="1"/>
<field name="name" string="Contract Name"/>
<field name="date_start"/>
<field name="date_end"/>
<field name="resource_calendar_id"/>
<field name="currency_id" invisible="1"/>
<field name="wage" string="Monthly Wage"/>
<field name="state" widget="badge" decoration-info="state == 'draft'" decoration-warning="state == 'close'" decoration-success="state == 'open'"/>
</tree>
</field>
</page>
<page string="Employee Information" name="contract_others">
<group>
<field name="date_hired"/>
<field name="hr_responsible_id"/>
<field name="company_id"/>
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="hr_contract_history_view_list_action" model="ir.actions.act_window">
<field name="name">Employees</field>
<field name="res_model">hr.contract.history</field>
<field name="view_mode">tree,kanban,form</field>
<field name="search_view_id" ref="hr_contract_history_view_search"/>
<field name="context">
{
'search_default_active_employees': 1,
'search_default_group_by_state': 1
}
</field>
<field name="help" type="html">
<p class="o_view_nocontent_empty_folder">
No data to display
</p>
</field>
</record>
<record id="hr_contract_history_to_review_view_list_action" model="ir.actions.act_window">
<field name="name">Contracts to Review</field>
<field name="res_model">hr.contract.history</field>
<field name="view_mode">tree,form</field>
<field name="search_view_id" ref="hr_contract_history_view_search"/>
<field name="context">
{
'search_default_to_review': 1,
'search_default_active_employees': 1
}
</field>
</record>
<record id="hr_contract_history_view_list" model="ir.ui.view">
<field name="name">hr.contract.history.list</field>
<field name="model">hr.contract.history</field>
<field name="arch" type="xml">
<tree string="Contracts"
js_class="hr_contract_history_list"
default_order = 'is_under_contract, date_start desc'
edit="false"
delete="false"
duplicate="false"
import="false"
create="false">
<field name="employee_id" widget="many2one_avatar_employee"/>
<field name="date_hired"/>
<field name="is_under_contract" invisible="1"/>
<field name="name"/>
<field name="date_start"/>
<field string="Reference Working Time" name="resource_calendar_id" optional="hide"/>
<field name="under_contract_state" widget="state_selection" optional="hide"/>
<field name="structure_type_id" optional="hide"/>
<field name="currency_id" invisible="1"/>
<field name="wage" optional="hide"/>
<field name="state"
widget="badge"
decoration-info="state == 'draft'"
decoration-warning="state == 'close'"
decoration-success="state == 'open'"/>
<field name="contract_count"/>
</tree>
</field>
</record>
<record id="hr_contract_history_view_kanban" model="ir.ui.view">
<field name="name">hr.contract.history.view.kanban</field>
<field name="model">hr.contract.history</field>
<field name="arch" type="xml">
<kanban class="o_kanban_small_column" default_order="date_end" sample="1" create="0">
<field name="employee_id"/>
<field name="activity_state"/>
<field name="state"/>
<progressbar field="activity_state" colors='{"planned": "success", "today": "warning", "overdue": "danger"}'/>
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_card oe_kanban_global_click">
<div class="oe_kanban_content">
<div class="o_hr_contract_state">
<strong class="o_kanban_record_title">
<field name="display_name"/>
</strong>
</div>
<div class="text-muted o_kanban_record_subtitle o_hr_contract_job_id">
<field name="job_id"/>
</div>
<div class="oe_kanban_bottom_right">
<span class="float-end">
<field name="employee_id" widget="many2one_avatar_employee"/>
</span>
</div>
</div>
<div class="clearfix"></div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<menuitem
id="hr_menu_contract_history"
action="hr_contract_history_view_list_action"
parent="hr.menu_hr_employee_payroll"
name="Contracts"
sequence="4"
groups="hr_contract.group_hr_contract_manager"/>
</odoo>