Ejemplo n.º 1
0
 def validate_time_limits(self, booking_start, booking_end):
     #localize UTC dates to be able to compare with hours in Working Time
     user_tz = pytz.timezone(self.env.context.get('tz') or 'UTC')
     start_dt = pytz.utc.localize(fields.Datetime.from_string(booking_start)).astimezone(user_tz)
     end_dt = pytz.utc.localize(fields.Datetime.from_string(booking_end)).astimezone(user_tz)
     for calendar in self:
         hours = calendar.get_working_accurate_hours(start_dt, end_dt)
         duration = seconds(end_dt - start_dt) / 3600.0
         if round(hours, 2) != round(duration, 2):
             return False
     return True
Ejemplo n.º 2
0
    def get_working_accurate_hours(self, start_dt=None, end_dt=None):
        """
            Replacement of resource calendar method get_working_hours
            Allows to handle hour_to = 00:00
            Takes in account minutes
            Adds public holidays time (resource leaves with reason = PH)
        """
        leave_obj = self.env['resource.calendar.leaves']
        for calendar in self:
            id = calendar.id
            hours = timedelta()
            for day in rrule.rrule(rrule.DAILY, dtstart=start_dt,
                                   until=end_dt.replace(hour=23, minute=59, second=59),
                                   byweekday=calendar.get_weekdays()[0]):
                day_start_dt = day.replace(hour=0, minute=0, second=0)
                if start_dt and day.date() == start_dt.date():
                    day_start_dt = start_dt
                day_end_dt = day.replace(hour=23, minute=59, second=59)
                if end_dt and day.date() == end_dt.date():
                    day_end_dt = end_dt
                work_limits = []
                work_limits.append((day_start_dt.replace(hour=0, minute=0, second=0), day_start_dt))
                work_limits.append((day_end_dt, day_end_dt.replace(hour=23, minute=59, second=59)))

                intervals = []
                work_dt = day_start_dt.replace(hour=0, minute=0, second=0)
                working_intervals = []
                for calendar_working_day in calendar.get_attendances_for_weekdays([day_start_dt.weekday()])[0]:
                    min_from = int((calendar_working_day.hour_from - int(calendar_working_day.hour_from)) * 60)
                    min_to = int((calendar_working_day.hour_to - int(calendar_working_day.hour_to)) * 60)
                    x = work_dt.replace(hour=int(calendar_working_day.hour_from), minute=min_from)
                    if calendar_working_day.hour_to == 0:
                        y = work_dt.replace(hour=0, minute=0) + timedelta(days=1)
                    else:
                        y = work_dt.replace(hour=int(calendar_working_day.hour_to), minute=min_to)
                    working_interval = (x, y)
                    working_intervals += calendar.interval_remove_leaves(working_interval, work_limits)
                for interval in working_intervals:
                    hours += interval[1] - interval[0]

            # Add public holidays
            leaves = leave_obj.search([('name', '=', 'PH'), ('calendar_id', '=', calendar.id)])
            leave_intervals = []
            for l in leaves:
                leave_intervals.append((datetime.strptime(l.date_from, DTF),
                                        datetime.strptime(l.date_to, DTF)
                                        ))
            clean_intervals = calendar.interval_remove_leaves((start_dt, end_dt), leave_intervals)

            for interval in clean_intervals:
                hours += (end_dt - start_dt) - (interval[1] - interval[0])

        return seconds(hours) / 3600.0
