diff --git a/addons_extensions/hr_timeoff_extended/models/hr_timeoff.py b/addons_extensions/hr_timeoff_extended/models/hr_timeoff.py index b8b484d22..680daf9d6 100644 --- a/addons_extensions/hr_timeoff_extended/models/hr_timeoff.py +++ b/addons_extensions/hr_timeoff_extended/models/hr_timeoff.py @@ -17,7 +17,7 @@ class hrLeaveAccrualLevel(models.Model): ('monthly', 'Monthly'), ('yearly', 'Yearly'), ], default='daily', required=True, string="Frequency") - emp_type = fields.Many2one('hr.contract.type', "Employee Type", tracking=True) + emp_type = fields.Many2many('hr.contract.type', string="Employee Type", tracking=True) max_start_count = fields.Integer( @@ -98,16 +98,17 @@ class hrTimeoffAllocation(models.Model): for level in level_ids: # Calculate the current frequency - run_allocation = self._handel_weekly_frequency(level) - if run_allocation: - if level.emp_type: - level_filtered_employees = employees.filtered(lambda emp: emp.emp_type == level.emp_type) - else: - level_filtered_employees = employees - qualified_employees = level_filtered_employees.filtered(lambda emp: self._emp_filter_by_level(emp, level)) - # After filtering, we create the leave allocation for each employee - for emp in qualified_employees: + if level.emp_type: + level_filtered_employees = employees.filtered(lambda emp: emp.emp_type.id in level.emp_type.ids) + else: + level_filtered_employees = employees + qualified_employees = level_filtered_employees.filtered(lambda emp: self._emp_filter_by_level(emp, level)) + + # After filtering, we create the leave allocation for each employee + for emp in qualified_employees: + run_allocation = self._handel_weekly_frequency(level,emp) + if run_allocation: allocations = self.env['hr.leave.allocation'].sudo().search([('employee_id','=',emp.id),('holiday_status_id','=',accrual.time_off_type_id.id),('state','=','validate')]) leaves = self.env['hr.leave'].sudo().search([('employee_id','=',emp.id),('holiday_status_id','=',accrual.time_off_type_id.id),('state','not in',['draft','refuse','cancel'])]) emp_leave_balance = sum(allocation.number_of_days for allocation in allocations) - sum(leave.number_of_days for leave in leaves) @@ -115,7 +116,7 @@ class hrTimeoffAllocation(models.Model): continue self._create_leave_allocation(emp, level, accrual) - def _handel_weekly_frequency(self,level): + def _handel_weekly_frequency(self,level,emp): today_date = datetime.today().date() if level.level_frequency == 'weekly': weekday_map = { @@ -125,14 +126,13 @@ class hrTimeoffAllocation(models.Model): elif level.level_frequency == 'daily': return True elif level.level_frequency == 'monthly': - return True if level.first_day_display == str(today_date.day) else False + return True if level.first_day_display == str(today_date.day) or (emp.doj and ((emp.doj + timedelta(days=2)) == today_date)) else False elif level.level_frequency == 'yearly': month_map = { 'jan': 1, 'feb': 2, 'mar': 3, 'apr': 4, 'may': 5, 'jun': 6, 'jul': 7, 'aug': 8, 'sep': 9, 'oct': 10, 'nov': 11, 'dec': 12 } - return True if level.first_day_display == str(today_date.day) and today_date.month == month_map.get(level.yearly_month) else False - + return True if (level.first_day_display == str(today_date.day) and today_date.month == month_map.get(level.yearly_month)) or (emp.doj and ((emp.doj + timedelta(days=2)) == today_date)) else False else: return True @@ -140,11 +140,29 @@ class hrTimeoffAllocation(models.Model): """ Create leave allocation for a qualified employee based on the accrual level and added value. """ + today_date = datetime.today().date() + number_of_days = level.added_value + if employee.doj and ((employee.doj + timedelta(days=2)) == today_date): + if level.level_frequency == 'monthly': + if employee.doj.day <= 10: + number_of_days = level.added_value + else: + number_of_days = level.added_value/2 + elif level.level_frequency == 'yearly': + start_month = int(level.yearly_month) + joining_month = employee.doj.month + # Compute remaining months in the allocation cycle + remaining_months = (start_month - joining_month) % 12 or 12 + # Calculate proportional leaves + number_of_days = (level.added_value / 12) * remaining_months + if employee.doj.day > 10: + number_of_days = number_of_days - ((level.added_value / 12)/2) + self.env['hr.leave.allocation'].sudo().create({ 'employee_id': employee.id, 'holiday_status_id': accrual.time_off_type_id.id, 'date_from': fields.Date.today(), - 'number_of_days': level.added_value, + 'number_of_days': number_of_days, 'allocation_type': 'auto_allocation' }).action_approve() @@ -238,10 +256,40 @@ class HRLeave(models.Model): "\nThe status is 'Approved', when time off request is approved by manager." + "\nThe status is 'Cancelled', when time off request is cancelled.") + + def _check_validity(self): + for rec in self: + if rec.holiday_status_id.limit_leave_requests: + if rec.holiday_status_id.limit_request_count and rec.holiday_status_id.limit_request_type and rec.holiday_status_id.limit_emp_type and rec.holiday_status_id.limit_request_count >= 0: + if rec.employee_id.id in rec.holiday_status_id.limit_emp_type.ids: + time_frame = { + 'week': timedelta(weeks=1), + 'month': timedelta(days=30), + 'year': timedelta(days=365), + }.get(rec.holiday_status_id.limit_request_type, timedelta(weeks=1)) # Default to 1 week + + restriction_start_date = datetime.now() - time_frame + + # Count the leave requests made by the employee within the restriction period + leave_count = self.env['hr.leave'].search_count([ + ('employee_id', '=', rec.employee_id.id), + ('state', 'not in', ['cancel', 'refuse', 'draft']), # Adjust states if needed + ('holiday_status_id', '=', rec.holiday_status_id.id), + ('request_date_from', '>=', restriction_start_date), + ('id','!=',rec.id) + ]) + if leave_count >= rec.holiday_status_id.limit_request_count: + raise ValidationError(_( + "You have exceeded the maximum allowed leave requests (%s) for the selected period (%s)." + ) % (rec.holiday_status_id.limit_request_count, rec.holiday_status_id.limit_request_type)) + + return super(HRLeave, self)._check_validity() + def action_draft(self): for rec in self: if rec.employee_id.user_id.id != self.env.user.id: raise ValidationError(_("Only employee can submit his own leave")) + self._check_validity() rec.state = 'confirm' @@ -284,4 +332,17 @@ class HRLeaveType(models.Model): request_unit_type = fields.Selection([ ('day', 'Day'), ('half_day', 'Half Day')], default='day', string='Take Time Off in', required=True) - request_unit = fields.Selection(related="request_unit_type",store=True) \ No newline at end of file + request_unit = fields.Selection(related="request_unit_type",store=True) + + limit_leave_requests = fields.Boolean(string='Limit Leave Requests', default=False) + limit_request_count = fields.Integer( + "limit Count", + help="Defines the minimum number of leave requests after which the restriction will apply. For example, set 1 to start restrictions after the first request.", + default="1") + limit_request_type = fields.Selection( + [('week', 'Week'), + ('month', 'Month'), + ('year', 'Year')], + default='day', string="Limit Type", required=True, + help="Specifies the type of time period (days, months, or years) for applying the leave request") + limit_emp_type = fields.Many2many('hr.contract.type', string="Employee Type") \ No newline at end of file diff --git a/addons_extensions/hr_timeoff_extended/views/hr_timeoff.xml b/addons_extensions/hr_timeoff_extended/views/hr_timeoff.xml index 1455598c6..ab35845c9 100644 --- a/addons_extensions/hr_timeoff_extended/views/hr_timeoff.xml +++ b/addons_extensions/hr_timeoff_extended/views/hr_timeoff.xml @@ -16,7 +16,7 @@
- Allocation Starts + Allocation Starts Running after employee joining date @@ -27,96 +27,124 @@ - - - - - - - -
-
-
- - Experience between and + + + + + + + +
+
+
+ + Experience between + + + and + + + + + initially + +
+ + + + + + + + +
+
+ +
+
+
+
+ + + , +
+
+ + + on + + + + on the + + day of the month + + + on the + + and on the + + days of the months + + + on the + + + and on the + + + + + on + + + +
+
+
+
+ Cap: +
+
+ + + - initially + Unlimited
- - - - - - - -
-
- -
-
-
-
- - , -
-
- - - on - - - on the day of the month - - - on the and on the days of the months - - - on the and on the - - - on - -
-
-
-
- Cap: -
-
- - - - - Unlimited - -
-
-
-
- Carry over: -
-
- all - - - Valid for - - - up to - - - Valid for - - - no -
-
+
+
+ Carry over: +
+
+ all + + - Valid for + + + + + + up to + + + + - Valid for + + + + + no
- - - +
+
+
+ + @@ -174,15 +202,17 @@ - +
- - + +
@@ -190,18 +220,22 @@
-
@@ -211,22 +245,27 @@
Min - - + + & Max of - - + + Experience is required
-
- +
@@ -258,6 +297,15 @@
+ +
+ Eligible to apply upto + + Leaves Per + + during + +