Esempio n. 1
0
 def name_get(self):
     result = []
     for attendance in self:
         if not attendance.check_out:
             result.append(
                 (attendance.id, _("%(empl_name)s from %(check_in)s") % {
                     'empl_name':
                     attendance.employee_id.name,
                     'check_in':
                     format_datetime(
                         self.env, attendance.check_in, dt_format=False),
                 }))
         else:
             result.append(
                 (attendance.id,
                  _("%(empl_name)s from %(check_in)s to %(check_out)s") % {
                      'empl_name':
                      attendance.employee_id.name,
                      'check_in':
                      format_datetime(
                          self.env, attendance.check_in, dt_format=False),
                      'check_out':
                      format_datetime(
                          self.env, attendance.check_out, dt_format=False),
                  }))
     return result
Esempio n. 2
0
    def get_rental_order_line_description(self):
        if (self.is_rental):
            if self.pickup_date.date() == self.return_date.date():
                # If return day is the same as pickup day, don't display return_date Y/M/D in description.
                return_date_part = format_datetime(
                    self.with_context(use_babel=True).env,
                    self.return_date,
                    tz=self.env.user.tz,
                    dt_format='HH:mm a')
            else:
                return_date_part = format_datetime(
                    self.with_context(use_babel=True).env,
                    self.return_date,
                    tz=self.env.user.tz,
                    dt_format='short')

            return "\n%s %s %s" % (
                format_datetime(self.with_context(use_babel=True).env,
                                self.pickup_date,
                                tz=self.env.user.tz,
                                dt_format='short'),
                _("to"),
                return_date_part,
            )
        else:
            return ""
Esempio n. 3
0
 def _check_date_range(self):
     for item in self:
         if item.date_start and item.date_end and item.date_start >= item.date_end:
             raise ValidationError(
                 _(
                     '%s : end date (%s) should be greater than start date (%s)',
                     item.display_name,
                     format_datetime(self.env, item.date_end),
                     format_datetime(self.env, item.date_start)))
     return True
Esempio n. 4
0
 def _compute_json_popover(self):
     previous_wo_data = self.env['mrp.workorder'].read_group(
         [('next_work_order_id', 'in', self.ids)],
         ['ids:array_agg(id)', 'date_planned_start:max', 'date_planned_finished:max'],
         ['next_work_order_id'])
     previous_wo_dict = dict([(x['next_work_order_id'][0], {
         'id': x['ids'][0],
         'date_planned_start': x['date_planned_start'],
         'date_planned_finished': x['date_planned_finished']})
         for x in previous_wo_data])
     if self.ids:
         conflicted_dict = self._get_conflicted_workorder_ids()
     for wo in self:
         infos = []
         if not wo.date_planned_start or not wo.date_planned_finished or not wo.ids:
             wo.show_json_popover = False
             wo.json_popover = False
             continue
         if wo.state in ['pending', 'ready']:
             previous_wo = previous_wo_dict.get(wo.id)
             prev_start = previous_wo and previous_wo['date_planned_start'] or False
             prev_finished = previous_wo and previous_wo['date_planned_finished'] or False
             if wo.state == 'pending' and prev_start and not (prev_start > wo.date_planned_start):
                 infos.append({
                     'color': 'text-primary',
                     'msg': _("Waiting the previous work order, planned from %(start)s to %(end)s",
                         start=format_datetime(self.env, prev_start, dt_format=False),
                         end=format_datetime(self.env, prev_finished, dt_format=False))
                 })
             if wo.date_planned_finished < fields.Datetime.now():
                 infos.append({
                     'color': 'text-warning',
                     'msg': _("The work order should have already been processed.")
                 })
             if prev_start and prev_start > wo.date_planned_start:
                 infos.append({
                     'color': 'text-danger',
                     'msg': _("Scheduled before the previous work order, planned from %(start)s to %(end)s",
                         start=format_datetime(self.env, prev_start, dt_format=False),
                         end=format_datetime(self.env, prev_finished, dt_format=False))
                 })
             if conflicted_dict.get(wo.id):
                 infos.append({
                     'color': 'text-danger',
                     'msg': _("Planned at the same time than other workorder(s) at %s", wo.workcenter_id.display_name)
                 })
         color_icon = infos and infos[-1]['color'] or False
         wo.show_json_popover = bool(color_icon)
         wo.json_popover = json.dumps({
             'infos': infos,
             'color': color_icon,
             'icon': 'fa-exclamation-triangle' if color_icon in ['text-warning', 'text-danger'] else 'fa-info-circle',
             'replan': color_icon not in [False, 'text-primary']
         })
