Пример #1
0
 def _execute_command_lead(self, **kwargs):
     partner = self.env.user.partner_id
     key = kwargs['body']
     channel_partners = self.env['mail.channel.partner'].search([
         ('partner_id', '!=', partner.id),
         ('channel_id', '=', self.id)], limit=1
     )
     if key.strip() == '/lead':
         msg = self._define_command_lead()['help']
     else:
         description = ''.join(
             '%s: %s\n' % (message.author_id.name or self.anonymous_name, message.body)
             for message in self.channel_message_ids.sorted('id')
         )
         lead = self.env['crm.lead'].create({
             'name': html2plaintext(key[5:]),
             'partner_id': channel_partners.partner_id.id,
             'user_id': None,
             'team_id': None,
             'description': html2plaintext(description),
             'referred': partner.name
         })
         lead._onchange_partner_id()
         msg = _('Created a new lead: <a href="#" data-oe-id="%s" data-oe-model="crm.lead">%s</a>') % (lead.id, lead.name)
     self._send_transient_message(partner, msg)
Пример #2
0
 def _compute_description(self):
     for message in self:
         if message.subject:
             message.description = message.subject
         else:
             plaintext_ct = '' if not message.body else html2plaintext(message.body)
             message.description = plaintext_ct[:30] + '%s' % (' [...]' if len(plaintext_ct) >= 30 else '')
Пример #3
0
 def _compute_teaser(self):
     for blog_post in self:
         if blog_post.teaser_manual:
             blog_post.teaser = blog_post.teaser_manual
         else:
             content = html2plaintext(blog_post.content).replace('\n', ' ')
             blog_post.teaser = ' '.join(filter(None, content.split(' '))[:50]) + '...'
Пример #4
0
 def _compute_teaser(self):
     for blog_post in self:
         if blog_post.teaser_manual:
             blog_post.teaser = blog_post.teaser_manual
         else:
             content = html2plaintext(blog_post.content).replace('\n', ' ')
             blog_post.teaser = content[:150] + '...'
Пример #5
0
 def _compute_description(self):
     for message in self:
         if message.subject:
             message.description = message.subject
         else:
             plaintext_ct = "" if not message.body else html2plaintext(message.body)
             message.description = plaintext_ct[:30] + "%s" % (" [...]" if len(plaintext_ct) >= 30 else "")
Пример #6
0
 def _compute_teaser(self):
     for blog_post in self:
         if blog_post.teaser_manual:
             blog_post.teaser = blog_post.teaser_manual
         else:
             content = html2plaintext(blog_post.content).replace("\n", " ")
             blog_post.teaser = " ".join(filter(None, content.split(" "))[:50]) + "..."
Пример #7
0
 def setHtmlFallbackText(self, padID, html):
     try:
         # Prevents malformed HTML errors
         html_wellformed = '<html><body>' + html + '</body></html>'
         return self.setHtml(padID, html_wellformed)
     except Exception:
         _logger.exception('Falling back to setText. SetHtml failed with message:')
         return self.setText(padID, html2plaintext(html).encode('UTF-8'))
Пример #8
0
 def _compute_teaser(self):
     for blog_post in self:
         if blog_post.teaser_manual:
             blog_post.teaser = blog_post.teaser_manual
         else:
             content = html2plaintext(blog_post.content).replace('\n', ' ')
             blog_post.teaser = ' '.join(itertools.islice(
                 (c for c in content.split(' ') if c),
                 50
             )) + '...'
Пример #9
0
 def action_create_calendar_event(self):
     self.ensure_one()
     action = self.env.ref('calendar.action_calendar_event').read()[0]
     action['context'] = {
         'default_activity_type_id': self.activity_type_id.id,
         'default_res_id': self.env.context.get('default_res_id'),
         'default_res_model': self.env.context.get('default_res_model'),
         'default_name': self.summary,
         'default_description': self.note and tools.html2plaintext(self.note) or '',
         'default_activity_ids': [(6, 0, self.ids)],
     }
     return action
Пример #10
0
    def pad_generate_url(self):
        company = self.env.user.sudo().company_id

        pad = {
            "server": company.pad_server,
            "key": company.pad_key,
        }

        # make sure pad server in the form of http://hostname
        if not pad["server"]:
            return pad
        if not pad["server"].startswith('http'):
            pad["server"] = 'http://' + pad["server"]
        pad["server"] = pad["server"].rstrip('/')
        # generate a salt
        s = string.ascii_uppercase + string.digits
        salt = ''.join([s[random.SystemRandom().randint(0, len(s) - 1)] for i in range(10)])
        # path
        # etherpad hardcodes pad id length limit to 50
        path = '-%s-%s' % (self._name, salt)
        path = '%s%s' % (self.env.cr.dbname.replace('_', '-')[0:50 - len(path)], path)
        # contruct the url
        url = '%s/p/%s' % (pad["server"], path)

        # if create with content
        if self.env.context.get('field_name') and self.env.context.get('model') and self.env.context.get('object_id'):
            myPad = EtherpadLiteClient(pad["key"], pad["server"] + '/api')
            try:
                myPad.createPad(path)
            except IOError:
                raise UserError(_("Pad creation failed, either there is a problem with your pad server URL or with your connection."))

            # get attr on the field model
            model = self.env[self.env.context["model"]]
            field = model._fields[self.env.context['field_name']]
            real_field = field.pad_content_field

            # get content of the real field
            for record in model.browse([self.env.context["object_id"]]):
                if record[real_field]:
                    myPad.setText(path, (html2plaintext(record[real_field]).encode('utf-8')))
                    # Etherpad for html not functional
                    # myPad.setHTML(path, record[real_field])

        return {
            "server": pad["server"],
            "path": path,
            "url": url,
        }
Пример #11
0
    def _set_pad_value(self, vals):

        # Update the pad if the `pad_content_field` is modified
        for k, field in self._fields.items():
            if hasattr(field, 'pad_content_field') and vals.get(field.pad_content_field) and self[k]:
                company = self.env.user.sudo().company_id
                myPad = EtherpadLiteClient(company.pad_key, company.pad_server + '/api')
                path = self[k].split('/p/')[1]
                myPad.setText(path, (html2plaintext(vals[field.pad_content_field]).encode('utf-8')))

        # Update the `pad_content_field` if the pad is modified
        for k, v in list(vals.items()):
            field = self._fields[k]
            if hasattr(field, 'pad_content_field'):
                vals[field.pad_content_field] = self.pad_get_content(v)
