Example #1
0
    def test_is_html_empty(self):
        void_strings_samples = ['', False, ' ']
        for content in void_strings_samples:
            self.assertTrue(is_html_empty(content))

        void_html_samples = [
            '<p><br></p>',
            '<p><br> </p>',
            '<p><br /></p >',
            '<p style="margin: 4px"></p>',
            '<div style="margin: 4px"></div>',
            '<p class="oe_testing"><br></p>',
            '<p><span style="font-weight: bolder;"><font style="color: rgb(255, 0, 0);" class=" "></font></span><br></p>',
        ]
        for content in void_html_samples:
            self.assertTrue(is_html_empty(content), 'Failed with %s' % content)

        valid_html_samples = [
            '<p><br>1</p>',
            '<p>1<br > </p>',
            '<p style="margin: 4px">Hello World</p>',
            '<div style="margin: 4px"><p>Hello World</p></div>',
            '<p><span style="font-weight: bolder;"><font style="color: rgb(255, 0, 0);" class=" ">W</font></span><br></p>',
        ]
        for content in valid_html_samples:
            self.assertFalse(is_html_empty(content))
    def test_is_html_empty(self):
        void_strings_samples = ['', False, ' ']
        for content in void_strings_samples:
            self.assertTrue(is_html_empty(content))

        void_html_samples = ['<p><br></p>', '<p><br> </p>', '<p><br /></p >']
        for content in void_html_samples:
            self.assertTrue(is_html_empty(content), 'Failed with %s' % content)

        valid_html_samples = ['<p><br>1</p>', '<p>1<br > </p>']
        for content in valid_html_samples:
            self.assertFalse(is_html_empty(content))
Example #3
0
    def write(self, vals):
        # If description_short wasn't manually modified, there is an implicit link between this field and description.
        if not is_html_empty(vals.get('description')) and is_html_empty(vals.get('description_short')) and self.description == self.description_short:
            vals['description_short'] = vals.get('description')

        res = super(Channel, self).write(vals)

        if vals.get('user_id'):
            self._action_add_members(self.env['res.users'].sudo().browse(vals['user_id']).partner_id)
            self.activity_reschedule(['website_slides.mail_activity_data_access_request'], new_user_id=vals.get('user_id'))
        if 'enroll_group_ids' in vals:
            self._add_groups_members()

        return res
Example #4
0
 def _compute_note(self):
     super()._compute_note()
     for order in self.filtered('sale_order_template_id'):
         template = order.sale_order_template_id.with_context(
             lang=order.partner_id.lang)
         order.note = template.note if not is_html_empty(
             template.note) else order.note
Example #5
0
    def _get_alias_bounced_body(self, message_dict):
        """Get the body of the email return in case of bounced email.

        :param message_dict: dictionary of mail values
        """
        lang_author = False
        if message_dict.get('author_id'):
            try:
                lang_author = self.env['res.partner'].browse(
                    message_dict['author_id']).lang
            except:
                pass

        if lang_author:
            self = self.with_context(lang=lang_author)

        if not is_html_empty(self.alias_bounced_content):
            body = self.alias_bounced_content
        else:
            body = self._get_alias_bounced_body_fallback(message_dict)
        template = self.env.ref('mail.mail_bounce_alias_security',
                                raise_if_not_found=True)
        return template._render({
            'body': body,
            'message': message_dict
        },
                                engine='ir.qweb',
                                minimal_qcontext=True)