Esempio n. 5
0
 def _get_delay_line_description(self):
     # Shouldn't tz be taken from self.order_id.user_id.tz ?
     return "%s\n%s: %s\n%s: %s" % (
         self.product_id.name, _("Expected"),
         format_datetime(self.with_context(use_babel=True).env,
                         self.pickup_date,
                         tz=self.env.user.tz,
                         dt_format='short'), _("Returned"),
         format_datetime(self.with_context(use_babel=True).env,
                         fields.Datetime.now(),
                         tz=self.env.user.tz,
                         dt_format='short'))
Esempio n. 6
0
 def _final_vals_to_lines(self, final_vals, level):
     lines = []
     for data in final_vals:
         lines.append({
             'id': autoIncrement(),
             'model': data['model'],
             'model_id': data['model_id'],
             'parent_id': data['parent_id'],
             'usage': data.get('usage', False),
             'is_used': data.get('is_used', False),
             'lot_name': data.get('lot_name', False),
             'lot_id': data.get('lot_id', False),
             'reference': data.get('reference_id', False),
             'res_id': data.get('res_id', False),
             'res_model': data.get('res_model', False),
             'columns': [data.get('reference_id', False),
                         data.get('product_id', False),
                         format_datetime(self.env, data.get('date', False), tz=False, dt_format=False),
                         data.get('lot_name', False),
                         data.get('location_source', False),
                         data.get('location_destination', False),
                         data.get('product_qty_uom', 0)],
             'level': level,
             'unfoldable': data['unfoldable'],
         })
     return lines
Esempio n. 7
0
 def _compute_date_end_tz(self):
     for event in self:
         if event.date_end:
             event.date_end_located = format_datetime(
                 self.env, event.date_end, tz=event.date_tz, dt_format='medium')
         else:
             event.date_end_located = False
Esempio n. 8
0
 def _compute_date_end_tz(self):
     if self.date_end:
         self.date_end_located = format_datetime(self.env,
                                                 self.date_end,
                                                 tz=self.date_tz,
                                                 dt_format='medium')
     else:
         self.date_end_located = False
Esempio n. 9
0
 def _compute_date_begin_tz(self):
     if self.date_begin:
         self.date_begin_located = format_datetime(self.env,
                                                   self.date_begin,
                                                   tz=self.date_tz,
                                                   dt_format='medium')
     else:
         self.date_begin_located = False
Esempio n. 10
0
    def get_account_balance(self):
        if not self.user_has_groups('base.group_erp_manager'):
            raise AccessError(_("You can't access account balance."))

        if not self.env.company.adyen_account_id:
            return {}

        balance_fields = {'balance': 'balance', 'onHoldBalance': 'on_hold', 'pendingBalance': 'pending'}
        balances = self.env['adyen.account.balance'].sudo().search([
            ('adyen_account_id', '=', self.env.company.adyen_account_id.id)
        ])

        delta = fields.Datetime.now() - timedelta(hours=1)
        if not balances or any(b.write_date <= delta for b in balances):
            response = {}
            try:
                response = self.env.company.adyen_account_id._adyen_rpc('v1/account_holder_balance', {
                    'accountHolderCode': self.env.company.adyen_account_id.account_holder_code,
                })
            except UserError as e:
                _logger.warning(_('Cannot update account balance, showing previous values: %s', e))

            balances.write({
                f: 0 for f in balance_fields.values()
            })
            for total_balance, adyen_balances in response.get('totalBalance', {}).items():
                for balance in adyen_balances:
                    currency_id = self.env['res.currency'].search([('name', '=', balance.get('currency'))])
                    bal = balances.filtered(lambda b: b.currency_id == currency_id)
                    if not bal:
                        bal = self.env['adyen.account.balance'].sudo().create({
                            'adyen_account_id': self.env.company.adyen_account_id.id,
                            'currency_id': currency_id.id,
                        })
                        balances |= bal
                    bal[balance_fields.get(total_balance)] = to_major_currency(balance.get('value', 0), currency_id.decimal_places)

        warning_delta = fields.Datetime.now() - timedelta(hours=2)
        return [{
            'currency': b.currency_id.name,
            'balance': format_amount(self.env, b.balance, b.currency_id),
            'payout_date': format_datetime(self.env, self.env.company.adyen_account_id.next_scheduled_payout, dt_format='short'),
            'last_update_warning': b.write_date <= warning_delta,
            'last_update': format_datetime(self.env, b.write_date),
        } for b in balances]
