Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { registry } from "@web/core/registry";
import { SelectionField, selectionField } from "@web/views/fields/selection/selection_field";

export class DayMonthSelectionField extends SelectionField {
static props = {
...SelectionField.props,
selectedMonth: { type: String, optional: true },
};


get options() {
const selectedMonth = this.props.record.data[this.props.selectedMonth];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If selectedMonth is undefined or invalid, this could fail silently.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a condition to prompt the user and not give him any day options to select from

if (isNaN(selectedMonth) || selectedMonth < 1 || selectedMonth > 12){
throw new Error("Please select a valid month.");
}
// The year 2024 is used as a default year as it's a leap year so it will alow us to select the 29th of February (to be more generic)
const date = new Date(2024, selectedMonth, 0);
const days = date.getDate();
let newChoicesList = Array.from({length: days}, (_, i) => [(i + 1).toString(), (i + 1).toString()])
return newChoicesList;
}

}

export const dayMonthSelectionField = {
...selectionField,
component: DayMonthSelectionField,
extractProps: (fieldInfo, dynamicInfo) => {
const props = selectionField.extractProps(fieldInfo, dynamicInfo);
props.selectedMonth = fieldInfo.attrs.selected_month;
return props;
},
};

registry.category("fields").add("day_month_selection", dayMonthSelectionField);
57 changes: 57 additions & 0 deletions addons/hr_holidays/tests/test_expiring_leaves.py
Original file line number Diff line number Diff line change
Expand Up @@ -809,3 +809,60 @@ def test_carried_over_days_expiration_date_2(self):

# Assert the number of expiring leaves
self.assertEqual(allocation_data[logged_in_emp][0][1]['closest_allocation_remaining'], 1)

def test_leap_year_in_accrual_plan(self):
"""
This test case aims to assert that the system correctly handles recurring plan dates that are set on 29th of February

- We create an accrual plan with a carryover date of 29/2 as well as first_month accrual cutoff set to 29/2
- We create a new testing accrual plan on 1/8/2023 to be followed by a leap year
- We test the correct accrual on 29/2/2024 to check that the 10 days have been accrued correctly
- We test the correct accrual on 1/7/2024 to check that it doesn't affect the other cycle in a biyearly setup
- Finally, we test the correct accrual on 28/2/2025 to check the number of days was accrued correctly even though
it's a normal year with 1 day less than a leap one
"""
accrual_plan = self.env['hr.leave.accrual.plan'].with_context(tracking_disable=True).create({
'name': 'Accrual Plan For Test',
'can_be_carryover': True,
'carryover_date': 'other',
'carryover_day': 29,
'carryover_month': '2',
'level_ids': [(0, 0, {
'added_value_type': 'day',
'milestone_date': 'creation',
'start_type': 'day',
'added_value': 10,
'frequency': 'biyearly',
'first_month': '2',
'first_month_day': 29,
'second_month': '7',
'second_month_day': 1,
'action_with_unused_accruals': 'all',
'accrual_validity': True,
'accrual_validity_type': 'month',
'accrual_validity_count': 5,
})],
})
with freeze_time('2023-07-01'):
allocation = self.env['hr.leave.allocation'].sudo().with_context(tracking_disable=True).create({
'name': 'Accrual allocation for employee',
'accrual_plan_id': accrual_plan.id,
'employee_id': self.employee_emp.id,
'holiday_status_id': self.leave_type.id,
'number_of_days': 0,
'allocation_type': 'accrual',
})
allocation.action_approve()

with freeze_time('2023-07-01'):
allocation._update_accrual()
self.assertEqual(allocation.number_of_days, 0)
with freeze_time('2024-02-29'):
allocation._update_accrual()
self.assertEqual(allocation.number_of_days, 10)
with freeze_time('2025-01-01'):
allocation._update_accrual()
self.assertEqual(allocation.number_of_days, 20)
with freeze_time('2025-02-28'):
allocation._update_accrual()
self.assertEqual(allocation.number_of_days, 30)
8 changes: 4 additions & 4 deletions addons/hr_holidays/views/hr_leave_accrual_views.xml

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a user has selected "31" for the day and then changes the month to one with only 30 days, what happens? 🤔

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The widget automatically adjusts I think it's because it's watching on the selectedMonth prop to recall the function on change

Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,17 @@
</span>
<span name="biyearly" invisible="frequency != 'biyearly'">
on the
<field nolabel="1" name="first_month_day" class="o_field_accrual" placeholder="select a day" required="frequency == 'biyearly'"/>
<field nolabel="1" name="first_month_day" class="o_field_accrual" widget="day_month_selection" selected_month="first_month" placeholder="select a day" required="frequency == 'biyearly'"/>
of
<field name="first_month" class="o_field_accrual" placeholder="select a month" required="frequency == 'biyearly'"/>
and the
<field nolabel="1" name="second_month_day" class="o_field_accrual" placeholder="select a day" required="frequency == 'biyearly'"/>
<field nolabel="1" name="second_month_day" class="o_field_accrual" widget="day_month_selection" selected_month="second_month" placeholder="select a day" required="frequency == 'biyearly'"/>
of
<field nolabel="1" name="second_month" class="o_field_accrual" placeholder="select a month" required="frequency == 'biyearly'"/>
</span>
<span name="yearly" invisible="frequency != 'yearly'">
on the
<field nolabel="1" name="yearly_day" class="o_field_accrual" required="frequency == 'yearly'" placeholder="select a day"/>
<field nolabel="1" name="yearly_day" class="o_field_accrual" widget="day_month_selection" selected_month="yearly_month" required="frequency == 'yearly'" placeholder="select a day"/>
of
<field nolabel="1" name="yearly_month" class="o_field_accrual" required="frequency == 'yearly'" placeholder="select a month"/>
</span>
Expand Down Expand Up @@ -192,7 +192,7 @@
options="{'links': {'other': 'carryover_custom_date'}, 'observe': 'carryover'}"/>
<span id="carryover_custom_date">
: the
<field name="carryover_day" placeholder="select a day"
<field name="carryover_day" placeholder="select a day" widget="day_month_selection" selected_month="carryover_month"
required="carryover_date == 'other'"/>
of
<field name="carryover_month" placeholder="select a month"
Expand Down