errors.append(f"Max {cfg.MAX_GMAIL_ACCOUNTS_PER_CAMPAIGN} accounts per campaign") else: for aid in data.account_ids: acc = await db.get(GmailAccount, aid) if not acc or acc.user_id != user.id: errors.append(f"Gmail account {aid} not found") elif acc.status != "authorized" or not acc.is_active: errors.append(f"Account {acc.email} is not authorized/active") if data.attach_mode == "pdf": if not (data.attach_pdf or data.attach_pdf_template_id or data.attachment_id): errors.append("PDF attachment mode requires a template or attachment to convert") if data.attach_mode in ("file", "image") and not data.attachment_id: errors.append("File/image attachment mode requires selecting an attachment") if len(data.rotation_weights or {}) > 0 and data.rotation_strategy != "weighted": errors.append("rotation_weights only applies when rotation_strategy is weighted") # variable config sanity vc = data.variable_config or {} try: ---STEP5-UI--- ${["repeat", "stop", "random"].map(m => ` `).join("")} `; } else if (step === 5) { const am = C.attach_mode; b.innerHTML = H("Step 5 — Attachments") + hint("Choose what to attach. PDF mode renders a template to a unique Invoice PDF per recipient.") + `
${[["none", "No attachment"], ["pdf", "Generated PDF invoice"], ["file", "Stored file attachment"], ["image", "Image attachment"]].map(([m, t]) => ` `).join("")}
`; bindAttExtra(); } else if (step === 6) { const vc = C.variable_config; b.innerHTML = H("Step 6 — Variables & random amounts") + hint("Configure the invoice number format and random amount ranges used by %RandomInvoiceNumber%, %RandomTotal% and %RandomShippingCharge%.") +