Esempio n. 11
0
    def _prepare_survey_data(self, survey_sudo, answer_sudo, **post):
        """ This method prepares all the data needed for template rendering, in function of the survey user input state.
            :param post:
                - previous_page_id : come from the breadcrumb or the back button and force the next questions to load
                                     to be the previous ones. """
        data = {
            'survey':
            survey_sudo,
            'answer':
            answer_sudo,
            'breadcrumb_pages': [{
                'id': page.id,
                'title': page.title,
            } for page in survey_sudo.page_ids],
            'format_datetime':
            lambda dt: format_datetime(request.env, dt, dt_format=False),
            'format_date':
            lambda date: format_date(request.env, date)
        }

        page_or_question_key = 'question' if survey_sudo.questions_layout == 'page_per_question' else 'page'

        # Bypass all if page_id is specified (comes from breadcrumb or previous button)
        if 'previous_page_id' in post:
            previous_page_or_question_id = int(post['previous_page_id'])
            new_previous_id = survey_sudo._previous_page_or_question_id(
                answer_sudo, previous_page_or_question_id)
            data.update({
                page_or_question_key:
                request.env['survey.question'].sudo().browse(
                    previous_page_or_question_id),
                'previous_page_id':
                new_previous_id
            })
            return data

        if answer_sudo.state == 'in_progress':
            page_or_question_id, is_last = survey_sudo.next_page_or_question(
                answer_sudo, answer_sudo.last_displayed_page_id.id
                if answer_sudo.last_displayed_page_id else 0)

            data.update({
                page_or_question_key: page_or_question_id,
            })
            if survey_sudo.questions_layout != 'one_page':
                data.update({
                    'previous_page_id':
                    survey_sudo._previous_page_or_question_id(
                        answer_sudo, page_or_question_id.id)
                })
            if is_last:
                data.update({'last': True})
        elif answer_sudo.state == 'done' or answer_sudo.is_time_limit_reached:  # Display success message
            return self._prepare_survey_finished_values(
                survey_sudo, answer_sudo)

        return data
Esempio n. 12
0
def format_datetime(env, dt, tz=False, dt_format='medium', lang_code=False):
    try:
        return tools.format_datetime(env,
                                     dt,
                                     tz=tz,
                                     dt_format=dt_format,
                                     lang_code=lang_code)
    except babel.core.UnknownLocaleError:
        return dt
    def _check_validity(self):
        """ Verifies the validity of the attendance record compared to the others from the same employee.
            For the same employee we must have :
                * maximum 1 "open" attendance record (without check_out)
                * no overlapping time slices with previous employee records
        """
        for attendance in self:
            # we take the latest attendance before our check_in time and check it doesn't overlap with ours
            last_attendance_before_check_in = self.env['hr.attendance'].search([
                ('employee_id', '=', attendance.employee_id.id),
                ('check_in', '<=', attendance.check_in),
                ('id', '!=', attendance.id),
            ], order='check_in desc', limit=1)
            if last_attendance_before_check_in and last_attendance_before_check_in.check_out and last_attendance_before_check_in.check_out > attendance.check_in:
                raise exceptions.ValidationError(_("Cannot create new attendance record for %(empl_name)s, the employee was already checked in on %(datetime)s") % {
                    'empl_name': attendance.employee_id.name,
                    'datetime': format_datetime(self.env, attendance.check_in, dt_format=False),
                })

            if not attendance.check_out:
                # if our attendance is "open" (no check_out), we verify there is no other "open" attendance
                no_check_out_attendances = self.env['hr.attendance'].search([
                    ('employee_id', '=', attendance.employee_id.id),
                    ('check_out', '=', False),
                    ('id', '!=', attendance.id),
                ], order='check_in desc', limit=1)
                if no_check_out_attendances:
                    raise exceptions.ValidationError(_("Cannot create new attendance record for %(empl_name)s, the employee hasn't checked out since %(datetime)s") % {
                        'empl_name': attendance.employee_id.name,
                        'datetime': format_datetime(self.env, no_check_out_attendances.check_in, dt_format=False),
                    })
            else:
                # we verify that the latest attendance with check_in time before our check_out time
                # is the same as the one before our check_in time computed before, otherwise it overlaps
                last_attendance_before_check_out = self.env['hr.attendance'].search([
                    ('employee_id', '=', attendance.employee_id.id),
                    ('check_in', '<', attendance.check_out),
                    ('id', '!=', attendance.id),
                ], order='check_in desc', limit=1)
                if last_attendance_before_check_out and last_attendance_before_check_in != last_attendance_before_check_out:
                    raise exceptions.ValidationError(_("Cannot create new attendance record for %(empl_name)s, the employee was already checked in on %(datetime)s") % {
                        'empl_name': attendance.employee_id.name,
                        'datetime': format_datetime(self.env, last_attendance_before_check_out.check_in, dt_format=False),
                    })