Ejemplo n.º 3
0
    def get_working_accurate_hours(self, start_dt=None, end_dt=None):
        """
            Replacement of resource calendar method get_working_hours
            Allows to handle hour_to = 00:00
            Takes in account minutes
            Adds public holidays time (resource leaves with reason = PH)
        """
        leave_obj = self.env['resource.calendar.leaves']
        for calendar in self:
            id = calendar.id
            hours = timedelta()
            for day in rrule.rrule(rrule.DAILY, dtstart=start_dt,
                                   until=end_dt.replace(hour=23, minute=59, second=59),
                                   byweekday=calendar.get_weekdays()[0]):
                day_start_dt = day.replace(hour=0, minute=0, second=0)
                if start_dt and day.date() == start_dt.date():
                    day_start_dt = start_dt
                day_end_dt = day.replace(hour=23, minute=59, second=59)
                if end_dt and day.date() == end_dt.date():
                    day_end_dt = end_dt
                work_limits = []
                work_limits.append((day_start_dt.replace(hour=0, minute=0, second=0), day_start_dt))
                work_limits.append((day_end_dt, day_end_dt.replace(hour=23, minute=59, second=59)))

                intervals = []
                work_dt = day_start_dt.replace(hour=0, minute=0, second=0)
                working_intervals = []
                for calendar_working_day in calendar.get_attendances_for_weekdays([day_start_dt.weekday()])[0]:
                    min_from = int((calendar_working_day.hour_from - int(calendar_working_day.hour_from)) * 60)
                    min_to = int((calendar_working_day.hour_to - int(calendar_working_day.hour_to)) * 60)
                    x = work_dt.replace(hour=int(calendar_working_day.hour_from), minute=min_from)
                    if calendar_working_day.hour_to == 0:
                        y = work_dt.replace(hour=0, minute=0)+timedelta(days=1)
                    else:
                        y = work_dt.replace(hour=int(calendar_working_day.hour_to), minute=min_to)
                    working_interval = (x, y)
                    working_intervals += calendar.interval_remove_leaves(working_interval, work_limits)
                for interval in working_intervals:
                    hours += interval[1] - interval[0]

            #Add public holidays
            leaves = leave_obj.search([('name', '=', 'PH'), ('calendar_id', '=', calendar.id)])
            leave_intervals = []
            for l in leaves:
                leave_intervals.append((datetime.strptime(l.date_from, DTF),
                                        datetime.strptime(l.date_to, DTF)
                ))
            clean_intervals = calendar.interval_remove_leaves((start_dt, end_dt), leave_intervals)

            for interval in clean_intervals:
                hours += (end_dt - start_dt) - (interval[1] - interval[0])

        return seconds(hours) / 3600.0
Ejemplo n.º 4
0
 def validate_time_limits(self, booking_start, booking_end):
     # localize UTC dates to be able to compare with hours in Working Time
     tz_offset = self.env.context.get('tz_offset')
     if tz_offset:
         start_dt = datetime.strptime(booking_start, DTF) - timedelta(minutes=tz_offset)
         end_dt = datetime.strptime(booking_end, DTF) - timedelta(minutes=tz_offset)
     else:
         user_tz = pytz.timezone(self.env.context.get('tz') or 'UTC')
         start_dt = pytz.utc.localize(fields.Datetime.from_string(booking_start)).astimezone(user_tz)
         end_dt = pytz.utc.localize(fields.Datetime.from_string(booking_end)).astimezone(user_tz)
     for calendar in self:
         hours = calendar.get_working_accurate_hours(start_dt, end_dt)
         duration = seconds(end_dt - start_dt) / 3600.0
         if round(hours, 2) != round(duration, 2):
             return False
     return True