Пример #12
0
 def _get_discussion_detail(self, ids, publish=False, **post):
     values = []
     for message in request.env['mail.message'].sudo().browse(ids):
         values.append({
             "id": message.id,
             "author_name": message.author_id.name,
             "author_image": message.author_id.image and \
                 (b"data:image/png;base64,%s" % message.author_id.image) or \
                 b'/website_blog/static/src/img/anonymous.png',
             "date": message.date,
             'body': html2plaintext(message.body),
             'website_published' : message.website_published,
             'publish' : publish,
         })
     return values
Пример #13
0
    def send_get_email_dict(self, partner=None):
        """Return a dictionary for specific email values, depending on a
        partner, or generic to the whole recipients given by mail.email_to.

            :param Model partner: specific recipient partner
        """
        self.ensure_one()
        body = self.send_get_mail_body(partner=partner)
        body_alternative = tools.html2plaintext(body)
        res = {
            'body': body,
            'body_alternative': body_alternative,
            'email_to': self.send_get_mail_to(partner=partner),
        }
        return res
Пример #14
0
    def _chatbot_find_customer_values_in_messages(self, step_type_to_field):
        """
        Look for user's input in the channel's messages based on a dictionary
        mapping the step_type to the field name of the model it will be used on.

        :param dict step_type_to_field: a dict of step types to customer fields
            to fill, like : {'question_email': 'email_from', 'question_phone': 'mobile'}
        """
        values = {}
        filtered_message_ids = self.chatbot_message_ids.filtered(
            lambda m: m.script_step_id.step_type in step_type_to_field.keys()
        )
        for message_id in filtered_message_ids:
            field_name = step_type_to_field[message_id.script_step_id.step_type]
            if not values.get(field_name):
                values[field_name] = html2plaintext(message_id.user_raw_answer or '')

        return values
Пример #15
0
 def send_pos_message(self):
     try:
         sms_account = self.env['sms.account'].search([], limit=1)
         pos_order = self.env['sms.template'].search(
             [('model_id', '=', 'pos.order')], limit=1)
         if pos_order and sms_account:
             for order in self:
                 if order.partner_id and order.partner_id.mobile or order.partner_id and order.partner_id.phone:
                     number = order.partner_id.mobile or order.partner_id.phone
                     to_mobile = number.replace(" ", "")
                     render_msg = self.env['mail.template'].render_template(
                         pos_order.template_body, 'pos.order', order.id)
                     message = tools.html2plaintext(render_msg)
                     sms_account.send_message(to_mobile, message,
                                              self._name, order.id)
     except Exception as e:
         _logger.error("Error in pos sms template :\n{}".format(e))
     return True
Пример #16
0
 def _create_from_table4(self, html_body):
     """ This Method returns plain text dict for Template
     """
     plain_text_body = tools.html2plaintext(html_body)
     newdict = {}
     aa = find_between_r(plain_text_body, '- Aanvraag',
                         '- Koopwijzer').split('\n')
     if len(aa) == 1:
         return {}
     l = []
     for i in aa:
         if i and i != '\n':
             l.append(i)
     for i in l:
         splitted = i.split(':')
         if len(splitted) > 1:
             newdict[splitted[0] + ':'] = splitted[1]
     return newdict
Пример #17
0
 def action_create_calendar_event(self):
     self.ensure_one()
     action = self.env["ir.actions.actions"]._for_xml_id(
         "calendar.action_calendar_event")
     action['context'] = {
         'default_activity_type_id':
         self.activity_type_id.id,
         'default_res_id':
         self.env.context.get('default_res_id'),
         'default_res_model':
         self.env.context.get('default_res_model'),
         'default_name':
         self.summary or self.res_name,
         'default_description':
         self.note and tools.html2plaintext(self.note).strip() or '',
         'default_activity_ids': [(6, 0, self.ids)],
     }
     return action
Пример #18
0
 def _recompute_completion(self):
     res = super(SlideChannelPartner, self)._recompute_completion()
     partner_has_completed = {channel_partner.partner_id.id: channel_partner.channel_id for channel_partner in self}
     employees = self.env['hr.employee'].sudo().search([('user_id.partner_id', 'in', list(partner_has_completed.keys()))])
     for employee in employees:
         line_type = self.env.ref('hr_skills_slides.resume_type_training', raise_if_not_found=False)
         channel = partner_has_completed[employee.user_id.partner_id.id]
         self.env['hr.resume.line'].create({
             'employee_id': employee.id,
             'name': channel.name,
             'date_start': fields.Date.today(),
             'date_end': fields.Date.today(),
             'description': html2plaintext(channel.description),
             'line_type_id': line_type and line_type.id,
             'display_type': 'course',
             'channel_id': channel.id
         })
     return res
Пример #19
0
    def _validate_email(self, email_address, mail_channel):
        email_address = html2plaintext(email_address)
        email_normalized = email_normalize(email_address)

        posted_message = False
        error_message = False
        if not email_normalized:
            error_message = _(
                "'%(input_email)s' does not look like a valid email. Can you please try again?",
                input_email=email_address)
            posted_message = mail_channel._chatbot_post_message(
                self, plaintext2html(error_message))

        return {
            'success': bool(email_normalized),
            'posted_message': posted_message,
            'error_message': error_message,
        }
Пример #20
0
    def _set_pad_value(self, vals):

        # Update the pad if the `pad_content_field` is modified
        for k, field in self._fields.items():
            if hasattr(field, 'pad_content_field') and vals.get(
                    field.pad_content_field) and self[k]:
                company = self.env.user.sudo().company_id
                myPad = EtherpadLiteClient(company.pad_key,
                                           company.pad_server + '/api')
                path = self[k].split('/p/')[1]
                myPad.setText(path, (html2plaintext(
                    vals[field.pad_content_field]).encode('utf-8')))

        # Update the `pad_content_field` if the pad is modified
        for k, v in list(vals.items()):
            field = self._fields[k]
            if hasattr(field, 'pad_content_field'):
                vals[field.pad_content_field] = self.pad_get_content(v)