Esempio n. 14
0
 def _prepare_report_line(self,
                          quantity,
                          move_out=None,
                          move_in=None,
                          replenishment_filled=True,
                          product=False):
     timezone = self._context.get('tz')
     product = product or (move_out.product_id
                           if move_out else move_in.product_id)
     is_late = move_out.date < move_in.date if (move_out
                                                and move_in) else False
     return {
         'document_in':
         move_in._get_source_document() if move_in else False,
         'document_out':
         move_out._get_source_document() if move_out else False,
         'product': {
             'id': product.id,
             'display_name': product.display_name
         },
         'replenishment_filled':
         replenishment_filled,
         'uom_id':
         product.uom_id,
         'receipt_date':
         format_datetime(self.env, move_in.date, timezone, 'medium')
         if move_in else False,
         'delivery_date':
         format_datetime(self.env, move_out.date, timezone, 'medium')
         if move_out else False,
         'receipt_date_short':
         format_date(self.env, move_in.date) if move_in else False,
         'delivery_date_short':
         format_date(self.env, move_out.date) if move_out else False,
         'is_late':
         is_late,
         'quantity':
         quantity,
         'move_out':
         move_out,
         'move_in':
         move_in,
     }
Esempio n. 15
0
    def _prepare_statistics_email_values(self):
        """Return some statistics that will be displayed in the mailing statistics email.

        Each item in the returned list will be displayed as a table, with a title and
        1, 2 or 3 columns.
        """
        self.ensure_one()

        random_tip = self.env['digest.tip'].search(
            [('group_id.category_id', '=', self.env.ref('base.module_category_marketing_email_marketing').id)]
        )
        if random_tip:
            random_tip = random.choice(random_tip).tip_description

        formatted_date = tools.format_datetime(
            self.env, self.sent_date, self.user_id.tz, 'MMM dd, YYYY',  self.user_id.lang
        ) if self.sent_date else False

        web_base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')

        return {
            'title': _('24H Stats of mailing'),
            'sub_title': '"%s"' % self.subject,
            'top_button_label': _('More Info'),
            'top_button_url': url_join(web_base_url, f'/web#id={self.id}&model=mailing.mailing&view_type=form'),
            'kpi_data': [
                {
                    'kpi_fullname': _('Engagement on %i Emails Sent') % self.sent,
                    'kpi_action': None,
                    'kpi_col1': {
                        'value': f'{self.received_ratio}%',
                        'col_subtitle': '%s (%i)' % (_('RECEIVED'), self.delivered),
                    },
                    'kpi_col2': {
                        'value': f'{self.opened_ratio}%',
                        'col_subtitle': '%s (%i)' % (_('OPENED'), self.opened),
                    },
                    'kpi_col3': {
                        'value': f'{self.replied_ratio}%',
                        'col_subtitle': '%s (%i)' % (_('REPLIED'), self.replied),
                    },
                }, {
                    'kpi_fullname': _('Business Benefits on %i Emails Sent') % self.sent,
                    'kpi_action': None,
                    'kpi_col1': {},
                    'kpi_col2': {},
                    'kpi_col3': {},
                },
            ],
            'tips': [random_tip] if random_tip else False,
            'formatted_date': formatted_date,
        }
Esempio n. 16
0
 def get_date_range_str(self):
     self.ensure_one()
     today = fields.Datetime.now()
     event_date = self.event_begin_date
     diff = (event_date.date() - today.date())
     if diff.days <= 0:
         return _('today')
     elif diff.days == 1:
         return _('tomorrow')
     elif (diff.days < 7):
         return _('in %d days') % (diff.days, )
     elif (diff.days < 14):
         return _('next week')
     elif event_date.month == (today + relativedelta(months=+1)).month:
         return _('next month')
     else:
         return _('on %(date)s', date=format_datetime(self.env, self.event_begin_date, tz=self.event_id.date_tz, dt_format='medium'))
Esempio n. 17
0
    def survey_print(self,
                     survey_token,
                     review=False,
                     answer_token=None,
                     **post):
        '''Display an survey in printable view; if <answer_token> is set, it will
        grab the answers of the user_input_id that has <answer_token>.'''
        access_data = self._get_access_data(survey_token,
                                            answer_token,
                                            ensure_token=False)
        if access_data['validity_code'] is not True and (
                access_data['has_survey_access']
                or access_data['validity_code']
                not in ['token_required', 'survey_closed', 'survey_void']):
            return self._redirect_with_error(access_data,
                                             access_data['validity_code'])

        survey_sudo, answer_sudo = access_data['survey_sudo'], access_data[
            'answer_sudo']

        if survey_sudo.scoring_type == 'scoring_without_answers':
            return request.render("survey.survey_403_page",
                                  {'survey': survey_sudo})

        return request.render(
            'survey.survey_page_print', {
                'is_html_empty':
                is_html_empty,
                'review':
                review,
                'survey':
                survey_sudo,
                'answer':
                answer_sudo,
                'questions_to_display':
                answer_sudo._get_print_questions(),
                'scoring_display_correction':
                survey_sudo.scoring_type == 'scoring_with_answers'
                and answer_sudo,
                'format_datetime':
                lambda dt: format_datetime(request.env, dt, dt_format=False),
                'format_date':
                lambda date: format_date(request.env, date),
            })