Ejemplo n.º 5
0
    def get_working_hours_of_date(self,
                                  cr,
                                  uid,
                                  id,
                                  start_dt=None,
                                  end_dt=None,
                                  leaves=None,
                                  compute_leaves=False,
                                  resource_id=None,
                                  default_interval=None,
                                  context=None):
        """ Get the working hours of the day based on calendar. This method uses
		get_working_intervals_of_day to have the work intervals of the day. It
		then calculates the number of hours contained in those intervals. """

        date_holiday = start_dt.strftime('%Y-%m-%d')
        is_holiday = self.pool.get('hr.holidays.public.line').search(
            cr, uid, [('date', '=', date_holiday)])
        if is_holiday:
            return 0.0
        intervalx = super(resource_calendar, self).get_working_hours_of_date(
            cr,
            uid,
            id,
            start_dt=start_dt,
            end_dt=end_dt,
            leaves=leaves,
            compute_leaves=compute_leaves,
            resource_id=resource_id,
            default_interval=default_interval,
            context=context)
        res = datetime.timedelta()
        intervals = self.get_working_intervals_of_day(
            cr, uid, id, start_dt, end_dt, leaves, compute_leaves, resource_id,
            default_interval, context)
        for interval in intervals:
            res += interval[1] - interval[0]

        return seconds(res) / 3600.0
Ejemplo n.º 6
0
 def events_to_bookings(self, events):
     calendar_obj = self.env['resource.calendar']
     resource_obj = self.env['resource.resource']
     lang_obj = self.env['res.lang']
     lang = lang_obj.search([('code', '=', self.env.context.get('lang'))])
     user_df = ('%s %s' %
                (lang.date_format, lang.time_format)) if lang else DTF
     products = self.env['product.product'].search([('calendar_id', '!=',
                                                     False)])
     bookings = {}
     for event in events:
         r = event['resource']
         if not r in bookings:
             bookings[r] = {}
         start_dt = datetime.strptime(event['start'], DTF)
         end_dt = datetime.strptime(event['end'], DTF)
         #check products and its working calendars by every hour booked by user
         hour_dt = start_dt
         while hour_dt < end_dt:
             hour = hour_dt.strftime(DTF)
             if hour_dt < end_dt:
                 bookings[r][hour] = {
                     'start':
                     hour_dt,
                     'start_f': (hour_dt).strftime(user_df),
                     'end': (hour_dt + timedelta(hours=MIN_TIMESLOT_HOURS)),
                     'end_f': (hour_dt + timedelta(hours=MIN_TIMESLOT_HOURS)
                               ).strftime(user_df),
                     'resource':
                     resource_obj.browse(int(event['resource'])),
                     'products': {}
                 }
                 hour_end_dt = hour_dt + timedelta(hours=MIN_TIMESLOT_HOURS)
                 duration = seconds(hour_end_dt - hour_dt) / 3600
                 for product in products:
                     hours = product.calendar_id.get_working_accurate_hours(
                         hour_dt, hour_end_dt)
                     if hours == duration:
                         bookings[r][hour]['products'][str(product.id)] = {
                             'id': product.id,
                             'name': product.name,
                             'price': product.lst_price or product.price,
                             'currency': product.company_id.currency_id.name
                         }
                 #join adjacent hour intervals to one SO position
                 for h in bookings[r]:
                     if h == hour or bookings[r][h]['products'].keys(
                     ) != bookings[r][hour]['products'].keys():
                         continue
                     adjacent = False
                     if bookings[r][hour]['start'] == bookings[r][h]['end']:
                         adjacent = True
                         bookings[r][h].update({
                             'end':
                             bookings[r][hour]['end'],
                             'end_f':
                             bookings[r][hour]['end_f']
                         })
                     elif bookings[r][hour]['end'] == bookings[r][h][
                             'start']:
                         adjacent = True
                         bookings[r][h].update({
                             'start':
                             bookings[r][hour]['end'],
                             'start_f':
                             bookings[r][hour]['start_f']
                         })
                     if adjacent:
                         for id, p in bookings[r][h]['products'].iteritems(
                         ):
                             bookings[r][h]['products'][id][
                                 'price'] += bookings[r][hour]['products'][
                                     id]['price']
                         del bookings[r][hour]
                         break
             hour_dt += timedelta(hours=MIN_TIMESLOT_HOURS)
     res = []
     for r in bookings.values():
         res += r.values()
     return res
