Ejemplo n.º 1
0
 def _get_unavailable_dates(self, start_date, end_date):
     """
     Returns the list of days when the current company is closed (we, or holidays)
     """
     start_dt = datetime(year=start_date.year,
                         month=start_date.month,
                         day=start_date.day)
     end_dt = datetime(year=end_date.year,
                       month=end_date.month,
                       day=end_date.day,
                       hour=23,
                       minute=59,
                       second=59)
     # naive datetimes are made explicit in UTC
     from_datetime, dummy = make_aware(start_dt)
     to_datetime, dummy = make_aware(end_dt)
     # We need to display in grey the unavailable full days
     # We start by getting the availability intervals to avoid false positive with range outside the office hours
     items = self.env.company.resource_calendar_id._work_intervals(
         from_datetime, to_datetime)
     # get the dates where some work can be done in the interval. It returns a list of sets.
     available_dates = list(
         map(lambda item: {item[0].date(), item[1].date()}, items))
     # flatten the list of sets to get a simple list of dates and add it to the pile.
     avaibilities = [date for dates in available_dates for date in dates]
     unavailable_days = []
     cur_day = from_datetime
     while cur_day <= to_datetime:
         if not cur_day.date() in avaibilities:
             unavailable_days.append(cur_day.date())
         cur_day = cur_day + timedelta(days=1)
     return set(unavailable_days)
Ejemplo n.º 2
0
    def _get_first_available_slot(self, start_datetime, duration):
        """Get the first available interval for the workcenter in `self`.

        The available interval is disjoinct with all other workorders planned on this workcenter, but
        can overlap the time-off of the related calendar (inverse of the working hours).
        Return the first available interval (start datetime, end datetime) or,
        if there is none before 700 days, a tuple error (False, 'error message').

        :param start_datetime: begin the search at this datetime
        :param duration: minutes needed to make the workorder (float)
        :rtype: tuple
        """
        self.ensure_one()
        start_datetime, revert = make_aware(start_datetime)

        resource = self.resource_id
        get_available_intervals = partial(
            self.resource_calendar_id._work_intervals_batch,
            domain=[('time_type', 'in', ['other', 'leave'])],
            resources=resource,
            tz=timezone(self.resource_calendar_id.tz))
        get_workorder_intervals = partial(
            self.resource_calendar_id._leave_intervals_batch,
            domain=[('time_type', '=', 'other')],
            resources=resource,
            tz=timezone(self.resource_calendar_id.tz))

        remaining = duration
        start_interval = start_datetime
        delta = timedelta(days=14)

        for n in range(50):  # 50 * 14 = 700 days in advance (hardcoded)
            dt = start_datetime + delta * n
            available_intervals = get_available_intervals(dt, dt +
                                                          delta)[resource.id]
            workorder_intervals = get_workorder_intervals(dt, dt +
                                                          delta)[resource.id]
            for start, stop, dummy in available_intervals:
                # Shouldn't loop more than 2 times because the available_intervals contains the workorder_intervals
                # And remaining == duration can only occur at the first loop and at the interval intersection (cannot happen several time because available_intervals > workorder_intervals
                for _i in range(2):
                    interval_minutes = (stop - start).total_seconds() / 60
                    # If the remaining minutes has never decrease update start_interval
                    if remaining == duration:
                        start_interval = start
                    # If there is a overlap between the possible available interval and a others WO
                    if Intervals(
                        [(start_interval, start +
                          timedelta(minutes=min(remaining, interval_minutes)),
                          dummy)]) & workorder_intervals:
                        remaining = duration
                    elif float_compare(interval_minutes,
                                       remaining,
                                       precision_digits=3) >= 0:
                        return revert(start_interval), revert(
                            start + timedelta(minutes=remaining))
                    else:
                        # Decrease a part of the remaining duration
                        remaining -= interval_minutes
        return False, 'Not available slot 700 days after the planned start'
Ejemplo n.º 3
0
    def plan_days_to_resource(self,
                              days,
                              day_dt,
                              compute_leaves=False,
                              resource=None,
                              domain=None):
        day_dt, revert = make_aware(day_dt)

        # which method to use for retrieving intervals
        if compute_leaves:
            get_intervals = partial(self._work_intervals,
                                    resource=resource,
                                    domain=domain)
        else:
            get_intervals = partial(self._attendance_intervals,
                                    resource=resource)

        if days > 0:
            found = set()
            delta = timedelta(days=14)
            for n in range(100):
                dt = day_dt + delta * n
                for start, stop, meta in get_intervals(dt, dt + delta):
                    found.add(start.date())
                    if len(found) == days:
                        return revert(stop)
            return False

        elif days < 0:
            days = abs(days)
            found = set()
            delta = timedelta(days=14)
            for n in range(100):
                dt = day_dt - delta * n
                for start, stop, meta in reversed(get_intervals(
                        dt - delta, dt)):
                    found.add(start.date())
                    if len(found) == days:
                        return revert(start)
            return False

        else:
            return revert(day_dt)
Ejemplo n.º 4
0
    def __new_plan_hours(self,
                         hours,
                         day_dt,
                         compute_leaves=False,
                         domain=None,
                         resource=None):
        day_dt, revert = make_aware(day_dt)

        # which method to use for retrieving intervals
        if compute_leaves:
            get_intervals = partial(self._work_intervals,
                                    domain=domain,
                                    resource=resource)
        else:
            get_intervals = self._attendance_intervals

        if hours >= 0:
            delta = timedelta(days=14)
            for n in range(100):
                dt = day_dt + delta * n
                for start, stop, meta in get_intervals(dt, dt + delta):
                    interval_hours = self._get_work_hours_interval(
                        start, stop, meta)
                    if hours <= interval_hours:
                        return revert(start + timedelta(hours=hours))
                    hours -= interval_hours
            return False
        else:
            hours = abs(hours)
            delta = timedelta(days=14)
            for n in range(100):
                dt = day_dt - delta * n
                for start, stop, meta in reversed(get_intervals(
                        dt - delta, dt)):
                    interval_hours = self._get_work_hours_interval(
                        start, stop, meta)
                    if hours <= interval_hours:
                        return revert(stop - timedelta(hours=hours))
                    hours -= interval_hours
            return False
Ejemplo n.º 5
0
    def plan_days_end(self, days, day_dt, compute_leaves=False, domain=None):
        """
        Override to `plan_days` that allows you to get the nearest 'end' including today.
        """
        day_dt, revert = make_aware(day_dt)

        # which method to use for retrieving intervals
        if compute_leaves:
            get_intervals = partial(self._work_intervals, domain=domain)
        else:
            get_intervals = self._attendance_intervals

        if days >= 0:
            found = set()
            delta = timedelta(days=14)
            for n in range(100):
                dt = day_dt + delta * n
                for start, stop, meta in get_intervals(dt, dt + delta):
                    found.add(start.date())
                    if len(found) >= days:
                        return revert(stop)
            return False

        elif days < 0:
            days = abs(days)
            found = set()
            delta = timedelta(days=14)
            for n in range(100):
                dt = day_dt - delta * n
                for start, stop, meta in reversed(get_intervals(
                        dt - delta, dt)):
                    found.add(start.date())
                    if len(found) == days:
                        return revert(start)
            return False

        else:
            return revert(day_dt)