Пример #21
0
    def _send_prepare_values(self, partner=None):
        """Return a dictionary for specific email values, depending on a
        partner, or generic to the whole recipients given by mail.email_to.

            :param Model partner: specific recipient partner
        """
        self.ensure_one()
        body = self._send_prepare_body()
        body_alternative = tools.html2plaintext(body)
        if partner:
            email_to = [formataddr((partner.name or 'False', partner.email or 'False'))]
        else:
            email_to = tools.email_split_and_format(self.email_to)
        res = {
            'body': body,
            'body_alternative': body_alternative,
            'email_to': email_to,
        }
        return res
 def action_create_calendar_event(self):
     self.ensure_one()
     action = self.env.ref('calendar.action_calendar_event').read()[0]
     action['context'] = {
         'default_activity_type_id':
         self.activity_type_id.id,
         'default_res_id':
         self.env.context.get('default_res_id'),
         'default_res_model':
         self.env.context.get('default_res_model'),
         'default_name':
         self.summary or self.res_name,
         'default_description':
         self.note and tools.html2plaintext(self.note).strip() or '',
         'default_activity_ids': [(6, 0, self.ids)],
         'initial_date':
         self.date_deadline,
     }
     return action
Пример #23
0
 def message_new(self, msg, custom_values=None):
     """ Overrides mail_thread message_new that is called by the mailgateway
         through message_process.
         This override updates the document according to the email.
     """
     if custom_values is None:
         custom_values = {}
     desc = html2plaintext(msg.get("body")) if msg.get("body") else ""
     defaults = {
         "name": msg.get("subject") or _("No Subject"),
         "description": desc,
         "email_from": msg.get("from"),
         "email_cc": msg.get("cc"),
         "partner_id": msg.get("author_id", False),
     }
     if msg.get("priority"):
         defaults["priority"] = msg.get("priority")
     defaults.update(custom_values)
     return super(CrmClaim, self).message_new(msg, custom_values=defaults)
Пример #24
0
 def message_new(self, msg, custom_values=None):
     """ Overrides mail_thread message_new that is called by the mailgateway
         through message_process.
         This override updates the document according to the email.
     """
     if custom_values is None:
         custom_values = {}
     desc = html2plaintext(msg.get('body')) if msg.get('body') else ''
     defaults = {
         'name': msg.get('subject') or _("No Subject"),
         'description': desc,
         'email_from': msg.get('from'),
         'email_cc': msg.get('cc'),
         'partner_id': msg.get('author_id', False),
     }
     if msg.get('priority'):
         defaults['priority'] = msg.get('priority')
     defaults.update(custom_values)
     return super(CrmClaim, self).message_new(msg, custom_values=defaults)
Пример #25
0
    def _mark_done(self):
        """ Will add certification to employee's resumé if
        - The survey is a certification
        - The user is linked to an employee
        - The user succeeded the test """

        super(SurveyUserInput, self)._mark_done()

        certificate_user_inputs = self.filtered(
            lambda user_input: user_input.survey_id.certificate and user_input.
            quizz_passed)
        partner_has_completed = {
            user_input.partner_id.id: user_input.survey_id
            for user_input in certificate_user_inputs
        }
        employees = self.env['hr.employee'].sudo().search([
            ('user_id.partner_id', 'in',
             certificate_user_inputs.mapped('partner_id').ids)
        ])
        for employee in employees:
            line_type = self.env.ref(
                'hr_skills_survey.resume_type_certification',
                raise_if_not_found=False)
            survey = partner_has_completed.get(employee.user_id.partner_id.id)
            self.env['hr.resume.line'].create({
                'employee_id':
                employee.id,
                'name':
                survey.title,
                'date_start':
                fields.Date.today(),
                'date_end':
                fields.Date.today(),
                'description':
                html2plaintext(survey.description),
                'line_type_id':
                line_type and line_type.id,
                'display_type':
                'certification',
                'survey_id':
                survey.id
            })
Пример #26
0
    def message_new(self, msg_dict, custom_values=None):
        """ Overrides mail_thread message_new that is called by the mailgateway
            through message_process.
            This override updates the document according to the email.
        """
        self = self.with_context(default_user_id=False)

        if custom_values is None:
            custom_values = {}
        defaults = {
            'name':  msg_dict.get('subject') or _("No Subject"),
            'email': msg_dict.get('from'),
            'email_cc': msg_dict.get('cc'),
            'partner_id': msg_dict.get('author_id', False),
        }
        if 'body' in msg_dict:
            body_msg = tools.html2plaintext(msg_dict['body'])
            defaults.update(description=body_msg)
        defaults.update(custom_values)
        return super(HelpdeskSupport, self).message_new(msg_dict, custom_values=defaults)
Пример #27
0
 def send_account_invoice_sms(self):
     message = ''
     try:
         sms_account = self.env['sms.account'].search([], limit=1)
         template_rec = self.env['sms.template'].search(
             [('model_id', '=', 'account.invoice')], limit=1)
         if sms_account and template_rec:
             for invoice in self:
                 if invoice.partner_id and invoice.partner_id.mobile or invoice.partner_id and invoice.partner_id.phone:
                     number = invoice.partner_id.mobile or invoice.partner_id.phone
                     to_mobile = number.replace(" ", "")
                     render_msg = self.env['mail.template'].render_template(
                         template_rec.template_body, 'account.invoice',
                         invoice.id)
                     message = tools.html2plaintext(render_msg)
                     sms_account.send_message(to_mobile, message,
                                              self._name, invoice.id)
     except Exception as e:
         _logger.error("Error in invoice sms template:\n{}".format(e))
     return True
Пример #28
0
    def calendar_appointment_view(self, access_token, edit=False, message=False, **kwargs):
        event = request.env['calendar.event'].sudo().search([('access_token', '=', access_token)], limit=1)
        if not event:
            return request.not_found()
        timezone = request.session.get('timezone')
        if not timezone:
            timezone = request.env.context.get('tz') or event.appointment_type_id.appointment_tz or event.partner_ids and event.partner_ids[0].tz
            request.session['timezone'] = timezone
        tz_session = pytz.timezone(timezone)

        date_start_suffix = ""
        format_func = format_datetime
        if not event.allday:
            url_date_start = fields.Datetime.from_string(event.start_datetime).strftime('%Y%m%dT%H%M%SZ')
            url_date_stop = fields.Datetime.from_string(event.stop_datetime).strftime('%Y%m%dT%H%M%SZ')
            date_start = fields.Datetime.from_string(event.start_datetime).replace(tzinfo=pytz.utc).astimezone(tz_session)
        else:
            url_date_start = url_date_stop = fields.Date.from_string(event.start_date).strftime('%Y%m%d')
            date_start = fields.Date.from_string(event.start_date)
            format_func = format_date
            date_start_suffix = _(', All Day')

        locale = get_lang(request.env).code
        day_name = format_func(date_start, 'EEE', locale=locale)
        date_start = day_name + ' ' + format_func(date_start, locale=locale) + date_start_suffix
        details = event.appointment_type_id and event.appointment_type_id.message_confirmation or event.description or ''
        params = url_encode({
            'action': 'TEMPLATE',
            'text': event.name,
            'dates': url_date_start + '/' + url_date_stop,
            'details': html2plaintext(details.encode('utf-8'))
        })
        google_url = 'https://www.google.com/calendar/render?' + params

        return request.render("website_calendar.appointment_validated", {
            'event': event,
            'datetime_start': date_start,
            'google_url': google_url,
            'message': message,
            'edit': edit,
        })