Esempio n. 18
0
 def _make_dict_move(self, level, parent_id, move_line, unfoldable=False):
     res_model, res_id, ref = self._get_reference(move_line)
     dummy, is_used = self._get_linked_move_lines(move_line)
     data = [{
         'level': level,
         'unfoldable': unfoldable,
         'date': format_datetime(self.env, move_line.move_id.date, tz=False, dt_format=False),
         'parent_id': parent_id,
         'is_used': bool(is_used),
         'usage': self._get_usage(move_line),
         'model_id': move_line.id,
         'model': 'stock.move.line',
         'product_id': move_line.product_id.display_name,
         'product_qty_uom': "%s %s" % (self._quantity_to_str(move_line.product_uom_id, move_line.product_id.uom_id, move_line.qty_done), move_line.product_id.uom_id.name),
         'lot_name': move_line.lot_id.name,
         'lot_id': move_line.lot_id.id,
         'location_source': move_line.location_id.name,
         'location_destination': move_line.location_dest_id.name,
         'reference_id': ref,
         'res_id': res_id,
         'res_model': res_model}]
     return data
Esempio n. 19
0
    def _prepare_survey_data(self, survey_sudo, answer_sudo, **post):
        """ This method prepares all the data needed for template rendering, in function of the survey user input state.
            :param post:
                - previous_page_id : come from the breadcrumb or the back button and force the next questions to load
                                     to be the previous ones. """
        data = {
            'is_html_empty':
            is_html_empty,
            'survey':
            survey_sudo,
            'answer':
            answer_sudo,
            'breadcrumb_pages': [{
                'id': page.id,
                'title': page.title,
            } for page in survey_sudo.page_ids],
            'format_datetime':
            lambda dt: format_datetime(request.env, dt, dt_format=False),
            'format_date':
            lambda date: format_date(request.env, date)
        }
        if survey_sudo.questions_layout != 'page_per_question':
            triggering_answer_by_question, triggered_questions_by_answer, selected_answers = answer_sudo._get_conditional_values(
            )
            data.update({
                'triggering_answer_by_question': {
                    question.id: triggering_answer_by_question[question].id
                    for question in triggering_answer_by_question.keys()
                    if triggering_answer_by_question[question]
                },
                'triggered_questions_by_answer': {
                    answer.id: triggered_questions_by_answer[answer].ids
                    for answer in triggered_questions_by_answer.keys()
                },
                'selected_answers': selected_answers.ids
            })

        if not answer_sudo.is_session_answer and survey_sudo.is_time_limited and answer_sudo.start_datetime:
            data.update({
                'server_time': fields.Datetime.now(),
                'timer_start': answer_sudo.start_datetime.isoformat(),
                'time_limit_minutes': survey_sudo.time_limit
            })

        page_or_question_key = 'question' if survey_sudo.questions_layout == 'page_per_question' else 'page'

        # Bypass all if page_id is specified (comes from breadcrumb or previous button)
        if 'previous_page_id' in post:
            previous_page_or_question_id = int(post['previous_page_id'])
            new_previous_id = survey_sudo._get_next_page_or_question(
                answer_sudo, previous_page_or_question_id, go_back=True).id
            page_or_question = request.env['survey.question'].sudo().browse(
                previous_page_or_question_id)
            data.update({
                page_or_question_key:
                page_or_question,
                'previous_page_id':
                new_previous_id,
                'has_answered':
                answer_sudo.user_input_line_ids.filtered(
                    lambda line: line.question_id.id == new_previous_id),
                'can_go_back':
                survey_sudo._can_go_back(answer_sudo, page_or_question),
            })
            return data

        if answer_sudo.state == 'in_progress':
            if answer_sudo.is_session_answer:
                next_page_or_question = survey_sudo.session_question_id
            else:
                next_page_or_question = survey_sudo._get_next_page_or_question(
                    answer_sudo, answer_sudo.last_displayed_page_id.id
                    if answer_sudo.last_displayed_page_id else 0)

                if next_page_or_question:
                    data.update({
                        'survey_last':
                        survey_sudo._is_last_page_or_question(
                            answer_sudo, next_page_or_question)
                    })

            if answer_sudo.is_session_answer and next_page_or_question.is_time_limited:
                data.update({
                    'timer_start':
                    survey_sudo.session_question_start_time.isoformat(),
                    'time_limit_minutes':
                    next_page_or_question.time_limit / 60
                })

            data.update({
                page_or_question_key:
                next_page_or_question,
                'has_answered':
                answer_sudo.user_input_line_ids.filtered(
                    lambda line: line.question_id == next_page_or_question),
                'can_go_back':
                survey_sudo._can_go_back(answer_sudo, next_page_or_question),
            })
            if survey_sudo.questions_layout != 'one_page':
                data.update({
                    'previous_page_id':
                    survey_sudo._get_next_page_or_question(
                        answer_sudo, next_page_or_question.id, go_back=True).id
                })
        elif answer_sudo.state == 'done' or answer_sudo.survey_time_limit_reached:
            # Display success message
            return self._prepare_survey_finished_values(
                survey_sudo, answer_sudo)

        return data
