def _get_current_accrual_plan_level_id(self, date, level_ids=False): """ Returns a pair (accrual_plan_level, idx) where accrual_plan_level is the level for the given date and idx is the index for the plan in the ordered set of levels """ self.ensure_one() if not self.accrual_plan_id.level_ids: return (False, False) # Sort by sequence which should be equivalent to the level if not level_ids: level_ids = self.accrual_plan_id.level_ids.sorted('sequence') current_level = False current_level_idx = -1 for idx, level in enumerate(level_ids): if date >= self.date_from + get_timedelta(level.start_count, level.start_type): current_level = level current_level_idx = idx # If transition_mode is set to `immediately` or we are currently on the first level # the current_level is simply the first level in the list. if current_level_idx <= 0 or self.accrual_plan_id.transition_mode == "immediately": return (current_level, current_level_idx) # In this case we have to verify that the 'previous level' is not the current one due to `end_of_accrual` level_start_date = self.date_from + get_timedelta( current_level.start_count, current_level.start_type) previous_level = level_ids[current_level_idx - 1] # If the next date from the current level's start date is before the last call of the previous level # return the previous level if current_level._get_next_date( level_start_date) < previous_level._get_next_date( level_start_date): return (previous_level, current_level_idx - 1) return (current_level, current_level_idx)
def _process_accrual_plans(self): """ This method is part of the cron's process. The goal of this method is to retroactively apply accrual plan levels and progress from nextcall to today """ today = fields.Date.today() first_allocation = _("""This allocation have already ran once, any modification won't be effective to the days allocated to the employee. If you need to change the configuration of the allocation, cancel and create a new one.""") for allocation in self: level_ids = allocation.accrual_plan_id.level_ids.sorted('sequence') if not level_ids: continue if not allocation.nextcall: first_level = level_ids[0] first_level_start_date = allocation.date_from + get_timedelta(first_level.start_count, first_level.start_type) if today < first_level_start_date: # Accrual plan is not configured properly or has not started continue allocation.lastcall = max(allocation.lastcall, first_level_start_date) allocation.nextcall = first_level._get_next_date(allocation.lastcall) allocation._message_log(body=first_allocation) days_added_per_level = defaultdict(lambda: 0) while allocation.nextcall <= today: (current_level, current_level_idx) = allocation._get_current_accrual_plan_level_id(allocation.nextcall) nextcall = current_level._get_next_date(allocation.nextcall) # Since _get_previous_date returns the given date if it corresponds to a call date # this will always return lastcall except possibly on the first call # this is used to prorate the first number of days given to the employee period_start = current_level._get_previous_date(allocation.lastcall) period_end = current_level._get_next_date(allocation.lastcall) # Also prorate this accrual in the event that we are passing from one level to another if current_level_idx < (len(level_ids) - 1) and allocation.accrual_plan_id.transition_mode == 'immediately': next_level = level_ids[current_level_idx + 1] current_level_last_date = allocation.date_from + get_timedelta(next_level.start_count, next_level.start_type) - relativedelta(days=1) if allocation.nextcall != current_level_last_date: nextcall = min(nextcall, current_level_last_date) days_added_per_level[current_level] += allocation._process_accrual_plan_level( current_level, period_start, allocation.lastcall, period_end, allocation.nextcall) allocation.lastcall = allocation.nextcall allocation.nextcall = nextcall if days_added_per_level: number_of_days_to_add = 0 for value in days_added_per_level.values(): number_of_days_to_add += value # Let's assume the limit of the last level is the correct one allocation.write({'number_of_days': min(allocation.number_of_days + number_of_days_to_add, current_level.maximum_leave)})
def _process_accrual_plans(self): """ This method is part of the cron's process. The goal of this method is to retroactively apply accrual plan levels and progress from nextcall to today """ today = fields.Date.today() first_allocation = _( """This allocation have already ran once, any modification won't be effective to the days allocated to the employee. If you need to change the configuration of the allocation, cancel and create a new one.""" ) for allocation in self: level_ids = self.accrual_plan_id.level_ids.sorted('sequence') if not level_ids: continue if not allocation.nextcall: first_level = level_ids[0] first_level_start_date = allocation.date_from + get_timedelta( first_level.start_count, first_level.start_type) if today < first_level_start_date: # Accrual plan is not configured properly or has not started continue allocation.lastcall = max(allocation.lastcall, first_level_start_date) allocation.nextcall = first_level._get_next_date( allocation.lastcall) allocation._message_log(body=first_allocation) days_added_per_level = defaultdict(lambda: 0) while allocation.nextcall <= today: current_level = allocation._get_current_accrual_plan_level_id( allocation.nextcall) nextcall = current_level._get_next_date(allocation.nextcall) days_added_per_level[ current_level] += allocation._process_accrual_plan_level( current_level, allocation.lastcall, allocation.nextcall) allocation.lastcall = allocation.nextcall allocation.nextcall = nextcall if days_added_per_level: number_of_days_to_add = 0 for value in days_added_per_level.values(): number_of_days_to_add += value # Let's assume the limit of the last level is the correct one allocation.write({ 'number_of_days': min(allocation.number_of_days + number_of_days_to_add, current_level.maximum_leave) })
def _get_accrual_values(self, allocation_create_date): """ This method returns all the accrual linked to their accrual_plan with the updated dynamic parameters depending on the date. :return: dict: {accrual_id, accrual_start, accrual_stop, nextcall, sufficient_seniority} where accrual_start and accrual_stop are start and stop of the current period """ today = fields.Date.context_today(self, ) results = [] for accrual in self: seniority = allocation_create_date + get_timedelta( accrual.start_count, accrual.start_type) frequency = accrual.frequency if frequency == 'daily': accrual_start = max(today, seniority.date()) accrual_stop = accrual_start + relativedelta(days=1) nextcall = accrual_stop elif frequency == 'weekly': min_accrual_date = max(today, seniority.date()) if min_accrual_date.isoweekday() == DAYS.index( accrual.week_day): accrual_stop = min_accrual_date else: accrual_stop = accrual._get_next_weekday( min_accrual_date, accrual.week_day) accrual_start = accrual_stop - relativedelta(days=7) nextcall = accrual._get_next_weekday(min_accrual_date, accrual.week_day) elif frequency == 'bimonthly': if today.day <= accrual.first_day: accrual_start = datetime.date( today.year, today.month, accrual.second_day) - relativedelta(months=1) accrual_stop = datetime.date(today.year, today.month, accrual.first_day) nextcall = datetime.date(today.year, today.month, accrual.second_day) else: if today.day <= accrual.second_day: accrual_start = datetime.date(today.year, today.month, accrual.first_day) accrual_stop = datetime.date(today.year, today.month, accrual.second_day) nextcall = datetime.date( today.year, today.month, accrual.first_day) + relativedelta(months=1) else: accrual_start = datetime.date(today.year, today.month, accrual.second_day) accrual_stop = datetime.date( today.year, today.month, accrual.first_day) + relativedelta(months=1) nextcall = datetime.date( today.year, today.month, accrual.second_day) + relativedelta(months=1) elif frequency == 'monthly': if today.day <= accrual.first_day: accrual_start = datetime.date( today.year, today.month, accrual.first_day) - relativedelta(months=1) accrual_stop = datetime.date(today.year, today.month, accrual.first_day) nextcall = datetime.date( today.year, today.month, accrual.first_day) + relativedelta(months=1) else: accrual_start = datetime.date(today.year, today.month, accrual.first_day) accrual_stop = datetime.date( today.year, today.month, accrual.first_day) + relativedelta(months=1) nextcall = datetime.date( today.year, today.month, accrual.first_day) + relativedelta(months=2) elif frequency == 'biyearly': first_month = MONTHS.index(accrual.first_month) + 1 second_month = MONTHS.index(accrual.second_month) + 1 potential_first_accrual_date = datetime.date( today.year, first_month, accrual.first_month_day) potential_second_accrual_date = datetime.date( today.year, second_month, accrual.second_month_day) if today <= potential_first_accrual_date: accrual_start = potential_second_accrual_date - relativedelta( years=1) accrual_stop = potential_first_accrual_date nextcall = potential_second_accrual_date else: if today <= potential_second_accrual_date: accrual_start = potential_first_accrual_date accrual_stop = potential_second_accrual_date nextcall = potential_first_accrual_date + relativedelta( years=1) else: accrual_start = potential_second_accrual_date accrual_stop = potential_first_accrual_date + relativedelta( years=1) nextcall = potential_first_accrual_date + relativedelta( years=1) elif frequency == 'yearly': month = MONTHS.index(accrual.yearly_month) + 1 potential_accrual_date = datetime.date(today.year, month, accrual.yearly_day) if today <= potential_accrual_date: accrual_start = potential_accrual_date - relativedelta( years=1) accrual_stop = potential_accrual_date nextcall = potential_accrual_date + relativedelta(years=1) else: accrual_start = potential_accrual_date accrual_stop = potential_accrual_date + relativedelta( years=1) nextcall = accrual_stop results.append({ 'accrual_level_id': accrual.id, 'start_after': accrual.start_count, 'accrual_start': datetime.datetime.combine(accrual_start, datetime.datetime.min.time()), 'accrual_stop': datetime.datetime.combine(accrual_stop, datetime.datetime.min.time()), 'nextcall': nextcall, 'sufficient_seniority': seniority.date() <= today }) return results