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
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 ""
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
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'] })
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'))
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
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
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
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
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]
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
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), })
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, }
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, }
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'))
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), })
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
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
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
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]]
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, })
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})