"""Proposal email to the owner: subject, body, action links, .ics (§9.1).""" from __future__ import annotations from datetime import datetime from sqlalchemy.ext.asyncio import AsyncSession from gogo.config import get_settings from gogo.domain import Slot from gogo.email import EmailMessage, send_email from gogo.i18n import fmt_slot from gogo.models import BookingRequest, EmailLog, Service, Tenant from gogo.proposals.ics import build_ics from gogo.proposals.tokens import action_url, create_action_token def _slots_from_request(req: BookingRequest) -> list[Slot]: return [Slot.model_validate(s) for s in (req.slots or [])] async def send_proposal_email( session: AsyncSession, tenant: Tenant, req: BookingRequest, service: Service | None ) -> None: recipients = list(tenant.notify_emails or []) if not recipients: return slots = _slots_from_request(req) service_name = service.name if service else (req.service_name_raw or "termin") days = ", ".join(sorted({fmt_slot(s.start, tenant.timezone).split(",")[0] for s in slots})) subject = f"Novi zahtjev za termin — {service_name} — {req.client_name}" if days: subject += f" ({days})" resolve_url = action_url(await create_action_token(session, req.id, "resolve")) reject_url = action_url(await create_action_token(session, req.id, "reject")) confirm_urls = [ ( fmt_slot(s.start, tenant.timezone), action_url(await create_action_token(session, req.id, "confirm", slot_index=i)), ) for i, s in enumerate(slots) ] transcript_url = f"{get_settings().base_url}/t/{req.id}" lines = [ f"Novi zahtjev za termin — {tenant.name}", "", f"Klijent: {req.client_name}", f"Telefon: {req.client_phone}", f"Usluga: {service_name}", ] if req.home_visit: lines.append(f"Dolazak na adresu: DA — {req.address or 'adresa nije navedena'}") if slots: lines.append("Traženi termini (po redoslijedu želje):") lines += [f" {i + 1}. {fmt_slot(s.start, tenant.timezone)}" for i, s in enumerate(slots)] if req.time_preference_text and not slots: lines.append(f"Željeno vrijeme: {req.time_preference_text}") if req.summary: lines += ["", f"Sažetak razgovora: {req.summary}"] lines += [ "", f"Cijeli razgovor: {transcript_url}", "", "─" * 40, f"RIJEŠENO — kontaktirao/la sam klijenta:\n {resolve_url}", ] for label, url in confirm_urls: lines.append(f"POTVRDI {label} + pošalji SMS klijentu:\n {url}") lines.append(f"ODBIJ zahtjev:\n {reject_url}") html = _render_html( tenant, req, service_name, slots, resolve_url, confirm_urls, reject_url, transcript_url ) attachments = [] if slots: first = slots[0] ics = build_ics( uid=str(req.id), start=first.start, end=first.end, summary=f"{service_name} — {req.client_name} (zahtjev)", description=( f"Zahtjev putem Gogo Telefona.\nKlijent: {req.client_name}, {req.client_phone}\n" f"{req.summary}" ), ) attachments.append(("termin.ics", "text/calendar", ics)) await send_email(EmailMessage(recipients, subject, "\n".join(lines), html, attachments)) session.add( EmailLog( tenant_id=tenant.id, to_addr=", ".join(recipients), subject=subject, kind="proposal", status="sent", ) ) def _render_html( tenant: Tenant, req: BookingRequest, service_name: str, slots: list[Slot], resolve_url: str, confirm_urls: list[tuple[str, str]], reject_url: str, transcript_url: str, ) -> str: def esc(s: str) -> str: return ( str(s).replace("&", "&").replace("<", "<").replace(">", ">") ) slot_rows = "".join( f"
' f"Potvrdi {esc(label)} + SMS
" for label, url in confirm_urls ) home = ( f"Dolazak na adresu: DA — {esc(req.address or 'adresa nije navedena')}
" if req.home_visit else "" ) pref = ( f"Željeno vrijeme: {esc(req.time_preference_text)}
" if req.time_preference_text and not slots else "" ) summary = f"Sažetak: {esc(req.summary)}
" if req.summary else "" return f"""{esc(tenant.name)}
Klijent: {esc(req.client_name)}
Telefon: {esc(req.client_phone)}
Usluga: {esc(service_name)}
Traženi termini:
✓ Riješeno — kontaktirao/la sam klijenta
{confirm_buttons}Gogo Telefon — virtuelni asistent salona. U prilogu je .ics za prvi traženi termin (možete ga pomjeriti prije spremanja u kalendar).