570 lines
26 KiB
Python
570 lines
26 KiB
Python
"""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 2–3 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 Wi‑Fi/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))
|