Пример #29
0
    def _message_sms(self, body, subtype_id=False, partner_ids=False, number_field=False,
                     sms_numbers=None, sms_pid_to_number=None, **kwargs):
        """ Main method to post a message on a record using SMS-based notification
        method.

        :param body: content of SMS;
        :param subtype_id: mail.message.subtype used in mail.message associated
          to the sms notification process;
        :param partner_ids: if set is a record set of partners to notify;
        :param number_field: if set is a name of field to use on current record
          to compute a number to notify;
        :param sms_numbers: see ``_notify_record_by_sms``;
        :param sms_pid_to_number: see ``_notify_record_by_sms``;
        """
        self.ensure_one()
        sms_pid_to_number = sms_pid_to_number if sms_pid_to_number is not None else {}

        if number_field or (partner_ids is False and sms_numbers is None):
            info = self._sms_get_recipients_info(force_field=number_field)[self.id]
            info_partner_ids = info['partner'].ids if info['partner'] else False
            info_number = info['sanitized'] if info['sanitized'] else info['number']
            if info_partner_ids and info_number:
                sms_pid_to_number[info_partner_ids[0]] = info_number
            if info_partner_ids:
                partner_ids = info_partner_ids + (partner_ids or [])
            if not info_partner_ids:
                if info_number:
                    sms_numbers = [info_number] + (sms_numbers or [])
                    # will send a falsy notification allowing to fix it through SMS wizards
                elif not sms_numbers:
                    sms_numbers = [False]

        if subtype_id is False:
            subtype_id = self.env['ir.model.data'].xmlid_to_res_id('mail.mt_note')

        return self.message_post(
            body=plaintext2html(html2plaintext(body)), partner_ids=partner_ids or [],  # TDE FIXME: temp fix otherwise crash mail_thread.py
            message_type='sms', subtype_id=subtype_id,
            sms_numbers=sms_numbers, sms_pid_to_number=sms_pid_to_number,
            **kwargs
        )
 def send_sms_reply(self):
     SmsShortcode = self.env['sms.shortcode']
     ResPartner = self.env['res.partner']
     SmsCompose = self.env['sms.compose']
     for message in self:
         shortcode = SmsShortcode.search([('keyword', '=', message.keyword)], limit=1)
         if shortcode and shortcode.member_required and message.model_id.model == 'res.partner':
             partner = ResPartner.browse(message.record_id)
             if partner.mobile:
                 render_msg = self.env['sms.template'].render_template(shortcode.sms_template_id.template_body, 'res.partner', partner.id)
                 message = tools.html2plaintext(render_msg)
                 msg_compose = SmsCompose.create({
                     'record_id': partner.id,
                     'model': 'res.partner',
                     'sms_template_id': shortcode.sms_template_id.id,
                     'from_mobile_id': self.env.ref('sms_frame.sms_number_inuka_international').id,
                     'to_number': partner.mobile,
                     'sms_content': message,
                 })
                 msg_compose.send_entity()
         elif shortcode and shortcode.member_required and message.model_id.model != 'res.partner':
             if message.from_mobile:
                 msg_compose = SmsCompose.create({
                     'record_id': message.record_id,
                     'model': message.model_id.model,
                     'sms_template_id': shortcode.no_member_sms_template_id.id,
                     'from_mobile_id': self.env.ref('sms_frame.sms_number_inuka_international').id,
                     'to_number': message.from_mobile,
                     'sms_content': shortcode.no_member_sms_template_id.template_body,
                 })
                 msg_compose.send_entity()
         elif shortcode and not shortcode.member_required:
             msg_compose = SmsCompose.create({
                 'record_id': message.record_id,
                 'model': message.model_id.model,
                 'sms_template_id': shortcode.sms_template_id.id,
                 'from_mobile_id': self.env.ref('sms_frame.sms_number_inuka_international').id,
                 'to_number': message.from_mobile,
                 'sms_content': shortcode.sms_template_id.template_body,
             })
             msg_compose.send_entity()
Пример #31
0
 def _create_from_table3(self, html_body):
     """ This Method returns plain text dict for Template
     """
     plain_text_body = tools.html2plaintext(html_body)
     newdict = {}
     aa = find_between_r(plain_text_body, '-----------------',
                         'Welke muuroppervlakte ').split('\n')
     if len(aa) == 1:
         return {}
     l = []
     for i in aa:
         if i and i != '\n':
             l.append(i)
     for i in l:
         splitted = i.split(':')
         if len(splitted) == 1:
             if 'Telefoon' in i:
                 newdict['Telefoon:'] = i[9:]
         if len(splitted) > 1:
             newdict[splitted[0] + ':'] = splitted[1]
     return newdict
Пример #32
0
    def test_invoice_less_than_delivered(self):
        """
        Suppose the lots are printed on the invoices.
        A user invoice a tracked product with a smaller quantity than delivered.
        On the invoice, the quantity of the used lot should be the invoiced one.
        """
        display_lots = self.env.ref('sale_stock.group_lot_on_invoice')
        display_uom = self.env.ref('uom.group_uom')
        self.env.user.write(
            {'groups_id': [(4, display_lots.id), (4, display_uom.id)]})

        so = self.env['sale.order'].create({
            'partner_id':
            self.partner_a.id,
            'order_line': [
                (0, 0, {
                    'name': self.product_by_lot.name,
                    'product_id': self.product_by_lot.id,
                    'product_uom_qty': 5
                }),
            ],
        })
        so.action_confirm()

        picking = so.picking_ids
        picking.move_lines.quantity_done = 5
        picking.button_validate()

        invoice = so._create_invoices()
        with Form(invoice) as form:
            with form.invoice_line_ids.edit(0) as line:
                line.quantity = 2
        invoice.action_post()

        report = self.env['ir.actions.report']._get_report_from_name(
            'account.report_invoice_with_payments')
        html = report._render_qweb_html(invoice.ids)[0]
        text = html2plaintext(html)
        self.assertRegex(text, r'Product By Lot\n2.00\nUnits\nLOT0001',
                         "There should be a line that specifies 2 x LOT0001")
