subj = subj_sel.next_subject() cr.generated_subject_id = subj.id if subj else None cr.generated_template_id = tpl.id if tpl else None phone = phone_sel.next_phone() cr.generated_phone = phone g.phone = phone account = rot.next_account() if account is None: cr.status = "skipped" cr.last_error = "No available sender account (limits or cooldown)" else: cr.assigned_account_id = account.id rows.append(cr) plan.append({"seq": seq, "email": r.email, "invoice": inv_num, "amount": g.amount, "subject": subj.subject if subj else "", "template": tpl.name if tpl else None, "phone": phone, "account": account.email if account else None, "status": cr.status}) if preview and seq >= (campaign.preview_sample_size or 5): break db.add_all(rows) await db.flush() return {"rows": rows, "plan": plan, "rotation": rot.status(), "account_count": len(accounts), "subjects": len(subjects), "templates": len(templates), "phones": len(phones)} ===GENVALS=== 28:class GeneratedValues: 29- invoice_number: str = "" 30- amount: Optional[float] = None 31- shipping: Optional[float] = None 32- random_name: str = "" 33- invoice_date: str = "" 34- due_date: str = "" 35- phone: str = "" 36- 37- 38-@dataclass 39-class SubstitutionResult: 40- text: str 41- unknown: list = field(default_factory=list) 42- missing: list = field(default_factory=list) # known but no data available ===CFG=== 15: BASE_URL: str = "http://localhost:8000" 16: FRONTEND_BASE_URL: str = "http://localhost:8000" 19: DATABASE_URL: str = "postgresql+psycopg://mailer:mailer_dev_pw@localhost:5432/bulkmailer" 70: UNSUBSCRIBE_BASE: str = "" # defaults to BASE_URL