Example #6
0
    def onchange_sale_order_template_id(self):

        if not self.sale_order_template_id:
            self.require_signature = self._get_default_require_signature()
            self.require_payment = self._get_default_require_payment()
            return

        template = self.sale_order_template_id.with_context(
            lang=self.partner_id.lang)

        # --- first, process the list of products from the template
        order_lines = [(5, 0, 0)]
        for line in template.sale_order_template_line_ids:
            data = self._compute_line_data_for_template_change(line)

            if line.product_id:
                price = line.product_id.lst_price
                discount = 0

                if self.pricelist_id:
                    pricelist_price = self.pricelist_id.with_context(
                        uom=line.product_uom_id.id).get_product_price(
                            line.product_id, 1, False)

                    if self.pricelist_id.discount_policy == 'without_discount' and price:
                        discount = max(0,
                                       (price - pricelist_price) * 100 / price)
                    else:
                        price = pricelist_price

                data.update({
                    'price_unit': price,
                    'discount': discount,
                    'product_uom_qty': line.product_uom_qty,
                    'product_id': line.product_id.id,
                    'product_uom': line.product_uom_id.id,
                })

            order_lines.append((0, 0, data))

        self.order_line = order_lines

        # then, process the list of optional products from the template
        option_lines = [(5, 0, 0)]
        for option in template.sale_order_template_option_ids:
            data = self._compute_option_data_for_template_change(option)
            option_lines.append((0, 0, data))

        self.sale_order_option_ids = option_lines

        if template.number_of_days > 0:
            self.validity_date = fields.Date.context_today(self) + timedelta(
                template.number_of_days)

        self.require_signature = template.require_signature
        self.require_payment = template.require_payment

        if not is_html_empty(template.note):
            self.note = template.note
Example #7
0
    def _get_ics_file(self):
        """ Returns iCalendar file for the event invitation.
            :returns a dict of .ics file content for each meeting
        """
        result = {}

        def ics_datetime(idate, allday=False):
            if idate:
                if allday:
                    return idate
                return idate.replace(tzinfo=pytz.timezone('UTC'))
            return False

        if not vobject:
            return result

        for meeting in self:
            cal = vobject.iCalendar()
            event = cal.add('vevent')

            if not meeting.start or not meeting.stop:
                raise UserError(_("First you have to specify the date of the invitation."))
            event.add('created').value = ics_datetime(fields.Datetime.now())
            event.add('dtstart').value = ics_datetime(meeting.start, meeting.allday)
            event.add('dtend').value = ics_datetime(meeting.stop, meeting.allday)
            event.add('summary').value = meeting.name
            if not is_html_empty(meeting.description):
                if 'appointment_type_id' in meeting._fields and self.appointment_type_id:
                    # convert_online_event_desc_to_text method for correct data formatting in external calendars
                    event.add('description').value = self.convert_online_event_desc_to_text(meeting.description)
                else:
                    event.add('description').value = html2plaintext(meeting.description)
            if meeting.location:
                event.add('location').value = meeting.location
            if meeting.rrule:
                event.add('rrule').value = meeting.rrule

            if meeting.alarm_ids:
                for alarm in meeting.alarm_ids:
                    valarm = event.add('valarm')
                    interval = alarm.interval
                    duration = alarm.duration
                    trigger = valarm.add('TRIGGER')
                    trigger.params['related'] = ["START"]
                    if interval == 'days':
                        delta = timedelta(days=duration)
                    elif interval == 'hours':
                        delta = timedelta(hours=duration)
                    elif interval == 'minutes':
                        delta = timedelta(minutes=duration)
                    trigger.value = delta
                    valarm.add('DESCRIPTION').value = alarm.name or u'Odoo'
            for attendee in meeting.attendee_ids:
                attendee_add = event.add('attendee')
                attendee_add.value = u'MAILTO:' + (attendee.email or u'')
            result[meeting.id] = cal.serialize().encode('utf-8')

        return result
