"""Prepare a reviewed email draft; never sends mail automatically.""" from email.message import EmailMessage from email.policy import SMTP from pathlib import Path import hashlib import json from .io import load_invoice_yaml def prepare_email(invoice_dir: Path, jar: Path, logger, recipient: str | None = None) -> Path: # Invalidate any previous draft before rebuilding. out = invoice_dir / 'out' (out / 'invoice.eml').unlink(missing_ok=True) (out / 'manifest.json').unlink(missing_ok=True) inv = load_invoice_yaml(invoice_dir / 'invoice.yaml') recipient = recipient or inv.buyer.email import re if not recipient or not re.fullmatch(r'[^\s@<>]+@[^\s@<>]+\.[^\s@<>]+', recipient): raise ValueError('Set buyer.email or pass --to with the agreed invoice address') out = invoice_dir / 'out' # Rebuild from current YAML so the draft cannot attach an obsolete invoice. from .pipeline import build_in_folder template = Path(__file__).resolve().parent.parent / 'templates/invoice.html.j2' if not template.is_file(): template = Path(__file__).resolve().parent / 'data/templates/invoice.html.j2' result = build_in_folder(invoice_dir, 'out', template, logger, jar, True, True) pdf = result.facturx_pdf msg = EmailMessage(policy=SMTP) msg['From'] = inv.seller.email or '' if not inv.seller.email: raise ValueError('seller.email is required for an email draft') msg['To'] = recipient msg['Subject'] = f'Rechnung {inv.meta.invoice_number} – {inv.seller.name}' msg['X-Unsent'] = '1' msg.set_content(f'Sehr geehrte Damen und Herren,\n\nanbei erhalten Sie unsere E-Rechnung {inv.meta.invoice_number} als ZUGFeRD/Factur-X-PDF mit eingebetteten strukturierten Rechnungsdaten.\n\nVielen Dank für Ihren Auftrag.\n\nFreundliche Grüße\n{inv.seller.name}\n') msg.add_attachment(pdf.read_bytes(), maintype='application', subtype='pdf', filename=f'Rechnung_{_safe_name(inv.meta.invoice_number)}.pdf') target = out / 'invoice.eml' target.write_bytes(msg.as_bytes()) manifest = {p.name: hashlib.sha256(p.read_bytes()).hexdigest() for p in [invoice_dir / 'invoice.yaml', result.facturx_xml, pdf, result.validate_report, target]} (out / 'manifest.json').write_text(json.dumps(manifest, indent=2) + '\n', encoding='utf-8') return target def _safe_name(number: str) -> str: import re return re.sub(r'[^A-Za-z0-9_-]', '_', number)