result_text = engine.substitute(template.body_text or _plain_fallback(result_html), rec, g, custom_fields=cf, extra={"unsubscribe_link": unsub_url}).text attachments, att_names = [], [] if campaign.attach_mode == "pdf" and campaign.attach_pdf: att_html = "" if campaign.attach_pdf_template_id: att_tpl = db.get(EmailTemplate, campaign.attach_pdf_template_id) if att_tpl: att_html = engine.substitute(att_tpl.body_html, rec, g, custom_fields=cf, extra={"unsubscribe_link": unsub_url}).text elif campaign.attachment_id: att = db.get(Attachment, campaign.attachment_id) if att and att.kind == "html": att_html = engine.substitute(att.html_attachment_body or "", rec, g, custom_fields=cf, extra={"unsubscribe_link": unsub_url}).text if att_html: pdf_bytes = message_builder.html_to_pdf(att_html) fname = message_builder.generate_attachment_filename( var_cfg.get("attachment_kind", "invoice"), cr.generated_invoice_number) attachments.append({"filename": fname, "content": pdf_bytes, "mime_type": "application/pdf"}) att_names.append(fname) if campaign.attach_mode in ("file", "image") and campaign.attachment_id: att = db.get(Attachment, campaign.attachment_id) if att: data = attachments_service.read_attachment_bytes(att) if data is not None: attachments.append({"filename": att.filename, "content": data, "mime_type": att.mime_type}) att_names.append(att.filename) html_problems = message_builder.validate_html(result_html) if html_problems: log.warning("HTML validation warnings for %s: %s", cr.email, html_problems) mime = message_builder.build_mime_message( from_email=account.email, # real authenticated account; never spoofed to_email=cr.email, subject=subject, html_body=result_html if campaign.message_type != "plain" else "", text_body=result_text, attachments=attachments, unsubscribe_url=unsub_url if campaign.unsubscribe_enabled else "", ===ATT MODEL FIELDS OK=== 146: description = Column(Text, default="") 295: mime_type = Column(String(120), default="application/octet-stream") 298: description = Column(Text, default="") 388: description = Column(String(300), default="")