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))
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
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
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)
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
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
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
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))
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
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
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
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, } }
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 }
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
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
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)
def _compute_is_body_empty(self): for mailing in self: mailing.is_body_empty = tools.is_html_empty(mailing.body_html)
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
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 = [
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
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
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)