Ejemplo n.º 7
0
 def events_to_bookings(self, events):
     calendar_obj = self.env['resource.calendar']
     resource_obj = self.env['resource.resource']
     lang_obj = self.env['res.lang']
     lang = lang_obj.search([('code', '=', self.env.context.get('lang'))])
     user_df = ('%s %s' %
                (lang.date_format, lang.time_format)) if lang else DTF
     products = self.env['product.product'].search([
         ('calendar_id', '!=', False), ('website_published', '=', True)
     ])
     bookings = {}
     partner = self.env.user.partner_id
     pricelist_id = partner.property_product_pricelist.id
     for event in events:
         r = event['resource']
         if r not in bookings:
             bookings[r] = {}
         start_dt = datetime.strptime(event['start'], DTF)
         end_dt = datetime.strptime(event['end'], DTF)
         # check products and its working calendars by every hour booked by user
         hour_dt = start_dt
         while hour_dt < end_dt:
             hour = hour_dt.strftime(DTF)
             if hour_dt < end_dt:
                 bookings[r][hour] = {
                     'start':
                     hour_dt,
                     'start_f': (hour_dt).strftime(user_df),
                     'end': (hour_dt + timedelta(hours=MIN_TIMESLOT_HOURS)),
                     'end_f': (hour_dt + timedelta(hours=MIN_TIMESLOT_HOURS)
                               ).strftime(user_df),
                     'resource':
                     resource_obj.browse(int(event['resource'])),
                     'products': {}
                 }
                 hour_end_dt = hour_dt + timedelta(hours=MIN_TIMESLOT_HOURS)
                 duration = seconds(hour_end_dt - hour_dt) / 3600
                 for product in self.get_booking_available_products(
                         event, products):
                     hours = product.calendar_id.get_working_accurate_hours(
                         hour_dt, hour_end_dt)
                     if hours == duration:
                         bookings[r][hour]['products'][str(product.id)] = {
                             'id': product.id,
                             'name': product.name,
                             'quantity': 1,
                             'currency': product.company_id.currency_id.name
                         }
                 # join adjacent hour intervals to one SO position
                 for h in bookings[r]:
                     if h == hour or bookings[r][h]['products'].keys(
                     ) != bookings[r][hour]['products'].keys():
                         continue
                     adjacent = False
                     if bookings[r][hour]['start'] == bookings[r][h]['end']:
                         adjacent = True
                         bookings[r][h].update({
                             'end':
                             bookings[r][hour]['end'],
                             'end_f':
                             bookings[r][hour]['end_f']
                         })
                     elif bookings[r][hour]['end'] == bookings[r][h][
                             'start']:
                         adjacent = True
                         bookings[r][h].update({
                             'start':
                             bookings[r][hour]['end'],
                             'start_f':
                             bookings[r][hour]['start_f']
                         })
                     if adjacent:
                         for id, p in bookings[r][h]['products'].iteritems(
                         ):
                             bookings[r][h]['products'][id][
                                 'quantity'] += bookings[r][hour][
                                     'products'][id]['quantity']
                         del bookings[r][hour]
                         break
             hour_dt += timedelta(hours=MIN_TIMESLOT_HOURS)
     # calculate prices according to pricelists
     for k1, v1 in bookings.iteritems():
         for k2, v2 in v1.iteritems():
             for id, product in v2['products'].iteritems():
                 bookings[k1][k2]['products'][id]['price'] = self.env[
                     'product.product'].browse(product['id']).with_context(
                         {
                             'quantity': product['quantity'],
                             'pricelist': pricelist_id,
                             'partner': partner.id
                         }).price * product['quantity']
     res = []
     for r in bookings.values():
         res += r.values()
     return res
