Files
clientflow_backend/app/message_templates.py

570 lines
26 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Commercial reply templates for ClientFlow v4928.1.5.0.
Templates are deterministic and safe by default. The opportunity/documents are
source of truth; optional LLM wording can adapt these drafts without choosing
prices, stock, fiscal documents or attachments.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Dict, Iterable, List, Optional
@dataclass(frozen=True)
class MessageTemplate:
code: str
name: str
category: str
compatible_actions: tuple[str, ...]
body: str
expected_document_kinds: tuple[str, ...] = field(default_factory=tuple)
requires_opportunity: bool = True
requires_conversation: bool = True
requires_fiscal_customer: bool = False
complete_note: str = "Resposta enviada ao cliente via ClientFlow."
reply_type: str = "send_document"
def expects_documents(self) -> bool:
return bool(self.expected_document_kinds)
TEMPLATES: Dict[str, MessageTemplate] = {
"BUSINESS_KNOWLEDGE_REPLY": MessageTemplate(
code="BUSINESS_KNOWLEDGE_REPLY",
name="Resposta com conhecimento BLIF",
category="conhecimento",
compatible_actions=("SEND_INFO", "SEND_QUOTE", "SEND_PROFORMA", "SEND_INVOICE", "CONFIRM_PAYMENT", "PREPARE_ORDER", "REVIEW_MANUALLY"),
body=(
"Olá {customer_first_name},\n\n"
"Segue a resposta com base no conhecimento comercial da BLIF.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_fiscal_customer=False,
complete_note="Esclarecimento enviado ao cliente.",
reply_type="answer_without_attachment",
),
"LLM_BUSINESS_REPLY": MessageTemplate(
code="LLM_BUSINESS_REPLY",
name="Resposta LLM com conhecimento BLIF",
category="llm",
compatible_actions=("SEND_INFO", "SEND_QUOTE", "SEND_PROFORMA", "SEND_INVOICE", "CONFIRM_PAYMENT", "PREPARE_ORDER", "SUPPORT", "REVIEW_MANUALLY", "MARK_NO_INTEREST", "REMOVE_FROM_LIST"),
body=(
"Olá {customer_first_name},\n\n"
"Vou analisar o pedido e responder com base no contexto e conhecimento aprovado da BLIF.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_conversation=False,
requires_fiscal_customer=False,
complete_note="Resposta assistida por LLM preparada para revisão.",
reply_type="llm_assisted",
),
"CLARIFY_INSTALLATION_SCOPE": MessageTemplate(
code="CLARIFY_INSTALLATION_SCOPE",
name="Esclarecer instalação / âmbito do preço",
category="conhecimento",
compatible_actions=("SEND_INFO", "SEND_QUOTE", "REVIEW_MANUALLY"),
body=(
"Olá {customer_first_name},\n\n"
"Sim, confirmo que o valor indicado é referente ao equipamento, sem instalação incluída.\n\n"
"A BLIF não presta serviço direto de instalação, mas o equipamento é de instalação simples e pode ser instalado por qualquer eletricista qualificado. Se necessário, prestamos suporte remoto ao eletricista.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_fiscal_customer=False,
complete_note="Esclarecimento sobre instalação enviado ao cliente.",
reply_type="answer_without_attachment",
),
"CLARIFY_VAT_POLICY": MessageTemplate(
code="CLARIFY_VAT_POLICY",
name="Esclarecer IVA",
category="conhecimento",
compatible_actions=("SEND_INFO", "SEND_QUOTE", "SEND_PROFORMA", "SEND_INVOICE", "REVIEW_MANUALLY"),
body=(
"Olá {customer_first_name},\n\n"
"Os valores indicados nas nossas comunicações comerciais são apresentados sem IVA. Na fatura final, os valores surgem com IVA incluído.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_fiscal_customer=False,
complete_note="Esclarecimento sobre IVA enviado ao cliente.",
reply_type="answer_without_attachment",
),
"CLARIFY_DELIVERY_PAYMENT": MessageTemplate(
code="CLARIFY_DELIVERY_PAYMENT",
name="Esclarecer entrega/pagamento",
category="conhecimento",
compatible_actions=("SEND_INFO", "SEND_QUOTE", "SEND_PROFORMA", "CONFIRM_PAYMENT", "PREPARE_ORDER", "REVIEW_MANUALLY"),
body=(
"Olá {customer_first_name},\n\n"
"A condição padrão é pronto pagamento. Após confirmação do pagamento, a entrega estimada é de 23 dias úteis.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_fiscal_customer=False,
complete_note="Esclarecimento sobre entrega/pagamento enviado ao cliente.",
reply_type="answer_without_attachment",
),
"CLARIFY_RFID": MessageTemplate(
code="CLARIFY_RFID",
name="Esclarecer RFID",
category="conhecimento",
compatible_actions=("SEND_INFO", "SEND_QUOTE", "REVIEW_MANUALLY"),
body=(
"Olá {customer_first_name},\n\n"
"Sim, os carregadores BLIF são compatíveis com leitor RFID. O leitor RFID tem o valor de 16 € s/IVA e permite associar cartões ilimitados para controlo de acessos e separação de consumos por utilizador.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_fiscal_customer=False,
complete_note="Esclarecimento sobre RFID enviado ao cliente.",
reply_type="answer_without_attachment",
),
"CLARIFY_LOAD_BALANCER": MessageTemplate(
code="CLARIFY_LOAD_BALANCER",
name="Esclarecer balanceador",
category="conhecimento",
compatible_actions=("SEND_INFO", "SEND_QUOTE", "REVIEW_MANUALLY"),
body=(
"Olá {customer_first_name},\n\n"
"Os carregadores BLIF podem funcionar com balanceador dinâmico de carga no quadro elétrico. O balanceador ajusta automaticamente a potência disponível e ajuda a evitar sobrecargas ou disparos do disjuntor.\n\n"
"Em edifícios com vários pisos ou estrutura densa, recomendamos a versão com fios (Modbus).\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_fiscal_customer=False,
complete_note="Esclarecimento sobre balanceador enviado ao cliente.",
reply_type="answer_without_attachment",
),
"CLARIFY_LOCAL_HISTORY": MessageTemplate(
code="CLARIFY_LOCAL_HISTORY",
name="Esclarecer histórico sem Internet",
category="conhecimento",
compatible_actions=("SEND_INFO", "SEND_QUOTE", "REVIEW_MANUALLY"),
body=(
"Olá {customer_first_name},\n\n"
"É possível consultar o histórico de carregamentos localmente, por WiFi/Bluetooth, mesmo sem Internet. O histórico pode incluir datas, duração e kWh por sessão; com RFID, permite separar por utilizador.\n\n"
"Nota: o histórico local pode perder-se em caso de reset/factory reset.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_fiscal_customer=False,
complete_note="Esclarecimento sobre histórico local enviado ao cliente.",
reply_type="answer_without_attachment",
),
"CLARIFY_CONDOMINIUM_MOBIE": MessageTemplate(
code="CLARIFY_CONDOMINIUM_MOBIE",
name="Esclarecer condomínio / MOBI.E DPC",
category="conhecimento",
compatible_actions=("SEND_INFO", "SEND_QUOTE", "REVIEW_MANUALLY"),
body=(
"Olá {customer_first_name},\n\n"
"É viável instalar um carregador BLIF em garagem de condomínio, seja em box ou lugar partilhado, desde que exista ligação elétrica adequada.\n\n"
"Quando o objetivo é separar consumos da fatura do condomínio, pode ser analisada a integração MOBI.E em modo DPC, que permite registo de sessões e faturação ao utilizador.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_fiscal_customer=False,
complete_note="Esclarecimento sobre condomínio/MOBI.E enviado ao cliente.",
reply_type="answer_without_attachment",
),
"CLARIFY_TECHNICAL_FEATURES": MessageTemplate(
code="CLARIFY_TECHNICAL_FEATURES",
name="Esclarecer funcionalidades técnicas",
category="conhecimento",
compatible_actions=("SEND_INFO", "SEND_QUOTE", "REVIEW_MANUALLY"),
body=(
"Olá {customer_first_name},\n\n"
"Os carregadores BLIF permitem controlo de potência entre 6 A e 32 A, gestão e monitorização através da app, controlo remoto e agendamento. O ajuste de corrente ajuda a evitar disparos do disjuntor quando a instalação está corretamente dimensionada.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_fiscal_customer=False,
complete_note="Esclarecimento técnico enviado ao cliente.",
reply_type="answer_without_attachment",
),
"INTERNAL_BOUNCE_EMAIL": MessageTemplate(
code="INTERNAL_BOUNCE_EMAIL",
name="Bounce / email devolvido — não responder",
category="triagem",
compatible_actions=("SEND_INFO", "SEND_QUOTE", "REVIEW_MANUALLY"),
body=(
"Não enviar resposta ao cliente.\n\n"
"Esta mensagem aparenta ser uma devolução automática de email/bounce. "
"A ação recomendada é atualizar o contacto, validar o email e, se aplicável, remover o contacto de campanhas."
),
requires_opportunity=False,
requires_conversation=False,
requires_fiscal_customer=False,
complete_note="Bounce identificado; contacto deve ser revisto internamente.",
reply_type="no_customer_reply",
),
"MANUAL_REVIEW_REQUIRED": MessageTemplate(
code="MANUAL_REVIEW_REQUIRED",
name="Revisão manual — sem sugestão automática",
category="triagem",
compatible_actions=("SEND_INFO", "SEND_QUOTE", "SEND_PROFORMA", "SEND_INVOICE", "CONFIRM_PAYMENT", "PREPARE_ORDER", "REVIEW_MANUALLY"),
body=(
"Rever manualmente antes de responder.\n\n"
"O assistente não encontrou intenção suficientemente segura para gerar uma resposta comercial automática."
),
requires_opportunity=False,
requires_conversation=False,
requires_fiscal_customer=False,
complete_note="Tarefa marcada para revisão manual.",
reply_type="manual_internal",
),
"INTERNAL_AUTO_REPLY": MessageTemplate(
code="INTERNAL_AUTO_REPLY",
name="Resposta automática / ausência — não responder",
category="triagem",
compatible_actions=("SEND_INFO", "SEND_QUOTE", "REVIEW_MANUALLY"),
body=(
"Não enviar resposta ao cliente.\n\n"
"Esta mensagem aparenta ser uma resposta automática/ausência. A ação recomendada é aguardar ou rever manualmente, sem enviar conteúdo comercial."
),
requires_opportunity=False,
requires_conversation=False,
requires_fiscal_customer=False,
complete_note="Resposta automática identificada; sem resposta ao cliente.",
reply_type="no_customer_reply",
),
"ACK_UNSUBSCRIBE": MessageTemplate(
code="ACK_UNSUBSCRIBE",
name="Confirmar remoção da lista",
category="triagem",
compatible_actions=("SEND_INFO", "SEND_QUOTE", "REVIEW_MANUALLY"),
body=(
"Olá {customer_first_name},\n\n"
"Confirmamos a receção do pedido e vamos remover/atualizar o contacto para não receber novas comunicações comerciais.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_fiscal_customer=False,
complete_note="Pedido de remoção/privacidade respondido ao cliente.",
reply_type="operational_ack",
),
"ACK_ADDRESS_UPDATE": MessageTemplate(
code="ACK_ADDRESS_UPDATE",
name="Confirmar atualização de contacto",
category="triagem",
compatible_actions=("SEND_INFO", "SEND_QUOTE", "REVIEW_MANUALLY"),
body=(
"Olá {customer_first_name},\n\n"
"Obrigado pela informação. Vamos atualizar o contacto nos nossos registos.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_fiscal_customer=False,
complete_note="Atualização de contacto confirmada ao cliente.",
reply_type="operational_ack",
),
"ACK_NO_INTEREST": MessageTemplate(
code="ACK_NO_INTEREST",
name="Responder sem interesse",
category="triagem",
compatible_actions=("SEND_INFO", "SEND_QUOTE", "REVIEW_MANUALLY"),
body=(
"Olá {customer_first_name},\n\n"
"Obrigado pelo feedback. Registamos a informação e não insistiremos neste contacto comercial.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_fiscal_customer=False,
complete_note="Sem interesse registado e respondido ao cliente.",
reply_type="operational_ack",
),
"ACK_SUPPORT_RECEIVED": MessageTemplate(
code="ACK_SUPPORT_RECEIVED",
name="Acusar receção de suporte/incidente",
category="suporte",
compatible_actions=("SEND_INFO", "SEND_QUOTE", "REVIEW_MANUALLY", "PREPARE_ORDER"),
body=(
"Olá {customer_first_name},\n\n"
"Lamentamos a situação. Vamos verificar internamente o estado do processo e dar seguimento com prioridade.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_fiscal_customer=False,
complete_note="Incidente/suporte acusado ao cliente.",
reply_type="support_ack",
),
"ACK_CALLBACK_REQUEST": MessageTemplate(
code="ACK_CALLBACK_REQUEST",
name="Confirmar pedido de contacto",
category="triagem",
compatible_actions=("SEND_INFO", "SEND_QUOTE", "REVIEW_MANUALLY"),
body=(
"Olá {customer_first_name},\n\n"
"Obrigado pelo contacto. Vamos analisar o pedido e responder/entrar em contacto assim que possível.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_fiscal_customer=False,
complete_note="Pedido de contacto acusado ao cliente.",
reply_type="operational_ack",
),
"SEND_INFO_EQUIPMENT_LIST": MessageTemplate(
code="SEND_INFO_EQUIPMENT_LIST",
name="Enviar lista de equipamentos",
category="informacao",
compatible_actions=("SEND_INFO", "SEND_QUOTE", "REVIEW_MANUALLY"),
body=(
"Olá {customer_first_name},\n\n"
"Segue abaixo a lista de equipamentos disponíveis:\n\n"
"- Carregador EV monofásico 7,4 kW\n"
"- Carregador EV trifásico 11 kW\n"
"- Carregador EV trifásico 22 kW\n\n"
"Todos os equipamentos incluem fatura, garantia e apoio técnico.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_fiscal_customer=False,
complete_note="Lista de equipamentos enviada ao cliente.",
reply_type="answer_without_attachment",
),
"SEND_PRICE_LIST": MessageTemplate(
code="SEND_PRICE_LIST",
name="Enviar preços indicativos",
category="informacao",
compatible_actions=("SEND_INFO", "SEND_QUOTE", "REVIEW_MANUALLY"),
body=(
"Olá {customer_first_name},\n\n"
"Segue a informação de preços disponível para os equipamentos solicitados.\n\n"
"Os valores finais devem ser confirmados no orçamento/proposta antes de adjudicação.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_fiscal_customer=False,
complete_note="Preços indicativos enviados ao cliente.",
reply_type="answer_without_attachment",
),
"SEND_QUOTE": MessageTemplate(
code="SEND_QUOTE",
name="Enviar orçamento/proposta",
category="proposta",
compatible_actions=("SEND_QUOTE", "SEND_INFO", "REVIEW_MANUALLY"),
# SEND_QUOTE pode ser uma proposta textual quando ainda não existe ORC.*.
# Se o operador selecionar um documento, validamos que é quotation.
expected_document_kinds=("quotation",),
requires_fiscal_customer=False,
body=(
"Olá {customer_first_name},\n\n"
"Segue abaixo a proposta/preços indicativos para o pedido.\n\n"
"Se pretender avançar, confirmamos os dados necessários e preparamos o orçamento formal.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
complete_note="Orçamento/proposta enviado ao cliente.",
reply_type="send_document_or_text_quote",
),
"SEND_PROFORMA": MessageTemplate(
code="SEND_PROFORMA",
name="Enviar orçamento para pagamento (ORC Jasmin)",
category="documento",
compatible_actions=("SEND_PROFORMA", "SEND_INVOICE", "REVIEW_MANUALLY"),
# Regra BLIF/ClientFlow: a orçamento para pagamento operacional é o orçamento Jasmin ORC.*.
# Não exigir um tipo documental separado "proforma"; o documento correto é quotation.
expected_document_kinds=("quotation",),
requires_fiscal_customer=True,
body=(
"Olá {customer_first_name},\n\n"
"Segue em anexo o orçamento {document_numbers} para pagamento e avanço da encomenda.\n\n"
"Após o pagamento, envie por favor o comprovativo para confirmação e preparação da encomenda.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
complete_note="Orçamento para pagamento/ORC Jasmin enviada ao cliente.",
reply_type="send_document",
),
"SEND_INVOICE": MessageTemplate(
code="SEND_INVOICE",
name="Enviar fatura",
category="documento",
compatible_actions=("SEND_INVOICE", "SEND_PROFORMA", "REVIEW_MANUALLY"),
expected_document_kinds=("invoice",),
requires_fiscal_customer=True,
body=(
"Olá {customer_first_name},\n\n"
"Segue em anexo a fatura {document_numbers}.\n\n"
"Qualquer questão, estamos ao dispor.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
complete_note="Fatura enviada ao cliente.",
reply_type="send_document",
),
"REQUEST_FISCAL_DATA": MessageTemplate(
code="REQUEST_FISCAL_DATA",
name="Pedir dados fiscais",
category="dados",
compatible_actions=("SEND_PROFORMA", "SEND_INVOICE", "SEND_QUOTE", "REVIEW_MANUALLY"),
body=(
"Olá {customer_first_name},\n\n"
"Para avançarmos corretamente, envie por favor os dados fiscais para faturação:\n\n"
"- Nome fiscal\n"
"- NIF\n"
"- Morada fiscal\n"
"- Email para envio do documento\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_fiscal_customer=False,
complete_note="Pedido de dados fiscais enviado ao cliente.",
reply_type="request_missing_data",
),
"REQUEST_PAYMENT_PROOF": MessageTemplate(
code="REQUEST_PAYMENT_PROOF",
name="Pedir comprovativo de pagamento",
category="pagamento",
compatible_actions=("CONFIRM_PAYMENT", "SEND_PROFORMA", "REVIEW_MANUALLY"),
body=(
"Olá {customer_first_name},\n\n"
"Para conseguirmos confirmar o processo, envie por favor o comprovativo de pagamento.\n\n"
"Assim que recebermos a confirmação, damos seguimento à encomenda.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_fiscal_customer=False,
complete_note="Pedido de comprovativo de pagamento enviado ao cliente.",
reply_type="request_missing_data",
),
"CONFIRM_PAYMENT_RECEIVED": MessageTemplate(
code="CONFIRM_PAYMENT_RECEIVED",
name="Confirmar pagamento recebido",
category="pagamento",
compatible_actions=("CONFIRM_PAYMENT", "PREPARE_ORDER", "REVIEW_MANUALLY"),
body=(
"Olá {customer_first_name},\n\n"
"Confirmamos a receção do pagamento.\n\n"
"Vamos dar seguimento à preparação da encomenda e manteremos o contacto com a atualização do envio.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_fiscal_customer=False,
complete_note="Pagamento confirmado ao cliente.",
reply_type="answer_without_attachment",
),
"FOLLOW_UP_QUOTE": MessageTemplate(
code="FOLLOW_UP_QUOTE",
name="Follow-up de orçamento/proposta",
category="followup",
compatible_actions=("FOLLOW_UP_QUOTE", "FOLLOW_UP_GENERIC", "SEND_QUOTE", "REVIEW_MANUALLY"),
expected_document_kinds=("quotation",),
body=(
"Olá {customer_first_name},\n\n"
"Gostaria apenas de confirmar se recebeu a proposta/orçamento {document_numbers} "
"e se ficou com alguma dúvida.\n\n"
"Caso pretenda avançar, posso também enviar os dados para emissão da orçamento para pagamento.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_conversation=False,
requires_fiscal_customer=False,
complete_note="Follow-up de orçamento/proposta enviado ao cliente.",
reply_type="follow_up",
),
"FOLLOW_UP_PROFORMA": MessageTemplate(
code="FOLLOW_UP_PROFORMA",
name="Follow-up de orçamento para pagamento",
category="followup",
compatible_actions=("FOLLOW_UP_PROFORMA", "FOLLOW_UP_PAYMENT", "FOLLOW_UP_GENERIC", "SEND_PROFORMA", "REVIEW_MANUALLY"),
expected_document_kinds=("proforma", "invoice", "quotation"),
body=(
"Olá {customer_first_name},\n\n"
"Gostaria apenas de confirmar se recebeu a orçamento/dados de pagamento {document_numbers} "
"e se precisa de alguma informação adicional para avançar.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_conversation=False,
requires_fiscal_customer=False,
complete_note="Follow-up de orçamento para pagamento enviado ao cliente.",
reply_type="follow_up",
),
"FOLLOW_UP_PAYMENT": MessageTemplate(
code="FOLLOW_UP_PAYMENT",
name="Follow-up de pagamento",
category="followup",
compatible_actions=("FOLLOW_UP_PAYMENT", "FOLLOW_UP_PROFORMA", "CONFIRM_PAYMENT", "REVIEW_MANUALLY"),
expected_document_kinds=("proforma", "invoice", "quotation"),
body=(
"Olá {customer_first_name},\n\n"
"Gostaria apenas de confirmar se recebeu a orçamento/dados de pagamento {document_numbers} "
"e se precisa de alguma informação adicional para avançar com o pagamento.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_conversation=False,
requires_fiscal_customer=False,
complete_note="Follow-up de pagamento enviado ao cliente.",
reply_type="follow_up",
),
"FOLLOW_UP_CUSTOMER_REVIEW": MessageTemplate(
code="FOLLOW_UP_CUSTOMER_REVIEW",
name="Follow-up de interesse/revisão",
category="followup",
compatible_actions=("FOLLOW_UP_CUSTOMER_REVIEW", "FOLLOW_UP_GENERIC", "SEND_INFO", "REVIEW_MANUALLY"),
body=(
"Olá {customer_first_name},\n\n"
"Gostaria apenas de confirmar se mantém interesse no carregador e se precisa de alguma informação adicional.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_conversation=False,
requires_fiscal_customer=False,
complete_note="Follow-up de interesse enviado ao cliente.",
reply_type="follow_up",
),
"FOLLOW_UP_GENERIC": MessageTemplate(
code="FOLLOW_UP_GENERIC",
name="Follow-up genérico",
category="followup",
compatible_actions=("FOLLOW_UP_GENERIC", "FOLLOW_UP_QUOTE", "FOLLOW_UP_PAYMENT", "FOLLOW_UP_CUSTOMER_REVIEW", "REVIEW_MANUALLY"),
body=(
"Olá {customer_first_name},\n\n"
"Gostaria apenas de dar seguimento ao processo e confirmar se precisa de alguma informação adicional.\n\n"
"Cumprimentos,\nEquipa BLIF"
),
requires_opportunity=False,
requires_conversation=False,
requires_fiscal_customer=False,
complete_note="Follow-up enviado ao cliente.",
reply_type="follow_up",
),
}
# Intent gate fallback anchor: "SEND_INFO": "MANUAL_REVIEW_REQUIRED"
DEFAULT_TEMPLATE_BY_ACTION: Dict[str, str] = {
"SEND_INFO": "SEND_INFO_EQUIPMENT_LIST",
"SEND_QUOTE": "SEND_QUOTE",
"SEND_PROFORMA": "SEND_PROFORMA",
"SEND_INVOICE": "SEND_INVOICE",
"CONFIRM_PAYMENT": "REQUEST_PAYMENT_PROOF",
"PREPARE_ORDER": "CONFIRM_PAYMENT_RECEIVED",
"FOLLOW_UP_QUOTE": "FOLLOW_UP_QUOTE",
"FOLLOW_UP_PROFORMA": "FOLLOW_UP_PROFORMA",
"FOLLOW_UP_PAYMENT": "FOLLOW_UP_PAYMENT",
"FOLLOW_UP_CUSTOMER_REVIEW": "FOLLOW_UP_CUSTOMER_REVIEW",
"FOLLOW_UP_GENERIC": "FOLLOW_UP_GENERIC",
"REVIEW_MANUALLY": "MANUAL_REVIEW_REQUIRED",
}
def get_template(code: Optional[str]) -> Optional[MessageTemplate]:
return TEMPLATES.get(str(code or "").strip().upper())
def default_template_for_action(action_code: Optional[str]) -> MessageTemplate:
code = DEFAULT_TEMPLATE_BY_ACTION.get(str(action_code or "").strip().upper(), "MANUAL_REVIEW_REQUIRED")
return TEMPLATES[code]
def list_templates_for_action(action_code: Optional[str] = None) -> List[MessageTemplate]:
action = str(action_code or "").strip().upper()
items: Iterable[MessageTemplate] = TEMPLATES.values()
if action:
filtered = [tpl for tpl in items if action in tpl.compatible_actions or "REVIEW_MANUALLY" in tpl.compatible_actions]
return sorted(filtered, key=lambda tpl: (tpl.category, tpl.name))
return sorted(items, key=lambda tpl: (tpl.category, tpl.name))