Example #8
0
 def _google_values(self):
     if self.allday:
         start = {'date': self.start_date.isoformat()}
         end = {'date': (self.stop_date + relativedelta(days=1)).isoformat()}
     else:
         event_tz = self.event_tz or 'Etc/UTC'
         start = {'dateTime': self.start.isoformat(), 'timeZone': event_tz}
         end = {'dateTime': self.stop.isoformat(), 'timeZone': event_tz}
     reminders = [{
         'method': "email" if alarm.alarm_type == "email" else "popup",
         'minutes': alarm.duration_minutes
     } for alarm in self.alarm_ids]
     attendee_values = [{'email': attendee.partner_id.email_normalized, 'responseStatus': attendee.state} for attendee in self.attendee_ids if attendee.partner_id.email_normalized]
     # We sort the attendees to avoid undeterministic test fails. It's not mandatory for Google.
     attendee_values.sort(key=lambda k: k['email'])
     values = {
         'id': self.google_id,
         'start': start,
         'end': end,
         'summary': self.name,
         'description': tools.html2plaintext(self.description) if not tools.is_html_empty(self.description) else '',
         'location': self.location or '',
         'guestsCanModify': True,
         'organizer': {'email': self.user_id.email, 'self': self.user_id == self.env.user},
         'attendees': attendee_values,
         'extendedProperties': {
             'shared': {
                 '%s_odoo_id' % self.env.cr.dbname: self.id,
             },
         },
         'reminders': {
             'overrides': reminders,
             'useDefault': False,
         }
     }
     if self.privacy:
         values['visibility'] = self.privacy
     if not self.active:
         values['status'] = 'cancelled'
     if self.user_id and self.user_id != self.env.user and not bool(self.user_id.sudo().google_calendar_token):
         # The organizer is an Odoo user that do not sync his calendar
         values['extendedProperties']['shared']['%s_owner_id' % self.env.cr.dbname] = self.user_id.id
     elif not self.user_id:
         # We can't store on the shared properties in that case without getting a 403. It can happen when
         # the owner is not an Odoo user: We don't store the real owner identity (mail)
         # If we are not the owner, we should change the post values to avoid errors because we don't have
         # write permissions
         # See https://developers.google.com/calendar/concepts/sharing
         keep_keys = ['id', 'summary', 'attendees', 'start', 'end', 'reminders']
         values = {key: val for key, val in values.items() if key in keep_keys}
         # values['extendedProperties']['private] should be used if the owner is not an odoo user
         values['extendedProperties'] = {
             'private': {
                 '%s_odoo_id' % self.env.cr.dbname: self.id,
             },
         }
     return values
Example #9
0
    def test_is_html_empty(self):
        void_strings_samples = ['', False, ' ']
        for content in void_strings_samples:
            self.assertTrue(is_html_empty(content))

        void_html_samples = [
            '<p><br></p>', '<p><br> </p>', '<p><br /></p >',
            '<p style="margin: 4px"></p>', '<div style="margin: 4px"></div>'
        ]
        for content in void_html_samples:
            self.assertTrue(is_html_empty(content), 'Failed with %s' % content)

        valid_html_samples = [
            '<p><br>1</p>', '<p>1<br > </p>',
            '<p style="margin: 4px">Hello World</p>',
            '<div style="margin: 4px"><p>Hello World</p></div>'
        ]
        for content in valid_html_samples:
            self.assertFalse(is_html_empty(content))
Example #10
0
    def create(self, vals_list):
        for vals in vals_list:
            # Ensure creator is member of its channel it is easier for him to manage it (unless it is odoobot)
            if not vals.get('channel_partner_ids') and not self.env.is_superuser():
                vals['channel_partner_ids'] = [(0, 0, {
                    'partner_id': self.env.user.partner_id.id
                })]
            if not is_html_empty(vals.get('description')) and is_html_empty(vals.get('description_short')):
                vals['description_short'] = vals['description']

        channels = super(Channel, self.with_context(mail_create_nosubscribe=True)).create(vals_list)

        for channel in channels:
            if channel.user_id:
                channel._action_add_members(channel.user_id.partner_id)
            if channel.enroll_group_ids:
                channel._add_groups_members()

        return channels
Example #11
0
 def _action_done(self, feedback=False, attachment_ids=False):
     events = self.mapped('calendar_event_id')
     messages, activities = super(MailActivity, self)._action_done(feedback=feedback, attachment_ids=attachment_ids)
     if feedback:
         for event in events:
             description = event.description
             description = '%s<br />%s' % (
                 description if not tools.is_html_empty(description) else '',
                 _('Feedback: %(feedback)s', feedback=tools.plaintext2html(feedback)) if feedback else '',
             )
             event.write({'description': description})
     return messages, activities
