DEV Community

Harena Sarobidy RAMALANJAONA
Harena Sarobidy RAMALANJAONA

Posted on

Odoo 15 – Multiple mails sent on order confirmation but wrong order lines rendered in templates

Hello everyone, is there any Odoo developer here?

I’m facing an issue on Odoo 15 (Website Sale) related to automatic email sending after order confirmation.

This worked correctly before, but the issue appeared after introducing multiple email templates, one per rule.

Functional overview (minimal)

Sale order is confirmed from the website

After confirmation:

One standard order email is sent

Additional emails are sent depending on product rules

Each rule uses a different mail.template

Each email should display only the order lines related to that rule

Controller used on order confirmation

@http.route(['/shop/confirmation'], type='http', auth="public", website=True, sitemap=False)ute(['/shop/confirmation'], type='http', auth="public", website=True, sitemap=False)
def shop_payment_confirmation(self, **post):
    """ Automatically confirm an order from website """
    if 'valid_rules' in post and not bool(int(post['valid_rules'])):
        return

    purchase_capability, msg = request.env.user.partner_id.check_purchase_capability(
        request.website.company_id
    )
    if msg:
        return

    sale_order_id = request.session.get('sale_last_order_id') or 0
    order = request.env['sale.order'].sudo().browse(sale_order_id)
    if order.exists():
        if order.state not in ('draft', 'sent'):
            return request.redirect('/shop/cart')

        for line in order.order_line.filtered('product_packaging_id'):
            max_qty = line.product_packaging_id.virtual_available_qty
            if max_qty <= 0:
                return request.redirect('/shop/confirm_order')

        order.action_confirm()
        order.action_create_activity()

    request.website.sale_reset()
    res = super(WebsiteSale, self).shop_payment_confirmation(**post)

    # send mail if there is at least one product for distribution
    so_line_with_distribution_id = order.order_line.filtered(
        lambda r: r.product_id.product_tmpl_id.is_for_distribution
    )
    if so_line_with_distribution_id:
        product_tmpl_ids = so_line_with_distribution_id.mapped('product_id').mapped('product_tmpl_id')
        order._send_distribution_mail(product_tmpl_ids)

    return res
Enter fullscreen mode Exit fullscreen mode

Mail sending logic

def get_coupon_domain(self):
    return [('promo_code_usage', '=', 'distribution')]

def _send_distribution_mail(self, product_tmpl_ids):
    self.ensure_one()
    coupon_program_ids = self.code_promo_program_id.search(self.get_coupon_domain())
    email_values = list()
    used_coupon_ids = self.env['coupon.program']

    for product_tmpl_id in product_tmpl_ids:
        used_coupon_ids |= coupon_program_ids.get_the_right_one(product_tmpl_id)

    for coupon_program_id in used_coupon_ids:
        email_values.append(self._get_mail_values(coupon_program_id))

    mail_ids = self.env['mail.mail'].sudo().create(email_values)
    mail_ids.send(raise_exception=False)

Enter fullscreen mode Exit fullscreen mode

Mail values generation

def _get_mail_values(self, coupon_program_id):
    template_id = (
        coupon_program_id.email_template_id
        or self.env.ref(
            'sol_don_website.mail_template_distribution_tracking',
            raise_if_not_found=True
        )
    )

    email_values = {
        'email_to': self.partner_id.email_formatted,
        'email_from': self.company_id.email_formatted,
        'author_id': self.env.user.partner_id.id,
        'subject': _('Distribution condition %s') % (self.name,),
        'body_html': template_id._render_field(
            'body_html',
            [self.id],
            compute_lang=True,
            post_process=True
        )[self.id],
        'recipient_ids': self.message_follower_ids.mapped('partner_id')
                          + self.partner_invoice_id
    }
    return email_values
Enter fullscreen mode Exit fullscreen mode

Coupon program model

class CouponProgram(models.Model):
    _inherit = "coupon.program"

    promo_code_usage = fields.Selection(
        selection_add=[('distribution', 'Distribution condition')]
    )
    distribution_msg = fields.Text('Distribution condition body')
    email_template_id = fields.Many2one(
        'mail.template',
        'Mail template',
        domain=[('model_id', '=', 'sale.order')]
    )

    def get_the_right_one(self, product_tmpl_ids):
        product_ids = product_tmpl_ids.mapped('id')
        for program in self:
            domain = ast.literal_eval(
                program.rule_products_domain
            ) if program.rule_products_domain else []
            if any(
                i['id'] in product_ids
                for i in product_tmpl_ids.search_read(domain, ['id'])
                if i
            ):
                return program
        return self - self
Enter fullscreen mode Exit fullscreen mode

** Extract of the Mail template**

<t t-foreach="
    object.order_line.filtered(
        lambda r: r.product_id.product_tmpl_id.is_for_distribution
    )
" t-as="so_line_id">
    <tr>
        <td>
            <span t-field="so_line_id.product_id.default_code"/>
        </td>
        <td>
            <span t-field="so_line_id.name"/>
        </td>
        <td>
            <span t-field="so_line_id.price_subtotal"/>
        </td>
    </tr>
</t>
Enter fullscreen mode Exit fullscreen mode

Observed issue

When:

  • A sale order contains products matching multiple coupon programs

  • Each coupon program has its own mail template

  • Multiple emails are generated during the same confirmation

Then:

  • All emails are sent successfully

  • But the rendered order lines are incorrect:

    • Only one order line is displayed
    • Usually the last added line
    • Lines appear in templates where they should not belong

There are:

  • No errors

  • No tracebacks

  • _get_mail_values() is called correctly

  • _render_field() returns HTML

  • But the final email content is wrong

Question

Is this a known behavior or limitation in Odoo 15 mail.template rendering when:

  • Sending multiple emails

  • Same model (sale.order)

  • Same res_id

  • Different templates

  • Same HTTP request / transaction

What is the recommended approach to isolate data per email in this case?

Thanks in advance for any insight. You can also see this post in the Odoo forum here: Odoo 15 – Multiple mails sent on order confirmation but wrong order lines rendered in templates. Unfortunately the odoo forum is unavailable but you can still view it when you are logged in

Top comments (0)