Пример #33
0
    def test_invoice_before_delivery(self):
        """
        Suppose the lots are printed on the invoices.
        The user sells a tracked product, its invoicing policy is "Ordered quantities"
        A user invoice a tracked product with a smaller quantity than delivered.
        On the invoice, the quantity of the used lot should be the invoiced one.
        """
        display_lots = self.env.ref('stock_account.group_lot_on_invoice')
        display_uom = self.env.ref('uom.group_uom')
        self.env.user.write(
            {'groups_id': [(4, display_lots.id), (4, display_uom.id)]})

        self.product_by_lot.invoice_policy = "order"

        so = self.env['sale.order'].create({
            'partner_id':
            self.partner_a.id,
            'order_line': [
                (0, 0, {
                    'name': self.product_by_lot.name,
                    'product_id': self.product_by_lot.id,
                    'product_uom_qty': 4
                }),
            ],
        })
        so.action_confirm()

        invoice = so._create_invoices()
        invoice.action_post()

        picking = so.picking_ids
        picking.move_ids.quantity_done = 4
        picking.button_validate()

        report = self.env['ir.actions.report']._get_report_from_name(
            'account.report_invoice_with_payments')
        html = report._render_qweb_html(invoice.ids)[0]
        text = html2plaintext(html)
        self.assertRegex(text, r'Product By Lot\n4.00Units\nLOT0001',
                         "There should be a line that specifies 4 x LOT0001")
Пример #34
0
    def _get_ubl_values(self, invoice):
        ''' Get the necessary values to generate the XML. These values will be used in the qweb template when
        rendering. Needed values differ depending on the implementation of the UBL, as (sub)template can be overriden
        or called dynamically.
        :returns:   a dictionary with the value used in the template has key and the value as value.
        '''
        def format_monetary(amount):
            # Format the monetary values to avoid trailing decimals (e.g. 90.85000000000001).
            return float_repr(amount, invoice.currency_id.decimal_places)

        return {
            **invoice._prepare_edi_vals_to_export(),
            'tax_details': invoice._prepare_edi_tax_details(),
            'ubl_version': 2.1,
            'type_code': 380 if invoice.move_type == 'out_invoice' else 381,
            'payment_means_code': 42 if invoice.journal_id.bank_account_id else 31,
            'bank_account': invoice.partner_bank_id,
            'note': html2plaintext(invoice.narration) if invoice.narration else False,
            'format_monetary': format_monetary,
            'customer_vals': {'partner': invoice.commercial_partner_id},
            'supplier_vals': {'partner': invoice.company_id.partner_id.commercial_partner_id},
        }
Пример #35
0
    def action_create_calendar_event(self):
        """
        Modified functionality of the method:
        If meeting is created from contact / client, the name of the client +
        name of the contact is automatically put in the subject of the meeting
        :return:
        """
        self.ensure_one()

        action = self.env.ref('calendar.action_calendar_event').read()[0]
        message = self.summary
        attends = []
        try:
            customer = self.env['res.partner'].search([
                ('id', '=', self.env.context.get('default_res_id'))
            ])
            if customer and customer.is_company:
                message = "{} - {}".format(customer.name, message)
            else:
                message = "{}, {} - {}".format(
                    customer.commercial_company_name, customer.name, message)
                attends.append(customer)
        except:
            pass

        action['context'] = {
            'default_activity_type_id':
            self.activity_type_id.id,
            'default_res_id':
            self.env.context.get('default_res_id'),
            'default_res_model':
            self.env.context.get('default_res_model'),
            'default_name':
            message,
            'default_description':
            self.note and tools.html2plaintext(self.note) or '',
            'default_activity_ids': [(6, 0, self.ids)],
        }
        return action
Пример #36
0
 def create(self, vals):
     result = super(ProjectTask, self).create(vals)
     lines = []
     for rec in result.custom_partner_ids:
         lines.append((4,rec.id))
     lines.append((4,result.user_id.partner_id.id))
     meeting = self.env['calendar.event'].create({
       'name':vals['name'],
       'start':vals['date_start'],
       'stop': vals['date_start'],
       'custom_task_id':result.id,
       'partner_ids':lines,
       'duration':vals['custom_planned_hours'],
       'user_id':result.user_id.id,
       'custom_project_id':result.project_id.id,
       'description': html2plaintext(vals['description']),
       'location':vals['custom_location'],
       'privacy':vals['custom_privacy'],
       'show_as':vals['custom_show_as'],
       })
     result.write({'custom_event_id':meeting.id})
     return result
Пример #37
0
 def _create_from_table2(self, html_body):
     """ This Method returns plain text dict for Template
     """
     plain_text_body = tools.html2plaintext(html_body)
     newdict = {}
     aa = find_between_r(
         plain_text_body,
         'Gelieve binnen 24 uur contact op te nemen voor het beste resultaat.',
         'Google maps').split('\n')
     if len(aa) == 1:
         return {}
     l = []
     for i in aa:
         if i and i != '\n':
             l.append(i)
     if len(l) == 8:
         newdict['Naam:'] = l[2]
         newdict['Adres:'] = l[3]
         newdict['Postcode:'] = l[4][0:6]
         newdict['Woonplaats:'] = l[4][7:]
         newdict['E-mail:'] = l[6]
         newdict['Telefoon:'] = l[7]
     return newdict
Пример #38
0
    def _process_answer(self, mail_channel, message_body):
        """ Method called when the user reacts to the current chatbot.script step.
        For most chatbot.script.step#step_types it simply returns the next chatbot.script.step of
        the script (see '_fetch_next_step').

        Some extra processing is done for steps of type 'question_email' and 'question_phone' where
        we store the user raw answer (the mail message HTML body) into the chatbot.message in order
        to be able to recover it later (see '_chatbot_prepare_customer_values').

        :param mail_channel:
        :param message_body:
        :return: script step to display next
        :rtype: 'chatbot.script.step' """

        self.ensure_one()

        user_text_answer = html2plaintext(message_body)
        if self.step_type == 'question_email' and not email_normalize(
                user_text_answer):
            # if this error is raised, display an error message but do not go to next step
            raise ValidationError(
                _('"%s" is not a valid email.', user_text_answer))

        if self.step_type in ['question_email', 'question_phone']:
            chatbot_message = self.env['chatbot.message'].search([
                ('mail_channel_id', '=', mail_channel.id),
                ('script_step_id', '=', self.id),
            ],
                                                                 limit=1)

            if chatbot_message:
                chatbot_message.write({'user_raw_answer': message_body})
                self.env.flush_all()

        return self._fetch_next_step(
            mail_channel.chatbot_message_ids.user_script_answer_id)
