diff --git a/addons/hr/models/hr_employee.py b/addons/hr/models/hr_employee.py
index f9d74830bd0ae..14a81f967dedf 100644
--- a/addons/hr/models/hr_employee.py
+++ b/addons/hr/models/hr_employee.py
@@ -579,8 +579,7 @@ def _compute_legal_name(self):
@api.depends('current_version_id')
@api.depends_context('version_id')
def _compute_version_id(self):
- context_version_id = self.env.context.get('version_id', False)
- context_version = self.env['hr.version'].browse(context_version_id).exists() if context_version_id else self.env['hr.version']
+ context_version = self.env['hr.version'].browse(self.env.context.get('version_id', False))
for employee in self:
if context_version.employee_id == self:
diff --git a/addons/hr/static/src/components/button_new_contract/button_new_contract.xml b/addons/hr/static/src/components/button_new_contract/button_new_contract.xml
index 8abdd473fe7e2..0c640d5b88d4c 100644
--- a/addons/hr/static/src/components/button_new_contract/button_new_contract.xml
+++ b/addons/hr/static/src/components/button_new_contract/button_new_contract.xml
@@ -3,7 +3,7 @@
+ t-ref="datetime-picker-target-new-contract" t-if="props.record.resId">New Contract
diff --git a/addons/hr/tests/test_hr_version.py b/addons/hr/tests/test_hr_version.py
index d23cfcbf63131..92fb938aa5863 100644
--- a/addons/hr/tests/test_hr_version.py
+++ b/addons/hr/tests/test_hr_version.py
@@ -523,27 +523,6 @@ def test_multi_edit_other_and_contract_date_sync(self):
self.assertEqual(version.job_id.id, jobB.id)
self.assertEqual(version.contract_date_end, date(2020, 9, 30))
- def test_delete_version(self):
- employee = self.env['hr.employee'].create({
- 'name': 'John Doe',
- 'date_version': '2020-01-01',
- })
- v1 = employee.version_id
- v2 = employee.create_version({
- 'date_version': '2021-01-01',
- })
- v3 = employee.create_version({
- 'date_version': '2022-01-01',
- })
- self.assertEqual(employee.current_version_id, v3)
-
- v3.unlink()
- self.assertEqual(employee.current_version_id, v2)
- v1.unlink()
- self.assertEqual(employee.current_version_id, v2)
- with self.assertRaises(ValidationError):
- v2.unlink()
-
def test_multi_edit_multi_employees_no_contract(self):
"""
Test the multi-edit when there is one version per employee, without contract
diff --git a/addons/hr/views/hr_employee_views.xml b/addons/hr/views/hr_employee_views.xml
index 8b7d13fa38ee9..fa457799d554f 100644
--- a/addons/hr/views/hr_employee_views.xml
+++ b/addons/hr/views/hr_employee_views.xml
@@ -360,7 +360,7 @@
to
-
+
diff --git a/addons/hr_holidays/models/hr_leave_type.py b/addons/hr_holidays/models/hr_leave_type.py
index 98fddbbd93ddf..a5441a565e9b4 100644
--- a/addons/hr_holidays/models/hr_leave_type.py
+++ b/addons/hr_holidays/models/hr_leave_type.py
@@ -275,14 +275,15 @@ def _search_max_leaves(self, operator, value):
return [('id', 'in', valid_leaves)]
def _search_virtual_remaining_leaves(self, operator, value):
- def is_valid(leave_type):
- return not leave_type.requires_allocation or op(leave_type.virtual_remaining_leaves, value)
op = PY_OPERATORS.get(operator)
if not op:
return NotImplemented
if operator != 'in':
value = float(value)
leave_types = self.env['hr.leave.type'].search([])
+
+ def is_valid(leave_type):
+ return not leave_type.requires_allocation or op(leave_type.virtual_remaining_leaves, value)
return [('id', 'in', leave_types.filtered(is_valid).ids)]
@api.depends_context('uid', 'employee_id', 'default_employee_id', 'leave_date_from', 'default_date_from')
diff --git a/addons/hr_holidays/static/src/components/accrual_level/accrual_levels.scss b/addons/hr_holidays/static/src/components/accrual_level/accrual_levels.scss
index 9cec634e1b2dd..5ea81942c9be6 100644
--- a/addons/hr_holidays/static/src/components/accrual_level/accrual_levels.scss
+++ b/addons/hr_holidays/static/src/components/accrual_level/accrual_levels.scss
@@ -10,7 +10,7 @@
}
.o_accrual {
- .o_field_accrual, .o_field_selection, .o_field_day_selection, .o_field_filterable_selection {
+ .o_field_accrual, .o_field_selection, .o_field_filterable_selection {
width: fit-content !important;
&:not(.o_readonly_modifier) > *:first-child {
@@ -20,7 +20,7 @@
field-sizing: content;
}
- &:not(.o_field_selection, .o_field_day_selection, .o_field_filterable_selection) > *:first-child {
+ &:not(.o_field_selection, .o_field_filterable_selection) > *:first-child {
max-width: 8ch;
}
}
diff --git a/addons/hr_holidays/static/src/components/day_selection/day_selection.js b/addons/hr_holidays/static/src/components/day_selection/day_selection.js
index 05288069a543b..5dba8f53c0f94 100644
--- a/addons/hr_holidays/static/src/components/day_selection/day_selection.js
+++ b/addons/hr_holidays/static/src/components/day_selection/day_selection.js
@@ -1,41 +1,31 @@
+/** @odoo-module **/
import { registry } from "@web/core/registry";
import { SelectionField, selectionField } from "@web/views/fields/selection/selection_field";
-export class DaySelectionField extends SelectionField {
+export class DaySelection extends SelectionField {
+
static props = {
...SelectionField.props,
- monthField: String,
+ monthField: { type: String},
};
- /**
- * @override
- * return the available days in the carryover_month
- * e.g. February -> [1, 29], april -> [1, 30]
- */
+
get options() {
- let options = super.options;
- const carryover_month = this.props.record.data[this.props.monthField];
- // lastDay is the last day of the current_month for the leap year 2020
- const lastDay = new Date(2020, carryover_month, 0).getDate();
- options = options.filter((option) => option[0] <= lastDay);
- return options;
+ const month = this.props.record.data[this.props.monthField];
+
+ const allDays = this.props.record.fields[this.props.name].selection;
+
+ const maxDays = new Date(2024, month, 0).getDate();
+ return allDays.filter(opt => parseInt(opt[0]) <= maxDays);
}
}
-export const daySelectionField = {
+export const daySelection={
...selectionField,
- component: DaySelectionField,
- extractProps({ attrs }) {
- return {
- ...selectionField.extractProps(...arguments),
- monthField: attrs.month_field,
- };
- },
- fieldDependencies: ({ attrs }) => [
- {
- name: attrs.month_field,
- type: "selection",
- },
- ],
-};
+ component: DaySelection,
+
+ extractProps: ({ options }) => ({
+ monthField: options.month_field,
+ }),
+}
-registry.category("fields").add("day_selection", daySelectionField);
+registry.category("fields").add("day_selection", daySelection);
diff --git a/addons/hr_holidays/static/src/components/day_selection/day_selection.xml b/addons/hr_holidays/static/src/components/day_selection/day_selection.xml
new file mode 100644
index 0000000000000..6362b22288547
--- /dev/null
+++ b/addons/hr_holidays/static/src/components/day_selection/day_selection.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/addons/hr_holidays/tests/__init__.py b/addons/hr_holidays/tests/__init__.py
index 14964fc0304be..c5dfc11fa7898 100644
--- a/addons/hr_holidays/tests/__init__.py
+++ b/addons/hr_holidays/tests/__init__.py
@@ -36,3 +36,4 @@
from . import test_time_off_allocation_tour
from . import test_flexible_resource_calendar
from . import test_calendar_leaves_count
+from . import test_time_off_type
diff --git a/addons/hr_holidays/tests/test_time_off_type.py b/addons/hr_holidays/tests/test_time_off_type.py
new file mode 100644
index 0000000000000..bb6f100c82d65
--- /dev/null
+++ b/addons/hr_holidays/tests/test_time_off_type.py
@@ -0,0 +1,57 @@
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from odoo.tests import tagged
+
+from odoo.addons.hr_holidays.tests.common import TestHrHolidaysCommon
+
+
+@tagged('moali_test', '-at_install')
+class TestTimeOffType(TestHrHolidaysCommon):
+
+ def test_time_off_type(self):
+ """ A test file to check for the time-off type field breaking on time off creation from smart button """
+ trackable_enough_leave_type = self.env['hr.leave.type'].create({
+ 'name': 'Trackable Time Off Type 1',
+ 'requires_allocation': True,
+ 'request_unit': 'day',
+ 'unit_of_measure': 'day',
+ })
+
+ trackable_not_enough_leave_type = self.env['hr.leave.type'].create({
+ 'name': 'Trackable Time Off Type 2',
+ 'requires_allocation': True,
+ 'request_unit': 'day',
+ 'unit_of_measure': 'day',
+ })
+
+ untrackable_leave_type = self.env['hr.leave.type'].create({
+ 'name': 'Untrackable Time Off Type',
+ 'requires_allocation': False,
+ 'request_unit': 'day',
+ 'unit_of_measure': 'day',
+ })
+
+ employee = self.env['hr.employee'].create({
+ 'name': 'Test user',
+ })
+
+ enough_allocation = self.env['hr.leave.allocation'].create({
+ 'employee_id': employee.id,
+ 'number_of_days': 5,
+ 'holiday_status_id': trackable_enough_leave_type.id,
+ 'state': 'confirm',
+ }).action_approve()
+
+ not_enough_allocation = self.env['hr.leave.allocation'].create({
+ 'employee_id': employee.id,
+ 'number_of_days': 2,
+ 'holiday_status_id': trackable_not_enough_leave_type.id,
+ 'state': 'confirm',
+ }).action_approve()
+
+ available_types = self.env['hr.leave.type'].with_context(
+ employee_id=employee.id).search([('virtual_remaining_leaves', '>', 3)])
+
+ self.assertNotIn(trackable_not_enough_leave_type.id, available_types.ids)
+ self.assertIn(trackable_enough_leave_type.id, available_types.ids)
+ self.assertIn(untrackable_leave_type.id, available_types.ids)
+
diff --git a/addons/hr_holidays/views/hr_leave_accrual_views.xml b/addons/hr_holidays/views/hr_leave_accrual_views.xml
index 12a8650c1e058..083e52d8be479 100644
--- a/addons/hr_holidays/views/hr_leave_accrual_views.xml
+++ b/addons/hr_holidays/views/hr_leave_accrual_views.xml
@@ -36,17 +36,17 @@
on the
-
+
of
and the
-
+
of
on the
-
+
of
@@ -192,8 +192,9 @@
options="{'links': {'other': 'carryover_custom_date'}, 'observe': 'carryover'}"/>
: the
-
+
of
diff --git a/addons/hr_skills/report/__init__.py b/addons/hr_skills/report/__init__.py
index 8e82b9a1d6f11..bad5c5e9967fe 100644
--- a/addons/hr_skills/report/__init__.py
+++ b/addons/hr_skills/report/__init__.py
@@ -1,5 +1,6 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from . import hr_employee_certification_report
from . import hr_employee_cv_report
from . import hr_employee_skill_history_report
from . import hr_employee_skill_report
diff --git a/addons/hr_skills/report/hr_employee_certification_report.py b/addons/hr_skills/report/hr_employee_certification_report.py
new file mode 100644
index 0000000000000..b70b034e5db95
--- /dev/null
+++ b/addons/hr_skills/report/hr_employee_certification_report.py
@@ -0,0 +1,47 @@
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import fields, models, tools
+
+
+class HrEmployeeCertificationReport(models.BaseModel):
+ _name = 'hr.employee.certification.report'
+ _auto = False
+ _inherit = ["hr.manager.department.report"]
+ _description = 'Employee Certification Report'
+ _order = 'employee_id, level_progress desc'
+
+ company_id = fields.Many2one('res.company', readonly=True)
+ department_id = fields.Many2one('hr.department', readonly=True)
+
+ skill_id = fields.Many2one('hr.skill', readonly=True)
+ skill_type_id = fields.Many2one('hr.skill.type', readonly=True)
+ skill_level = fields.Char(readonly=True)
+ level_progress = fields.Float(readonly=True, aggregator='avg')
+ active = fields.Boolean(readonly=False)
+
+ def init(self):
+ tools.drop_view_if_exists(self.env.cr, self._table)
+
+ self.env.cr.execute("""
+ CREATE OR REPLACE VIEW %(table)s AS (
+ SELECT
+ row_number() OVER () AS id,
+ e.id AS employee_id,
+ e.company_id AS company_id,
+ v.department_id AS department_id,
+ s.skill_id AS skill_id,
+ s.skill_type_id AS skill_type_id,
+ sl.level_progress / 100.0 AS level_progress,
+ sl.name AS skill_level,
+ (s.valid_to IS NULL OR s.valid_to >= '%(date)s') AND s.valid_from <= '%(date)s' AS active
+ FROM hr_employee e
+ LEFT JOIN hr_version v ON e.current_version_id = v.id
+ LEFT OUTER JOIN hr_employee_skill s ON e.id = s.employee_id
+ LEFT OUTER JOIN hr_skill_level sl ON sl.id = s.skill_level_id
+ LEFT OUTER JOIN hr_skill_type st ON st.id = sl.skill_type_id
+ WHERE e.active AND st.active IS True AND st.is_certification IS TRUE
+ )
+ """ % {
+ 'table': self._table,
+ 'date': fields.Date.context_today(self)
+ })
diff --git a/addons/hr_skills/report/hr_employee_certification_report_views.xml b/addons/hr_skills/report/hr_employee_certification_report_views.xml
new file mode 100644
index 0000000000000..079ef1f790fd9
--- /dev/null
+++ b/addons/hr_skills/report/hr_employee_certification_report_views.xml
@@ -0,0 +1,71 @@
+
+
+
+ hr.employee.certification.report
+
+
+
+
+
+
+
+
+
+
+
+ hr.employee.certification.report
+
+
+
+
+
+
+
+
+
+
+
+
+ hr.employee.certification.report
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Certification
+ hr.employee.certification.report
+
+ list,pivot
+ {
+ 'search_default_employee': 1,
+ }
+
+
+
+ This report will give you an overview of the certification per Employee.
+ Create them in configuration and add them on the Employee.
+
+
+
+
+
+
diff --git a/addons/hr_skills/report/hr_employee_skill_report_views.xml b/addons/hr_skills/report/hr_employee_skill_report_views.xml
index 86b997da5ffbb..bddca497eb6ef 100644
--- a/addons/hr_skills/report/hr_employee_skill_report_views.xml
+++ b/addons/hr_skills/report/hr_employee_skill_report_views.xml
@@ -89,6 +89,6 @@
id="hr_employee_skill_inventory_report_menu"
name="Skills Inventory"
action="hr_employee_skill_report_action"
- parent="hr.hr_menu_hr_reports"
+ parent="hr_skills.hr_employee_skill_report_menu"
sequence="15"/>
diff --git a/addons/hr_skills/views/hr_views.xml b/addons/hr_skills/views/hr_views.xml
index 3c16c4c9fecb3..37903bb69394b 100644
--- a/addons/hr_skills/views/hr_views.xml
+++ b/addons/hr_skills/views/hr_views.xml
@@ -581,7 +581,7 @@
hr.employee.skill
certifications
[('is_certification', '=', True), ('company_id', 'in', allowed_company_ids)]
- {'show_employee': True}
+ {'show_employee': True, 'search_default_group_by_type': 1}
list,form
+
+