Example #12
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 if not is_html_empty(self.note) else '',
         'default_activity_ids': [(6, 0, self.ids)],
     }
     return action
Example #13
0
    def chatbot_trigger_step(self, channel_uuid, chatbot_script_id=None):
        mail_channel = request.env['mail.channel'].sudo().search(
            [('uuid', '=', channel_uuid)], limit=1)
        if not mail_channel:
            return None

        next_step = False
        if mail_channel.chatbot_current_step_id:
            chatbot = mail_channel.chatbot_current_step_id.chatbot_script_id
            user_messages = mail_channel.message_ids.filtered(
                lambda message: message.author_id != chatbot.
                operator_partner_id)
            user_answer = request.env['mail.message'].sudo()
            if user_messages:
                user_answer = user_messages.sorted(
                    lambda message: message.id)[-1]
            next_step = mail_channel.chatbot_current_step_id._process_answer(
                mail_channel, user_answer.body)
        elif chatbot_script_id:  # when restarting, we don't have a "current step" -> set "next" as first step of the script
            chatbot = request.env['chatbot.script'].sudo().browse(
                chatbot_script_id)
            if chatbot.exists():
                next_step = chatbot.script_step_ids[:1]

        if not next_step:
            return None

        posted_message = next_step._process_step(mail_channel)
        return {
            'chatbot_posted_message':
            posted_message.message_format()[0] if posted_message else None,
            'chatbot_step': {
                'chatbot_operator_found':
                next_step.step_type == 'forward_operator'
                and len(mail_channel.channel_last_seen_partner_ids) > 2,
                'chatbot_script_step_id':
                next_step.id,
                'chatbot_step_answers': [{
                    'id': answer.id,
                    'label': answer.name,
                    'redirect_link': answer.redirect_link,
                } for answer in next_step.answer_ids],
                'chatbot_step_is_last':
                next_step._is_last_step(mail_channel),
                'chatbot_step_message':
                plaintext2html(next_step.message)
                if not is_html_empty(next_step.message) else False,
                'chatbot_step_type':
                next_step.step_type,
            }
        }
Example #14
0
    def _format_for_frontend(self):
        """ Small utility method that formats the step into a dict usable by the frontend code. """
        self.ensure_one()

        return {
            'chatbot_script_step_id': self.id,
            'chatbot_step_answers': [{
                'id': answer.id,
                'label': answer.name,
                'redirect_link': answer.redirect_link,
            } for answer in self.answer_ids],
            'chatbot_step_message': plaintext2html(self.message) if not is_html_empty(self.message) else False,
            'chatbot_step_is_last': self._is_last_step(),
            'chatbot_step_type': self.step_type
        }
Example #15
0
    def _get_pages_or_questions(self, user_input):
        """ Returns the pages or questions (depending on the layout) that will be shown
        to the user taking the survey.
        In 'page_per_question' layout, we also want to show pages that have a description. """

        result = self.env['survey.question']
        if self.questions_layout == 'page_per_section':
            result = self.page_ids
        elif self.questions_layout == 'page_per_question':
            if self.questions_selection == 'random':
                result = user_input.predefined_question_ids
            else:
                result = self.question_and_page_ids.filtered(
                    lambda question: not question.is_page or not is_html_empty(question.description))

        return result
Example #16
0
    def _post_welcome_steps(self, mail_channel):
        """ Welcome messages are only posted after the visitor's first interaction with the chatbot.
        See 'chatbot.script#_get_welcome_steps()' for more details.

        Side note: it is important to set the 'chatbot_current_step_id' on each iteration so that
        it's correctly set when going into 'mail_channel#_message_post_after_hook()'. """

        self.ensure_one()
        posted_messages = self.env['mail.message']

        for welcome_step in self._get_welcome_steps():
            mail_channel.chatbot_current_step_id = welcome_step.id

            if not is_html_empty(welcome_step.message):
                posted_messages += mail_channel.with_context(
                    mail_create_nosubscribe=True).message_post(
                        author_id=self.operator_partner_id.id,
                        body=plaintext2html(welcome_step.message),
                        message_type='comment',
                        subtype_xmlid='mail.mt_comment',
                    )

        return posted_messages