Пример #39
0
    def _mark_done(self):
        """ Will add certification to employee's resumé if
        - The survey is a certification
        - The user is linked to an employee
        - The user succeeded the test """

        super(SurveyUserInput, self)._mark_done()

        certificate_user_inputs = self.filtered(lambda user_input: user_input.survey_id.certificate and user_input.quizz_passed)
        partner_has_completed = {user_input.partner_id.id: user_input.survey_id for user_input in certificate_user_inputs}
        employees = self.env['hr.employee'].sudo().search([('user_id.partner_id', 'in', certificate_user_inputs.mapped('partner_id').ids)])
        for employee in employees:
            line_type = self.env.ref('hr_skills_survey.resume_type_certification', raise_if_not_found=False)
            survey = partner_has_completed.get(employee.user_id.partner_id.id)
            self.env['hr.resume.line'].create({
                'employee_id': employee.id,
                'name': survey.title,
                'date_start': fields.Date.today(),
                'date_end': fields.Date.today(),
                'description': html2plaintext(survey.description),
                'line_type_id': line_type and line_type.id,
                'display_type': 'certification',
                'survey_id': survey.id
            })
Пример #40
0
    def _convert_visitor_to_lead(self, partner, key):
        """ Create a lead from channel /lead command
        :param partner: internal user partner (operator) that created the lead;
        :param key: operator input in chat ('/lead Lead about Product')
        """
        # if public user is part of the chat: consider lead to be linked to an
        # anonymous user whatever the participants. Otherwise keep only share
        # partners (no user or portal user) to link to the lead.
        customers = self.env['res.partner']
        for customer in self.with_context(
                active_test=False).channel_partner_ids.filtered(
                    lambda p: p != partner and p.partner_share):
            if customer.is_public:
                customers = self.env['res.partner']
                break
            else:
                customers |= customer

        utm_source = self.env.ref('crm_livechat.utm_source_livechat',
                                  raise_if_not_found=False)
        return self.env['crm.lead'].create({
            'name':
            html2plaintext(key[5:]),
            'partner_id':
            customers[0].id if customers else False,
            'user_id':
            False,
            'team_id':
            False,
            'description':
            self._get_channel_history(),
            'referred':
            partner.name,
            'source_id':
            utm_source and utm_source.id,
        })
Пример #41
0
 def test_new_google_event(self):
     values = {
         'id': 'oj44nep1ldf8a3ll02uip0c9aa',
         'description': 'Small mini desc',
         'organizer': {'email': '*****@*****.**', 'self': True},
         'summary': 'Pricing new update',
         'visibility': 'public',
         'attendees': [{
             'displayName': 'Mitchell Admin',
             'email': '*****@*****.**',
             'responseStatus': 'needsAction'
         },],
         'reminders': {'useDefault': True},
         'start': {
             'dateTime': '2020-01-13T16:55:00+01:00',
             'timeZone': 'Europe/Brussels'
         },
         'end': {
             'dateTime': '2020-01-13T19:55:00+01:00',
             'timeZone': 'Europe/Brussels'
         },
     }
     self.env['calendar.event']._sync_google2odoo(GoogleEvent([values]))
     event = self.env['calendar.event'].search([('google_id', '=', values.get('id'))])
     self.assertTrue(event, "It should have created an event")
     self.assertEqual(event.name, values.get('summary'))
     self.assertFalse(event.allday)
     self.assertEqual(html2plaintext(event.description), values.get('description'))
     self.assertEqual(event.start, datetime(2020, 1, 13, 15, 55))
     self.assertEqual(event.stop, datetime(2020, 1, 13, 18, 55))
     admin_attendee = event.attendee_ids.filtered(lambda e: e.email == '*****@*****.**')
     self.assertEqual('*****@*****.**', admin_attendee.email)
     self.assertEqual('Mitchell Admin', admin_attendee.partner_id.name)
     self.assertEqual(event.partner_ids, event.attendee_ids.partner_id)
     self.assertEqual('needsAction', admin_attendee.state)
     self.assertGoogleAPINotCalled()
Пример #42
0
 def _create_from_table5(self, html_body):
     """ This Method returns plain text dict for Template Comparisa - Uw groei, onze ambiti - Sub:Aanvraag van Comparisa
     """
     names = [
         "Voornaam", 'Naam', "E-mail", "Telefoon", "Adres", "Postcode",
         "Stad", "Land", "Straat"
     ]
     plain_text_body = tools.html2plaintext(html_body)
     aa = find_between_r(plain_text_body, 'Lead Nr.', '1.').split('\n')
     if aa and len(aa) > 1:
         if plain_text_body.find(
                 "Type bedrijf") != -1 and plain_text_body.find(
                     "ROPA") == -1:
             return {}
         newdict = {}
         for idx, word in enumerate(aa):
             for name in names:
                 if name in word:
                     newdict[name + ":"] = aa[idx + 1]
         if len(aa) == 1:
             return {}
     else:
         bb = find_between_r(plain_text_body, 'Informatie',
                             'Uitvoerdatum').split('\n')
         if len(bb) == 1:
             return {}
         newdict = {}
         l = []
         for i in bb:
             if i and i != '\n':
                 l.append(i)
         for i in l:
             splitted = i.split(':')
             if len(splitted) > 1:
                 newdict[splitted[0].strip() + ':'] = splitted[1]
     return newdict
Пример #43
0
 def _get_plain_content(self):
     self.plain_content = tools.html2plaintext(self.content)[0:500] if self.content else False
Пример #44
0
 def name_get(self):
     return [(ribbon.id,
              '%s (#%d)' % (tools.html2plaintext(ribbon.html), ribbon.id))
             for ribbon in self]
Пример #45
0
 def _get_note_first_line(self):
     for message in self:
         message.name = (
             (message.message and html2plaintext(message.message) or "").strip().replace("*", "").split("\n")[0]
         )
Пример #46
0
 def _get_plain_content(self):
     for post in self:
         post.plain_content = tools.html2plaintext(post.content)[0:500] if post.content else False
Пример #47
0
 def _compute_name(self):
     """ Read the first line of the memo to determine the question name """
     for question in self:
         text = html2plaintext(question.memo) if question.memo else ''
         question.name = text.strip().replace('*', '').split("\n")[0]