Esempio n. 20
0
    def _planning_get(self, planning_token, employee_token, message=False):
        employee_sudo = request.env['hr.employee'].sudo().search(
            [('employee_token', '=', employee_token)], limit=1)
        if not employee_sudo:
            return

        planning_sudo = request.env['planning.planning'].sudo().search(
            [('access_token', '=', planning_token)], limit=1)
        if not planning_sudo:
            return

        employee_tz = pytz.timezone(employee_sudo.tz or 'UTC')
        employee_fullcalendar_data = []
        open_slots = []

        if planning_sudo.include_unassigned:
            planning_slots = planning_sudo.slot_ids.filtered(
                lambda s: s.employee_id == employee_sudo or not s.employee_id)
        else:
            planning_slots = planning_sudo.slot_ids.filtered(
                lambda s: s.employee_id == employee_sudo)

        planning_slots = planning_slots._filter_slots_front_end(employee_sudo)

        # filter and format slots
        slots_start_datetime = []
        slots_end_datetime = []
        # Default values. In case of missing slots (an error message is shown)
        # Avoid errors if the _work_intervals are not defined.
        checkin_min = 8
        checkout_max = 18
        planning_values = {
            'employee_slots_fullcalendar_data': employee_fullcalendar_data,
            'open_slots_ids': open_slots,
            'planning_planning_id': planning_sudo,
            'employee': employee_sudo,
            'employee_token': employee_token,
            'planning_token': planning_token,
            'no_data': True
        }
        for slot in planning_slots:
            if planning_sudo.start_datetime <= slot.start_datetime <= planning_sudo.end_datetime:
                # We only display slots starting in the planning_sudo range
                # If a slot is moved outside the planning_sudo range, the url remains valid but the slot is hidden.
                if slot.employee_id:
                    employee_fullcalendar_data.append({
                        'title':
                        '%s%s' % (slot.role_id.name or _("Shift"),
                                  u' \U0001F4AC' if slot.name else ''),
                        'start':
                        str(
                            pytz.utc.localize(slot.start_datetime).astimezone(
                                employee_tz).replace(tzinfo=None)),
                        'end':
                        str(
                            pytz.utc.localize(slot.end_datetime).astimezone(
                                employee_tz).replace(tzinfo=None)),
                        'color':
                        self._format_planning_shifts(slot.role_id.color),
                        'alloc_hours':
                        '%d:%02d' % (int(slot.allocated_hours),
                                     round(slot.allocated_hours % 1 * 60)),
                        'alloc_perc':
                        slot.allocated_percentage,
                        'slot_id':
                        slot.id,
                        'note':
                        slot.name,
                        'allow_self_unassign':
                        slot.allow_self_unassign,
                        'role':
                        slot.role_id.name,
                    })
                    # We add the slot start and stop into the list after converting it to the timezone of the employee
                    slots_start_datetime.append(
                        pytz.utc.localize(slot.start_datetime).astimezone(
                            employee_tz).replace(tzinfo=None))
                    slots_end_datetime.append(
                        pytz.utc.localize(
                            slot.end_datetime).astimezone(employee_tz).replace(
                                tzinfo=None))
                elif not slot.is_past and (not employee_sudo.planning_role_ids
                                           or not slot.role_id or slot.role_id
                                           in employee_sudo.planning_role_ids):
                    open_slots.append(slot)
        # Calculation of the events to define the default calendar view:
        # If all the events are the same day/week the default view is week. Else, the month is displayed
        min_start_datetime = slots_start_datetime and min(
            slots_start_datetime) or planning_sudo.start_datetime
        max_end_datetime = slots_end_datetime and max(
            slots_end_datetime) or planning_sudo.end_datetime
        if min_start_datetime.isocalendar()[1] == max_end_datetime.isocalendar(
        )[1]:
            # isocalendar returns (year, week number, and weekday)
            default_view = 'timeGridWeek'
        else:
            default_view = 'dayGridMonth'
        # Calculation of the minTime and maxTime values in timeGridDay and timeGridWeek
        # We want to avoid displaying overly large hours range each day or hiding slots outside the
        # normal working hours
        attendances = employee_sudo.resource_calendar_id._work_intervals(
            pytz.utc.localize(planning_sudo.start_datetime),
            pytz.utc.localize(planning_sudo.end_datetime),
            resource=employee_sudo.resource_id,
            tz=employee_tz)
        if attendances and attendances._items:
            checkin_min = min(map(
                lambda a: a[0].hour,
                attendances._items))  # hour in the timezone of the employee
            checkout_max = max(map(lambda a: a[1].hour,
                                   attendances._items))  # idem
        # We calculate the earliest/latest hour of the slots. It is used in the weekview.
        if slots_start_datetime and slots_end_datetime:
            event_hour_min = min(map(lambda s: s.hour,
                                     slots_start_datetime))  # idem
            event_hour_max = max(map(lambda s: s.hour,
                                     slots_end_datetime))  # idem
            mintime_weekview, maxtime_weekview = self._get_hours_intervals(
                checkin_min, checkout_max, event_hour_min, event_hour_max)
        else:
            # Fallback when no slot is available. Still needed because open slots display a calendar
            mintime_weekview, maxtime_weekview = checkin_min, checkout_max
        defaut_start = pytz.utc.localize(
            planning_sudo.start_datetime).astimezone(employee_tz).replace(
                tzinfo=None)
        if employee_fullcalendar_data or open_slots:
            planning_values.update({
                'employee_slots_fullcalendar_data':
                employee_fullcalendar_data,
                'open_slots_ids':
                open_slots,
                # fullcalendar does not understand complex iso code like fr_BE
                'locale':
                get_lang(request.env).iso_code.split("_")[0],
                'format_datetime':
                lambda dt, dt_format: tools.format_datetime(
                    request.env, dt, tz=employee_tz.zone, dt_format=dt_format),
                'notification_text':
                message in ['assign', 'unassign', 'already_assign'],
                'message_slug':
                message,
                'has_role':
                any([s.role_id.id for s in open_slots]),
                'has_note':
                any([s.name for s in open_slots]),
                # start_datetime and end_datetime are used in the banner. This ensure that these values are
                # coherent with the sended mail.
                'start_datetime':
                planning_sudo.start_datetime,
                'end_datetime':
                planning_sudo.end_datetime,
                'mintime':
                '%02d:00:00' % mintime_weekview,
                'maxtime':
                '%02d:00:00' % maxtime_weekview,
                'default_view':
                default_view,
                'default_start':
                defaut_start.date(),
                'no_data':
                False
            })
        return planning_values