Ejemplo n.º 8
0
    def get_working_accurate_hours(self, start_dt=None, end_dt=None):
        """
            Replacement of resource calendar method get_working_hours
            Allows to handle hour_to = 00:00
            Takes in account minutes
            Adds public holidays time (resource leaves with reason = PH)
        """
        leave_obj = self.env['resource.calendar.leaves']
        for calendar in self:
            product = self.env['product.template'].search([('calendar_id', '=', calendar.id)])
            hours = timedelta()

            day_start_dt = start_dt
            day_end_dt = end_dt
            # sometimes one booking may be on two different weekdays
            # call get_working_accurate_hours recursively for that cases
            if day_end_dt.date() == day_start_dt.date() + timedelta(1) and \
                    day_end_dt != day_end_dt.replace(hour=0, minute=0, second=0):
                    hours += timedelta(hours=self.get_working_accurate_hours(start_dt=day_end_dt.replace(hour=0, minute=0, second=0), end_dt=day_end_dt))

            weekday = [day_start_dt.weekday()]
            if product and product[0].work_on_holidays and product[0].holidays_country_id and product[0].holidays_schedule == 'premium':
                if calendar.get_attendances_for_weekdays([5]):
                    holidays = self.env['hr.holidays.public'].search([
                        ('country_id', '=', product[0].holidays_country_id.id),
                        ('year', '=', start_dt.year),
                    ], limit=1)
                    for h in holidays[0].line_ids.filtered(lambda r: r.date == start_dt.strftime(DF)):
                        weekday = [5]

            work_limits = []
            work_limits.append((day_start_dt.replace(hour=0, minute=0, second=0), day_start_dt))
            work_limits.append((day_end_dt, day_end_dt.replace(hour=23, minute=59, second=59)))

            work_dt = day_start_dt.replace(hour=0, minute=0, second=0)
            working_intervals = []
            for calendar_working_day in calendar.get_attendances_for_weekdays(weekday)[0]:
                min_from = int((calendar_working_day.hour_from - int(calendar_working_day.hour_from)) * 60)
                min_to = int((calendar_working_day.hour_to - int(calendar_working_day.hour_to)) * 60)
                x = work_dt.replace(hour=int(calendar_working_day.hour_from), minute=min_from)
                if calendar_working_day.hour_to == 0:
                    y = work_dt.replace(hour=0, minute=0) + timedelta(days=1)
                else:
                    y = work_dt.replace(hour=int(calendar_working_day.hour_to), minute=min_to)
                working_interval = (x, y)
                leaves = self.get_leave_intervals()
                leaves = leaves and self.localize_time_intervals(leaves[0])
                work_limits += leaves
                if product and not product[0].work_on_holidays and product[0].holidays_country_id:
                    holidays = self.env['hr.holidays.public'].search([
                        ('country_id', '=', product[0].holidays_country_id.id),
                        ('year', '=', start_dt.year),
                    ], limit=1)
                    for h in holidays[0].line_ids.filtered(lambda r: r.date == start_dt.strftime(DF)):
                        holiday_interval = [(datetime.combine(datetime.strptime(h.date, DF), time(0, 0)),
                                             datetime.combine(datetime.strptime(h.date, DF) + timedelta(1), time(0, 0)))]
                        holiday_interval = self.make_offset_aware(holiday_interval)
                        work_limits += holiday_interval
                work_limits = self.make_offset_aware(work_limits)
                working_interval = self.make_offset_aware([working_interval])[0]
                working_intervals += calendar.interval_remove_leaves(working_interval, work_limits)
            for interval in working_intervals:
                hours += interval[1] - interval[0]

            # Add public holidays
            leaves = leave_obj.search([('name', '=', 'PH'), ('calendar_id', '=', calendar.id)])
            leave_intervals = []
            for l in leaves:
                leave_intervals.append((datetime.strptime(l.date_from, DTF),
                                        datetime.strptime(l.date_to, DTF)
                                        ))
            clean_intervals = calendar.interval_remove_leaves((start_dt, end_dt), leave_intervals)

            for interval in clean_intervals:
                hours += (end_dt - start_dt) - (interval[1] - interval[0])

        return seconds(hours) / 3600.0