Example #17
0
    def _create_invoices(self, group=False):
        """ Creates invoice(s) for repair order.
        @param group: It is set to true when group invoice is to be generated.
        @return: Invoice Ids.
        """
        grouped_invoices_vals = {}
        repairs = self.filtered(lambda repair: repair.state not in
                                ('draft', 'cancel') and not repair.invoice_id
                                and repair.invoice_method != 'none')
        for repair in repairs:
            repair = repair.with_company(repair.company_id)
            partner_invoice = repair.partner_invoice_id or repair.partner_id
            if not partner_invoice:
                raise UserError(
                    _('You have to select an invoice address in the repair form.'
                      ))

            narration = repair.quotation_notes
            currency = repair.pricelist_id.currency_id
            company = repair.env.company

            journal = repair.env['account.move'].with_context(
                move_type='out_invoice')._get_default_journal()
            if not journal:
                raise UserError(
                    _('Please define an accounting sales journal for the company %s (%s).'
                      ) % (company.name, company.id))

            if (partner_invoice.id, currency.id,
                    company.id) not in grouped_invoices_vals:
                grouped_invoices_vals[(partner_invoice.id, currency.id,
                                       company.id)] = []
            current_invoices_list = grouped_invoices_vals[(partner_invoice.id,
                                                           currency.id,
                                                           company.id)]

            if not group or len(current_invoices_list) == 0:
                fpos = self.env['account.fiscal.position'].get_fiscal_position(
                    partner_invoice.id, delivery_id=repair.address_id.id)
                invoice_vals = {
                    'move_type': 'out_invoice',
                    'partner_id': partner_invoice.id,
                    'partner_shipping_id': repair.address_id.id,
                    'currency_id': currency.id,
                    'narration':
                    narration if not is_html_empty(narration) else '',
                    'invoice_origin': repair.name,
                    'repair_ids': [(4, repair.id)],
                    'invoice_line_ids': [],
                    'fiscal_position_id': fpos.id
                }
                if partner_invoice.property_payment_term_id:
                    invoice_vals[
                        'invoice_payment_term_id'] = partner_invoice.property_payment_term_id.id
                current_invoices_list.append(invoice_vals)
            else:
                # if group == True: concatenate invoices by partner and currency
                invoice_vals = current_invoices_list[0]
                invoice_vals['invoice_origin'] += ', ' + repair.name
                invoice_vals['repair_ids'].append((4, repair.id))
                if not is_html_empty(narration):
                    if is_html_empty(invoice_vals['narration']):
                        invoice_vals['narration'] = narration
                    else:
                        invoice_vals['narration'] += Markup(
                            '<br/>') + narration

            # Create invoice lines from operations.
            for operation in repair.operations.filtered(
                    lambda op: op.type == 'add'):
                if group:
                    name = repair.name + '-' + operation.name
                else:
                    name = operation.name

                account = operation.product_id.product_tmpl_id._get_product_accounts(
                )['income']
                if not account:
                    raise UserError(
                        _('No account defined for product "%s".',
                          operation.product_id.name))

                invoice_line_vals = {
                    'name': name,
                    'account_id': account.id,
                    'quantity': operation.product_uom_qty,
                    'tax_ids': [(6, 0, operation.tax_id.ids)],
                    'product_uom_id': operation.product_uom.id,
                    'price_unit': operation.price_unit,
                    'product_id': operation.product_id.id,
                    'repair_line_ids': [(4, operation.id)],
                }

                if currency == company.currency_id:
                    balance = -(operation.product_uom_qty *
                                operation.price_unit)
                    invoice_line_vals.update({
                        'debit':
                        balance > 0.0 and balance or 0.0,
                        'credit':
                        balance < 0.0 and -balance or 0.0,
                    })
                else:
                    amount_currency = -(operation.product_uom_qty *
                                        operation.price_unit)
                    balance = currency._convert(amount_currency,
                                                company.currency_id, company,
                                                fields.Date.today())
                    invoice_line_vals.update({
                        'amount_currency':
                        amount_currency,
                        'debit':
                        balance > 0.0 and balance or 0.0,
                        'credit':
                        balance < 0.0 and -balance or 0.0,
                        'currency_id':
                        currency.id,
                    })
                invoice_vals['invoice_line_ids'].append(
                    (0, 0, invoice_line_vals))

            # Create invoice lines from fees.
            for fee in repair.fees_lines:
                if group:
                    name = repair.name + '-' + fee.name
                else:
                    name = fee.name

                if not fee.product_id:
                    raise UserError(_('No product defined on fees.'))

                account = fee.product_id.product_tmpl_id._get_product_accounts(
                )['income']
                if not account:
                    raise UserError(
                        _('No account defined for product "%s".',
                          fee.product_id.name))

                invoice_line_vals = {
                    'name': name,
                    'account_id': account.id,
                    'quantity': fee.product_uom_qty,
                    'tax_ids': [(6, 0, fee.tax_id.ids)],
                    'product_uom_id': fee.product_uom.id,
                    'price_unit': fee.price_unit,
                    'product_id': fee.product_id.id,
                    'repair_fee_ids': [(4, fee.id)],
                }

                if currency == company.currency_id:
                    balance = -(fee.product_uom_qty * fee.price_unit)
                    invoice_line_vals.update({
                        'debit':
                        balance > 0.0 and balance or 0.0,
                        'credit':
                        balance < 0.0 and -balance or 0.0,
                    })
                else:
                    amount_currency = -(fee.product_uom_qty * fee.price_unit)
                    balance = currency._convert(amount_currency,
                                                company.currency_id, company,
                                                fields.Date.today())
                    invoice_line_vals.update({
                        'amount_currency':
                        amount_currency,
                        'debit':
                        balance > 0.0 and balance or 0.0,
                        'credit':
                        balance < 0.0 and -balance or 0.0,
                        'currency_id':
                        currency.id,
                    })
                invoice_vals['invoice_line_ids'].append(
                    (0, 0, invoice_line_vals))

        # Create invoices.
        invoices_vals_list_per_company = defaultdict(list)
        for (partner_invoice_id, currency_id,
             company_id), invoices in grouped_invoices_vals.items():
            for invoice in invoices:
                invoices_vals_list_per_company[company_id].append(invoice)

        for company_id, invoices_vals_list in invoices_vals_list_per_company.items(
        ):
            # VFE TODO remove the default_company_id ctxt key ?
            # Account fallbacks on self.env.company, which is correct with with_company
            self.env['account.move'].with_company(company_id).with_context(
                default_company_id=company_id,
                default_move_type='out_invoice').create(invoices_vals_list)

        repairs.write({'invoiced': True})
        repairs.mapped('operations').filtered(
            lambda op: op.type == 'add').write({'invoiced': True})
        repairs.mapped('fees_lines').write({'invoiced': True})

        return dict((repair.id, repair.invoice_id.id) for repair in repairs)