Пример #48
0
Файл: note.py Проект: 10537/odoo
 def _compute_name(self):
     """ Read the first line of the memo to determine the note name """
     for note in self:
         text = html2plaintext(note.memo) if note.memo else ''
         note.name = text.strip().replace('*', '').split("\n")[0]
Пример #49
0
    def channel(self, channel, category=None, tag=None, page=1, slide_type=None, uncategorized=False, sorting=None, search=None, **kw):
        if not channel.can_access_from_current_website():
            raise werkzeug.exceptions.NotFound()

        domain = self._get_channel_slides_base_domain(channel)

        pager_url = "/slides/%s" % (channel.id)
        pager_args = {}
        slide_types = dict(request.env['slide.slide']._fields['slide_type']._description_selection(request.env))

        if search:
            domain += [
                '|', '|', '|',
                ('name', 'ilike', search),
                ('description', 'ilike', search),
                ('index_content', 'ilike', search),
                ('html_content', 'ilike', search)]
            pager_args['search'] = search
        else:
            if category:
                domain += [('category_id', '=', category.id)]
                pager_url += "/category/%s" % category.id
            elif tag:
                domain += [('tag_ids.id', '=', tag.id)]
                pager_url += "/tag/%s" % tag.id
            if uncategorized:
                domain += [('category_id', '=', False)]
                pager_url += "?uncategorized=1"
            elif slide_type:
                domain += [('slide_type', '=', slide_type)]
                pager_url += "?slide_type=%s" % slide_type

        # sorting criterion
        if channel.channel_type == 'documentation':
            actual_sorting = sorting if sorting and sorting in request.env['slide.slide']._order_by_strategy else channel.promote_strategy
        else:
            actual_sorting = 'sequence'
        order = request.env['slide.slide']._order_by_strategy[actual_sorting]
        pager_args['sorting'] = actual_sorting

        slide_count = request.env['slide.slide'].sudo().search_count(domain)
        page_count = math.ceil(slide_count / self._slides_per_page)
        pager = request.website.pager(url=pager_url, total=slide_count, page=page,
                                      step=self._slides_per_page, url_args=pager_args,
                                      scope=page_count if page_count < self._pager_max_pages else self._pager_max_pages)

        values = {
            'channel': channel,
            # search
            'search_category': category,
            'search_tag': tag,
            'search_slide_type': slide_type,
            'search_uncategorized': uncategorized,
            'slide_types': slide_types,
            'sorting': actual_sorting,
            'search': search,
            # chatter
            'rating_avg': channel.rating_avg,
            'rating_count': channel.rating_count,
            # display data
            'user': request.env.user,
            'pager': pager,
            'is_public_user': request.website.is_public_user(),
        }
        if not request.env.user._is_public():
            last_message_values = request.env['mail.message'].search([
                ('model', '=', channel._name),
                ('res_id', '=', channel.id),
                ('author_id', '=', request.env.user.partner_id.id),
                ('message_type', '=', 'comment'),
                ('website_published', '=', True)
            ], order='write_date DESC', limit=1).read(['body', 'rating_value'])
            last_message_data = last_message_values[0] if last_message_values else {}
            values.update({
                'last_message_id': last_message_data.get('id'),
                'last_message': tools.html2plaintext(last_message_data.get('body', '')),
                'last_rating_value': last_message_data.get('rating_value'),
            })
            if channel.can_review:
                values.update({
                    'message_post_hash': channel._sign_token(request.env.user.partner_id.id),
                    'message_post_pid': request.env.user.partner_id.id,
                })

        # fetch slides and handle uncategorized slides; done as sudo because we want to display all
        # of them but unreachable ones won't be clickable (+ slide controller will crash anyway)
        # documentation mode may display less slides than content by category but overhead of
        # computation is reasonable
        values['slide_promoted'] = request.env['slide.slide'].sudo().search(domain, limit=1, order=order)
        values['category_data'] = channel._get_categorized_slides(
            domain, order,
            force_void=not category,
            limit=False if channel.channel_type != 'documentation' else self._slides_per_page if category else self._slides_per_category,
            offset=pager['offset'])
        values['channel_progress'] = self._get_channel_progress(channel, include_quiz=True)

        values = self._prepare_additional_channel_values(values, **kw)
        return request.render('website_slides.course_main', values)