Esempio n. 21
0
    def render_template(self, template_txt, model, res_ids,
                        post_process=False):
        """ Render the given template text, replace mako expressions
        ``${expr}`` with the result of evaluating these expressions
        with an evaluation context containing:

         - ``user``: browse_record of the current user
         - ``object``: record of the document record this mail is related to
         - ``context``: the context passed to the mail composition wizard

        :param str template_txt: the template text to render
        :param str model: model name of the document record
            this mail is related to.
        :param int res_ids: list of ids of document records
            those mails are related to.
        """
        multi_mode = True
        if isinstance(res_ids, (int, long)):
            multi_mode = False
            res_ids = [res_ids]

        results = dict.fromkeys(res_ids, u"")

        # try to load the template
        try:
            mako_env = jinja_safe_template_env if self._context.get('safe') \
                else jinja_template_env
            template = mako_env.from_string(tools.ustr(template_txt))
        except Exception:
            _logger.info("Failed to load template %r", template_txt,
                         exc_info=True)
            return multi_mode and results or results[res_ids[0]]

        # prepare template variables
        # filter to avoid browsing [None]
        records = self.env[model].browse(list(filter(None, res_ids)))
        res_to_rec = dict.fromkeys(res_ids, None)
        for record in records:
            res_to_rec[record.id] = record
        variables = {
            'format_date': lambda date, format=False, context=self._context:
                format_date(self.env, date, format),
            'format_datetime': lambda dt, tz=False, format=False,
                context=self._context:
                tools.format_datetime(self.env, dt, tz, format),
            'format_amount': lambda amount, currency, context=self._context:
                tools.format_amount(self.env, amount, currency),
            'format_numeric': lambda value, column, options=None:
                self.format_numeric(value, column, options),  # Added by Smile
            'user': self.env.user,
            'ctx': self._context,  # context kw would clash with mako internals
        }
        for res_id, record in res_to_rec.items():
            variables['object'] = record
            try:
                render_result = template.render(variables)
            except Exception:
                _logger.info("Failed to render template %r using values %r"
                             % (template, variables), exc_info=True)
                raise UserError(
                    _("Failed to render template %r using values %r")
                    % (template, variables))
            if render_result == u"False":
                render_result = u""
            results[res_id] = render_result

        if post_process:
            for res_id, result in results.items():
                results[res_id] = self.render_post_process(result)

        return multi_mode and results or results[res_ids[0]]
