diff --git a/addons_extensions/flutter_odoo/models/__init__.py b/addons_extensions/flutter_odoo/models/__init__.py index d25661f9c..a70ef9e7a 100644 --- a/addons_extensions/flutter_odoo/models/__init__.py +++ b/addons_extensions/flutter_odoo/models/__init__.py @@ -1 +1,2 @@ -from . import hr_employee \ No newline at end of file +from . import hr_employee +from . import hr_leave \ No newline at end of file diff --git a/addons_extensions/flutter_odoo/models/hr_leave.py b/addons_extensions/flutter_odoo/models/hr_leave.py new file mode 100644 index 000000000..518a49b1a --- /dev/null +++ b/addons_extensions/flutter_odoo/models/hr_leave.py @@ -0,0 +1,107 @@ +from odoo import api, models, _, fields +from odoo.exceptions import ValidationError +from odoo.tools.misc import format_date +from datetime import datetime + +class HRLeave(models.Model): + _inherit = "hr.leave" + + def flutter_check_overlap_constrain(self,employee_id, to_date, from_date): + if self.env.context.get('leave_skip_date_check', False): + return + date_from = datetime.fromisoformat(from_date).replace(hour=0, minute=0, second=0) + date_to = datetime.fromisoformat(to_date).replace(hour=23, minute=59, second=59) + employee_id = int(employee_id) + all_leaves = self.search([ + ('date_from', '<', date_to), + ('date_to', '>', date_from), + ('employee_id', 'in', [employee_id]), + ('state', 'not in', ['cancel', 'refuse']), + ]) + domain = [ + ('employee_id', '=', employee_id), + ('date_from', '<', date_to), + ('date_to', '>', date_from), + ('state', 'not in', ['cancel', 'refuse']), + ] + conflicting_holidays = all_leaves.filtered_domain(domain) + + if conflicting_holidays: + conflicting_holidays_list = [] + # Do not display the name of the employee if the conflicting holidays have an employee_id.user_id equivalent to the user id + holidays_only_have_uid = bool(employee_id) + holiday_states = dict(conflicting_holidays.fields_get(allfields=['state'])['state']['selection']) + for conflicting_holiday in conflicting_holidays: + conflicting_holiday_data = {} + conflicting_holiday_data['employee_name'] = conflicting_holiday.employee_id.name + conflicting_holiday_data['date_from'] = format_date(self.env, + min(conflicting_holiday.mapped('date_from'))) + conflicting_holiday_data['date_to'] = format_date(self.env, + min(conflicting_holiday.mapped('date_to'))) + conflicting_holiday_data['state'] = holiday_states[conflicting_holiday.state] + if conflicting_holiday.employee_id.user_id.id != self.env.uid: + holidays_only_have_uid = False + if conflicting_holiday_data not in conflicting_holidays_list: + conflicting_holidays_list.append(conflicting_holiday_data) + if not conflicting_holidays_list: + return + conflicting_holidays_strings = [] + if holidays_only_have_uid: + for conflicting_holiday_data in conflicting_holidays_list: + conflicting_holidays_string = _('from %(date_from)s to %(date_to)s - %(state)s', + date_from=conflicting_holiday_data['date_from'], + date_to=conflicting_holiday_data['date_to'], + state=conflicting_holiday_data['state']) + conflicting_holidays_strings.append(conflicting_holidays_string) + error = """\ +You've already booked time off which overlaps with this period: +%s +Attempting to double-book your time off won't magically make your vacation 2x better! +""".join(conflicting_holidays_strings) + return error + for conflicting_holiday_data in conflicting_holidays_list: + conflicting_holidays_string = "\n" + _( + '%(employee_name)s - from %(date_from)s to %(date_to)s - %(state)s', + employee_name=conflicting_holiday_data['employee_name'], + date_from=conflicting_holiday_data['date_from'], + date_to=conflicting_holiday_data['date_to'], + state=conflicting_holiday_data['state']) + conflicting_holidays_strings.append(conflicting_holidays_string) + error = "An employee already booked time off which overlaps with this period:%s","".join(conflicting_holidays_strings) + return error + + @api.model + def calculate_leave_duration(self, date_from, date_to, employee_id): + """ + Calculate the number of days and hours for the given date range and employee. + """ + employee = self.env['hr.employee'].browse(employee_id) + if not employee: + return {'error': 'Employee not found'} + + from_date = datetime.fromisoformat(date_from).replace(hour=0, minute=0, second=0) + to_date = datetime.fromisoformat(date_to).replace(hour=23, minute=59, second=59) + + # Define a fake leave record to use _get_durations + leave_values = { + 'employee_id': employee.id, + 'date_from': fields.Datetime.from_string(from_date), + 'date_to': fields.Datetime.from_string(to_date), + 'holiday_status_id': self.env['hr.leave.type'].search([], limit=1).id, + # Replace with appropriate leave type ID + 'resource_calendar_id': employee.resource_calendar_id.id, + } + leave = self.new(leave_values) + durations = leave._get_durations() + leave_id = list(durations.keys())[0] + days, hours = durations[leave_id] + + return { + 'days': float(days), + 'hours': float(hours), + } + + @api.model + def submit_leave_flutter_odoo(self,leave_request_data): + print(leave_request_data) + pass \ No newline at end of file diff --git a/addons_extensions/hr_attendance_extended/views/hr_attendance.xml b/addons_extensions/hr_attendance_extended/views/hr_attendance.xml index b250affe0..0b95086cc 100644 --- a/addons_extensions/hr_attendance_extended/views/hr_attendance.xml +++ b/addons_extensions/hr_attendance_extended/views/hr_attendance.xml @@ -35,7 +35,7 @@ - + @@ -57,7 +57,7 @@ - + @@ -77,11 +77,42 @@ - - + + + + + hr_attendance_management_view_filter_inherit + hr.attendance + + + + + + + + + + + + + hr_attendance_view_filter_inherit + hr.attendance + + + + + + + + + + diff --git a/addons_extensions/hr_employee_extended/models/hr_employee.py b/addons_extensions/hr_employee_extended/models/hr_employee.py index bcf7fa571..0476fae2e 100644 --- a/addons_extensions/hr_employee_extended/models/hr_employee.py +++ b/addons_extensions/hr_employee_extended/models/hr_employee.py @@ -18,6 +18,9 @@ class HrEmployeeBase(models.AbstractModel): total_exp = fields.Char(string='Total Experience', compute='_compute_total_experience', store=True) + emp_type = fields.Many2one('hr.contract.type', "Employee Type", tracking=True) + + @api.constrains('identification_id') def _check_identification_id(self): for record in self: @@ -79,4 +82,5 @@ class HrEmployeeBase(models.AbstractModel): # If there's no DOJ, total experience is just the previous experience total_years = record.previous_exp // 12 total_months = record.previous_exp % 12 - record.total_exp = f"{total_years} years {total_months} months 0 days" \ No newline at end of file + record.total_exp = f"{total_years} years {total_months} months 0 days" + diff --git a/addons_extensions/hr_employee_extended/views/hr_employee.xml b/addons_extensions/hr_employee_extended/views/hr_employee.xml index f907b70e7..f874bbf70 100644 --- a/addons_extensions/hr_employee_extended/views/hr_employee.xml +++ b/addons_extensions/hr_employee_extended/views/hr_employee.xml @@ -6,8 +6,12 @@ hr.employee + + 1 + + diff --git a/addons_extensions/hr_timeoff_extended/models/hr_timeoff.py b/addons_extensions/hr_timeoff_extended/models/hr_timeoff.py index 4d5150d27..b8b484d22 100644 --- a/addons_extensions/hr_timeoff_extended/models/hr_timeoff.py +++ b/addons_extensions/hr_timeoff_extended/models/hr_timeoff.py @@ -17,6 +17,8 @@ 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) + max_start_count = fields.Integer( "Start after", @@ -98,7 +100,11 @@ class hrTimeoffAllocation(models.Model): # Calculate the current frequency run_allocation = self._handel_weekly_frequency(level) if run_allocation: - qualified_employees = employees.filtered(lambda emp: self._is_experience_in_range(emp, level)) + 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: @@ -163,7 +169,7 @@ class hrTimeoffAllocation(models.Model): return fields.date.today() >= start_date return False - def _is_experience_in_range(self, employee, level): + def _emp_filter_by_level(self, employee, level): """ Helper method to check if the employee's total experience (including previous experience) is within the range defined by the accrual's start and max start counts and types. @@ -254,3 +260,28 @@ class HRLeave(models.Model): for rec in self: if rec.employee_id.leave_manager_id.id != self.env.user.id: raise ValidationError(_("Only Employees Time Off Approver can approve this ")) + + @api.ondelete(at_uninstall=False) + def _unlink_if_correct_states(self): + error_message = _('You cannot delete a time off which is in %s state') + state_description_values = {elem[0]: elem[1] for elem in self._fields['state']._description_selection(self.env)} + now = fields.Datetime.now().date() + + if not self.env.user.has_group('hr_holidays.group_hr_holidays_user'): + for hol in self: + if hol.state not in ['draft', 'cancel']: + raise UserError(error_message % state_description_values.get(self[:1].state)) + if hol.date_from.date() < now: + raise UserError(_('You cannot delete a time off which is in the past')) + else: + for holiday in self.filtered(lambda holiday: holiday.state not in ['cancel', 'draft']): + raise UserError(error_message % (state_description_values.get(holiday.state),)) + + +class HRLeaveType(models.Model): + _inherit='hr.leave.type' + + 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 diff --git a/addons_extensions/hr_timeoff_extended/views/hr_timeoff.xml b/addons_extensions/hr_timeoff_extended/views/hr_timeoff.xml index a1d9bae06..1455598c6 100644 --- a/addons_extensions/hr_timeoff_extended/views/hr_timeoff.xml +++ b/addons_extensions/hr_timeoff_extended/views/hr_timeoff.xml @@ -7,6 +7,12 @@ hr.leave.accrual.plan + + 1 + + + 1 + @@ -16,6 +22,9 @@ after employee joining date + + 1 + @@ -212,6 +221,15 @@ Experience is required + + + + + + + + + @@ -229,5 +247,20 @@ + + + hr_holidays.edit_holiday_status_form.inherit + hr.leave.type + + + + 1 + + + + + + + \ No newline at end of file