Пример #50
0
    def channel(self, channel, category=None, tag=None, page=1, slide_type=None, uncategorized=False, sorting=None, search=None, **kw):
        if not channel.can_access_from_current_website():
            raise werkzeug.exceptions.NotFound()

        domain = [('channel_id', '=', channel.id)]
        if not channel.can_publish:
            domain = expression.AND([
                domain,
                ['&', ('website_published', '=', True), ('channel_id.website_published', '=', True)]
            ])

        pager_url = "/slides/%s" % (channel.id)
        pager_args = {}
        slide_types = dict(request.env['slide.slide']._fields['slide_type']._description_selection(request.env))

        if search:
            domain += [
                '|', '|', '|',
                ('name', 'ilike', search),
                ('description', 'ilike', search),
                ('index_content', 'ilike', search),
                ('html_content', 'ilike', search)]
            pager_args['search'] = search
        else:
            if category:
                domain += [('category_id', '=', category.id)]
                pager_url += "/category/%s" % category.id
            elif tag:
                domain += [('tag_ids.id', '=', tag.id)]
                pager_url += "/tag/%s" % tag.id
            if uncategorized:
                domain += [('category_id', '=', False)]
                pager_url += "?uncategorized=1"
            elif slide_type:
                domain += [('slide_type', '=', slide_type)]
                pager_url += "?slide_type=%s" % slide_type

        # sorting criterion
        actual_sorting = sorting if sorting and sorting in request.env['slide.slide']._order_by_strategy else channel.promote_strategy
        order = request.env['slide.slide']._order_by_strategy[actual_sorting]
        pager_args['sorting'] = actual_sorting

        pager_count = request.env['slide.slide'].sudo().search_count(domain)
        pager = request.website.pager(url=pager_url, total=pager_count, page=page,
                                      step=self._slides_per_page, scope=self._slides_per_page,
                                      url_args=pager_args)

        values = {
            'channel': channel,
            # search
            'search_category': category,
            'search_tag': tag,
            'search_slide_type': slide_type,
            'search_uncategorized': uncategorized,
            'slide_types': slide_types,
            'sorting': actual_sorting,
            'search': search,
            # chatter
            'rating_avg': channel.rating_avg,
            'rating_count': channel.rating_count,
            # display data
            'user': request.env.user,
            'pager': pager,
            'is_public_user': request.website.is_public_user(),
            'is_slides_publisher': request.env.user.has_group('website.group_website_publisher'),
        }
        if not request.env.user._is_public():
            last_message_values = request.env['mail.message'].search([
                ('model', '=', channel._name),
                ('res_id', '=', channel.id),
                ('author_id', '=', request.env.user.partner_id.id),
                ('message_type', '=', 'comment'),
                ('website_published', '=', True)
            ], order='write_date DESC', limit=1).read(['body', 'rating_value'])
            last_message_data = last_message_values[0] if last_message_values else {}
            values.update({
                'message_post_hash': channel._sign_token(request.env.user.partner_id.id),
                'message_post_pid': request.env.user.partner_id.id,
                'last_message_id': last_message_data.get('id'),
                'last_message': tools.html2plaintext(last_message_data.get('body', '')),
                'last_rating_value': last_message_data.get('rating_value'),
            })

        # Display uncategorized slides
        # fetch slides; done as sudo because we want to display all of them but
        # unreachable ones won't be clickable (+ slide controller will crash anyway)
        if not category and not uncategorized:
            category_data = []
            for category in request.env['slide.slide'].sudo().read_group(domain, ['category_id'], ['category_id']):
                category_id, name = category.get('category_id') or (False, _('Uncategorized'))
                slides_sudo = request.env['slide.slide'].sudo().search(category['__domain'], limit=4, offset=0, order=order)
                category_data.append({
                    'id': category_id,
                    'name': name,
                    'slug_name': slug((category_id, name)) if category_id else name,
                    'total_slides': category['category_id_count'],
                    'slides': slides_sudo,
                })
        elif uncategorized:
            slides_sudo = request.env['slide.slide'].sudo().search(domain, limit=self._slides_per_page, offset=pager['offset'], order=order)
            category_data = [{
                'id': False,
                'name':  _('Uncategorized'),
                'slug_name':  _('Uncategorized'),
                'total_slides': len(slides_sudo),
                'slides': slides_sudo,
            }]
        else:
            slides_sudo = request.env['slide.slide'].sudo().search(domain, limit=self._slides_per_page, offset=pager['offset'], order=order)
            category_data = [{
                'id': category.id,
                'name': category.name,
                'slug_name': slug(category),
                'total_slides': len(slides_sudo),
                'slides': slides_sudo,
            }]

        # post slide-fetch computation: promoted, user completion (separated because sudo-ed)
        if not request.website.is_public_user() and channel.is_member:
            displayed_slide_ids = list(set(sid for item in category_data for sid in item['slides'].ids))
            done_slide_ids = request.env['slide.slide.partner'].sudo().search([
                ('slide_id', 'in', displayed_slide_ids),
                ('partner_id', '=', request.env.user.partner_id.id),
                ('completed', '=', True)
            ]).mapped('slide_id').ids
        else:
            done_slide_ids = []
        values['done_slide_ids'] = done_slide_ids
        values['slide_promoted'] = request.env['slide.slide'].sudo().search(domain, limit=1, order=order)
        values['category_data'] = category_data

        values = self._prepare_additional_channel_values(values, **kw)

        if channel.channel_type == "training":
            values.update(self._get_user_progress(channel))

        return request.render('website_slides.course_main', values)
Пример #51
0
    def channel(self, channel, category=None, tag=None, page=1, slide_type=None, sorting='creation', search=None, **kw):
        if not channel.can_access_from_current_website():
            raise werkzeug.exceptions.NotFound()

        user = request.env.user
        Slide = request.env['slide.slide']
        domain = [('channel_id', '=', channel.id)]
        pager_url = "/slides/%s" % (channel.id)
        pager_args = {}

        if search:
            domain += [
                '|', '|',
                ('name', 'ilike', search),
                ('description', 'ilike', search),
                ('index_content', 'ilike', search)]
            pager_args['search'] = search
        else:
            if category:
                domain += [('category_id', '=', category.id)]
                pager_url += "/category/%s" % category.id
            elif tag:
                domain += [('tag_ids.id', '=', tag.id)]
                pager_url += "/tag/%s" % tag.id
            if slide_type:
                domain += [('slide_type', '=', slide_type)]
                pager_url += "/%s" % slide_type

        if not sorting or sorting not in self._order_by_criterion:
            sorting = 'date'
        order = self._order_by_criterion[sorting]
        pager_args['sorting'] = sorting

        pager_count = Slide.search_count(domain)
        pager = request.website.pager(url=pager_url, total=pager_count, page=page,
                                      step=self._slides_per_page, scope=self._slides_per_page,
                                      url_args=pager_args)

        slides = Slide.search(domain, limit=self._slides_per_page, offset=pager['offset'], order=order)
        values = {
            'channel': channel,
            'category': category,
            'slides': slides,
            'tag': tag,
            'slide_type': slide_type,
            'sorting': sorting,
            'user': user,
            'pager': pager,
            'is_public_user': request.website.is_public_user(),
            'rating_avg': channel.rating_avg,
            'rating_count': channel.rating_count,
        }
        if not request.env.user._is_public():
            last_message_values = request.env['mail.message'].search([('model', '=', channel._name), ('res_id', '=', channel.id), ('author_id', '=', user.partner_id.id), ('website_published', '=', True)], order='write_date DESC', limit=1).read(['body', 'rating_value'])
            last_message_data = last_message_values[0] if last_message_values else {}
            values.update({
                'message_post_hash': channel._sign_token(request.env.user.partner_id.id),
                'message_post_pid': request.env.user.partner_id.id,
                'last_message_id': last_message_data.get('id'),
                'last_message': html2plaintext(last_message_data.get('body', '')),
                'last_rating_value': last_message_data.get('rating_value'),
            })
        if search:
            values['search'] = search
            return request.render('website_slides.slides_search', values)

        # Display uncategorized slides
        if not slide_type and not category:
            category_datas = []
            for category in Slide.read_group(domain, ['category_id'], ['category_id']):
                category_id, name = category.get('category_id') or (False, _('Uncategorized'))
                category_datas.append({
                    'id': category_id,
                    'name': name,
                    'total': category['category_id_count'],
                    'slides': Slide.search(category['__domain'], limit=4, offset=0, order=order)
                })
            values.update({
                'category_datas': category_datas,
            })

        return request.render('website_slides.home', values)