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)
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 '')
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]) + '...'
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] + '...'
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 "")
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]) + "..."
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'))
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 )) + '...'
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
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, }
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)
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
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
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
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
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
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
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
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, }
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)
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
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)
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)
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 })
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)
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
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, })
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()
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
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")
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")
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}, }
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
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
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
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)
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 })
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, })
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()
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
def _get_plain_content(self): self.plain_content = tools.html2plaintext(self.content)[0:500] if self.content else False
def name_get(self): return [(ribbon.id, '%s (#%d)' % (tools.html2plaintext(ribbon.html), ribbon.id)) for ribbon in self]
def _get_note_first_line(self): for message in self: message.name = ( (message.message and html2plaintext(message.message) or "").strip().replace("*", "").split("\n")[0] )
def _get_plain_content(self): for post in self: post.plain_content = tools.html2plaintext(post.content)[0:500] if post.content else False
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]
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]
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)
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)
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)