Example #18
0
 def _compute_is_body_empty(self):
     for mailing in self:
         mailing.is_body_empty = tools.is_html_empty(mailing.body_html)
Example #19
0
 def onchange_partner_id(self):
     super(SaleOrder, self).onchange_partner_id()
     template = self.sale_order_template_id.with_context(
         lang=self.partner_id.lang)
     self.note = template.note if not is_html_empty(
         template.note) else self.note
Example #20
0
    def test_append_to_html(self):
        test_samples = [
            ('<!DOCTYPE...><HTML encoding="blah">some <b>content</b></HtMl>', '--\nYours truly', True, True, False,
             '<!DOCTYPE...><html encoding="blah">some <b>content</b>\n<pre>--\nYours truly</pre>\n</html>'),
            ('<!DOCTYPE...><HTML encoding="blah">some <b>content</b></HtMl>', '--\nYours truly', True, False, False,
             '<!DOCTYPE...><html encoding="blah">some <b>content</b>\n<p>--<br/>Yours truly</p>\n</html>'),
            ('<html><body>some <b>content</b></body></html>', '<!DOCTYPE...>\n<html><body>\n<p>--</p>\n<p>Yours truly</p>\n</body>\n</html>', False, False, False,
             '<html><body>some <b>content</b>\n\n\n<p>--</p>\n<p>Yours truly</p>\n\n\n</body></html>'),
        ]
        for html, content, plaintext_flag, preserve_flag, container_tag, expected in test_samples:
            self.assertEqual(append_content_to_html(html, content, plaintext_flag, preserve_flag, container_tag), expected, 'append_content_to_html is broken')

    def test_is_html_empty(self):
        void_strings_samples = ['', False, ' ']
        for content in void_strings_samples:
            self.assertTrue(is_html_empty(content))

        void_html_samples = ['<p><br></p>', '<p><br> </p>']
        for content in void_html_samples:
            self.assertTrue(is_html_empty(content), 'Failed with %s' % content)

        valid_html_samples = ['<p><br>1</p>', '<p>1<br > </p>']
        for content in valid_html_samples:
            self.assertFalse(is_html_empty(content))