Esempio n. 22
0
    def planning(self,
                 planning_token,
                 employee_token,
                 message=False,
                 **kwargs):
        """ Displays an employee's calendar and the current list of open shifts """
        employee_sudo = request.env['hr.employee'].sudo().search(
            [('employee_token', '=', employee_token)], limit=1)
        if not employee_sudo:
            return request.not_found()

        planning_sudo = request.env['planning.planning'].sudo().search(
            [('access_token', '=', planning_token)], limit=1)
        if not planning_sudo:
            return request.not_found()

        employee_tz = pytz.timezone(employee_sudo.tz or 'UTC')
        employee_fullcalendar_data = []
        open_slots = []
        planning_slots = []

        # domain of slots to display
        domain = planning_sudo._get_domain_slots()[planning_sudo.id]
        if planning_sudo.include_unassigned:
            domain = expression.AND([
                domain,
                [
                    '|', ('employee_id', '=', employee_sudo.id),
                    ('employee_id', '=', False)
                ]
            ])
        else:
            domain = expression.AND(
                [domain, [('employee_id', '=', employee_sudo.id)]])

        planning_slots = request.env['planning.slot'].sudo().search(domain)

        # filter and format slots
        for slot in planning_slots:
            if slot.employee_id:
                employee_fullcalendar_data.append({
                    'title':
                    '%s%s' %
                    (slot.role_id.name, u' \U0001F4AC' if slot.name else ''),
                    'start':
                    str(
                        pytz.utc.localize(slot.start_datetime).astimezone(
                            employee_tz).replace(tzinfo=None)),
                    'end':
                    str(
                        pytz.utc.localize(
                            slot.end_datetime).astimezone(employee_tz).replace(
                                tzinfo=None)),
                    'color':
                    self._format_planning_shifts(slot.role_id.color),
                    'alloc_hours':
                    slot.allocated_hours,
                    'slot_id':
                    slot.id,
                    'note':
                    slot.name,
                    'allow_self_unassign':
                    slot.allow_self_unassign
                })
            else:
                open_slots.append(slot)

        return request.render(
            'planning.period_report_template', {
                'employee_slots_fullcalendar_data':
                employee_fullcalendar_data,
                'open_slots_ids':
                open_slots,
                'planning_slots_ids':
                planning_slots,
                'planning_planning_id':
                planning_sudo,
                'employee':
                employee_sudo,
                'format_datetime':
                lambda dt, dt_format: tools.format_datetime(
                    request.env, dt, dt_format=dt_format),
                'message_slug':
                message,
            })
Esempio n. 23
0
    def survey_display_page(self, survey_token, answer_token, **post):
        access_data = self._get_access_data(survey_token,
                                            answer_token,
                                            ensure_token=True)
        if access_data['validity_code'] is not True:
            return self._redirect_with_error(access_data,
                                             access_data['validity_code'])

        survey_sudo, answer_sudo = access_data['survey_sudo'], access_data[
            'answer_sudo']

        data = {
            'format_datetime':
            lambda dt: format_datetime(request.env, dt, dt_format=False),
            'format_date':
            lambda date: format_date(request.env, date)
        }

        page_or_question_key = 'question' if survey_sudo.questions_layout == 'page_per_question' else 'page'

        # Bypass all if page_id is specified (comes from breadcrumb or previous button)
        if 'previous_page_id' in post:
            previous_page_or_question_id = int(post['previous_page_id'])
            new_previous_id = survey_sudo._previous_page_or_question_id(
                answer_sudo, previous_page_or_question_id)
            data.update({
                'survey':
                survey_sudo,
                page_or_question_key:
                request.env['survey.question'].sudo().browse(
                    previous_page_or_question_id),
                'answer':
                answer_sudo,
                'previous_page_id':
                new_previous_id
            })
            return request.render('survey.survey_page_main', data)

        if survey_sudo.is_time_limited and not answer_sudo.start_datetime:
            # init start date when user starts filling in the survey
            answer_sudo.write({'start_datetime': fields.Datetime.now()})

        # Select the right page
        if answer_sudo.state == 'new':  # First page
            page_or_question_id, is_last = survey_sudo.next_page_or_question(
                answer_sudo, 0)
            data.update({
                'survey': survey_sudo,
                page_or_question_key: page_or_question_id,
                'answer': answer_sudo,
            })
            if is_last:
                data.update({'last': True})
            return request.render('survey.survey_page_main', data)
        elif answer_sudo.state == 'done':  # Display success message
            return request.render(
                'survey.survey_closed_finished',
                self._prepare_survey_finished_values(survey_sudo, answer_sudo))
        elif answer_sudo.state == 'skip':
            page_or_question_id, is_last = survey_sudo.next_page_or_question(
                answer_sudo, answer_sudo.last_displayed_page_id.id)
            previous_id = survey_sudo._previous_page_or_question_id(
                answer_sudo, page_or_question_id.id)

            data.update({
                'survey': survey_sudo,
                page_or_question_key: page_or_question_id,
                'answer': answer_sudo,
                'previous_page_id': previous_id
            })
            if is_last:
                data.update({'last': True})

            return request.render('survey.survey_page_main', data)
        else:
            return request.render("survey.survey_403_page",
                                  {'survey': survey_sudo})