46 lines
2.4 KiB
Python
46 lines
2.4 KiB
Python
"""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)
|