class TestEmailTools(BaseCase):
    """ Test some of our generic utility functions for emails """

    def test_email_split(self):
        cases = [
Example #21
0
    def _microsoft_values(self, fields_to_sync, initial_values={}):
        values = dict(initial_values)
        if not fields_to_sync:
            return values

        values['id'] = self.microsoft_id
        microsoft_guid = self.env['ir.config_parameter'].sudo().get_param(
            'microsoft_calendar.microsoft_guid', False)
        values['singleValueExtendedProperties'] = [{
            'id':
            'String {%s} Name odoo_id' % microsoft_guid,
            'value':
            str(self.id),
        }, {
            'id':
            'String {%s} Name owner_odoo_id' % microsoft_guid,
            'value':
            str(self.user_id.id),
        }]

        if self.microsoft_recurrence_master_id and 'type' not in values:
            values['seriesMasterId'] = self.microsoft_recurrence_master_id
            values['type'] = 'exception'

        if 'name' in fields_to_sync:
            values['subject'] = self.name or ''

        if 'description' in fields_to_sync:
            values['body'] = {
                'content':
                html2plaintext(self.description)
                if not is_html_empty(self.description) else '',
                'contentType':
                "text",
            }

        if any(x in fields_to_sync
               for x in ['allday', 'start', 'date_end', 'stop']):
            if self.allday:
                start = {
                    'dateTime': self.start_date.isoformat(),
                    'timeZone': 'Europe/London'
                }
                end = {
                    'dateTime':
                    (self.stop_date + relativedelta(days=1)).isoformat(),
                    'timeZone':
                    'Europe/London'
                }
            else:
                start = {
                    'dateTime': pytz.utc.localize(self.start).isoformat(),
                    'timeZone': 'Europe/London'
                }
                end = {
                    'dateTime': pytz.utc.localize(self.stop).isoformat(),
                    'timeZone': 'Europe/London'
                }

            values['start'] = start
            values['end'] = end
            values['isAllDay'] = self.allday

        if 'location' in fields_to_sync:
            values['location'] = {'displayName': self.location or ''}

        if 'alarm_ids' in fields_to_sync:
            alarm_id = self.alarm_ids.filtered(
                lambda a: a.alarm_type == 'notification')[:1]
            values['isReminderOn'] = bool(alarm_id)
            values['reminderMinutesBeforeStart'] = alarm_id.duration_minutes

        if 'user_id' in fields_to_sync:
            values['organizer'] = {
                'emailAddress': {
                    'address': self.user_id.email or '',
                    'name': self.user_id.display_name or ''
                }
            }
            values['isOrganizer'] = self.user_id == self.env.user

        if 'attendee_ids' in fields_to_sync:
            attendees = self.attendee_ids.filtered(
                lambda att: att.partner_id not in self.user_id.partner_id)
            values['attendees'] = [{
                'emailAddress': {
                    'address': attendee.email or '',
                    'name': attendee.display_name or ''
                },
                'status': {
                    'response': self._get_attendee_status_o2m(attendee)
                }
            } for attendee in attendees]

        if 'privacy' in fields_to_sync or 'show_as' in fields_to_sync:
            values['showAs'] = self.show_as
            sensitivity_o2m = {
                'public': 'normal',
                'private': 'private',
                'confidential': 'confidential',
            }
            values['sensitivity'] = sensitivity_o2m.get(self.privacy)

        if 'active' in fields_to_sync and not self.active:
            values['isCancelled'] = True

        if values.get('type') == 'seriesMaster':
            recurrence = self.recurrence_id
            pattern = {'interval': recurrence.interval}
            if recurrence.rrule_type in ['daily', 'weekly']:
                pattern['type'] = recurrence.rrule_type
            else:
                prefix = 'absolute' if recurrence.month_by == 'date' else 'relative'
                pattern['type'] = prefix + recurrence.rrule_type.capitalize()

            if recurrence.month_by == 'date':
                pattern['dayOfMonth'] = recurrence.day

            if recurrence.month_by == 'day' or recurrence.rrule_type == 'weekly':
                pattern['daysOfWeek'] = [
                    weekday_name for weekday_name, weekday in {
                        'monday': recurrence.mon,
                        'tuesday': recurrence.tue,
                        'wednesday': recurrence.wed,
                        'thursday': recurrence.thu,
                        'friday': recurrence.fri,
                        'saturday': recurrence.sat,
                        'sunday': recurrence.sun,
                    }.items() if weekday
                ]
                pattern['firstDayOfWeek'] = 'sunday'

            if recurrence.rrule_type == 'monthly' and recurrence.month_by == 'day':
                byday_selection = {
                    '1': 'first',
                    '2': 'second',
                    '3': 'third',
                    '4': 'fourth',
                    '-1': 'last',
                }
                pattern['index'] = byday_selection[recurrence.byday]

            rule_range = {'startDate': (recurrence.dtstart.date()).isoformat()}

            if recurrence.end_type == 'count':  # e.g. stop after X occurence
                rule_range['numberOfOccurrences'] = min(
                    recurrence.count, MAX_RECURRENT_EVENT)
                rule_range['type'] = 'numbered'
            elif recurrence.end_type == 'forever':
                rule_range['numberOfOccurrences'] = MAX_RECURRENT_EVENT
                rule_range['type'] = 'numbered'
            elif recurrence.end_type == 'end_date':  # e.g. stop after 12/10/2020
                rule_range['endDate'] = recurrence.until.isoformat()
                rule_range['type'] = 'endDate'

            values['recurrence'] = {'pattern': pattern, 'range': rule_range}

        return values
Example #22
0
 def _compute_note(self):
     for event in self:
         if event.event_type_id and not is_html_empty(
                 event.event_type_id.note):
             event.note = event.event_type_id.note
 def _compute_website_description(self):
     for sponsor in self:
         if is_html_empty(sponsor.website_description):
             sponsor.website_description = sponsor.partner_id.website_description
Example #24
0
 def _compute_ticket_instructions(self):
     for event in self:
         if is_html_empty(event.ticket_instructions) and not \
            is_html_empty(event.event_type_id.ticket_instructions):
             event.ticket_instructions = event.event_type_id.ticket_instructions
 def _compute_send_email(self):
     for wizard in self:
         wizard.send_email = not tools.is_html_empty(wizard.body)