Import ClientFlow production v4928.1.5.132.4
This commit is contained in:
67
.env.example
67
.env.example
@@ -9,7 +9,7 @@ DATABASE_URL=postgresql+psycopg://clientflow:password@127.0.0.1:5432/clientflow
|
||||
PSQL_DATABASE_URL=postgresql://clientflow:password@127.0.0.1:5432/clientflow
|
||||
|
||||
# LLM / OpenRouter
|
||||
OPENROUTER_API_KEY=coloca_a_tua_chave_aqui
|
||||
OPENROUTER_API_KEY=
|
||||
OPENROUTER_MODEL=qwen/qwen3-30b-a3b
|
||||
|
||||
# Chatwoot
|
||||
@@ -89,3 +89,68 @@ JASMIN_DEFAULT_UNIT=UN
|
||||
JASMIN_DEFAULT_ITEM_TAX_SCHEMA=NORMAL
|
||||
# Opcional: fallback quando a linha da oportunidade não tem SKU/Jasmin item.
|
||||
JASMIN_DEFAULT_SALES_ITEM=CARREGADOR_MONO_7KW
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# URLs e configuração geral do ClientFlow
|
||||
# ---------------------------------------------------------------------------
|
||||
CLIENTFLOW_BASE_URL=http://127.0.0.1:8020
|
||||
CLIENTFLOW_PUBLIC_URL=
|
||||
CLIENTFLOW_BACKUP_DIR=/var/backups/clientflow
|
||||
CLIENTFLOW_DISABLE_CHATWOOT_PRIVATE_NOTES=false
|
||||
CLIENTFLOW_NON_BILLABLE_ODOO_LINES=
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Chatwoot
|
||||
# ---------------------------------------------------------------------------
|
||||
CHATWOOT_AUTO_COMPLETE_ON_OUTGOING=true
|
||||
CHATWOOT_AUTO_COMPLETE_ACTION_CODES=
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Identificação por email / LLM
|
||||
# ---------------------------------------------------------------------------
|
||||
EMAIL_IDENTITY_LLM_MODEL=
|
||||
EMAIL_IDENTITY_LLM_FALLBACK_MODEL=
|
||||
EMAIL_IDENTITY_LLM_TIMEOUT_SECONDS=30
|
||||
EMAIL_IDENTITY_LLM_MAX_BODY_CHARS=12000
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Jasmin — impressão de faturas
|
||||
# ---------------------------------------------------------------------------
|
||||
JASMIN_INVOICE_TYPE=
|
||||
JASMIN_INVOICE_SERIE=
|
||||
JASMIN_INVOICE_PRINT_LAYOUT=
|
||||
JASMIN_INVOICE_PRINTED_REPORT_NAME=
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Mautic
|
||||
# ---------------------------------------------------------------------------
|
||||
MAUTIC_BASE_URL=
|
||||
MAUTIC_API_TOKEN=
|
||||
MAUTIC_ADD_TAG_URL=
|
||||
MAUTIC_REMOVE_TAG_URL=
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Outbox
|
||||
# ---------------------------------------------------------------------------
|
||||
OUTBOX_LIMIT=50
|
||||
OUTBOX_TARGET_SYSTEM=
|
||||
OUTBOX_WORKER_ID=
|
||||
OUTBOX_STALE_PROCESSING_MINUTES=30
|
||||
OUTBOX_STALE_RECOVERY_LIMIT=100
|
||||
OUTBOX_STALE_RECOVERY_MODE=false
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Packlink — testes
|
||||
# ---------------------------------------------------------------------------
|
||||
PACKLINK_TEST_FROM_ZIP=
|
||||
PACKLINK_TEST_TO_ZIP=
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Assistente de resposta
|
||||
# ---------------------------------------------------------------------------
|
||||
CLIENTFLOW_REPLY_LLM_ENABLED=false
|
||||
CLIENTFLOW_REPLY_LLM_FIRST_ENABLED=true
|
||||
CLIENTFLOW_REPLY_LLM_MODEL=
|
||||
CLIENTFLOW_EMAIL_REPLY_AGENT_ENABLED=false
|
||||
OPENAI_VECTOR_STORE_ID=
|
||||
OPENAI_API_KEY=
|
||||
|
||||
114
CLIENTFLOW_SALES_MANAGEMENT_KNOWLEDGE_v1_2.md
Normal file
114
CLIENTFLOW_SALES_MANAGEMENT_KNOWLEDGE_v1_2.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# ClientFlow — conhecimento do assistente de gestão comercial
|
||||
|
||||
Versão: 1.2
|
||||
Release: v4928.1.5.125
|
||||
Idioma: português de Portugal
|
||||
|
||||
## Objetivo
|
||||
|
||||
Responder a perguntas internas sobre metas, desempenho, pipeline e ações comerciais. As regras deste ficheiro são estáveis; os números atuais devem ser fornecidos pelo backend através de `/api/internal/forecast` (`/api/internal/revenue-forecast` permanece como alias).
|
||||
|
||||
## Regra fundamental
|
||||
|
||||
Nunca inventar metas, valores, datas, taxas ou estados. Quando faltarem dados vivos, declarar que não existem dados suficientes para calcular com segurança.
|
||||
|
||||
## Conceitos
|
||||
|
||||
- **Meta mensal:** objetivo para um mês civil e uma métrica explícita.
|
||||
- **Realizado:** valor que já cumpre a métrica da meta no período.
|
||||
- **Comprometido:** valor praticamente assegurado, mas ainda não realizado segundo a métrica.
|
||||
- **Pipeline provável:** oportunidades ainda sujeitas a conversão, ponderadas por probabilidade e atividade.
|
||||
- **Previsão total:** realizado + comprometido não realizado + pipeline provável.
|
||||
- **Desvio:** meta − previsão total.
|
||||
- **Cumprimento previsto:** previsão total ÷ meta.
|
||||
- **Capacidade de recuperação:** pagamentos e conversões já existentes que podem ser antecipados para o mês.
|
||||
- **Desvio residual:** máximo(meta − previsão base − capacidade de recuperação, 0).
|
||||
- **Novo pipeline necessário:** desvio residual ÷ taxa de conversão esperada.
|
||||
|
||||
## Métricas suportadas
|
||||
|
||||
1. `invoiced` — faturação emitida no mês.
|
||||
2. `cash_received` — pagamentos confirmados no mês.
|
||||
3. `won_sales` — vendas ganhas no mês.
|
||||
|
||||
Não tratar as três métricas como equivalentes.
|
||||
|
||||
## Fim do mês e próximos 30 dias
|
||||
|
||||
- “Este mês” significa até ao último dia do mês civil.
|
||||
- “Próximos 30 dias” é uma janela móvel e pode incluir parte do mês seguinte.
|
||||
- Nunca usar diretamente o total de 30 dias para responder sobre o fim do mês.
|
||||
|
||||
## Prevenção de dupla contagem
|
||||
|
||||
Um processo comercial contribui apenas uma vez para a previsão total. Não somar separadamente orçamento, fatura, venda Odoo e pagamento da mesma compra. Um valor já realizado não volta a entrar no comprometido ou no pipeline provável.
|
||||
|
||||
## Confiança
|
||||
|
||||
Interpretar a cobertura de valor:
|
||||
|
||||
- 80% ou mais: boa.
|
||||
- 50% a 79%: moderada.
|
||||
- Menos de 50%: baixa.
|
||||
- Menos de 25%: previsão monetária muito incompleta.
|
||||
|
||||
Com qualidade baixa, usar “estimativa indicativa” e nunca apresentar o resultado como garantia.
|
||||
|
||||
## Semáforo
|
||||
|
||||
- **Meta suportada:** previsão ≥ meta e qualidade ≥ 60%.
|
||||
- **Suportada com baixa confiança:** previsão ≥ meta, mas qualidade < 60%.
|
||||
- **Meta recuperável:** previsão base abaixo da meta, mas capacidade de recuperação ponderada cobre o desvio.
|
||||
- **Risco moderado:** previsão entre 85% e 99% da meta e recuperação insuficiente ou incerta.
|
||||
- **Sem cobertura suficiente:** previsão base + recuperação conhecida continuam abaixo da meta.
|
||||
|
||||
## Estratégia recomendada
|
||||
|
||||
- Valor suficiente, mas bloqueado: priorizar pagamento, produção, expedição e tasks vencidas.
|
||||
- Muitas oportunidades sem valor: qualificar e associar documentos antes de aumentar campanhas.
|
||||
- Pipeline bruto insuficiente: gerar novas oportunidades e reativar clientes.
|
||||
- Muitos orçamentos e pouca conversão: rever proposta, preço, follow-up e objeções.
|
||||
|
||||
## Formato de resposta sobre a meta
|
||||
|
||||
Apresentar sempre, quando disponíveis:
|
||||
|
||||
- meta;
|
||||
- realizado;
|
||||
- comprometido;
|
||||
- pipeline provável;
|
||||
- previsão total;
|
||||
- desvio;
|
||||
- cumprimento previsto;
|
||||
- qualidade/cobertura;
|
||||
- principais riscos;
|
||||
- três ações prioritárias.
|
||||
|
||||
Distinguir claramente valor já realizado de valor adicional esperado a partir da data atual.
|
||||
|
||||
|
||||
## Política de follow-up e atividade comercial
|
||||
|
||||
O estado técnico `open` não significa, por si só, que a oportunidade esteja ativa. Usar os estados operacionais:
|
||||
|
||||
- `active` — existe atividade recente ou trabalho comercial em curso;
|
||||
- `awaiting_customer` — comunicação enviada e próximo contacto agendado;
|
||||
- `follow_up_due` — a data do próximo contacto chegou;
|
||||
- `recovery` — a sequência normal terminou sem resposta e requer decisão;
|
||||
- `nurture` — o cliente tem potencial, mas o timing é futuro.
|
||||
|
||||
Nunca usar `updated_at` técnico para concluir que o cliente esteve ativo. Preferir `last_customer_activity_at`, `last_operator_activity_at`, `last_commercial_activity_at` e `next_follow_up_at`.
|
||||
|
||||
### Cadência recomendada
|
||||
|
||||
- Informação: confirmar receção no dia útil seguinte; contactos adicionais após 2, 4 e 5 dias úteis.
|
||||
- Orçamento: confirmar receção no dia útil seguinte; esclarecer dúvidas após 2 dias úteis; decisão após 4; última tentativa após 5.
|
||||
- Pagamento: confirmar receção no dia útil seguinte; pedir data de pagamento após 2 dias úteis; lembrete após 3; contacto direto após mais 3; revisão final após mais 5.
|
||||
|
||||
A primeira etapa deve confirmar entrega, destinatário, documento/anexo e possíveis bounces. Não assumir que ausência de resposta significa falta de interesse.
|
||||
|
||||
### Recuperação, nurture e perda
|
||||
|
||||
Depois da última tentativa, mover para `recovery`; não marcar automaticamente como perdida. Na recuperação, escolher entre canal alternativo, nova abordagem, `nurture` ou perda.
|
||||
|
||||
Marcar como perdida apenas com motivo obrigatório. `future_timing` deve gerar nurture, não perda. Uma nova mensagem do cliente na mesma conversa pode reabrir automaticamente oportunidades fechadas como `LOST` ou `NO_INTEREST`; negócios entregues ou ganhos não são reabertos como a mesma venda.
|
||||
17
E2E_CHATWOOT_HEADER_FIX_README.md
Normal file
17
E2E_CHATWOOT_HEADER_FIX_README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Fix E2E Chatwoot private notes
|
||||
|
||||
Este overlay corrige o mock Chatwoot para aceitar o header real `api_access_token` usado pelo ClientFlow.
|
||||
|
||||
Sintoma anterior:
|
||||
|
||||
- inbound WebSocket passa;
|
||||
- `/webhooks/chatwoot` devolve 200;
|
||||
- tarefas são criadas;
|
||||
- outgoing auto-complete passa;
|
||||
- mas as notas privadas ficam a `notes=0`.
|
||||
|
||||
Causa:
|
||||
|
||||
O FastAPI transformava o parâmetro `api_access_token` em header esperado `api-access-token`, enquanto o ClientFlow envia o header Chatwoot real `api_access_token`. O mock passava a responder 401 ao POST da nota privada, mas o webhook principal continuava 200.
|
||||
|
||||
Aplicar e reiniciar o Terminal 1 dos mocks.
|
||||
48
E2E_CHATWOOT_WS_README.md
Normal file
48
E2E_CHATWOOT_WS_README.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# ClientFlow E2E Chatwoot WebSocket overlay
|
||||
|
||||
Acrescenta ao E2E Test Lab:
|
||||
|
||||
- `mock_chatwoot_api.py` na porta `18004`.
|
||||
- REST compatível com `POST /api/v1/accounts/{account_id}/conversations/{conversation_id}/messages`.
|
||||
- WebSocket `ws://127.0.0.1:18004/e2e/ws` para simular mensagens incoming/outgoing.
|
||||
- Testes E2E adicionais para conversas Chatwoot:
|
||||
- cliente envia mensagem;
|
||||
- mock Chatwoot envia webhook para `/webhooks/chatwoot`;
|
||||
- ClientFlow classifica a ação;
|
||||
- ClientFlow publica nota privada no Chatwoot mock quando permitido;
|
||||
- operador envia resposta outgoing;
|
||||
- ClientFlow auto-completa tarefas para ações em que responder ao cliente fecha a tarefa.
|
||||
|
||||
## Aplicar
|
||||
|
||||
```bash
|
||||
cd ~/Transferências/clientflow_backend_v4928_1_5_40_with_e2e_test_lab/cf_v1532_work
|
||||
unzip -o ~/Transferências/clientflow_e2e_chatwoot_ws_overlay.zip -d .
|
||||
chmod +x e2e_test_lab/scripts/*.sh
|
||||
source .venv/bin/activate
|
||||
pip install -r e2e_test_lab/requirements-e2e.txt
|
||||
```
|
||||
|
||||
## Terminais
|
||||
|
||||
Terminal 1:
|
||||
|
||||
```bash
|
||||
source .venv/bin/activate
|
||||
./e2e_test_lab/scripts/run_mock_servers.sh
|
||||
```
|
||||
|
||||
Terminal 2:
|
||||
|
||||
```bash
|
||||
source .venv/bin/activate
|
||||
./e2e_test_lab/scripts/start_clientflow_e2e.sh
|
||||
```
|
||||
|
||||
Terminal 3:
|
||||
|
||||
```bash
|
||||
source .venv/bin/activate
|
||||
./e2e_test_lab/scripts/check_e2e_ports.sh
|
||||
./e2e_test_lab/scripts/run_e2e_lab.sh | tee e2e_test_lab/latest_e2e_run.log
|
||||
```
|
||||
27
E2E_DEEP_6_ERRORS_FIX_README.md
Normal file
27
E2E_DEEP_6_ERRORS_FIX_README.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# ClientFlow Deep Audit 6 Errors Fix
|
||||
|
||||
Corrige os 6 `HTTP 500` encontrados no deep audit:
|
||||
|
||||
- `POST /opportunities/{id}/stage NEW`
|
||||
- `POST /opportunities/{id}/jasmin/create-quotation`
|
||||
- `POST /opportunities/{id}/jasmin/convert-invoice`
|
||||
- `POST /opportunities/{id}/operations/prepare_order`
|
||||
- `POST /opportunities/{id}/operations/prepare_shipping`
|
||||
- `POST /opportunities/{id}/operations/send_followup`
|
||||
|
||||
Alterações:
|
||||
|
||||
1. `stage=NEW` passa a ser aceite como alias de `NEW_LEAD`.
|
||||
2. Falhas de criação/conversão Jasmin deixam de devolver `500` em pedidos normais; redirecionam para a oportunidade com `notice`.
|
||||
3. Ações legadas de operações passam a mapear para ações canónicas:
|
||||
- `prepare_order` -> `odoo_sale_order`
|
||||
- `prepare_shipping` -> `packlink_shipment`
|
||||
- `send_followup` -> `tracking_sent`
|
||||
4. Bloqueios/erros operacionais deixam de rebentar a UI com `500`; devolvem `409` em HTMX ou `303` com aviso na UI normal.
|
||||
|
||||
Depois de aplicar, reiniciar o ClientFlow e voltar a correr:
|
||||
|
||||
```bash
|
||||
./e2e_test_lab/scripts/run_deep_e2e_lab.sh | tee e2e_test_lab/latest_deep_e2e_run.log
|
||||
cat e2e_test_lab/latest_deep_audit_report.md
|
||||
```
|
||||
78
E2E_DEEP_COVERAGE_README.md
Normal file
78
E2E_DEEP_COVERAGE_README.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# ClientFlow E2E Deep Coverage Overlay
|
||||
|
||||
Acrescenta uma camada de cobertura mais agressiva ao E2E já existente.
|
||||
|
||||
## O que testa
|
||||
|
||||
1. Core E2E existente:
|
||||
- mocks OpenRouter, Jasmin, Contactos, Odoo, Chatwoot;
|
||||
- `/analyze`;
|
||||
- Chatwoot WebSocket;
|
||||
- reconciliação;
|
||||
- páginas principais;
|
||||
- APIs internas.
|
||||
|
||||
2. Deep UI/API audit:
|
||||
- GET de páginas, aliases e partials HTMX;
|
||||
- links internos encontrados no HTML;
|
||||
- forms sem action, métodos inválidos, ids duplicados;
|
||||
- botões sem label;
|
||||
- padrões de erro renderizados no HTML;
|
||||
- API de contactos com GET/POST e casos inválidos;
|
||||
- criação/update de cliente;
|
||||
- criação/update/toggle de produto;
|
||||
- criação de oportunidade;
|
||||
- add/delete de item de oportunidade;
|
||||
- lifecycle de tarefas: reclassify, reschedule, complete, complete-with-note, skip;
|
||||
- endpoints de reconciliação;
|
||||
- endpoints de integração Odoo;
|
||||
- operações de oportunidade quando aplicáveis.
|
||||
|
||||
3. Playwright UI opcional:
|
||||
- navegação real em browser;
|
||||
- clique em links de navegação;
|
||||
- submit visual de forms de cliente/produto/oportunidade;
|
||||
- botões de reconciliação;
|
||||
- captura de screenshots em falha;
|
||||
- deteção de `pageerror` e `console.error`.
|
||||
|
||||
## Comandos
|
||||
|
||||
Aplicar overlay:
|
||||
|
||||
```bash
|
||||
cd ~/Transferências/clientflow_backend_v4928_1_5_40_with_e2e_test_lab/cf_v1532_work
|
||||
unzip -o ~/Transferências/clientflow_e2e_deep_coverage_overlay.zip -d .
|
||||
chmod +x e2e_test_lab/scripts/*.sh
|
||||
source .venv/bin/activate
|
||||
pip install -r e2e_test_lab/requirements-e2e.txt
|
||||
```
|
||||
|
||||
Correr core + deep API/HTML:
|
||||
|
||||
```bash
|
||||
./e2e_test_lab/scripts/run_deep_e2e_lab.sh | tee e2e_test_lab/latest_deep_e2e_run.log
|
||||
```
|
||||
|
||||
Instalar browser Playwright:
|
||||
|
||||
```bash
|
||||
./e2e_test_lab/scripts/install_playwright_ui.sh
|
||||
```
|
||||
|
||||
Correr tudo incluindo browser real:
|
||||
|
||||
```bash
|
||||
RUN_PLAYWRIGHT_UI=1 ./e2e_test_lab/scripts/run_deep_e2e_lab.sh | tee e2e_test_lab/latest_deep_e2e_run.log
|
||||
```
|
||||
|
||||
Ver resumos:
|
||||
|
||||
```bash
|
||||
tail -120 e2e_test_lab/latest_deep_e2e_run.log
|
||||
cat e2e_test_lab/latest_deep_audit_report.md
|
||||
```
|
||||
|
||||
## Nota
|
||||
|
||||
Isto aumenta muito a cobertura, mas `100%` literal não é garantível sem instrumentação de cobertura por código, mapa completo de estados de negócio e dados de teste para todas as combinações. Este overlay aproxima o E2E de um teste de regressão funcional completo.
|
||||
18
E2E_FAILURE_HUNT_AUTH_README.md
Normal file
18
E2E_FAILURE_HUNT_AUTH_README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# ClientFlow Failure Hunt v2 — authenticated probes
|
||||
|
||||
Esta versão corrige o runner para enviar `X-ClientFlow-Admin-Token` usando `CLIENTFLOW_ADMIN_TOKEN` do `.env.e2e`.
|
||||
|
||||
A primeira versão encontrava muitos `401`, o que impedia os probes de chegar à lógica real da app. Esta versão acrescenta ainda probes para:
|
||||
|
||||
- contratos negativos de `/analyze` e `/webhooks/chatwoot`;
|
||||
- formulários POST/HTMX sem CSRF visível;
|
||||
- cache-control em páginas operacionais;
|
||||
- autocomplete em campos sensíveis;
|
||||
- validação interna autenticada de clientes, produtos, oportunidades, items e tarefas.
|
||||
|
||||
Executar:
|
||||
|
||||
```bash
|
||||
./e2e_test_lab/scripts/run_failure_hunt.sh | tee e2e_test_lab/latest_failure_hunt_run.log
|
||||
cat e2e_test_lab/latest_failure_hunt_report.md
|
||||
```
|
||||
15
E2E_FAILURE_HUNT_DBFIX_README.md
Normal file
15
E2E_FAILURE_HUNT_DBFIX_README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# ClientFlow E2E Failure Hunt DB URL Fix
|
||||
|
||||
Corrige o runner `failure_hunt_audit.py` para:
|
||||
|
||||
- aceitar `DATABASE_URL` no formato SQLAlchemy `postgresql+psycopg://...`;
|
||||
- converter esse URL para `postgresql://...` antes de chamar `psycopg.connect()`;
|
||||
- não abortar o relatório se uma secção exploratória falhar;
|
||||
- continuar a escrever `latest_failure_hunt_report.md` e `latest_failure_hunt_findings.json`.
|
||||
|
||||
Aplicar:
|
||||
|
||||
```bash
|
||||
unzip -o clientflow_e2e_failure_hunt_dbfix_overlay.zip -d .
|
||||
chmod +x e2e_test_lab/scripts/*.sh
|
||||
```
|
||||
47
E2E_FAILURE_HUNT_README.md
Normal file
47
E2E_FAILURE_HUNT_README.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# ClientFlow Failure Hunt Overlay
|
||||
|
||||
Camada exploratória para procurar falhas adicionais depois do E2E base e do Deep Audit.
|
||||
|
||||
## Objetivo
|
||||
|
||||
Encontrar até 10 achados prioritários sem inventar falsos positivos. O relatório mistura:
|
||||
|
||||
- `HTTP 500` e tracebacks;
|
||||
- validação ausente em formulários;
|
||||
- payloads inválidos/extremos;
|
||||
- IDs inválidos;
|
||||
- idempotência e concorrência;
|
||||
- duplicados Chatwoot/webhook;
|
||||
- possíveis XSS persistentes;
|
||||
- fuga de segredos/configuração;
|
||||
- headers de segurança ausentes;
|
||||
- ações perigosas sem confirmação.
|
||||
|
||||
## Aplicar
|
||||
|
||||
```bash
|
||||
cd ~/Transferências/clientflow_backend_v4928_1_5_40_with_e2e_test_lab/cf_v1532_work
|
||||
unzip -o ~/Transferências/clientflow_e2e_failure_hunt_overlay.zip -d .
|
||||
chmod +x e2e_test_lab/scripts/*.sh
|
||||
source .venv/bin/activate
|
||||
pip install -r e2e_test_lab/requirements-e2e.txt
|
||||
```
|
||||
|
||||
## Correr
|
||||
|
||||
Com Terminal 1 mocks e Terminal 2 ClientFlow ativos:
|
||||
|
||||
```bash
|
||||
./e2e_test_lab/scripts/run_failure_hunt.sh | tee e2e_test_lab/latest_failure_hunt_run.log
|
||||
```
|
||||
|
||||
## Ver relatório
|
||||
|
||||
```bash
|
||||
cat e2e_test_lab/latest_failure_hunt_report.md
|
||||
cat e2e_test_lab/latest_failure_hunt_findings.json
|
||||
```
|
||||
|
||||
## Nota
|
||||
|
||||
Este runner é deliberadamente mais agressivo. Nem todo achado é um bug fatal; alguns são validação, hardening ou UX operacional. Deve ser usado para priorizar correções antes de produção.
|
||||
5
E2E_JASMIN_HEALTH_FIX_README.md
Normal file
5
E2E_JASMIN_HEALTH_FIX_README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Fix Jasmin /health no E2E Test Lab
|
||||
|
||||
Este overlay adiciona `/health` ao mock Jasmin e torna o `check_e2e_ports.sh` mais tolerante.
|
||||
|
||||
Depois de aplicar, é necessário reiniciar o Terminal 1 dos mocks, porque o processo Python antigo continua em memória.
|
||||
17
E2E_LLM_MOCK_FIX_README.md
Normal file
17
E2E_LLM_MOCK_FIX_README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Fix E2E LLM mock
|
||||
|
||||
Este overlay corrige o mock OpenRouter usado nos testes E2E.
|
||||
|
||||
Problema corrigido:
|
||||
|
||||
- O mock antigo lia o prompt completo, incluindo o system prompt.
|
||||
- O system prompt contém a lista de ações e a palavra `IGNORE_SPAM` / `spam`.
|
||||
- Como o mock tinha regra para `spam`, quase todos os casos `/analyze` eram classificados como `IGNORE_SPAM`.
|
||||
|
||||
Correção:
|
||||
|
||||
- O mock agora ignora o system prompt.
|
||||
- Lê apenas a última mensagem `role=user`.
|
||||
- Dentro desse prompt, extrai apenas o bloco depois de `Última mensagem do cliente:`.
|
||||
|
||||
Depois de aplicar o overlay, reinicia o Terminal 1 dos mocks.
|
||||
38
E2E_RUNTIME_FIX_README.md
Normal file
38
E2E_RUNTIME_FIX_README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Fix runtime E2E
|
||||
|
||||
Inclui:
|
||||
|
||||
- `.env.e2e` na raiz, com `APP_NAME="ClientFlow E2E"`.
|
||||
- `run_mock_servers.sh` a carregar automaticamente `.env.e2e`.
|
||||
- `start_clientflow_e2e.sh` para arrancar o ClientFlow no ambiente E2E.
|
||||
- `check_e2e_ports.sh` para confirmar portas antes do runner.
|
||||
- `run_e2e_lab.sh` com preflight amigável, em vez de traceback longo quando faltam mocks.
|
||||
|
||||
Uso:
|
||||
|
||||
```bash
|
||||
unzip -o clientflow_e2e_runtime_fix_overlay.zip -d .
|
||||
chmod +x e2e_test_lab/scripts/*.sh
|
||||
```
|
||||
|
||||
Terminal 1:
|
||||
|
||||
```bash
|
||||
source .venv/bin/activate
|
||||
./e2e_test_lab/scripts/run_mock_servers.sh
|
||||
```
|
||||
|
||||
Terminal 2:
|
||||
|
||||
```bash
|
||||
source .venv/bin/activate
|
||||
./e2e_test_lab/scripts/start_clientflow_e2e.sh
|
||||
```
|
||||
|
||||
Terminal 3:
|
||||
|
||||
```bash
|
||||
source .venv/bin/activate
|
||||
./e2e_test_lab/scripts/check_e2e_ports.sh
|
||||
./e2e_test_lab/scripts/run_e2e_lab.sh
|
||||
```
|
||||
38
E2E_START_FIX_README.md
Normal file
38
E2E_START_FIX_README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Correção de arranque E2E ClientFlow
|
||||
|
||||
Este overlay corrige dois problemas comuns:
|
||||
|
||||
1. cria `.env.e2e` na raiz do projeto, porque o exemplo estava em `e2e_test_lab/.env.e2e.example`;
|
||||
2. força o arranque através da `.venv` com `python -m uvicorn`, evitando usar o `uvicorn` instalado no sistema.
|
||||
|
||||
## Como aplicar
|
||||
|
||||
A partir da raiz do projeto `cf_v1532_work`:
|
||||
|
||||
```bash
|
||||
unzip -o clientflow_e2e_startup_fix_overlay.zip -d .
|
||||
chmod +x e2e_test_lab/scripts/*.sh
|
||||
./e2e_test_lab/scripts/bootstrap_e2e.sh
|
||||
```
|
||||
|
||||
Depois abre dois terminais:
|
||||
|
||||
Terminal 1:
|
||||
|
||||
```bash
|
||||
cd /caminho/para/cf_v1532_work
|
||||
./e2e_test_lab/scripts/start_mocks_e2e.sh
|
||||
```
|
||||
|
||||
Terminal 2:
|
||||
|
||||
```bash
|
||||
cd /caminho/para/cf_v1532_work
|
||||
./e2e_test_lab/scripts/start_clientflow_e2e.sh
|
||||
```
|
||||
|
||||
Quando o ClientFlow estiver ativo em `http://127.0.0.1:8020`, corre:
|
||||
|
||||
```bash
|
||||
./e2e_test_lab/scripts/run_e2e_lab.sh
|
||||
```
|
||||
25
E2E_WORKFLOW_ANOMALY_HUNT_README.md
Normal file
25
E2E_WORKFLOW_ANOMALY_HUNT_README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# ClientFlow E2E Workflow Anomaly Hunt
|
||||
|
||||
Camada de testes funcionais orientada para comportamento de negócio, não para detalhes técnicos.
|
||||
|
||||
Simula:
|
||||
|
||||
- vários orçamentos Jasmin no mesmo momento para o mesmo cliente;
|
||||
- várias faturas Jasmin para o mesmo NIF/valor;
|
||||
- várias vendas Odoo para o mesmo cliente, com e sem NIF;
|
||||
- comprovativo solto;
|
||||
- pedidos repetidos do mesmo cliente/produto;
|
||||
- conversa Chatwoot com mensagem duplicada e mudança de intenção para pagamento;
|
||||
- oportunidades marcadas como WON/LOST com tarefas/documentos pendentes;
|
||||
- ações operacionais repetidas.
|
||||
|
||||
Gera:
|
||||
|
||||
- `e2e_test_lab/latest_workflow_anomaly_report.md`
|
||||
- `e2e_test_lab/latest_workflow_anomaly_findings.json`
|
||||
|
||||
Executar:
|
||||
|
||||
```bash
|
||||
./e2e_test_lab/scripts/run_workflow_anomaly_hunt.sh | tee e2e_test_lab/latest_workflow_anomaly_run.log
|
||||
```
|
||||
23
E2E_WORKFLOW_ANOMALY_HUNT_V2_README.md
Normal file
23
E2E_WORKFLOW_ANOMALY_HUNT_V2_README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# ClientFlow Workflow Anomaly Hunt v2
|
||||
|
||||
Camada adicional de stress funcional, focada em:
|
||||
|
||||
- follow-ups duplicados, inválidos, em WON/LOST e invisíveis na lista de tarefas;
|
||||
- oportunidades paralelas para a mesma compra provável;
|
||||
- regressões perigosas de estado;
|
||||
- operations/centro de trabalho e todo duplicado/invisível;
|
||||
- documentos fora de ordem e pagamentos com valor divergente;
|
||||
- normalização de email/NIF para evitar clientes duplicados.
|
||||
|
||||
Executar:
|
||||
|
||||
```bash
|
||||
./e2e_test_lab/scripts/run_workflow_anomaly_hunt_v2.sh | tee e2e_test_lab/latest_workflow_anomaly_v2_run.log
|
||||
```
|
||||
|
||||
Relatórios:
|
||||
|
||||
```bash
|
||||
cat e2e_test_lab/latest_workflow_anomaly_v2_report.md
|
||||
cat e2e_test_lab/latest_workflow_anomaly_v2_findings.json
|
||||
```
|
||||
7
E2E_WORKFLOW_ANOMALY_RUNNER_FIX_README.md
Normal file
7
E2E_WORKFLOW_ANOMALY_RUNNER_FIX_README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Workflow Anomaly Hunt runner/schema fix
|
||||
|
||||
Corrige o runner de anomalias funcionais para:
|
||||
|
||||
- não usar `ON CONFLICT (idempotency_key)` quando a coluna não tem constraint UNIQUE;
|
||||
- limpar o item do próprio cenário antes de inserir;
|
||||
- continuar as fases seguintes e escrever relatório mesmo quando uma fase falha.
|
||||
15
FIX_E2E_COMMAND_NOT_FOUND.md
Normal file
15
FIX_E2E_COMMAND_NOT_FOUND.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Fix `E2E: comando não encontrado`
|
||||
|
||||
A causa era esta linha sem aspas no `.env.e2e`:
|
||||
|
||||
```bash
|
||||
APP_NAME=ClientFlow E2E
|
||||
```
|
||||
|
||||
Quando o ficheiro é carregado com `source .env.e2e`, o Bash interpreta `E2E` como se fosse um comando.
|
||||
|
||||
Este overlay substitui por:
|
||||
|
||||
```bash
|
||||
APP_NAME="ClientFlow E2E"
|
||||
```
|
||||
@@ -142,3 +142,9 @@ A navegação da UI admin foi reorganizada para separar operação diária de di
|
||||
## v4.8.4 — Fiscal Link Consistency Hotfix
|
||||
|
||||
Corrige a leitura do cliente fiscal em Operations: a fila passa a usar o cliente ligado à oportunidade antes de qualquer fallback da task. Isto evita cards que dizem "Cliente fiscal por associar" quando a ficha da task já mostra um cliente fiscal válido.
|
||||
|
||||
## Previsão comercial de receitas
|
||||
|
||||
A versão v4928.1.5.122 disponibiliza uma previsão ponderada do pipeline em `/finance/forecast` e na API interna `/api/internal/revenue-forecast`.
|
||||
|
||||
A previsão combina valor comercial, probabilidade da fase e atividade operacional. É uma ferramenta de gestão comercial e não substitui dados contabilísticos ou de tesouraria.
|
||||
|
||||
6
README_OVERLAY.txt
Normal file
6
README_OVERLAY.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
Overlay para adicionar o ClientFlow E2E Test Lab.
|
||||
|
||||
Copiar/descompactar este conteúdo na raiz do projeto clientflow_backend.
|
||||
O diretório criado é: e2e_test_lab/
|
||||
|
||||
Ver instruções completas em: e2e_test_lab/README.md
|
||||
12
README_OVERLAY_v4928_1_5_61.md
Normal file
12
README_OVERLAY_v4928_1_5_61.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Overlay v4928.1.5.61
|
||||
|
||||
Aplicar sobre v4928.1.5.60.
|
||||
|
||||
```bash
|
||||
cd /mnt/ssd/home/plx/clientflow_backend
|
||||
unzip -o /caminho/para/clientflow_workflow_decision_alignment_overlay.zip -d .
|
||||
python3 -m compileall -q app tests
|
||||
PYTHONPATH=. pytest -q
|
||||
sudo systemctl restart clientflow-api
|
||||
sudo systemctl status clientflow-api --no-pager -l
|
||||
```
|
||||
17
README_OVERLAY_v4928_1_5_62.md
Normal file
17
README_OVERLAY_v4928_1_5_62.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Overlay v4928.1.5.62
|
||||
|
||||
Task schema hotfix for production deployments where `opportunities.local_customer_id` is the fiscal/customer link column.
|
||||
|
||||
## Fixes
|
||||
|
||||
- Removes SQL references to non-existing `opportunities.fiscal_customer_id`.
|
||||
- Keeps the workflow engine alias `fiscal_customer_id` internally by mapping it from `local_customer_id`.
|
||||
- Fixes `/tasks/<id>` HTTP 500 caused by task detail joins.
|
||||
- Keeps fiscal/customer inheritance behavior introduced in v1.5.61 without requiring a DB migration.
|
||||
|
||||
## Validate
|
||||
|
||||
```bash
|
||||
python3 -m compileall -q app tests
|
||||
PYTHONPATH=. pytest -q
|
||||
```
|
||||
8
README_PLAYWRIGHT_CORS_FIX.txt
Normal file
8
README_PLAYWRIGHT_CORS_FIX.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
ClientFlow Playwright CORS/Auth fix v1.5.47
|
||||
|
||||
Aplicar:
|
||||
unzip -o clientflow_e2e_playwright_cors_fix_overlay.zip -d .
|
||||
chmod +x e2e_test_lab/scripts/*.sh
|
||||
|
||||
Correr:
|
||||
./e2e_test_lab/scripts/run_ui_e2e_playwright.sh | tee e2e_test_lab/latest_playwright_ui_run.log
|
||||
68
RELEASE_NOTES_STABILIZATION_20260708.md
Normal file
68
RELEASE_NOTES_STABILIZATION_20260708.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# ClientFlow Backend — versão de estabilização 2026-07-08
|
||||
|
||||
## Objetivo
|
||||
Consolidar a última versão do backend antes de novas funcionalidades, corrigindo falhas P0/P1 encontradas na review: suíte de testes não-verde, bug em `list_tasks()`, divergências no workflow de oportunidades, linking ambíguo, `.env.example` ausente e problemas de coleção do pytest.
|
||||
|
||||
## Correções principais
|
||||
|
||||
### Testes e packaging
|
||||
- Adicionado `pytest.ini` com `testpaths = tests`, `pythonpath = .` e `--import-mode=importlib`.
|
||||
- Removido o teste duplicado de raiz `test_v4928_1_5_72_opportunity_consistency_static.py` para evitar `import file mismatch`.
|
||||
- Adicionado `.env.example` com flags obrigatórias e opcionais documentadas.
|
||||
- Consolidado teste estático contraditório de `workflow_guard`: a implementação mantém compatibilidade com schema sem coluna `completed_at` e continua a aceitar `completed_at` quando a evidência vem em payload/dict.
|
||||
- Consolidado teste de materialização `PREPARE_ORDER` com a regra de arquitetura que remove esse código da triagem LLM em `action_catalog.py`.
|
||||
|
||||
### `task_service.py`
|
||||
- Corrigido bug P0 em `list_tasks()`: `customer_column` agora é calculado antes do SQL através de `opportunity_customer_column()` e tem fallback seguro para `local_customer_id`.
|
||||
- Removida duplicação acidental de coluna em `get_task_detail()`.
|
||||
|
||||
### Workflow de oportunidades
|
||||
- `WAIT_PRODUCTION` volta a mostrar a label operacional “Aguardar produção”, mantendo alias/compatibilidade UI para “Aguardar WH/OUT”.
|
||||
- Fluxo `after_delivery` com envio já criado e pagamento por confirmar passa a sugerir `FOLLOW_UP_PAYMENT`, em vez de voltar para `PREPARE_ORDER`.
|
||||
- Mantida prioridade de fecho/entrega sobre estados de produção quando WH/OUT/picking já está concluído.
|
||||
- Evidência de fatura enviada continua a considerar payload do documento e tasks `SEND_INVOICE` concluídas.
|
||||
|
||||
### UI/admin
|
||||
- A página de oportunidades filtra tasks de pagamento/follow-up obsoletas quando o pagamento já está confirmado.
|
||||
- O resumo operacional passa a usar `display_next_action_code` para evitar fallback para `last_action_code` antigo.
|
||||
- Adicionada mensagem explícita quando o Jasmin existe mas não tem novos campos fiscais para importar.
|
||||
- `admin_dashboard.py` ficou abaixo do limite legado de 1800 linhas sem alterar rotas principais.
|
||||
|
||||
### Segurança funcional / linking
|
||||
- Ambiguidade de oportunidades por contacto Chatwoot passa a usar razão explícita `multiple_recent_open_opportunities_for_chatwoot_contact`.
|
||||
- `action_catalog.py` mantém `PREPARE_ORDER` fora do catálogo de triagem LLM; o código interno é tratado dinamicamente para compatibilidade operacional.
|
||||
|
||||
### Outros ajustes
|
||||
- Corrigidos warnings de `compileall` por escapes inválidos em SQL LIKE/ESCAPE.
|
||||
- Adicionado marcador de identificação ao script `probe_jasmin_print_layout_catalog.py`.
|
||||
|
||||
## Validação local
|
||||
|
||||
Com variáveis de teste:
|
||||
|
||||
```bash
|
||||
OPENROUTER_API_KEY=test \
|
||||
DATABASE_URL=postgresql+psycopg://u:p@localhost:5432/db \
|
||||
CLIENTFLOW_ADMIN_TOKEN=test \
|
||||
PYTHONPATH=. \
|
||||
pytest -q
|
||||
```
|
||||
|
||||
Resultado:
|
||||
|
||||
```text
|
||||
463 passed in 1.12s
|
||||
```
|
||||
|
||||
Compilação:
|
||||
|
||||
```bash
|
||||
python -m compileall -q app scripts tests
|
||||
```
|
||||
|
||||
Resultado: sem erros e sem warnings reportados.
|
||||
|
||||
## Limitações
|
||||
- Não foram executadas integrações reais com Postgres, Jasmin, Odoo, Packlink, Chatwoot ou OpenAI/OpenRouter.
|
||||
- A validação foi feita por testes locais/estáticos/unitários e compilação Python.
|
||||
- Antes de produção, correr migrações e smoke tests contra uma base staging.
|
||||
34
RELEASE_NOTES_v4928_1_4_5.md
Normal file
34
RELEASE_NOTES_v4928_1_4_5.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# ClientFlow backend v4928.1.4.5 — outbox claim psycopg hotfix
|
||||
|
||||
Hotfix para o worker `clientflow-outbox-jasmin.service` falhar antes de reclamar itens pendentes da `integration_outbox` com:
|
||||
|
||||
```text
|
||||
psycopg.errors.AmbiguousParameter: could not determine data type of parameter $1
|
||||
LINE 6: AND ($1 IS NULL OR target_system = $1)
|
||||
```
|
||||
|
||||
## Alteração
|
||||
|
||||
- `app/integration_outbox_service.py`
|
||||
- `claim_pending_outbox()` deixa de usar a expressão SQL opcional `(:target_system IS NULL OR target_system = :target_system)`.
|
||||
- A cláusula `AND target_system = :target_system` passa a ser adicionada apenas quando `target_system` existe.
|
||||
|
||||
## Validação local
|
||||
|
||||
```bash
|
||||
python3 -m compileall -q app scripts
|
||||
```
|
||||
|
||||
## Teste recomendado em produção
|
||||
|
||||
```bash
|
||||
cd /mnt/ssd/home/plx/clientflow_backend
|
||||
|
||||
OUTBOX_TARGET_SYSTEM=jasmin \
|
||||
JASMIN_OUTBOX_ENABLED=true \
|
||||
OUTBOX_DRY_RUN=false \
|
||||
.venv/bin/python scripts/process_outbox.py
|
||||
|
||||
sudo systemctl restart clientflow-outbox-jasmin.service
|
||||
sudo systemctl status clientflow-outbox-jasmin.service --no-pager -l
|
||||
```
|
||||
65
RELEASE_NOTES_v4928_1_4_6.md
Normal file
65
RELEASE_NOTES_v4928_1_4_6.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# ClientFlow backend v4928.1.4.6 — auto-create Jasmin customer before quotation
|
||||
|
||||
Hotfix/feature curta para o fluxo Jasmin `create_quotation`.
|
||||
|
||||
## Problema
|
||||
|
||||
O worker Jasmin passou a processar corretamente a outbox, mas falhava ao criar orçamento quando o cliente fiscal validado no ClientFlow ainda não existia como Customer no Jasmin:
|
||||
|
||||
```text
|
||||
Jasmin error 400 em GET /salesCore/customerParties/getCustomerByCompanyTaxId/<NIF>:
|
||||
The company tax id provided does not correspond to an existing Customer.
|
||||
```
|
||||
|
||||
## Alteração
|
||||
|
||||
- `app/jasmin_service.py`
|
||||
- Reconhece esse erro específico de Customer inexistente como caso esperado.
|
||||
- Quando o cliente fiscal local está validado e tem dados mínimos, cria automaticamente o Customer no Jasmin antes do orçamento.
|
||||
- Mantém erro explícito se faltarem dados obrigatórios para criar Customer: NIF, nome fiscal, morada, código postal ou cidade.
|
||||
- A confirmação pós-criação por NIF deixa de bloquear o fluxo se o Jasmin ainda não indexou imediatamente o novo Customer; nesses casos usa a `partyKey` enviada e grava `confirm_warning` em metadata.
|
||||
|
||||
## Guardrails
|
||||
|
||||
- Outros erros Jasmin continuam a falhar normalmente.
|
||||
- Não cria Customer sem NIF.
|
||||
- Não cria Customer sem nome fiscal.
|
||||
- Não cria Customer sem morada fiscal mínima.
|
||||
- O orçamento só é criado depois de existir `jasmin_customer_party_key` local.
|
||||
|
||||
## Validação local
|
||||
|
||||
```bash
|
||||
python3 -m compileall -q app scripts tests
|
||||
pytest -q tests/test_v4928_1_4_6_jasmin_autocustomer_static.py
|
||||
```
|
||||
|
||||
Resultado local:
|
||||
|
||||
```text
|
||||
3 passed
|
||||
```
|
||||
|
||||
## Teste recomendado em produção
|
||||
|
||||
Reprocessar o item que falhou por Customer inexistente:
|
||||
|
||||
```bash
|
||||
sudo -u postgres psql -d clientflow -c "
|
||||
UPDATE integration_outbox
|
||||
SET status='pending',
|
||||
retry_count=0,
|
||||
last_error=NULL,
|
||||
locked_at=NULL,
|
||||
lock_owner=NULL,
|
||||
updated_at=now()
|
||||
WHERE id='97a36017-4ff1-4ace-a1b0-b90cc5c5cf84';
|
||||
"
|
||||
|
||||
OUTBOX_TARGET_SYSTEM=jasmin \
|
||||
JASMIN_OUTBOX_ENABLED=true \
|
||||
OUTBOX_DRY_RUN=false \
|
||||
.venv/bin/python scripts/process_outbox.py
|
||||
```
|
||||
|
||||
Depois confirmar se o item fica `sent` e se foi criado o documento Jasmin na oportunidade.
|
||||
36
RELEASE_NOTES_v4928_1_4_9.md
Normal file
36
RELEASE_NOTES_v4928_1_4_9.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Release Notes — ClientFlow v4928.1.4.9
|
||||
|
||||
## Tema
|
||||
|
||||
Opportunity Reply Assistant: responder ao cliente a partir da tarefa/oportunidade, usando modelos comerciais e anexos já associados à oportunidade.
|
||||
|
||||
## Alterações principais
|
||||
|
||||
- Adicionado catálogo de modelos comerciais em `app/message_templates.py`.
|
||||
- Adicionado serviço `app/reply_assistant_service.py` para gerar rascunhos, validar contexto e enviar resposta.
|
||||
- Adicionada tabela `message_drafts` no arranque do schema.
|
||||
- Adicionado bloco **Resposta ao cliente** na página de detalhe da tarefa.
|
||||
- Adicionados endpoints:
|
||||
- `POST /tasks/{task_id}/reply-draft`
|
||||
- `POST /tasks/{task_id}/send-reply`
|
||||
- Adicionadas funções públicas de envio Chatwoot:
|
||||
- `send_public_message()`
|
||||
- `send_public_message_with_attachments()`
|
||||
- Adicionado registo outbound em `communications`.
|
||||
- Adicionado evento `reply_sent` na timeline da oportunidade.
|
||||
- Adicionada opção de enviar e concluir tarefa no mesmo passo.
|
||||
- Adicionada variável futura `CLIENTFLOW_REPLY_LLM_ENABLED=false`.
|
||||
|
||||
## Segurança operacional
|
||||
|
||||
- Só permite anexar documentos da mesma oportunidade da tarefa.
|
||||
- Bloqueia envio sem conversa Chatwoot.
|
||||
- Bloqueia envio fiscal se cliente fiscal estiver incompleto.
|
||||
- Bloqueia PDF automático para documentos sem `external_id`.
|
||||
- Mantém `CHATWOOT_WRITE_ENABLED=false` como travão de segurança por defeito.
|
||||
|
||||
## Testes
|
||||
|
||||
- Adicionado `tests/test_v4928_1_4_9_reply_assistant_static.py`.
|
||||
- Validação executada: `225 passed`.
|
||||
|
||||
28
RELEASE_NOTES_v4928_1_5_0.md
Normal file
28
RELEASE_NOTES_v4928_1_5_0.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Release notes — ClientFlow v4928.1.5.0
|
||||
|
||||
## BLIF Knowledge Reply Assistant
|
||||
|
||||
Esta versão implementa o caminho realista para o LLM ter conhecimento do negócio sem fine-tuning.
|
||||
|
||||
### Adicionado
|
||||
|
||||
- `app/business_knowledge/blif_knowledge.json`
|
||||
- `app/business_knowledge_service.py`
|
||||
- `app/llm_reply_generator.py`
|
||||
- `app/reply_safety_validator.py`
|
||||
- novos templates de conhecimento em `app/message_templates.py`
|
||||
- painel de conhecimento usado na UI da tarefa
|
||||
- novas variáveis `CLIENTFLOW_REPLY_LLM_*`
|
||||
|
||||
### Comportamento novo
|
||||
|
||||
- Se a mensagem do cliente pergunta sobre instalação, IVA, entrega, RFID, balanceador, histórico local, condomínio/MOBI.E ou funcionalidades técnicas, o assistente pode sugerir uma resposta sem anexo.
|
||||
- O LLM via OpenRouter é opcional e desativado por defeito.
|
||||
- Quando o LLM está desativado, os templates determinísticos com conhecimento BLIF continuam a funcionar.
|
||||
- O envio para Chatwoot continua sujeito à validação da oportunidade, anexos e regras comerciais.
|
||||
|
||||
### Exemplo corrigido
|
||||
|
||||
Pergunta: “Presumo que o valor é sem instalação, pode confirmar?”
|
||||
|
||||
Resposta sugerida: confirmação de que o valor é do equipamento, sem instalação, com indicação de eletricista qualificado e suporte remoto BLIF. Sem exigir documento/anexo.
|
||||
40
RELEASE_NOTES_v4928_1_5_1.md
Normal file
40
RELEASE_NOTES_v4928_1_5_1.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Release Notes — ClientFlow v4928.1.5.1
|
||||
|
||||
## Task Reply Audit
|
||||
|
||||
Adicionado script para consultar tarefas e verificar, em lote, qual é o pedido do cliente e qual é a resposta sugerida pelo Reply Assistant.
|
||||
|
||||
## Novo ficheiro
|
||||
|
||||
```text
|
||||
scripts/audit_task_reply_suggestions.py
|
||||
```
|
||||
|
||||
## Documentação
|
||||
|
||||
```text
|
||||
docs/CLIENTFLOW_V4928_1_5_1_TASK_REPLY_AUDIT.md
|
||||
```
|
||||
|
||||
## Funcionalidades
|
||||
|
||||
- Consulta tarefas por estado, fila/route, pesquisa textual e limite.
|
||||
- Gera rascunho de resposta por tarefa usando `generate_reply_draft(..., persist=False)`.
|
||||
- Não envia mensagens e não conclui tarefas.
|
||||
- Não persiste rascunhos por defeito.
|
||||
- Desativa LLM por defeito para evitar custos.
|
||||
- Exporta relatório em Markdown, CSV ou JSON.
|
||||
- Inclui pedido do cliente, template escolhido, conhecimento BLIF usado, resposta sugerida, bloqueios e avisos.
|
||||
|
||||
## Comandos úteis
|
||||
|
||||
```bash
|
||||
python scripts/audit_task_reply_suggestions.py --status pending --format markdown --out /tmp/task_reply_audit.md
|
||||
python scripts/audit_task_reply_suggestions.py --status all --format csv --out /tmp/task_reply_audit.csv
|
||||
python scripts/audit_task_reply_suggestions.py --status pending --use-llm --format json
|
||||
```
|
||||
|
||||
## Validação
|
||||
|
||||
- `python scripts/audit_task_reply_suggestions.py --help`
|
||||
- Teste estático dedicado: `tests/test_v4928_1_5_1_task_reply_audit_static.py`
|
||||
26
RELEASE_NOTES_v4928_1_5_10.md
Normal file
26
RELEASE_NOTES_v4928_1_5_10.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# ClientFlow v4928.1.5.10 — Reply recipient from email signature
|
||||
|
||||
## Objetivo
|
||||
Corrigir a saudação dos rascunhos quando a oportunidade/cliente fiscal é uma empresa, mas a última mensagem está assinada por uma pessoa.
|
||||
|
||||
## Alterações
|
||||
- Novo helper `app/reply_recipient_utils.py`.
|
||||
- Extrai nome pessoal da assinatura da última mensagem do cliente.
|
||||
- Prioriza a pessoa que assinou a mensagem sobre nome da empresa, cliente fiscal ou oportunidade.
|
||||
- Evita cumprimentos como `Boa tarde Amorasub,` quando existe `Alexandra Silvestre` na assinatura.
|
||||
- Expõe ao LLM/OpenRouter contexto separado:
|
||||
- `recipient.person_name`
|
||||
- `recipient.company_name`
|
||||
- `preferred_greeting`
|
||||
- `greeting_source`
|
||||
- Expõe ao agente OpenAI/file_search contexto separado:
|
||||
- `destinatario_resposta.nome_pessoa`
|
||||
- `destinatario_resposta.nome_empresa`
|
||||
- `destinatario_resposta.cumprimento_preferido`
|
||||
- Reforça prompt para follow-ups sobre morada/endereço: confirmar receção da morada e indicar próximo passo concreto, em vez de resposta vaga.
|
||||
- Badge UI atualizado para `v4928.1.5.10`.
|
||||
|
||||
## Validação
|
||||
- `python -m compileall -q app scripts tests`
|
||||
- `pytest -q`
|
||||
- Resultado local: `275 passed`.
|
||||
@@ -0,0 +1,5 @@
|
||||
# v4928 1.5.100 — Jasmin ORC→FA API probe
|
||||
|
||||
Adiciona `scripts/probe_jasmin_quotation_invoice_api.py`, uma ferramenta segura para analisar um orçamento Jasmin específico, comparar PDF do ORC e FA ligada, inspecionar campos de layout/report/série e testar variantes controladas do endpoint `POST /billing/invoices/fromQuotation/{id}`.
|
||||
|
||||
Por defeito é read-only. Só cria fatura real quando usado com `--execute-convert` e `--confirm-document` exatamente igual ao orçamento.
|
||||
@@ -0,0 +1,7 @@
|
||||
# v4.928.1.5.101 — Probe API de impressão Jasmin FA
|
||||
|
||||
Adiciona `scripts/probe_jasmin_invoice_print_api.py`, auditoria read-only para testar variantes do endpoint `/billing/invoices/{id}/print` com parâmetros de layout/report/papel.
|
||||
|
||||
Objetivo: confirmar se é possível obter PDF A4 via API para faturas FA que, por defeito, saem em formato estreito após conversão ORC→FA.
|
||||
|
||||
Não cria, altera, envia nem cancela documentos.
|
||||
@@ -0,0 +1,8 @@
|
||||
# v4.928.1.5.102 — Jasmin print layout catalog probe
|
||||
|
||||
Adds a read-only Jasmin API probe to search for print-layout/report catalog endpoints containing UI labels such as "Fatura de Mercadorias" and "Talão de Fatura".
|
||||
|
||||
- New script: `scripts/probe_jasmin_print_layout_catalog.py`
|
||||
- Searches likely REST/OData catalog endpoints for visible print model labels.
|
||||
- Optional guarded `--probe-print-post` can test POST `/billing/invoices/{id}/print` with JSON bodies; requires `--confirm-invoice`.
|
||||
- Does not create documents and does not call `fromQuotation`.
|
||||
@@ -0,0 +1,5 @@
|
||||
# v4.928.1.5.103 — Jasmin invoice print template model probe
|
||||
|
||||
Adds `scripts/probe_jasmin_invoice_print_template_models.py` to test the print model keys visible in the Jasmin UI (`Document`, `Slip`, `SlipWithShippingDetails`) against the invoice `/print` endpoint and `/reporting/templates/list?listname=templates` routes.
|
||||
|
||||
Read-only by default. Optional POST `/print` variants require explicit confirmation.
|
||||
@@ -0,0 +1,29 @@
|
||||
# v4928.1.5.104 — concluir quando Odoo está fechado e pagamento confirmado
|
||||
|
||||
## Objetivo
|
||||
|
||||
Alinhar o fluxo pós-expedição: por agora, quando o Odoo já indica picking/entrega concluído e o pagamento está confirmado, o ClientFlow deve sugerir **Concluir oportunidade**, não criar/mostrar follow-up de tracking/entrega.
|
||||
|
||||
## Alterações
|
||||
|
||||
- Adiciona `CLOSE_OPPORTUNITY` ao vocabulário de ações do motor central.
|
||||
- Atualiza regras BLIF:
|
||||
- `invoice + payment_confirmed + order_shipped/order_delivered` → `CLOSE_OPPORTUNITY`.
|
||||
- no pagamento pós-entrega, só mantém `FOLLOW_UP_PAYMENT` enquanto o pagamento não estiver confirmado.
|
||||
- Atualiza o cockpit operacional (`workflow_guard`):
|
||||
- reconhece `physical_status=shipped/done/delivered` ou pickings Odoo `done` como fluxo físico fechado;
|
||||
- se houver fatura + pagamento confirmado + Odoo fechado, mostra botão **Concluir oportunidade**;
|
||||
- deixa de sugerir “Aguardar Odoo” quando Odoo já fechou;
|
||||
- não mostra “confirmar tracking/entrega” como próxima ação automática neste cenário.
|
||||
- Mantém bloqueio se pagamento pós-entrega ainda não estiver confirmado.
|
||||
|
||||
## Validação
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m py_compile \
|
||||
app/domain/opportunity_flow/types.py \
|
||||
app/domain/opportunity_flow/rules.py \
|
||||
app/workflow_guard.py
|
||||
|
||||
PYTHONPATH=. pytest -q tests/test_v4928_1_5_104_close_when_odoo_done_payment_confirmed_static.py
|
||||
```
|
||||
@@ -0,0 +1,44 @@
|
||||
# v4928.1.5.105 — fechar oportunidade só com fatura enviada e WH/OUT concluído
|
||||
|
||||
## Objetivo
|
||||
|
||||
Alinhar a decisão final da oportunidade com o fluxo operacional BLIF:
|
||||
|
||||
- pagamento antes do envio: orçamento → pagamento → fatura → envio fatura → Odoo/WH-OUT → concluir;
|
||||
- pagamento pós-entrega: Odoo/WH-OUT → fatura → envio fatura → follow-up pagamento → concluir.
|
||||
|
||||
## Alterações
|
||||
|
||||
- `CLOSE_OPPORTUNITY` só é sugerido quando existe:
|
||||
- fatura associada;
|
||||
- evidência de fatura enviada ao cliente;
|
||||
- pagamento confirmado;
|
||||
- venda Odoo ligada;
|
||||
- picking/WH-OUT concluído.
|
||||
- Se WH/OUT está concluído mas a fatura ainda não foi enviada, a próxima ação volta a ser `SEND_INVOICE`.
|
||||
- Se o pagamento é pós-entrega e WH/OUT/fatura enviada estão OK, mas pagamento ainda não está confirmado, a próxima ação é `FOLLOW_UP_PAYMENT`.
|
||||
- `FOLLOW_UP_PAYMENT` passa a poder ser materializado automaticamente em task pendente, tal como `SEND_INVOICE`.
|
||||
- Ordens de fabrico/MO (`WH/MO/...`) deixam de conduzir a decisão principal; ficam apenas como detalhe técnico Odoo.
|
||||
- O painel Odoo da oportunidade passa a apresentar produção/preparação dentro de `<details>` técnico, deixando a venda e WH/OUT como sinal operacional principal.
|
||||
- `workflow_guard.delivered` passa a bloquear fecho sem evidência de fatura enviada.
|
||||
|
||||
## Validação rápida
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m py_compile \
|
||||
app/domain/opportunity_flow/evidence.py \
|
||||
app/domain/opportunity_flow/rules.py \
|
||||
app/workflow_guard.py \
|
||||
app/opportunity_action_task_materializer.py \
|
||||
app/admin_ui/pages/opportunities.py \
|
||||
scripts/repair_missing_materialized_next_action_tasks.py
|
||||
|
||||
PYTHONPATH=. pytest -q tests/test_v4928_1_5_105_odoo_done_close_requires_invoice_sent_static.py
|
||||
```
|
||||
|
||||
## Backfill opcional
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python scripts/repair_missing_materialized_next_action_tasks.py
|
||||
PYTHONPATH=. python scripts/repair_missing_materialized_next_action_tasks.py --apply
|
||||
```
|
||||
@@ -0,0 +1,15 @@
|
||||
# v4928.1.5.106 — Completed SEND_INVOICE task as invoice-sent evidence
|
||||
|
||||
Fixes reconstructed opportunities where the invoice had already been sent via a completed `SEND_INVOICE` task, but `commercial_documents.payload` did not carry a local invoice-sent marker.
|
||||
|
||||
## Changes
|
||||
|
||||
- `build_opportunity_evidence()` now infers `invoice_sent=True` from completed `SEND_INVOICE` tasks in the same opportunity.
|
||||
- Ignored/cancelled/pending send-invoice tasks still do not count.
|
||||
- If a completed task names a different invoice, it does not count for the current invoice.
|
||||
- Prevents opportunities like VIDRALGAR from regressing from `CLOSE_OPPORTUNITY` back to `SEND_INVOICE`.
|
||||
|
||||
## Expected behavior
|
||||
|
||||
- Invoice exists + payment confirmed + WH/OUT done + completed SEND_INVOICE task → `CLOSE_OPPORTUNITY`.
|
||||
- Invoice exists + payment confirmed + WH/OUT done + no invoice-sent evidence → `SEND_INVOICE`.
|
||||
@@ -0,0 +1,22 @@
|
||||
# v4928.1.5.107 — close button and cockpit alignment
|
||||
|
||||
Fixes a UI/action mismatch introduced while moving close decisions to the central
|
||||
next-action engine.
|
||||
|
||||
## Changes
|
||||
|
||||
- The primary `Concluir oportunidade` button now performs the real POST action:
|
||||
`POST /opportunities/{id}/operations/delivered`.
|
||||
- The legacy operational cockpit now mirrors the central `get_opportunity_next_action()`
|
||||
decision when it is passed by the opportunity detail page.
|
||||
- Prevents cases where the top card says `Concluir oportunidade` but the lower
|
||||
`Fluxo operacional` still shows an old legacy action such as `Enviar fatura ao cliente`.
|
||||
|
||||
## Expected behavior
|
||||
|
||||
For opportunities with invoice sent + payment confirmed + WH/OUT done:
|
||||
|
||||
- top action: `Concluir oportunidade`;
|
||||
- cockpit action: `Concluir oportunidade`;
|
||||
- clicking the button closes/registers the opportunity through the existing
|
||||
`delivered` operation, subject to workflow guards.
|
||||
28
RELEASE_NOTES_v4928_1_5_108.md
Normal file
28
RELEASE_NOTES_v4928_1_5_108.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# ClientFlow v4.928.1.5.108 — pipeline centralizado + indicadores WH/OUT
|
||||
|
||||
## Correções
|
||||
|
||||
- A página `/opportunities` passa a usar `get_opportunity_next_action()` nos cards do pipeline.
|
||||
- Remove labels legacy na listagem como `Enviar tracking` quando o detalhe já recomenda `CLOSE_OPPORTUNITY`.
|
||||
- Reclassifica visualmente cards por próxima ação central: financeiro, envio/Odoo ou conclusão.
|
||||
- Corrige indicadores do cockpit operacional:
|
||||
- `Estado físico Odoo` fica verde quando WH/OUT/picking está `done`/`shipped`.
|
||||
- `Envio` fica verde quando WH/OUT está concluído no Odoo, mesmo sem Packlink/tracking.
|
||||
- `Produção`/WH/MO deixa de aparecer no mapa principal; fica apenas em detalhe técnico Odoo.
|
||||
- Renomeia ação de espera operacional para `Aguardar WH/OUT`, para não sugerir acompanhamento de produção.
|
||||
- Remove uma duplicação visual de `N resultado(s)` no cabeçalho do quadro.
|
||||
|
||||
## Regra operacional mantida
|
||||
|
||||
Para concluir oportunidade:
|
||||
|
||||
```text
|
||||
fatura emitida
|
||||
+ fatura enviada
|
||||
+ pagamento confirmado
|
||||
+ venda Odoo ligada
|
||||
+ WH/OUT done
|
||||
→ Concluir oportunidade
|
||||
```
|
||||
|
||||
WH/MO/produção não bloqueia nem conduz a próxima ação do operador.
|
||||
21
RELEASE_NOTES_v4928_1_5_109.md
Normal file
21
RELEASE_NOTES_v4928_1_5_109.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# v4928.1.5.109 — conclusão manual por canal externo
|
||||
|
||||
## Correção
|
||||
|
||||
Tarefas de comunicação/documentos (`SEND_PROFORMA`, `SEND_QUOTE`, `SEND_INVOICE` e follow-ups) sem email nem conversa Chatwoot passam a mostrar uma opção explícita para registar que o contacto/documento foi tratado por canal externo, por exemplo WhatsApp, telefone ou presencial.
|
||||
|
||||
## Impacto
|
||||
|
||||
- Um orçamento Jasmin já criado e enviado fora do ClientFlow pode ser marcado como enviado sem exigir email/Chatwoot.
|
||||
- A validação fiscal continua visível como aviso, mas não bloqueia o registo manual quando existe documento associado e o operador confirma canal externo.
|
||||
- A nota de conclusão regista o canal externo usado.
|
||||
|
||||
## Exemplo
|
||||
|
||||
Oportunidade manual criada por pedido WhatsApp, sem email/conversa Chatwoot:
|
||||
|
||||
- `ORC.ORC2026.200`
|
||||
- task `SEND_PROFORMA`
|
||||
- operador envia PDF por WhatsApp
|
||||
- operador usa “Marcar como enviado externamente”
|
||||
- task passa a `done` e o fluxo avança para confirmação de pagamento.
|
||||
16
RELEASE_NOTES_v4928_1_5_11.md
Normal file
16
RELEASE_NOTES_v4928_1_5_11.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# ClientFlow v4928.1.5.11 — Fiscal identity match guard
|
||||
|
||||
Correção focada no bug em que uma oportunidade Verifone/Nuno Silva foi auto-associada ao cliente fiscal ERT TÊXTIL por partilhar apenas o token genérico “Portugal”.
|
||||
|
||||
## Correções
|
||||
|
||||
- `fiscal_enrichment_service.py` deixa de tratar tokens fracos como `Portugal`, `S.A.`, `LDA`, `Unipessoal`, `Grupo` como evidência suficiente de correspondência fiscal.
|
||||
- `Verifone Portugal` já não pode fazer match com `ERT TÊXTIL PORTUGAL, S.A.` por token comum genérico.
|
||||
- Mantém correspondências reais por nome exato/substrings fortes, por exemplo `Dietimport S.A.` e `DIETIMPORT, S.A.`.
|
||||
- Se uma sugestão fiscal já foi rejeitada manualmente, o worker evita recriar/aplicar a mesma associação em execuções futuras.
|
||||
- Badge UI atualizado para `v4928.1.5.11`.
|
||||
|
||||
## Validação
|
||||
|
||||
- `python -m compileall -q app scripts tests`
|
||||
- `pytest -q` → 278 passed
|
||||
17
RELEASE_NOTES_v4928_1_5_110.md
Normal file
17
RELEASE_NOTES_v4928_1_5_110.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# v4928.1.5.110 — close guard recognizes completed invoice-send tasks
|
||||
|
||||
Fixes a mismatch where the opportunity detail and next-action engine could show
|
||||
`CLOSE_OPPORTUNITY`, but the operation button was blocked with “Concluir
|
||||
oportunidade exige evidência de fatura enviada ao cliente”.
|
||||
|
||||
The workflow guard now treats completed `SEND_INVOICE` tasks for the same current
|
||||
invoice as local evidence that the invoice was sent to the customer, matching the
|
||||
central opportunity-flow evidence builder.
|
||||
|
||||
Expected for VIDRALGAR / Maria Cândida style cases:
|
||||
|
||||
- invoice linked
|
||||
- payment confirmed
|
||||
- WH/OUT done
|
||||
- completed `SEND_INVOICE` task
|
||||
- `Concluir oportunidade` button allowed
|
||||
6
RELEASE_NOTES_v4928_1_5_111.md
Normal file
6
RELEASE_NOTES_v4928_1_5_111.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# v4928.1.5.111 — Task external completion scope hotfix
|
||||
|
||||
Corrige `UnboundLocalError: external_completion_html referenced before assignment` ao abrir detalhe de tarefa após a introdução do bloco de conclusão por canal externo.
|
||||
|
||||
- Inicializa `external_completion_html` antes do bloco `completion_html` em `task_detail_bootstrap_page`.
|
||||
- Mantém a opção de concluir tarefas enviadas por WhatsApp/outro canal externo.
|
||||
16
RELEASE_NOTES_v4928_1_5_112.md
Normal file
16
RELEASE_NOTES_v4928_1_5_112.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# v4928.1.5.112 — workflow guard tasks schema hotfix
|
||||
|
||||
Hotfix para o Internal Server Error em `/opportunities/{id}` depois da v1.5.110.
|
||||
|
||||
## Corrige
|
||||
|
||||
- `app/workflow_guard.py` deixava de abrir a oportunidade quando a tabela `tasks` não tinha coluna `completed_at`.
|
||||
- A query de evidência de envio de fatura passa a usar apenas colunas existentes no schema atual: `id`, `action_code`, `action`, `note`, `status`, `metadata`.
|
||||
- A evidência de fatura enviada continua a aceitar `status = done/completed/closed/concluída`.
|
||||
|
||||
## Validação
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m py_compile app/workflow_guard.py
|
||||
PYTHONPATH=. pytest -q tests/test_v4928_1_5_112_workflow_guard_tasks_schema_static.py
|
||||
```
|
||||
7
RELEASE_NOTES_v4928_1_5_113.md
Normal file
7
RELEASE_NOTES_v4928_1_5_113.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# v4928.1.5.113 — Opportunity detail deadlock guard
|
||||
|
||||
Hotfix for opportunity detail pages failing with `500 Internal Server Error` when PostgreSQL raises a transient deadlock while the advanced manual correction card reads `reconciliation_items` during reconciliation/sync jobs.
|
||||
|
||||
- Retries `_opportunity_manual_correction_state()` once on deadlock/lock-timeout errors.
|
||||
- Falls back to a safe degraded UI message instead of crashing the opportunity page.
|
||||
- Keeps the correction card auxiliary; the central opportunity flow continues rendering.
|
||||
17
RELEASE_NOTES_v4928_1_5_115.md
Normal file
17
RELEASE_NOTES_v4928_1_5_115.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# v4928.1.5.115 — Arquivar spam fora do funil + continuidade follow-ups em cascata
|
||||
|
||||
## Corrige
|
||||
|
||||
- Oportunidades criadas por spam/falso positivo deixam de ser marcadas como `LOST`.
|
||||
- Novo estado técnico/local: `stage = ARCHIVED`, `status = archived`.
|
||||
- Metadata marca `exclude_from_funnel = true`, `archived_reason = spam`.
|
||||
- Oportunidades arquivadas deixam de aparecer nas abertas e não contaminam estatísticas de perda.
|
||||
- Reclassificar uma task para `IGNORE_SPAM` arquiva automaticamente a oportunidade associada quando for seguro.
|
||||
- Ignorar uma task já classificada como `IGNORE_SPAM` também tenta arquivar a oportunidade quando for seguro.
|
||||
- A página da oportunidade ganha ação segura “Arquivar spam/falso positivo”; recusa se houver documentos Jasmin, Odoo, Packlink ou reconciliação externa.
|
||||
- Inclui script de manutenção: `scripts/archive_spam_opportunities.py`.
|
||||
|
||||
## Mantém
|
||||
|
||||
- Follow-ups em cascata da v1.5.114: cria apenas o próximo follow-up necessário, não três de uma vez.
|
||||
- Follow-ups param quando a oportunidade avança, fecha, arquiva ou ganha ação mais concreta.
|
||||
@@ -0,0 +1,18 @@
|
||||
# v4928.1.5.116 — PREPARE_ORDER materializado e indicadores Odoo corrigidos
|
||||
|
||||
## Correções
|
||||
|
||||
- Materializa `PREPARE_ORDER` como task humana pendente em `operacoes` quando a próxima ação central é preparar/criar/validar venda Odoo.
|
||||
- Evita que o mapa operacional marque `Venda` como concluída apenas porque já existe fatura/pagamento/stage avançada. A venda Odoo só fica verde com evidência real do snapshot/link Odoo.
|
||||
- Garante que orçamento Jasmin associado aparece como etapa concluída/histórica no cockpit quando já existe `ORC` ligado.
|
||||
- Rebaixa a label de stage `ODOO_ORDER_CREATED` de “Venda Odoo criada” para “Encomenda em preparação”, evitando falsa afirmação quando falta S00xxx ligado.
|
||||
- Atualiza o texto do “Financeiro rápido” para usar evidência de task `SEND_INVOICE` concluída e não pedir novamente envio de fatura quando já foi enviada.
|
||||
|
||||
## Resultado esperado
|
||||
|
||||
Para oportunidades com fatura + pagamento confirmado mas sem venda Odoo ligada:
|
||||
|
||||
- Próxima ação: `Preparar encomenda / Odoo`.
|
||||
- Task pendente criada: `PREPARE_ORDER` em `operacoes`.
|
||||
- Mapa: `Venda` fica pendente, `Orçamento` e `Fatura` ficam concluídos.
|
||||
- Estado não deve afirmar “Venda Odoo criada”.
|
||||
@@ -0,0 +1,6 @@
|
||||
# v4928.1.5.117 — Registo manual do nº de venda Odoo
|
||||
|
||||
- Adiciona formulário explícito para associar uma venda Odoo por número (ex.: `S00308`) no painel Odoo da oportunidade.
|
||||
- A ação `PREPARE_ORDER` no cockpit deixa de ser apenas navegação e passa a pedir o nº da venda Odoo.
|
||||
- Ao associar a venda, a task pendente `PREPARE_ORDER` é concluída e o estado WH/OUT é sincronizado.
|
||||
- A associação não cria nada no Odoo; apenas liga uma venda já existente ao processo ClientFlow.
|
||||
10
RELEASE_NOTES_v4928_1_5_118.md
Normal file
10
RELEASE_NOTES_v4928_1_5_118.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# v4928.1.5.118 — delivered guard invoice task text evidence
|
||||
|
||||
Fixes a mismatch where the opportunity detail page could show `CLOSE_OPPORTUNITY`
|
||||
because a completed invoice-send task was visible to the operator, while the
|
||||
`/operations/delivered` workflow guard still blocked the action with
|
||||
"Concluir oportunidade exige evidência de fatura enviada ao cliente."
|
||||
|
||||
The guard now accepts completed legacy task rows that clearly represent invoice
|
||||
sending by action/note text (for example `Enviar fatura FA.FA2026.143 ao cliente`),
|
||||
even when old task metadata/action_code is incomplete.
|
||||
8
RELEASE_NOTES_v4928_1_5_119.md
Normal file
8
RELEASE_NOTES_v4928_1_5_119.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# v4928.1.5.119 — delivered guard accepts legacy invoice-send evidence
|
||||
|
||||
Fixes a delivered workflow guard mismatch where the opportunity detail page suggested `CLOSE_OPPORTUNITY`, but the `/operations/delivered` action remained blocked because the guard required normalized current invoice rows before accepting completed `SEND_INVOICE` tasks as invoice-sent evidence.
|
||||
|
||||
Changes:
|
||||
- Broaden current invoice lookup in `workflow_guard.py`.
|
||||
- Accept completed same-opportunity invoice-send tasks as local invoice-sent evidence even when legacy/imported invoice rows are not returned by the stricter current invoice query.
|
||||
- Keep document-number matching when invoice rows are available.
|
||||
23
RELEASE_NOTES_v4928_1_5_12.md
Normal file
23
RELEASE_NOTES_v4928_1_5_12.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# ClientFlow v4928.1.5.12 — Fiscal enrichment guards from real audit
|
||||
|
||||
Correção baseada na auditoria de 87 oportunidades reais ao serviço de enriquecimento fiscal.
|
||||
|
||||
## Alterações
|
||||
|
||||
- `email_identity_company_internal` deixa de ser match muito forte para auto-aplicação.
|
||||
- Menções de empresa extraídas do email passam a gerar sugestão pendente, salvo prova adicional por domínio compatível.
|
||||
- Tokens genéricos/setoriais deixam de validar clientes fiscais por si só: `engenharia`, `construções`, `seguros`, `mediação`, `contabilidade`, `energy`, `power`, `solutions`, etc.
|
||||
- Lookup fiscal por `customer_name` só corre quando o nome parece empresa; nomes pessoais como `Nuno Silva` ou `Bárbara Gonçalves` são bloqueados.
|
||||
- Valores inválidos como `pt`, `com`, `geral`, `info`, `mail` não disparam pesquisas fiscais por nome.
|
||||
- Rejeição manual passa a bloquear a mesma sugestão/NIF, sem impedir uma sugestão correta futura para o mesmo texto de lookup.
|
||||
- Badge UI atualizado para `v4928.1.5.12`.
|
||||
|
||||
## Casos protegidos
|
||||
|
||||
- `Verifone Portugal` já não faz match com `ERT TÊXTIL PORTUGAL, S.A.` por `Portugal`.
|
||||
- `Feteira e Torrão Engenharia` já não faz match com `HUASI - ENGENHARIA E CONSTRUÇÕES` por `Engenharia/Construções`.
|
||||
- `DS Seguros` já não faz match com `Carlos Reis - Mediação de Seguros` apenas por `Seguros/Mediação`.
|
||||
|
||||
## Validação
|
||||
|
||||
- Novos testes funcionais em `tests/test_v4928_1_5_12_fiscal_enrichment_guards.py`.
|
||||
9
RELEASE_NOTES_v4928_1_5_120.md
Normal file
9
RELEASE_NOTES_v4928_1_5_120.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# v4.928.1.5.120 — delivered guard last-action invoice evidence
|
||||
|
||||
Hotfix for reconstructed opportunities where the detail page shows a completed SEND_INVOICE state but the delivered workflow guard cannot find the legacy task through `tasks.opportunity_id`.
|
||||
|
||||
Changes:
|
||||
- `workflow_guard.get_workflow_context()` now selects `last_action_code` and `last_task_id`.
|
||||
- Invoice task lookup also checks `opportunities.last_task_id`.
|
||||
- Adds a conservative fallback: `last_action_code=SEND_INVOICE` counts as invoice-sent evidence only when an invoice exists and no pending invoice-send task contradicts it.
|
||||
- Exposes `invoice_sent_last_action_evidence` in the workflow context for debugging.
|
||||
6
RELEASE_NOTES_v4928_1_5_121.md
Normal file
6
RELEASE_NOTES_v4928_1_5_121.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# v4928.1.5.121 — desbloquear envio manual SEND_INFO
|
||||
|
||||
- Permite enviar no Chatwoot uma mensagem editada pelo operador mesmo quando o rascunho persistido veio do fallback interno “Revisão manual — sem sugestão automática”.
|
||||
- Para SEND_INFO, normaliza o envio para template cliente “Enviar lista de equipamentos” quando o corpo editado é cliente-facing.
|
||||
- Mantém bloqueios para bounces, auto-replies e textos internos que não devem ser enviados.
|
||||
- Usa conversation_id efetivo da task/opportunity/raw_payload para mostrar e executar o envio Chatwoot.
|
||||
108
RELEASE_NOTES_v4928_1_5_122_STABILIZATION_FORECAST.md
Normal file
108
RELEASE_NOTES_v4928_1_5_122_STABILIZATION_FORECAST.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# ClientFlow v4928.1.5.122 — Stabilization & Revenue Forecast
|
||||
|
||||
Data: 2026-07-14
|
||||
|
||||
## Objetivo
|
||||
|
||||
Consolidar a versão remota após os hotfixes v4928.1.5.x, corrigir regressões de workflow observadas em produção e introduzir uma previsão comercial auditável de 30/60/90 dias.
|
||||
|
||||
## Correções
|
||||
|
||||
### Orçamento enviado sem documento associado
|
||||
|
||||
Uma task `SEND_QUOTE` concluída passa a contar como evidência de que o orçamento foi enviado, independentemente de o documento Jasmin já estar associado.
|
||||
|
||||
Nestes casos, a próxima ação passa a ser:
|
||||
|
||||
- `RECONCILE_DOCUMENTS`
|
||||
- **Associar orçamento enviado**
|
||||
|
||||
O motor deixa de sugerir `CREATE_JASMIN_QUOTE`, evitando orçamentos duplicados.
|
||||
|
||||
### Follow-ups alinhados com a fase
|
||||
|
||||
- `SEND_PROFORMA` passa a agendar `FOLLOW_UP_PAYMENT`.
|
||||
- Em `WAITING_PAYMENT`, `FOLLOW_UP_QUOTE` e `FOLLOW_UP_PROFORMA` são convertidos em `FOLLOW_UP_PAYMENT`.
|
||||
- Follow-ups de orçamento são encerrados quando existe uma task mais avançada, como `SEND_INVOICE`, `CONFIRM_PAYMENT`, `PREPARE_ORDER` ou `CREATE_SHIPMENT`.
|
||||
- O agendador bloqueia novas cascatas de orçamento quando a oportunidade já avançou para fatura, pagamento ou operação.
|
||||
|
||||
Script de auditoria/reparação:
|
||||
|
||||
```bash
|
||||
python scripts/repair_stale_followups.py
|
||||
python scripts/repair_stale_followups.py --apply
|
||||
```
|
||||
|
||||
O script é dry-run por defeito e não envia mensagens.
|
||||
|
||||
### Proteção do enriquecimento fiscal
|
||||
|
||||
Um resultado externo com `match_type=nif_exato` deixa de ser autoaplicado quando o nome empresarial devolvido contradiz o nome empresarial presente na fonte operacional.
|
||||
|
||||
- confiança máxima: 70%;
|
||||
- `source_name_conflict=true` no payload da sugestão;
|
||||
- autoaplicação bloqueada;
|
||||
- decisão fica disponível para revisão manual.
|
||||
|
||||
### Segurança de produção
|
||||
|
||||
Em `prod`, `production` e `staging`:
|
||||
|
||||
- `CLIENTFLOW_ADMIN_TOKEN` é obrigatório;
|
||||
- UI admin e API interna falham fechadas se a autenticação não estiver configurada;
|
||||
- `admin_token` por query string é desativado;
|
||||
- lookup externo ativo exige `EXTERNAL_COMPANY_LOOKUP_API_KEY`.
|
||||
|
||||
### Configuração e testes
|
||||
|
||||
- restaurado `.env.example` sem segredos;
|
||||
- suíte final: **476 testes passados**;
|
||||
- `compileall` sem erros.
|
||||
|
||||
## Previsão comercial de receitas
|
||||
|
||||
Novos endpoints:
|
||||
|
||||
- `/finance/forecast`
|
||||
- `/financeiro/previsao`
|
||||
- `GET /api/internal/revenue-forecast?limit=1000`
|
||||
|
||||
Modelo:
|
||||
|
||||
```text
|
||||
valor ponderado = valor da oportunidade × probabilidade da fase × fator de atividade
|
||||
```
|
||||
|
||||
Fontes de valor, por prioridade:
|
||||
|
||||
1. documento Jasmin atual/principal;
|
||||
2. linhas da oportunidade;
|
||||
3. `opportunities.value_amount`.
|
||||
|
||||
A probabilidade usa taxas históricas quando existem pelo menos oito oportunidades resolvidas que passaram pela fase. Sem amostra suficiente, usa probabilidades explícitas por fase.
|
||||
|
||||
O fator de atividade considera:
|
||||
|
||||
- recência da oportunidade;
|
||||
- tasks vencidas;
|
||||
- tasks concluídas recentemente;
|
||||
- conflitos fiscais/identidade.
|
||||
|
||||
A página apresenta pipeline bruto e ponderado em:
|
||||
|
||||
- até 30 dias;
|
||||
- 31–60 dias;
|
||||
- 61–90 dias;
|
||||
- mais de 90 dias.
|
||||
|
||||
Esta funcionalidade é uma previsão comercial. Não representa receita contabilística, faturação reconhecida ou tesouraria.
|
||||
|
||||
## Aplicação recomendada
|
||||
|
||||
```bash
|
||||
python -m compileall -q app scripts tests
|
||||
pytest -q
|
||||
python scripts/repair_stale_followups.py
|
||||
python scripts/repair_stale_followups.py --apply
|
||||
sudo systemctl restart clientflow-api
|
||||
```
|
||||
133
RELEASE_NOTES_v4928_1_5_123_SALES_TARGET_DASHBOARD.md
Normal file
133
RELEASE_NOTES_v4928_1_5_123_SALES_TARGET_DASHBOARD.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# ClientFlow v4928.1.5.123 — Meta e desempenho comercial
|
||||
|
||||
Data: 2026-07-16
|
||||
|
||||
## Objetivo
|
||||
|
||||
Transformar a previsão comercial numa ferramenta de decisão para responder:
|
||||
|
||||
- quanto já conta para a meta;
|
||||
- quanto está comprometido;
|
||||
- quanto ainda depende de conversão;
|
||||
- se a meta mensal está suportada;
|
||||
- que ações devem ser executadas para reduzir o desvio.
|
||||
|
||||
## Funcionalidades
|
||||
|
||||
### Meta mensal persistente
|
||||
|
||||
Nova tabela aditiva `sales_targets`, criada automaticamente no arranque.
|
||||
|
||||
A meta é configurável por:
|
||||
|
||||
- mês;
|
||||
- métrica;
|
||||
- valor;
|
||||
- moeda.
|
||||
|
||||
Métricas disponíveis:
|
||||
|
||||
- `invoiced` — faturação emitida;
|
||||
- `cash_received` — pagamentos confirmados;
|
||||
- `won_sales` — vendas ganhas.
|
||||
|
||||
### Separação sem dupla contagem
|
||||
|
||||
O dashboard distingue:
|
||||
|
||||
- realizado no mês;
|
||||
- comprometido ainda não realizado;
|
||||
- pipeline provável;
|
||||
- valor já realizado noutro período.
|
||||
|
||||
Uma oportunidade já realizada para a métrica escolhida deixa de ser somada novamente no pipeline futuro.
|
||||
|
||||
### Horizonte correto
|
||||
|
||||
São apresentados separadamente:
|
||||
|
||||
- até ao fim do mês civil;
|
||||
- próximos 30 dias adicionais;
|
||||
- buckets 30/60/90 dias na API.
|
||||
|
||||
### Semáforo e desvio
|
||||
|
||||
Estados:
|
||||
|
||||
- meta suportada;
|
||||
- suportada com baixa confiança;
|
||||
- risco moderado;
|
||||
- meta não suportada;
|
||||
- meta não configurada.
|
||||
|
||||
A qualidade dos dados impede que uma previsão com baixa cobertura apareça como verde sem ressalvas.
|
||||
|
||||
### Diagnóstico e ações
|
||||
|
||||
O dashboard identifica:
|
||||
|
||||
- oportunidades sem valor;
|
||||
- pagamentos pendentes;
|
||||
- tasks vencidas;
|
||||
- concentração da previsão;
|
||||
- desvio para a meta;
|
||||
- novo pipeline estimado necessário.
|
||||
|
||||
Também produz uma lista das 10 ações com maior impacto.
|
||||
|
||||
### Cenários
|
||||
|
||||
- conservador;
|
||||
- provável;
|
||||
- otimista.
|
||||
|
||||
### Probabilidades mais estáveis
|
||||
|
||||
Taxas históricas deixam de substituir integralmente a probabilidade padrão com apenas oito casos. É aplicada suavização entre histórico e prior da fase.
|
||||
|
||||
### Conhecimento para assistente OpenAI
|
||||
|
||||
Incluído:
|
||||
|
||||
`app/business_knowledge/sales_management_knowledge.md`
|
||||
|
||||
O ficheiro define conceitos, prevenção de dupla contagem, interpretação da qualidade e formato recomendado de respostas. Os números vivos devem vir da API interna.
|
||||
|
||||
## Rotas
|
||||
|
||||
UI:
|
||||
|
||||
- `/finance/forecast`
|
||||
- `/financeiro/previsao`
|
||||
|
||||
Guardar meta:
|
||||
|
||||
- `POST /finance/forecast/target`
|
||||
|
||||
API:
|
||||
|
||||
- `GET /api/internal/revenue-forecast?month=2026-07&metric=invoiced`
|
||||
|
||||
## Validação
|
||||
|
||||
```text
|
||||
python -m compileall -q app scripts tests
|
||||
483 passed
|
||||
```
|
||||
|
||||
## Instalação
|
||||
|
||||
A release preserva `.env`, `.venv`, dados e logs quando aplicada pelo procedimento `rsync` usado nas versões anteriores.
|
||||
|
||||
Após instalar:
|
||||
|
||||
```bash
|
||||
cd /mnt/ssd/home/plx/clientflow_backend
|
||||
source .venv/bin/activate
|
||||
python -m compileall -q app scripts tests
|
||||
pytest -q
|
||||
sudo systemctl restart clientflow-api
|
||||
curl -I http://127.0.0.1:8020/finance/forecast
|
||||
```
|
||||
|
||||
A tabela `sales_targets` é criada automaticamente no arranque.
|
||||
34
RELEASE_NOTES_v4928_1_5_124_OPERATIONAL_UI_ALIGNMENT.md
Normal file
34
RELEASE_NOTES_v4928_1_5_124_OPERATIONAL_UI_ALIGNMENT.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# ClientFlow v4928.1.5.124 — Alinhamento operacional e executivo
|
||||
|
||||
## Centro de Trabalho
|
||||
|
||||
- Candidatos Jasmin/Odoo sem associação passam a mostrar primeiro **Validar associação**, antes de ações comerciais ou financeiras.
|
||||
- O contador **Associações por confirmar** passa a usar o estado real carregado da metadata e também candidatos de reconciliação ainda não ligados.
|
||||
- Processos antigos/reconstruídos bloqueiam ações sensíveis até validação de cliente, documento, valor e pagamento.
|
||||
- Códigos técnicos de follow-up são convertidos em rótulos humanos.
|
||||
- Cada cartão mostra fila, idade/prazo, valor afetado e motivo da prioridade.
|
||||
- Bloqueios fiscais deixam de impedir a simples validação de uma associação documental.
|
||||
|
||||
## Metas e previsão
|
||||
|
||||
- Nova rota canónica `/forecast` e item próprio no menu principal.
|
||||
- `/finance/forecast` e `/financeiro/previsao` passam a redirecionar para `/forecast`.
|
||||
- API canónica `/api/internal/forecast`; o endpoint anterior continua como alias.
|
||||
- Novo estado **Meta recuperável** quando pagamentos existentes podem cobrir o desvio.
|
||||
- O novo pipeline necessário passa a ser calculado sobre o desvio residual depois da capacidade de recuperação.
|
||||
- Cenários passam a distinguir base, aceleração e potencial máximo conhecido.
|
||||
|
||||
## Dashboard inicial
|
||||
|
||||
- Resumo executivo de meta, realizado, previsão e desvio.
|
||||
- Saúde do funil com oportunidades valorizadas e por valorizar.
|
||||
- Pagamentos pendentes com valor conhecido.
|
||||
- Processos prontos para envio ou com envio criado.
|
||||
- Ações de maior impacto e alertas comerciais/operacionais.
|
||||
- Referência do modelo operacional atualizada para v4.7.
|
||||
|
||||
## Compatibilidade
|
||||
|
||||
- Sem alterações destrutivas de base de dados.
|
||||
- Rotas antigas mantidas por redirect/alias.
|
||||
- A transformação de associação e validação no Centro de Trabalho é apenas de apresentação; não altera tasks automaticamente.
|
||||
78
RELEASE_NOTES_v4928_1_5_125_FOLLOWUP_LIFECYCLE.md
Normal file
78
RELEASE_NOTES_v4928_1_5_125_FOLLOWUP_LIFECYCLE.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# ClientFlow v4928.1.5.125 — Follow-up e ciclo de vida comercial
|
||||
|
||||
## Objetivo
|
||||
|
||||
Reduzir o risco de perder oportunidades porque a primeira comunicação não chegou e impedir que oportunidades sem resposta permaneçam indefinidamente no funil ativo.
|
||||
|
||||
## Alterações principais
|
||||
|
||||
- Primeira confirmação no dia útil seguinte ao envio.
|
||||
- Cadências diferentes para informação, orçamento e pagamento.
|
||||
- Nova ação interna `CONFIRM_DELIVERY` para validar destinatário, documento, outbox e bounce.
|
||||
- Estados operacionais: ativa, a aguardar cliente, follow-up vencido, recuperação e acompanhamento futuro.
|
||||
- Fila e filtro de recuperação na página de oportunidades.
|
||||
- Filtros para follow-up vencido, inativas, por valorizar e nurture.
|
||||
- Cartões com valor, dias sem resposta, próximo contacto e número de tentativas.
|
||||
- Atividade comercial baseada em timestamps próprios, sem usar `updated_at` técnico.
|
||||
- Reabertura segura por resposta do cliente apenas para `LOST`/`NO_INTEREST` na mesma conversa.
|
||||
- Motivo obrigatório ao fechar como perdida; timing futuro deve usar nurture.
|
||||
- Resposta do cliente cancela follow-ups obsoletos e reativa o processo.
|
||||
- Script de backfill em dry-run por defeito.
|
||||
|
||||
## Cadências
|
||||
|
||||
### Informação e orçamento
|
||||
|
||||
1. Dia útil seguinte: confirmar receção e entrega técnica.
|
||||
2. +2 dias úteis: interesse/dúvidas.
|
||||
3. +4 dias úteis: decisão/qualificação.
|
||||
4. +5 dias úteis: última tentativa.
|
||||
5. Recuperação.
|
||||
|
||||
### Pagamento
|
||||
|
||||
1. Dia útil seguinte: confirmar receção.
|
||||
2. +2 dias úteis: pedir data prevista.
|
||||
3. +3 dias úteis: lembrete.
|
||||
4. +3 dias úteis: contacto direto/canal alternativo.
|
||||
5. +5 dias úteis: rever intenção.
|
||||
6. Recuperação.
|
||||
|
||||
## Base de dados
|
||||
|
||||
A atualização é aditiva. `ensure_opportunity_schema()` cria as colunas em falta:
|
||||
|
||||
- `lifecycle_state`
|
||||
- `last_customer_activity_at`
|
||||
- `last_operator_activity_at`
|
||||
- `last_outbound_sent_at`
|
||||
- `last_delivery_checked_at`
|
||||
- `last_delivery_status`
|
||||
- `last_commercial_activity_at`
|
||||
- `next_follow_up_at`
|
||||
- `follow_up_attempts`
|
||||
- `nurture_until`
|
||||
- `lost_reason`
|
||||
|
||||
## Migração segura
|
||||
|
||||
Simular:
|
||||
|
||||
```bash
|
||||
python scripts/sync_opportunity_followup_lifecycle.py --limit 50
|
||||
```
|
||||
|
||||
Aplicar depois de rever:
|
||||
|
||||
```bash
|
||||
python scripts/sync_opportunity_followup_lifecycle.py --apply --limit 50
|
||||
```
|
||||
|
||||
O script não envia comunicações. Cria somente tasks humanas e nunca usa `updated_at` como atividade comercial.
|
||||
|
||||
## Validação
|
||||
|
||||
```text
|
||||
compileall: OK
|
||||
496 testes passados
|
||||
```
|
||||
@@ -0,0 +1,31 @@
|
||||
# ClientFlow v4928.1.5.126 — Follow-up backfill `--apply` hotfix
|
||||
|
||||
Correção incremental sobre a v4928.1.5.125.
|
||||
|
||||
## Problema corrigido
|
||||
|
||||
O comando em modo de simulação funcionava, mas o caminho `--apply` terminava com:
|
||||
|
||||
```text
|
||||
NameError: name 'engine' is not defined
|
||||
```
|
||||
|
||||
A função `_rows()` importava o motor da base de dados apenas no seu âmbito local. O bloco de atualização dentro de `main()` tentava reutilizar esse nome fora do âmbito.
|
||||
|
||||
## Correção
|
||||
|
||||
- Importação explícita de `engine` dentro de `main()` antes do primeiro `engine.begin()`.
|
||||
- Metadados de migração atualizados para `v4928.1.5.126`.
|
||||
- Teste funcional do caminho `--apply` com motor e conexão simulados.
|
||||
- O script continua seguro: não envia mensagens e não fecha oportunidades.
|
||||
|
||||
## Impacto nos dados
|
||||
|
||||
A execução que falhou com `NameError` não alterou oportunidades nem criou tasks, porque o erro ocorreu antes da primeira transação de atualização.
|
||||
|
||||
## Validação
|
||||
|
||||
```text
|
||||
497 passed
|
||||
compileall OK
|
||||
```
|
||||
@@ -0,0 +1,30 @@
|
||||
# ClientFlow v4928.1.5.127.1 — CONFIRM_DELIVERY cleanup SQL hotfix
|
||||
|
||||
## Correction
|
||||
|
||||
Fixes `scripts/disable_automatic_confirm_delivery.py --apply` on PostgreSQL.
|
||||
|
||||
The cleanup used unqualified `done_at` and `metadata` expressions inside an
|
||||
`UPDATE tasks ... FROM opportunities`, where both tables expose similarly named
|
||||
columns. PostgreSQL therefore raised `AmbiguousColumn` and rolled back the
|
||||
transaction.
|
||||
|
||||
The hotfix qualifies the task-side expressions as:
|
||||
|
||||
- `COALESCE(t.done_at, now())`
|
||||
- `COALESCE(t.metadata, '{}'::jsonb)`
|
||||
|
||||
## Safety
|
||||
|
||||
- Dry-run behavior is unchanged.
|
||||
- Manual delivery checks remain preserved.
|
||||
- No customer communication is sent.
|
||||
- The failed pre-hotfix execution made no data changes because the transaction
|
||||
was rolled back.
|
||||
- The v127 runtime behavior and `CONFIRM_DELIVERY_AUTOMATION_ENABLED=false`
|
||||
configuration are unchanged.
|
||||
|
||||
## Validation
|
||||
|
||||
- Python compilation passed for the corrected script.
|
||||
- Static regression assertions verify both qualified column references.
|
||||
@@ -0,0 +1,77 @@
|
||||
# ClientFlow v4928.1.5.127 — redução de ruído em `CONFIRM_DELIVERY`
|
||||
|
||||
## Objetivo
|
||||
|
||||
Evitar que o fluxo de follow-up crie uma confirmação de receção para cada comunicação. `CONFIRM_DELIVERY` passa a ser uma exceção manual escolhida pelo operador quando vários contactos sem resposta sugerem um problema real de envio.
|
||||
|
||||
## Alterações
|
||||
|
||||
- `CONFIRM_DELIVERY_AUTOMATION_ENABLED=false` por defeito.
|
||||
- As cadências automáticas começam diretamente no follow-up adequado:
|
||||
- informação: `FOLLOW_UP_CUSTOMER_REVIEW` após 2 dias úteis;
|
||||
- orçamento: `FOLLOW_UP_QUOTE` após 2 dias úteis;
|
||||
- pagamento: `FOLLOW_UP_PAYMENT` após 2 dias úteis.
|
||||
- A página da oportunidade mantém **Verificar entrega (manual)**.
|
||||
- A verificação manual não inicia uma nova cascata automática.
|
||||
- O backfill:
|
||||
- não cria `CONFIRM_DELIVERY`;
|
||||
- exclui oportunidades de valor zero por defeito;
|
||||
- ignora oportunidades que já tenham qualquer tarefa humana pendente;
|
||||
- aplica `LIMIT` depois dos filtros de elegibilidade.
|
||||
- Novo script `scripts/disable_automatic_confirm_delivery.py`:
|
||||
- dry-run por defeito;
|
||||
- ignora apenas confirmações automáticas pendentes;
|
||||
- preserva verificações de entrega criadas manualmente.
|
||||
|
||||
## Atualização recomendada
|
||||
|
||||
```bash
|
||||
cd /mnt/ssd/home/plx/clientflow_backend
|
||||
source .venv/bin/activate
|
||||
|
||||
sudo systemctl stop clientflow-api.service
|
||||
|
||||
cp -a /mnt/ssd/home/plx/clientflow_backend \
|
||||
/mnt/ssd/backups/clientflow/clientflow_backend_pre_v4928_1_5_127_$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
unzip -o /caminho/clientflow_backend_v4928_1_5_127.zip \
|
||||
-d /mnt/ssd/home/plx/clientflow_backend
|
||||
```
|
||||
|
||||
Garantir na configuração:
|
||||
|
||||
```env
|
||||
CONFIRM_DELIVERY_AUTOMATION_ENABLED=false
|
||||
```
|
||||
|
||||
Validar e retirar as confirmações automáticas antigas:
|
||||
|
||||
```bash
|
||||
python scripts/disable_automatic_confirm_delivery.py
|
||||
python scripts/disable_automatic_confirm_delivery.py --apply
|
||||
```
|
||||
|
||||
O backfill v127 é opcional. Rever antes de aplicar:
|
||||
|
||||
```bash
|
||||
python scripts/sync_opportunity_followup_lifecycle.py --limit 50
|
||||
```
|
||||
|
||||
Não é necessário executar `--apply` imediatamente. O fluxo normal passará a criar os novos follow-ups à medida que as ações comerciais forem concluídas.
|
||||
|
||||
Finalizar:
|
||||
|
||||
```bash
|
||||
python -m py_compile \
|
||||
app/followup_service.py \
|
||||
app/config.py \
|
||||
scripts/sync_opportunity_followup_lifecycle.py \
|
||||
scripts/disable_automatic_confirm_delivery.py
|
||||
|
||||
sudo systemctl start clientflow-api.service
|
||||
sudo systemctl status clientflow-api.service --no-pager
|
||||
```
|
||||
|
||||
## Validação
|
||||
|
||||
Suite completa executada: **502 testes aprovados**.
|
||||
103
RELEASE_NOTES_v4928_1_5_129_RECONCILIATION_COHERENCE.md
Normal file
103
RELEASE_NOTES_v4928_1_5_129_RECONCILIATION_COHERENCE.md
Normal file
@@ -0,0 +1,103 @@
|
||||
# ClientFlow v4928.1.5.129 — coerência da Reconciliação e tasks operacionais
|
||||
|
||||
## Objetivo
|
||||
|
||||
Evitar que vendas Odoo já ligadas continuem na Reconciliação com ações obsoletas, impedir regressões da oportunidade e garantir que ações de expedição aparecem no Centro de Trabalho.
|
||||
|
||||
## Alterações
|
||||
|
||||
### Reconciliação Odoo
|
||||
|
||||
- Antes de criar um candidato, o sincronizador procura uma ligação exata em `operation_links` por ID Odoo ou número da venda.
|
||||
- Uma ligação exata e única faz com que o candidato antigo seja resolvido como `linked`, sem alterar oportunidade, fase, valor ou documentos.
|
||||
- Mais de uma ligação exata gera `conflict`; nunca é resolvida automaticamente.
|
||||
- Correspondências por nome, cliente ou valor continuam a exigir decisão do operador.
|
||||
- A interface passa a indicar vendas já ligadas, candidatos obsoletos resolvidos e conflitos.
|
||||
|
||||
### Proteção contra regressão
|
||||
|
||||
Ao ligar nova evidência a uma oportunidade existente:
|
||||
|
||||
- compara a fase sugerida com a fase atual;
|
||||
- preserva a fase e a ação atuais quando já estão no mesmo nível ou mais avançadas;
|
||||
- não cria `SEND_INVOICE` quando já existe fatura Jasmin ligada;
|
||||
- recalcula a próxima ação pelo motor central depois da transação.
|
||||
|
||||
### Expedição
|
||||
|
||||
- O motor central continua a usar `SHIP_ORDER` como decisão de domínio.
|
||||
- A task persistida é normalizada para `CREATE_SHIPMENT`.
|
||||
- Fila: `logistica`.
|
||||
- Texto do operador: `Enviar encomenda`.
|
||||
- A task não é concluída por mensagens Chatwoot e não depende de Packlink.
|
||||
|
||||
### Correção dos candidatos atuais
|
||||
|
||||
Novo script:
|
||||
|
||||
```bash
|
||||
python scripts/apply_reconciliation_coherence_fixes.py
|
||||
```
|
||||
|
||||
O script é dry-run por defeito. Só considera seguro um candidato Odoo que tenha exatamente uma ligação exata a uma oportunidade aberta.
|
||||
|
||||
Aplicação direcionada:
|
||||
|
||||
```bash
|
||||
python scripts/apply_reconciliation_coherence_fixes.py \
|
||||
--sale S00324 \
|
||||
--sale S00325 \
|
||||
--sale S00326 \
|
||||
--apply
|
||||
```
|
||||
|
||||
O script:
|
||||
|
||||
- resolve os candidatos como já ligados;
|
||||
- preserva fase, valor, documentos e `operation_links`;
|
||||
- materializa a próxima task calculada pelo motor central;
|
||||
- não altera BBKW/S00323 porque não existe ligação exata.
|
||||
|
||||
### Auditoria
|
||||
|
||||
Incluído o auditor read-only v128.1 em:
|
||||
|
||||
```bash
|
||||
scripts/audit_reconciliation_coherence.py
|
||||
```
|
||||
|
||||
Depois da correção:
|
||||
|
||||
```bash
|
||||
python scripts/audit_reconciliation_coherence.py \
|
||||
--days 3 \
|
||||
--limit 500 \
|
||||
--live odoo,jasmin \
|
||||
--max-live 30
|
||||
```
|
||||
|
||||
## Packlink
|
||||
|
||||
A configuração por defeito continua:
|
||||
|
||||
```env
|
||||
PACKLINK_ENABLED=false
|
||||
```
|
||||
|
||||
`CREATE_SHIPMENT` representa trabalho logístico do operador e não ativa Packlink. Packlink só poderá ser usado quando a integração for reativada explicitamente.
|
||||
|
||||
## Segurança
|
||||
|
||||
- dry-run por defeito;
|
||||
- correspondência automática apenas por ID/número exato;
|
||||
- exatamente uma oportunidade aberta;
|
||||
- sem correção automática de diferenças de valor;
|
||||
- sem criação automática de oportunidade para BBKW;
|
||||
- sem regressão de fase;
|
||||
- criação de tasks idempotente.
|
||||
|
||||
## Validação
|
||||
|
||||
```text
|
||||
508 testes aprovados
|
||||
```
|
||||
14
RELEASE_NOTES_v4928_1_5_13.md
Normal file
14
RELEASE_NOTES_v4928_1_5_13.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# ClientFlow v4928.1.5.13 — Task detail refresh after send + complete
|
||||
|
||||
## Fix
|
||||
|
||||
- Fixed task detail UI staying stale after **Enviar e concluir tarefa**.
|
||||
- The reply send form now targets `#task-detail-panel` on success, so the whole task detail is re-rendered with the fresh DB state.
|
||||
- This updates the top status badge from `Pendente` to `Concluída` and removes/updates completion controls after `send_and_complete`.
|
||||
- Reply send errors are retargeted back to `#reply-assistant-panel`, so validation/Chatwoot errors do not replace the full task page.
|
||||
|
||||
## Scope
|
||||
|
||||
- UI/HTMX only.
|
||||
- No database migration.
|
||||
- No change to Chatwoot sending, templates or fiscal enrichment rules.
|
||||
23
RELEASE_NOTES_v4928_1_5_130_ODOO_PHYSICAL_VALIDATION_GATE.md
Normal file
23
RELEASE_NOTES_v4928_1_5_130_ODOO_PHYSICAL_VALIDATION_GATE.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# ClientFlow v4928.1.5.130 — Odoo physical validation gate
|
||||
|
||||
## Problema corrigido
|
||||
|
||||
O estado Odoo `stock.picking.state=assigned` estava a ser interpretado como encomenda pronta para expedição. Em Odoo, `assigned` indica reserva/disponibilidade de stock, não validação física nem conclusão da preparação.
|
||||
|
||||
## Novo fluxo
|
||||
|
||||
1. `assigned` → `ORDER_PREPARATION` / **Validar encomenda física**.
|
||||
2. O operador confere picking, produtos, quantidades e embalagem.
|
||||
3. Ao concluir `VALIDATE_PHYSICAL_ORDER`, o ClientFlow regista `odoo:physical_validation=validated`.
|
||||
4. Só depois o motor emite `SHIP_ORDER`, materializado como `CREATE_SHIPMENT`.
|
||||
5. `done` continua a significar WH/OUT concluído/expedido.
|
||||
|
||||
## Interface
|
||||
|
||||
- O estado passa a mostrar “Picking reservado — validação física pendente”.
|
||||
- Tasks logísticas não apresentam compositor de resposta ao cliente.
|
||||
- A conclusão da validação materializa automaticamente a task de envio seguinte.
|
||||
|
||||
## Migração
|
||||
|
||||
O script `repair_assigned_picking_shipment_tasks.py` converte tasks `CREATE_SHIPMENT` prematuras em `VALIDATE_PHYSICAL_ORDER` e repõe a fase `ORDER_PREPARATION`, apenas quando existe picking `assigned` e não existe validação física. Dry-run por defeito.
|
||||
@@ -0,0 +1,20 @@
|
||||
# ClientFlow v4928.1.5.132.2.1
|
||||
|
||||
## Correção
|
||||
|
||||
Corrige um `UnboundLocalError` no ramo determinístico de reativação de tasks introduzido na v132.2.
|
||||
|
||||
O ramo de reativação usava `metadata`, `route`, `action_label`, `description` e `priority` antes da respetiva inicialização. A construção do payload canónico da task passa agora a ocorrer antes de qualquer tentativa de reativação ou inserção.
|
||||
|
||||
## Impacto operacional
|
||||
|
||||
- Não altera fases, oportunidades, documentos ou associações durante a instalação.
|
||||
- Permite que o reparador v132.2 reative corretamente uma task `VALIDATE_PHYSICAL_ORDER` em estado `skipped`/`ignored`.
|
||||
- Mantém os guards existentes: revisão reconstruída validada, decisão central coincidente, ausência de task pendente e ausência de validação física já confirmada.
|
||||
- Regista `task_reactivated` e metadata `reactivated_version=v4928.1.5.132.2.1`.
|
||||
|
||||
## Validação
|
||||
|
||||
- `python -m py_compile`: OK
|
||||
- Teste dinâmico do ramo que falhou na DUNAS: aprovado
|
||||
- Suite completa: 529 testes aprovados
|
||||
@@ -0,0 +1,30 @@
|
||||
# ClientFlow v4928.1.5.132.2
|
||||
|
||||
## Deterministic post-review task reactivation
|
||||
|
||||
### Problem confirmed
|
||||
A reconstructed review could be completed and persisted as `validated`, while the previously skipped blocked task remained `skipped`. The v132.1 implementation attempted a new INSERT first and only reactivated after an idempotency conflict. In production the post-review hook completed without producing `task_reactivated`.
|
||||
|
||||
### Changes
|
||||
- Controlled post-blocker flows now search and reactivate an existing skipped/ignored materialized task **before** attempting a new INSERT.
|
||||
- Reactivation is restricted to:
|
||||
- `reconstructed_review_completion`
|
||||
- `physical_validation_completion`
|
||||
- guarded manual repair `manual_v132_2_reactivation_repair`
|
||||
- Only tasks with `metadata.materialized_from_next_action=true` are eligible.
|
||||
- A `task_reactivated` event records source, prior status and version.
|
||||
- Review completion writes `post_review_task_materialization` to `opportunity_events`, including the central decision and materialization result.
|
||||
- Added guarded dry-run repair script for opportunities already affected.
|
||||
|
||||
### Safety
|
||||
The repair aborts unless:
|
||||
- review state is explicitly `validated`;
|
||||
- current central decision matches the recorded blocked action;
|
||||
- no pending task for the action exists;
|
||||
- a skipped/ignored materialized task exists;
|
||||
- physical validation has not already been recorded.
|
||||
|
||||
### Validation
|
||||
- Python compilation: OK
|
||||
- Focused tests: 6 passed
|
||||
- Full suite: 528 passed
|
||||
46
RELEASE_NOTES_v4928_1_5_132_3_INVOICE_EVIDENCE_ALIGNMENT.md
Normal file
46
RELEASE_NOTES_v4928_1_5_132_3_INVOICE_EVIDENCE_ALIGNMENT.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# ClientFlow v4928.1.5.132.3 — Invoice evidence alignment
|
||||
|
||||
## Problema corrigido
|
||||
|
||||
A oportunidade NOLTIA apresentava `CLOSE_OPPORTUNITY` como próxima ação, mas o endpoint `/operations/delivered` bloqueava com “Concluir oportunidade exige evidência de fatura enviada ao cliente”.
|
||||
|
||||
A task `SEND_INVOICE` tinha sido criada quando o documento principal ainda era o orçamento. Por isso, os campos históricos `metadata.document_id` e `metadata.document_number` continuavam a apontar para `ORC.ORC2026.220`. Ao concluir a task, o ClientFlow anexou a fatura real em `metadata.invoice_delivery_context` (`FA.FA2026.159`), mas o workflow guard ignorava esse contexto autoritativo.
|
||||
|
||||
## Alterações
|
||||
|
||||
- Nova regra comum em `app/invoice_evidence.py` usada pelo motor central e pelo workflow guard.
|
||||
- `invoice_delivery_context` passa a ter precedência sobre o contexto antigo do orçamento.
|
||||
- São aceites como identidade da mesma fatura:
|
||||
- UUID interno de `commercial_documents`;
|
||||
- `external_id` Jasmin;
|
||||
- número fiscal da fatura.
|
||||
- Uma referência autoritativa a outra fatura continua a ser rejeitada.
|
||||
- O motor central passa a carregar `tasks.metadata` e `commercial_documents.external_id`, eliminando decisões divergentes.
|
||||
- Mantida a compatibilidade com tasks antigas sem linhas de fatura normalizadas.
|
||||
|
||||
## Dados
|
||||
|
||||
Não existe migração nem alteração automática de dados. A task da NOLTIA já contém evidência suficiente em `invoice_delivery_context`; após instalar o código, o guard deve aceitá-la.
|
||||
|
||||
## Verificação
|
||||
|
||||
```bash
|
||||
python scripts/verify_v132_3_invoice_sent_evidence.py --self-test
|
||||
python scripts/verify_v132_3_invoice_sent_evidence.py \
|
||||
--opportunity-id 321aab34-31df-44bf-9bcd-7e7d86797598
|
||||
```
|
||||
|
||||
Resultado esperado para a NOLTIA:
|
||||
|
||||
```text
|
||||
central_action = CLOSE_OPPORTUNITY
|
||||
invoice_sent_evidence = true
|
||||
invoice_sent_task_evidence = true
|
||||
delivered_blocked_reason = null
|
||||
```
|
||||
|
||||
## Testes
|
||||
|
||||
- 533 testes aprovados com o módulo de base de dados substituído por stub no ambiente de construção.
|
||||
- Regressão específica com o payload real da NOLTIA.
|
||||
- `py_compile` aprovado.
|
||||
@@ -0,0 +1,44 @@
|
||||
# ClientFlow v4928.1.5.132.4 — Odoo done task reconciliation
|
||||
|
||||
## Problema corrigido
|
||||
|
||||
Quando um picking de saída Odoo passava de `assigned` para `done`, o ClientFlow atualizava a evidência física e a fase, mas podia manter pendentes tasks antigas:
|
||||
|
||||
- `VALIDATE_PHYSICAL_ORDER`
|
||||
- `CREATE_SHIPMENT`
|
||||
|
||||
A precedência da task pendente fazia a oportunidade continuar a mostrar “Validar encomenda física”, apesar de o WH/OUT já estar concluído e o motor central indicar `CLOSE_OPPORTUNITY`.
|
||||
|
||||
Além disso, `done` era traduzido visualmente para `SHIPMENT_CREATED` (“Envio criado”), quando a evidência real já corresponde a `SHIPPED` (“Enviado”).
|
||||
|
||||
## Correção
|
||||
|
||||
- Novo módulo `app/odoo_delivery_task_reconciliation.py`.
|
||||
- WH/OUT `done` cria/atualiza evidência explícita `odoo/physical_validation=validated`.
|
||||
- Tasks pendentes `VALIDATE_PHYSICAL_ORDER` e `CREATE_SHIPMENT` são concluídas automaticamente por evidência externa inequívoca.
|
||||
- Cada alteração grava `task_auto_completed` e um evento agregado na oportunidade.
|
||||
- A conclusão automática não executa a cascata normal da task, evitando criar trabalho obsoleto.
|
||||
- O estado derivado de WH/OUT `done` passa a `SHIPPED`.
|
||||
- Tanto a sincronização direta Odoo como a reconciliação externa aplicam a mesma regra.
|
||||
|
||||
## Reparação de dados existentes
|
||||
|
||||
O script `scripts/repair_v132_4_odoo_done_stale_tasks.py` é dry-run por omissão e só aplica alterações quando existe evidência inequívoca de entrega Odoo concluída.
|
||||
|
||||
## Segurança
|
||||
|
||||
A correção não:
|
||||
|
||||
- chama o Odoo em modo de escrita;
|
||||
- cria envio Packlink;
|
||||
- confirma entrega ao cliente;
|
||||
- fecha automaticamente a oportunidade;
|
||||
- altera documentos, pagamentos ou clientes.
|
||||
|
||||
Depois da correção, a próxima ação central esperada é `CLOSE_OPPORTUNITY`, sujeita aos guards existentes de fatura enviada e pagamento.
|
||||
|
||||
## Validação
|
||||
|
||||
- `py_compile`: OK
|
||||
- testes específicos v132.4: 3 aprovados
|
||||
- suite completa: 536 aprovados
|
||||
94
RELEASE_NOTES_v4928_1_5_132_OPERATIONAL_COHERENCE.md
Normal file
94
RELEASE_NOTES_v4928_1_5_132_OPERATIONAL_COHERENCE.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# ClientFlow v4928.1.5.132 — Coerência operacional e precedência do Centro de Trabalho
|
||||
|
||||
## Objetivo
|
||||
|
||||
Corrigir as incoerências identificadas pela auditoria forense entre:
|
||||
|
||||
- estado físico Odoo;
|
||||
- fase persistida da oportunidade;
|
||||
- revisão de processos reconstruídos;
|
||||
- task pendente;
|
||||
- ação apresentada no Centro de Trabalho;
|
||||
- candidatos de reconciliação já ligados.
|
||||
|
||||
A atualização não altera valores comerciais, documentos, pagamentos nem associações de clientes fiscais.
|
||||
|
||||
## Correções de código
|
||||
|
||||
### Odoo `assigned`
|
||||
|
||||
- `assigned` passa a significar apenas stock/picking reservado.
|
||||
- A fase segura é `ORDER_PREPARATION` enquanto não existir validação física.
|
||||
- Só uma validação física explícita permite `READY_TO_SHIP`.
|
||||
- Só um envio/tracking real permite `SHIPMENT_CREATED`.
|
||||
- O sincronizador pode corrigir de forma controlada uma fase demasiado avançada quando não existe validação, tracking nem envio.
|
||||
- Evidência `physical_validation` deixa de ser criada a partir de `assigned`.
|
||||
|
||||
### Revisão de processos reconstruídos
|
||||
|
||||
- Novo estado persistido: `required`, `validated` ou `waived`.
|
||||
- O título ou `clientflow_record_mode` deixam de ser suficientes para inventar um bloqueio.
|
||||
- `VALIDATE_PHYSICAL_ORDER` passa a ser tratada como ação sensível.
|
||||
- Uma revisão concluída não volta a abrir apenas porque o título continua histórico.
|
||||
- Tasks ignoradas ou saltadas não contam como revisão concluída.
|
||||
|
||||
### Precedência das ações
|
||||
|
||||
A primeira ação apresentada passa a respeitar:
|
||||
|
||||
1. associação explicitamente bloqueante;
|
||||
2. revisão reconstruída explicitamente obrigatória;
|
||||
3. task pendente executável;
|
||||
4. decisão central seguinte.
|
||||
|
||||
A lista e o detalhe das Oportunidades usam a mesma política.
|
||||
|
||||
### Reconciliação
|
||||
|
||||
- candidatos já ligados podem ser resolvidos por script com guards;
|
||||
- exige exatamente uma ligação externa;
|
||||
- exige NIF ou nome coerente por defeito;
|
||||
- identidade desconhecida fica para revisão;
|
||||
- `S00323` não é resolvido automaticamente sem confirmação explícita.
|
||||
|
||||
## Script de migração
|
||||
|
||||
`scripts/apply_v132_operational_coherence.py`
|
||||
|
||||
Características:
|
||||
|
||||
- dry-run por defeito;
|
||||
- filtros independentes para oportunidades e candidatos;
|
||||
- transação protegida;
|
||||
- idempotente;
|
||||
- relatório JSON e Markdown;
|
||||
- não modifica valores, documentos, pagamentos ou clientes.
|
||||
|
||||
Opções principais:
|
||||
|
||||
```text
|
||||
--focus <nome/UUID>
|
||||
--candidate-ref <S00...>
|
||||
--skip-opportunities
|
||||
--skip-candidates
|
||||
--allow-unknown-identity
|
||||
--apply
|
||||
```
|
||||
|
||||
## Casos atuais esperados
|
||||
|
||||
- NOLTIA: `SHIPMENT_CREATED` → `ORDER_PREPARATION`; revisão reconstruída antes da validação física.
|
||||
- MAFIROL: `SHIPMENT_CREATED` → `ORDER_PREPARATION`; revisão reconstruída antes da validação física.
|
||||
- DUNAS: `READY_TO_SHIP` → `ORDER_PREPARATION`; resolver S00318; revisão antes da validação física.
|
||||
- RICARDO: persistir revisão como validada e manter `SEND_INVOICE`.
|
||||
- ENVIENERGY: remover o falso override visual de associação e mostrar a task real.
|
||||
- S00323: permanece para revisão até confirmação da oportunidade BBKW.
|
||||
|
||||
## Validação
|
||||
|
||||
- `py_compile`: OK
|
||||
- self-test do corretor: OK
|
||||
- self-test do auditor forense: OK
|
||||
- suite completa: **522 testes aprovados**
|
||||
|
||||
A atualização não foi executada contra a base de dados de produção durante a preparação do pacote. Deve ser aplicada primeiro em dry-run no servidor.
|
||||
30
RELEASE_NOTES_v4928_1_5_14_task_reply_lazy.md
Normal file
30
RELEASE_NOTES_v4928_1_5_14_task_reply_lazy.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# ClientFlow v4928.1.5.14 — Task reply latency hotfix
|
||||
|
||||
## Objetivo
|
||||
Reduzir a latência ao abrir tarefas a partir de `/operations`, sobretudo no fluxo "Preparar resposta".
|
||||
|
||||
## Alterações
|
||||
- A página de detalhe da tarefa já não gera automaticamente o rascunho com IA/LLM ao abrir.
|
||||
- O painel "Resposta ao cliente" passa a ser lazy: mostra o botão "Gerar rascunho" e só chama o agente/LLM quando o operador clica.
|
||||
- Ao usar "Enviar e concluir tarefa" via HTMX, o backend redireciona diretamente para `/operations?scope=all` em vez de voltar a renderizar o detalhe completo da tarefa.
|
||||
|
||||
## Impacto esperado
|
||||
- Abertura de tarefa passa de ~9.5s para ~0.2s nos casos em que a lentidão era causada por `CLIENTFLOW_REPLY_LLM_FIRST_ENABLED=true` / agente de resposta.
|
||||
- A geração de rascunho continua disponível, mas deixa de bloquear a navegação inicial.
|
||||
|
||||
## Ficheiro alterado
|
||||
- `app/admin_ui/pages/tasks.py`
|
||||
|
||||
## Aplicação
|
||||
```bash
|
||||
cd /mnt/ssd/home/plx/clientflow_backend
|
||||
unzip -o /caminho/clientflow_backend_v4928_1_5_14_task_reply_lazy.zip
|
||||
sudo systemctl restart clientflow-api
|
||||
```
|
||||
|
||||
Se foi feito teste temporário com `.env.bak_latency_test`, repor primeiro o `.env` correto antes de reiniciar.
|
||||
|
||||
## Validação adicional
|
||||
- Mantidos os marcadores de compatibilidade `v4928.1.5.13` esperados pela suíte de testes existente.
|
||||
- `PYTHONPATH=. python -m compileall app scripts tests`
|
||||
- `PYTHONPATH=. pytest -q` → `285 passed`
|
||||
@@ -0,0 +1,6 @@
|
||||
# v4928.1.5.15 — Persistência de rascunho após refresh
|
||||
|
||||
- Mantém a abertura da tarefa lazy, sem chamada automática ao LLM/OpenAI.
|
||||
- Ao gerar rascunho, o conteúdo continua a ser gravado em `message_drafts`.
|
||||
- Ao refrescar a página da tarefa, carrega o último `message_drafts.status = draft` da task sem invocar LLM.
|
||||
- O operador pode editar, enviar ou enviar e concluir usando o rascunho persistido.
|
||||
53
RELEASE_NOTES_v4928_1_5_16_reply_revision_workflow.md
Normal file
53
RELEASE_NOTES_v4928_1_5_16_reply_revision_workflow.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# ClientFlow v4928.1.5.16 — Reply draft revision workflow
|
||||
|
||||
## Objetivo
|
||||
Melhorar o fluxo de resposta ao cliente para que o operador possa editar, guardar e pedir correções ao rascunho gerado pela IA antes de enviar.
|
||||
|
||||
## Alterações principais
|
||||
|
||||
- A abertura da tarefa continua rápida e não chama LLM automaticamente.
|
||||
- O botão **Gerar rascunho** chama o agente OpenAI/file_search e persiste o resultado em `message_drafts`.
|
||||
- Rascunhos persistidos continuam visíveis após refresh da página.
|
||||
- Adicionado botão **Guardar rascunho** para persistir edições manuais do operador.
|
||||
- Adicionado painel **Correção com IA** para aplicar instruções ao rascunho atual, por exemplo:
|
||||
- adicionar preço validado;
|
||||
- acrescentar ficha técnica/URL quando disponível no contexto;
|
||||
- tornar a resposta mais curta;
|
||||
- mencionar suporte remoto ao eletricista;
|
||||
- ajustar tom/estrutura.
|
||||
- A correção com IA considera:
|
||||
- rascunho atual;
|
||||
- instrução do operador;
|
||||
- últimas mensagens da conversa;
|
||||
- dados da tarefa/oportunidade;
|
||||
- documentos selecionados;
|
||||
- conhecimento BLIF via OpenAI file_search.
|
||||
- O envio guarda o texto final do operador no draft antes de enviar para Chatwoot.
|
||||
|
||||
## Segurança/guardrails
|
||||
|
||||
- A IA não deve inventar preços, URLs, anexos, stock, descontos ou condições comerciais.
|
||||
- Para preços, usa apenas dados explícitos no contexto/base.
|
||||
- Para fichas técnicas, usa apenas URLs/anexos existentes no contexto/base.
|
||||
- Para tomada/ficha/adaptações elétricas, responde apenas com informação tecnicamente validada e recomenda validação por eletricista qualificado quando aplicável.
|
||||
|
||||
## Ficheiros alterados
|
||||
|
||||
- `app/admin_ui/pages/tasks.py`
|
||||
- `app/reply_assistant_service.py`
|
||||
- `app/email_reply_agent_service.py`
|
||||
- `app/llm_reply_generator.py`
|
||||
- `tests/test_v4928_1_5_16_reply_revision_workflow.py`
|
||||
|
||||
## Validação
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m compileall app scripts tests
|
||||
PYTHONPATH=. pytest -q
|
||||
```
|
||||
|
||||
Resultado esperado:
|
||||
|
||||
```text
|
||||
288 passed
|
||||
```
|
||||
79
RELEASE_NOTES_v4928_1_5_17_semi_automatic_followups.md
Normal file
79
RELEASE_NOTES_v4928_1_5_17_semi_automatic_followups.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# Clientflow backend v4928.1.5.17 — Follow-ups semi-automáticos
|
||||
|
||||
Base: `clientflow_backend_v4928_1_5_16_reply_revision_workflow.zip`.
|
||||
|
||||
## Objetivo
|
||||
|
||||
Implementar follow-up de oportunidades como fluxo semi-automático:
|
||||
|
||||
- o sistema agenda e sugere;
|
||||
- o operador valida, contacta e conclui;
|
||||
- nenhum email é enviado automaticamente por esta alteração.
|
||||
|
||||
## Principais alterações
|
||||
|
||||
### 1. Novo serviço de follow-up
|
||||
|
||||
Ficheiro novo: `app/followup_service.py`.
|
||||
|
||||
Adiciona ações internas:
|
||||
|
||||
- `FOLLOW_UP_QUOTE`
|
||||
- `FOLLOW_UP_PROFORMA`
|
||||
- `FOLLOW_UP_PAYMENT`
|
||||
- `FOLLOW_UP_CUSTOMER_REVIEW`
|
||||
- `FOLLOW_UP_GENERIC`
|
||||
|
||||
Estas ações são tarefas internas, com `safe_to_post = false`.
|
||||
|
||||
### 2. Criação automática de tarefas de follow-up
|
||||
|
||||
Quando uma tarefa comercial é concluída, o sistema pode criar automaticamente uma tarefa de follow-up:
|
||||
|
||||
- `SEND_QUOTE` → follow-up de orçamento em D+3;
|
||||
- `SEND_PROFORMA` → follow-up de pró-forma em D+2;
|
||||
- `SEND_INVOICE` → follow-up de pagamento/fatura em D+3;
|
||||
- `SEND_INFO` → follow-up de análise do cliente em D+5.
|
||||
|
||||
### 3. Criação manual pela oportunidade
|
||||
|
||||
Na página de detalhe da oportunidade foi adicionado um bloco para criar follow-up manual, com:
|
||||
|
||||
- tipo de follow-up;
|
||||
- prazo em dias;
|
||||
- nota opcional.
|
||||
|
||||
### 4. Fecho e cancelamento automático
|
||||
|
||||
O sistema agora:
|
||||
|
||||
- fecha follow-ups pendentes quando há nova atividade do cliente associada à oportunidade;
|
||||
- cancela follow-ups pendentes quando a oportunidade é marcada como ganha, perdida ou sem interesse;
|
||||
- evita criar follow-ups duplicados com a mesma razão/ação na mesma oportunidade.
|
||||
|
||||
### 5. UI de tarefas
|
||||
|
||||
A página de tarefas passa a incluir:
|
||||
|
||||
- separador `Follow-ups`;
|
||||
- separador `Follow-ups vencidos`;
|
||||
- chips de vencimento baseados em `due_at`;
|
||||
- cartão de detalhe com mensagem sugerida;
|
||||
- botões para adiar 2 ou 7 dias;
|
||||
- opção de concluir normalmente após contacto humano.
|
||||
|
||||
### 6. Compatibilidade de schema
|
||||
|
||||
`ensure_core_schema()` passa a garantir as colunas:
|
||||
|
||||
- `tasks.priority`;
|
||||
- `tasks.assigned_to`.
|
||||
|
||||
## Segurança operacional
|
||||
|
||||
A alteração não cria envio automático de email. A comunicação continua dependente do operador.
|
||||
|
||||
## Validação feita
|
||||
|
||||
- `python -m compileall -q app` executado com sucesso.
|
||||
- O arranque real com base de dados não foi validado neste ambiente por falta das dependências/runtime de PostgreSQL no container local.
|
||||
35
RELEASE_NOTES_v4928_1_5_18_followup_draft_generator.md
Normal file
35
RELEASE_NOTES_v4928_1_5_18_followup_draft_generator.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# ClientFlow v4928.1.5.18 — Follow-up draft generator
|
||||
|
||||
## Objetivo
|
||||
Melhorar o fluxo de follow-ups semi-automáticos no Centro de trabalho: a task continua a ser humana, mas o operador passa a ter um botão específico para gerar um rascunho personalizado de follow-up.
|
||||
|
||||
## Alterações principais
|
||||
- Adicionado botão **Gerar rascunho de follow-up** no bloco `FOLLOW_UP_*` do detalhe da task.
|
||||
- O botão usa o assistente de resposta existente, mas com templates específicos de follow-up.
|
||||
- Nada é enviado automaticamente: o rascunho fica editável e deve ser revisto pelo operador.
|
||||
- O rascunho pode usar cliente, oportunidade, documentos associados e histórico recente da conversa quando disponível.
|
||||
- Separação mais clara entre:
|
||||
- mensagem base simples para copiar;
|
||||
- rascunho personalizado gerado com IA;
|
||||
- nota interna da task.
|
||||
|
||||
## Templates adicionados/atualizados
|
||||
- `FOLLOW_UP_QUOTE`
|
||||
- `FOLLOW_UP_PROFORMA`
|
||||
- `FOLLOW_UP_PAYMENT`
|
||||
- `FOLLOW_UP_CUSTOMER_REVIEW`
|
||||
- `FOLLOW_UP_GENERIC`
|
||||
|
||||
## Rotas/UI
|
||||
- Nova rota HTMX:
|
||||
- `POST /tasks/{task_id}/follow-up-draft`
|
||||
- A rota só aceita tasks `FOLLOW_UP_*`.
|
||||
- O painel de resposta passa a mostrar linguagem específica para follow-up nas tasks de follow-up.
|
||||
|
||||
## Segurança operacional
|
||||
- Não envia mensagens automaticamente.
|
||||
- Não exige documento associado para gerar rascunho de follow-up; se houver documento, pode ser usado como contexto.
|
||||
- Mantém a validação e edição humana antes de envio.
|
||||
|
||||
## Validação técnica
|
||||
- `python -m compileall -q app` executado sem erros.
|
||||
14
RELEASE_NOTES_v4928_1_5_19.md
Normal file
14
RELEASE_NOTES_v4928_1_5_19.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# ClientFlow Backend v4928.1.5.19
|
||||
|
||||
Hotfix sobre v4928.1.5.18.
|
||||
|
||||
## Correção
|
||||
|
||||
- Reduzido `app/admin_dashboard.py` removendo linhas em branco/trailing whitespace para cumprir o teste legado `test_v472_routes_are_no_longer_registered_in_admin_dashboard`.
|
||||
- Mantida a funcionalidade de geração de rascunho de follow-up introduzida em v4928.1.5.18.
|
||||
|
||||
## Validação
|
||||
|
||||
- `python -m compileall -q app`
|
||||
- `python -m pytest -q`
|
||||
- Resultado: `288 passed`
|
||||
18
RELEASE_NOTES_v4928_1_5_2.md
Normal file
18
RELEASE_NOTES_v4928_1_5_2.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# ClientFlow v4928.1.5.2 — Task Reply Audit Import Hotfix
|
||||
|
||||
## Correção
|
||||
|
||||
- Corrige `ModuleNotFoundError: No module named 'app'` ao executar `python scripts/audit_task_reply_suggestions.py` diretamente a partir da pasta do backend.
|
||||
- O script agora adiciona automaticamente a raiz do projeto ao `sys.path` antes de importar módulos `app.*`.
|
||||
|
||||
## Comando recomendado
|
||||
|
||||
```bash
|
||||
python scripts/audit_task_reply_suggestions.py --status pending --format markdown --out /tmp/task_reply_audit.md
|
||||
```
|
||||
|
||||
## Workaround alternativo para versões anteriores
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python scripts/audit_task_reply_suggestions.py --status pending --format markdown --out /tmp/task_reply_audit.md
|
||||
```
|
||||
26
RELEASE_NOTES_v4928_1_5_20.md
Normal file
26
RELEASE_NOTES_v4928_1_5_20.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# v4928.1.5.20 — Dedicated OpenAI follow-up draft prompt
|
||||
|
||||
## Objetivo
|
||||
Corrige o botão de geração de rascunho de follow-up para usar um modo LLM/OpenAI específico de follow-up, em vez de reutilizar o agente genérico de resposta ao cliente.
|
||||
|
||||
## Alterações principais
|
||||
- Novo agente OpenAI/file_search dedicado a follow-ups em `email_reply_agent_service.py`.
|
||||
- Novo schema JSON `FOLLOW_UP_SCHEMA` para forçar resposta estruturada.
|
||||
- Prompt específico para `FOLLOW_UP_PAYMENT`, `FOLLOW_UP_QUOTE`, `FOLLOW_UP_PROFORMA`, `FOLLOW_UP_CUSTOMER_REVIEW` e `FOLLOW_UP_GENERIC`.
|
||||
- Para `FOLLOW_UP_PAYMENT`, o prompt proíbe explicitamente frases que mudem o objetivo para confirmação interna de pagamento.
|
||||
- `generate_reply_draft` usa o agente dedicado para templates `reply_type="follow_up"`.
|
||||
- Se o agente OpenAI de follow-up falhar ou estiver desligado, mantém o template seguro determinístico e não cai no agente genérico.
|
||||
- UI atualizada para deixar claro que o botão chama OpenAI: “Gerar rascunho personalizado com IA”.
|
||||
- Em follow-ups, “Pedido do cliente” passa a “Contexto da tarefa”.
|
||||
- Em follow-ups, “Concluir” passa a “Marcar follow-up como feito”.
|
||||
- Badge atualizado para `v4928.1.5.20`.
|
||||
|
||||
## Segurança
|
||||
- Nada é enviado automaticamente.
|
||||
- O operador continua a rever, editar/copiar e marcar a task como feita.
|
||||
- O rascunho não deve incluir notas internas, backfill, metadata, ClientFlow ou informação fiscal incompleta.
|
||||
- O rascunho não deve inventar preços, documentos, URLs, stock, prazos, descontos ou estado de pagamento.
|
||||
|
||||
## Validação
|
||||
- `python -m compileall -q app`
|
||||
- `python -m pytest -q` → 288 passed
|
||||
21
RELEASE_NOTES_v4928_1_5_21.md
Normal file
21
RELEASE_NOTES_v4928_1_5_21.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# v4928.1.5.21 — Chatwoot message formatting hotfix
|
||||
|
||||
## Fix
|
||||
|
||||
- Preserve operator-written line breaks when sending public messages to Chatwoot.
|
||||
- Chatwoot renders message content as Markdown-like text, where single newlines can be collapsed into spaces.
|
||||
- ClientFlow now formats outgoing Chatwoot messages with Markdown hard breaks before posting, so price lists and manually separated lines remain readable after sending.
|
||||
|
||||
## Scope
|
||||
|
||||
- Applies to public Chatwoot sends through:
|
||||
- `send_public_message`
|
||||
- `send_public_message_with_attachments`
|
||||
- Does not change the editable draft shown to the operator.
|
||||
- Does not alter the saved communication body in ClientFlow; the formatting is applied at the Chatwoot delivery boundary.
|
||||
|
||||
## Validation
|
||||
|
||||
- `python -m compileall -q app`
|
||||
- `python -m pytest -q`
|
||||
- Added tests for price-list line break preservation.
|
||||
24
RELEASE_NOTES_v4928_1_5_22.md
Normal file
24
RELEASE_NOTES_v4928_1_5_22.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# v4928.1.5.22 — Operator Workbench communication UX
|
||||
|
||||
Esta versão inclui o hotfix v4928.1.5.21 de formatação Chatwoot e acrescenta melhorias operacionais no detalhe de tasks/follow-ups.
|
||||
|
||||
## Correções incluídas
|
||||
|
||||
- P1: alerta claro para tarefas de comunicação sem email nem conversa Chatwoot.
|
||||
- P2: fallback de nome/email no título e listas de tasks: cliente fiscal → oportunidade → contacto/email → título.
|
||||
- P3: ações práticas no rascunho: guardar, copiar mensagem e criar email na Outbox quando existe email.
|
||||
- P4: indicação/seleção de documentos da oportunidade antes de gerar rascunho IA; aviso quando há múltiplos documentos.
|
||||
- P5: notas de backfill passam para contexto interno recolhível; menos ruído técnico no fluxo normal.
|
||||
- Follow-up payment: prompt OpenAI reforçado para não chamar “pró-forma” a documentos Jasmin `quotation`.
|
||||
|
||||
## Segurança operacional
|
||||
|
||||
- Nada é enviado automaticamente por email.
|
||||
- “Criar email na Outbox” cria item `email.send_email` para revisão/processamento posterior.
|
||||
- Envio Chatwoot só aparece quando a task tem conversa Chatwoot associada.
|
||||
- Se não houver email nem conversa, a UI mostra bloqueio operacional de canal.
|
||||
|
||||
## Validação
|
||||
|
||||
- `python -m compileall -q app`
|
||||
- `python -m pytest -q` → 290 passed
|
||||
45
RELEASE_NOTES_v4928_1_5_24.md
Normal file
45
RELEASE_NOTES_v4928_1_5_24.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# ClientFlow v4928.1.5.24 — Contact-person greeting for follow-up drafts
|
||||
|
||||
Incremental update over v4928.1.5.23.
|
||||
|
||||
## Changes
|
||||
|
||||
- Adds deterministic recipient resolution for follow-up drafts from personal email addresses.
|
||||
- Example: `nuno.silva@verifone.com` resolves to `Nuno Silva`, first name `Nuno`, high confidence.
|
||||
- Generic mailboxes such as `info@`, `geral@`, `comercial@`, `vendas@`, `escritorio@` are not treated as people.
|
||||
- Dedicated OpenAI follow-up prompt now receives:
|
||||
- contact person full name,
|
||||
- first name,
|
||||
- confidence,
|
||||
- source of the inference,
|
||||
- preferred greeting.
|
||||
- Generated follow-up drafts are post-processed to preserve the preferred greeting.
|
||||
- Safe fallback templates also receive the same preferred greeting when OpenAI is disabled/unavailable.
|
||||
- Task detail UI now shows a small contact-person card when a person was safely resolved.
|
||||
|
||||
## Example
|
||||
|
||||
For a follow-up task with:
|
||||
|
||||
- Company: Verifone Portugal, Lda
|
||||
- Email: nuno.silva@verifone.com
|
||||
|
||||
The greeting becomes:
|
||||
|
||||
```text
|
||||
Bom dia Sr. Nuno,
|
||||
```
|
||||
|
||||
For generic emails such as `escritorio@eficen.pt`, the greeting remains neutral:
|
||||
|
||||
```text
|
||||
Bom dia,
|
||||
```
|
||||
|
||||
## Validation
|
||||
|
||||
```text
|
||||
python -m compileall -q app tests scripts
|
||||
pytest -q
|
||||
292 passed
|
||||
```
|
||||
32
RELEASE_NOTES_v4928_1_5_25.md
Normal file
32
RELEASE_NOTES_v4928_1_5_25.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# v4928.1.5.25 — Chatwoot inbound ingestion recovery
|
||||
|
||||
Correção focada no pipeline Chatwoot → ClientFlow.
|
||||
|
||||
## Corrige
|
||||
|
||||
- Emails inbound do Chatwoot são identificados por `payload.message_type = incoming`.
|
||||
- Deixa de depender de `sender.type = contact`, porque em webhooks de email esse campo pode vir vazio.
|
||||
- O webhook já não deixa eventos em `processed=false`, `ignored=false`, `processing_error=null` se o processamento levantar exceção.
|
||||
- Mensagens outgoing do operador sem task pendente deixam de contar como `processing_error`.
|
||||
- System health passa a mostrar `Chatwoot inbound pendente`.
|
||||
|
||||
## Scripts
|
||||
|
||||
- `scripts/reprocess_pending_chatwoot_raw_events.py`
|
||||
- Reprocessa inbound pendente de `raw_events`.
|
||||
- Suporta `--dry-run`, `--limit`, `--include-errors`, `--source-event-id`.
|
||||
- `scripts/audit_chatwoot_ingestion_gap.py`
|
||||
- Auditoria inbound correta por `payload.message_type`.
|
||||
- Separa outgoing de inbound para evitar falsos positivos.
|
||||
|
||||
## Comandos recomendados pós-instalação
|
||||
|
||||
```bash
|
||||
cd /mnt/ssd/home/plx/clientflow_backend
|
||||
source .venv/bin/activate
|
||||
export PYTHONPATH=.
|
||||
|
||||
python scripts/audit_chatwoot_ingestion_gap.py --hours 168
|
||||
python scripts/reprocess_pending_chatwoot_raw_events.py --dry-run --limit 50
|
||||
python scripts/reprocess_pending_chatwoot_raw_events.py --limit 50
|
||||
```
|
||||
10
RELEASE_NOTES_v4928_1_5_27.md
Normal file
10
RELEASE_NOTES_v4928_1_5_27.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# v4928.1.5.27 — LLM classifier with recent email context
|
||||
|
||||
- Classificador de actions passa a exigir JSON estruturado do LLM.
|
||||
- O LLM escolhe apenas dentro da allow-list de actions existente.
|
||||
- Removida classificação comercial por regras diretas; mantém-se apenas deteção técnica de bounce/NDR.
|
||||
- Adicionado contexto dos últimos 2 emails públicos antes da mensagem atual.
|
||||
- Quando o webhook não traz histórico, o classificador usa fallback a raw_events da conversa.
|
||||
- Classificações com `confidence < 0.80`, JSON inválido ou `needs_human_review=true` caem em `REVIEW_MANUALLY`.
|
||||
- `CONFIRM_PAYMENT` cobre também “vai pagar / vai enviar comprovativo”, com detalhe em `metadata.payment_intent`.
|
||||
- Metadata da task passa a guardar `llm_customer_intent`, `llm_evidence`, `llm_history_used`, `llm_confidence` e `payment_intent`.
|
||||
14
RELEASE_NOTES_v4928_1_5_28.md
Normal file
14
RELEASE_NOTES_v4928_1_5_28.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# v4928.1.5.28 — Chatwoot context SQL + audit allow-list hotfix
|
||||
|
||||
Correções:
|
||||
|
||||
- Corrige erro PostgreSQL `AmbiguousParameter` na query de histórico Chatwoot usada pelo classificador LLM com últimos emails.
|
||||
- A query passa a fazer `CAST(:current_source_event_id AS TEXT)` para parâmetros nulos.
|
||||
- Inclui `scripts/audit_system_health.py` no pacote.
|
||||
- Atualiza auditoria para aceitar actions operacionais válidas (`PREPARE_ORDER`, `REVIEW_RECONCILIATION`).
|
||||
- Distingue erros reais `processing_exception:*` de eventos benignos `outgoing:*` e `ignored:*`.
|
||||
|
||||
Validação:
|
||||
|
||||
- `python -m compileall -q app scripts tests`
|
||||
- `python -m pytest -q`
|
||||
20
RELEASE_NOTES_v4928_1_5_29.md
Normal file
20
RELEASE_NOTES_v4928_1_5_29.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# ClientFlow v4928.1.5.29 — Operator Workbench contact identity guard
|
||||
|
||||
## Correção principal
|
||||
|
||||
Corrige um bug no Centro de trabalho em que o nome `payload.sender.name` vindo do Chatwoot podia aparecer como título principal de várias tasks de empresas diferentes. Exemplo observado: `Alexandre Ruivo` aparecia em vários processos com assuntos `... para a Nova Maquiambiente`, `... para a Granjaluz`, `... para a SÓ FERREIRAS`, etc.
|
||||
|
||||
## O que mudou
|
||||
|
||||
- O Workbench deixa de confiar cegamente em `raw_events.payload.sender.name`.
|
||||
- Se o mesmo `sender.name` aparecer associado a vários emails diferentes na fila, a identidade é marcada como insegura.
|
||||
- Em contexto de campanhas/títulos `Carregadores ... para a <empresa>`, a UI passa a preferir a empresa/processo do título quando a identidade do Chatwoot é insegura.
|
||||
- Cliente fiscal validado continua a ter prioridade máxima.
|
||||
- A correção é apenas de apresentação/segurança UI: não altera clientes, contactos, oportunidades, tasks nem associações fiscais.
|
||||
- Adicionado script read-only `scripts/audit_contact_identity_collisions.py` para encontrar nomes Chatwoot reutilizados em tasks pendentes.
|
||||
|
||||
## Validação
|
||||
|
||||
- `python -m compileall -q app scripts tests`
|
||||
- `pytest -q`
|
||||
- Resultado: `304 passed`
|
||||
38
RELEASE_NOTES_v4928_1_5_3.md
Normal file
38
RELEASE_NOTES_v4928_1_5_3.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Release v4928.1.5.3 — Intent Gate & Reply Safety Fix
|
||||
|
||||
## Resumo
|
||||
|
||||
Esta versão adiciona uma camada de triagem antes da geração de respostas. O objetivo é impedir que mensagens não comerciais — bounces, RGPD/unsubscribe, sem interesse, atualização de contacto e suporte — caiam em templates comerciais.
|
||||
|
||||
## Novidades
|
||||
|
||||
- Novo `app/reply_intent_gate.py`.
|
||||
- Integração do `intent_gate` no `reply_assistant_service`.
|
||||
- Novos templates operacionais e de triagem.
|
||||
- Fallback seguro para `MANUAL_REVIEW_REQUIRED`.
|
||||
- Correção do validador de instalação para aceitar negações corretas.
|
||||
- UI da tarefa mostra “Triagem da mensagem”.
|
||||
- Auditoria mostra categoria de intenção e contagem por intenção.
|
||||
- Novos testes estáticos e comportamentais.
|
||||
|
||||
## Validação
|
||||
|
||||
```text
|
||||
242 passed
|
||||
```
|
||||
|
||||
## Próximo passo recomendado
|
||||
|
||||
Executar novamente:
|
||||
|
||||
```bash
|
||||
python scripts/audit_task_reply_suggestions.py --status pending --format markdown --out /tmp/task_reply_audit_v153.md
|
||||
```
|
||||
|
||||
Comparar com o relatório anterior, sobretudo:
|
||||
|
||||
- redução de `SEND_INFO_EQUIPMENT_LIST` indevido;
|
||||
- bounces classificados como `BOUNCE_EMAIL`;
|
||||
- pedidos RGPD/remover classificados como `UNSUBSCRIBE_REQUEST`;
|
||||
- suporte classificado como `SUPPORT_INCIDENT`;
|
||||
- Varisom classificado como `COMMERCIAL_CLARIFICATION` sem bloqueio falso de instalação.
|
||||
17
RELEASE_NOTES_v4928_1_5_30.md
Normal file
17
RELEASE_NOTES_v4928_1_5_30.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# v4928.1.5.30 — Task detail contact/fiscal identity guard
|
||||
|
||||
## Problema corrigido
|
||||
O Centro de trabalho já tinha proteção contra nomes Chatwoot contaminados, mas o detalhe da task ainda podia mostrar o contacto/fiscal errado como se fosse a empresa correta. Exemplo: processo "... para a Nova Maquiambiente" apresentado como "Enviar orçamento — Alexandre Ruivo" e fiscalmente pronto com NIF/morada de Alexandre Ruivo.
|
||||
|
||||
## Alterações
|
||||
- Adicionada proteção no detalhe da task para comparar o cliente fiscal ligado com a empresa/processo extraído do título/assunto.
|
||||
- Quando há mismatch claro, a UI passa a mostrar a empresa/processo como título operacional e mantém o remetente apenas como contacto Chatwoot.
|
||||
- O painel fiscal deixa de apresentar o cliente fiscal suspeito como confirmado.
|
||||
- A prontidão fiscal passa a bloquear com "Cliente fiscal por confirmar" para ações documentais.
|
||||
- Os dados confirmados deixam de mostrar NIF do cliente fiscal suspeito.
|
||||
- Adicionado aviso explícito: validar/corrigir cliente fiscal antes de emitir orçamento, pró-forma ou fatura.
|
||||
- Adicionado script read-only: `scripts/audit_task_identity_mismatch.py`.
|
||||
|
||||
## Validação
|
||||
- `python -m compileall -q app scripts tests`
|
||||
- `pytest -q` → 307 passed
|
||||
6
RELEASE_NOTES_v4928_1_5_31.md
Normal file
6
RELEASE_NOTES_v4928_1_5_31.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# v4928.1.5.31 — identity mismatch repair
|
||||
|
||||
- Improves task detail identity guard after fiscal customer detach: if the opportunity customer name is still polluted, the UI prefers the process/company hint from the subject.
|
||||
- Extends `scripts/audit_task_identity_mismatch.py` with `.env` loading and a safe `--apply` mode.
|
||||
- `--apply` detaches only unsafe `opportunities.local_customer_id`, updates opportunity display name/email from the process hint/contact email, and records an audit trail in metadata.
|
||||
- No customer records are deleted.
|
||||
26
RELEASE_NOTES_v4928_1_5_32.md
Normal file
26
RELEASE_NOTES_v4928_1_5_32.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# ClientFlow v4928.1.5.32 — Audit Stabilization Hotfix
|
||||
|
||||
## Objetivo
|
||||
|
||||
Versão pequena de estabilização depois da limpeza operacional e correção de associações fiscal/contacto.
|
||||
|
||||
## Alterações
|
||||
|
||||
- `scripts/audit_task_identity_mismatch.py` ficou conservador:
|
||||
- só sinaliza oportunidades com `local_customer_id` preenchido;
|
||||
- não considera suspeito quando o cliente fiscal partilha tokens fortes com o processo/título;
|
||||
- não considera suspeito quando o domínio do email fiscal coincide com o contacto/oportunidade;
|
||||
- o `--apply` só desassocia clientes fiscais claramente inseguros.
|
||||
- `scripts/audit_system_health.py` passa a tratar deadlocks/locks temporários em contagens de DB como `WARN`, sem abortar todo o audit.
|
||||
- Mantém as proteções anteriores de UI/detalhe contra identidades contaminadas.
|
||||
|
||||
## Não incluído
|
||||
|
||||
- Não altera regras comerciais.
|
||||
- Não limpa dados automaticamente.
|
||||
- Não altera tasks já reparadas manualmente.
|
||||
|
||||
## Validação local
|
||||
|
||||
- `python -m compileall -q app scripts tests`
|
||||
- `python -m pytest -q`
|
||||
25
RELEASE_NOTES_v4928_1_5_33.md
Normal file
25
RELEASE_NOTES_v4928_1_5_33.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# ClientFlow v4928.1.5.33 — Follow-up SQL Cast Hotfix
|
||||
|
||||
Correção pequena e segura para agendamento manual de follow-up em oportunidades.
|
||||
|
||||
## Corrigido
|
||||
|
||||
- `app/followup_service.py`: o update de metadata da oportunidade agora faz cast explícito de `task_id` para `TEXT` dentro de `jsonb_build_object`.
|
||||
- `last_follow_up_scheduled_at` também passa a ser gravado como texto ISO/SQL seguro (`now()::text`) no JSONB.
|
||||
|
||||
## Contexto
|
||||
|
||||
O PostgreSQL podia falhar com:
|
||||
|
||||
```text
|
||||
psycopg.errors.IndeterminateDatatype: could not determine data type of parameter $1
|
||||
```
|
||||
|
||||
quando a UI criava um follow-up manual e tentava gravar `last_follow_up_task_id` na metadata da oportunidade.
|
||||
|
||||
## Impacto
|
||||
|
||||
- Não altera schema.
|
||||
- Não altera dados existentes.
|
||||
- Não muda regras de follow-up.
|
||||
- Apenas evita erro SQL no update de metadata após a task de follow-up ser criada.
|
||||
58
RELEASE_NOTES_v4928_1_5_34.md
Normal file
58
RELEASE_NOTES_v4928_1_5_34.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# ClientFlow v4928.1.5.34 — Communication Composer + SQL safety foundation
|
||||
|
||||
Atualização conservadora focada em reduzir erros de rascunho/LLM e começar a padronizar SQL seguro.
|
||||
|
||||
## Principais melhorias
|
||||
|
||||
### 1. Communication Composer
|
||||
- O painel de resposta passa a pedir um **objetivo explícito** antes de gerar rascunho:
|
||||
- Enviar informação/lista de equipamentos
|
||||
- Enviar orçamento
|
||||
- Enviar pró-forma
|
||||
- Enviar fatura
|
||||
- Confirmar pagamento/follow-up
|
||||
- O operador pode escrever **instruções adicionais para a IA** antes da geração.
|
||||
- Os documentos/anexos da oportunidade ficam disponíveis no Composer antes de chamar IA, não apenas depois do rascunho.
|
||||
- A instrução do operador é guardada em `message_drafts.operator_instruction`.
|
||||
- O objetivo escolhido e os documentos selecionados ficam persistidos em `message_drafts.metadata.communication_objective`.
|
||||
|
||||
### 2. Guardrails de rascunho para faturas
|
||||
- Para `SEND_INVOICE`, o validador bloqueia rascunhos que:
|
||||
- dizem que a fatura ainda será emitida;
|
||||
- pedem comprovativo de pagamento indevidamente;
|
||||
- tratam uma fatura emitida como pró-forma/pagamento pendente.
|
||||
- Quando uma fatura está selecionada, o sistema recomenda mencionar que segue em anexo.
|
||||
- O prompt OpenAI/file_search recebe `objetivo_operador` e `instrucao_operador`, para impedir que a IA reinterprete o próximo passo.
|
||||
|
||||
### 3. Base SQL safety
|
||||
- Novo `app/db_helpers.py` com helpers para serialização JSONB segura.
|
||||
- Updates internos de estado de draft enviados/falhados passam a usar `metadata || CAST(:metadata_patch AS JSONB)` em vez de `jsonb_build_object` com binds soltos.
|
||||
- Novo script read-only `scripts/audit_sql_safety.py` para encontrar padrões SQL frágeis:
|
||||
- `jsonb_build_object(... :param ...)`
|
||||
- `:param IS NULL` sem cast
|
||||
- `ANY(:lista)` sem tipo explícito
|
||||
- concatenação JSONB sem `CAST(:metadata_patch AS JSONB)`
|
||||
|
||||
### 4. Auditoria profunda incluída no pacote
|
||||
- Incluído `scripts/audit_deep_system.py` com correção de serialização `Decimal`/datas/UUID.
|
||||
|
||||
## Segurança operacional
|
||||
- Não envia mensagens automaticamente.
|
||||
- Não altera tasks, oportunidades ou clientes existentes.
|
||||
- Não executa reparações automáticas.
|
||||
- A nova auditoria SQL é apenas diagnóstico.
|
||||
|
||||
## Validação
|
||||
- `python -m compileall -q app scripts tests`
|
||||
- `python -m pytest -q`
|
||||
- Resultado local: `312 passed`
|
||||
|
||||
## Comandos úteis pós-instalação
|
||||
|
||||
```bash
|
||||
python scripts/audit_system_health.py; echo "EXIT_CODE=$?"
|
||||
python scripts/audit_deep_system.py --window-hours 72 --sample-limit 25; echo "EXIT_CODE=$?"
|
||||
python scripts/audit_sql_safety.py; echo "EXIT_CODE=$?"
|
||||
```
|
||||
|
||||
Nota: `audit_sql_safety.py` pode devolver `EXIT_CODE=1` quando encontra padrões a rever. Isso é esperado nesta fase e não significa falha de runtime.
|
||||
25
RELEASE_NOTES_v4928_1_5_35.md
Normal file
25
RELEASE_NOTES_v4928_1_5_35.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# v4928.1.5.35 — Invoice delivery guard
|
||||
|
||||
Hotfix focada em envio de faturas/documentos e clareza operacional.
|
||||
|
||||
## Alta prioridade
|
||||
|
||||
- Mostra explicitamente o estado do PDF/anexo nos documentos usados pelo rascunho:
|
||||
- `✓ PDF/anexo disponível`
|
||||
- `⚠ PDF/anexo não disponível`
|
||||
- Mantém validação de envio: `SEND_INVOICE` exige documento Jasmin com PDF suportado antes de enviar via Chatwoot.
|
||||
- Ao gerar rascunho de `SEND_INVOICE` com fatura selecionada, sincroniza a task antiga de reconciliação para o contexto correto:
|
||||
- `Enviar fatura FA... ao cliente`
|
||||
- nota passa a dizer que o PDF/anexo está disponível ou que falta sincronizar/anexar.
|
||||
- Adiciona `scripts/sync_invoice_delivery_context.py` para alinhar tasks `SEND_INVOICE` pendentes que já têm fatura atual ligada.
|
||||
|
||||
## Média prioridade
|
||||
|
||||
- O Fluxo operacional passa a marcar `Fatura` como concluída quando existe fatura comercial atual ligada, mesmo que o snapshot operacional antigo ainda não tenha atualizado o cartão.
|
||||
- O painel de produtos separa linhas manuais atuais das linhas importadas de documentos/Odoo/Jasmin, evitando somar histórico/importações como total operacional atual.
|
||||
|
||||
## Notas
|
||||
|
||||
- Não envia emails automaticamente.
|
||||
- Não descarrega PDFs em massa na abertura da página; valida suporte por `external_id`/Jasmin e carrega o PDF apenas no envio ou download.
|
||||
- Mantém compatibilidade com o Composer v1.5.34.
|
||||
12
RELEASE_NOTES_v4928_1_5_36.md
Normal file
12
RELEASE_NOTES_v4928_1_5_36.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# v4928.1.5.36 — terminal opportunity flow guard
|
||||
|
||||
Correções focadas em consistência operacional depois de faturação/conclusão:
|
||||
|
||||
- Oportunidades terminais (`WON`, `LOST`, `NO_INTEREST`, `DELIVERED` ou `status=closed`) deixam de recomendar próximas ações/follow-ups.
|
||||
- Follow-ups pendentes são fechados automaticamente quando a oportunidade é movida para fase terminal.
|
||||
- Script `scripts/sync_closed_opportunity_followups.py` para corrigir follow-ups antigos ainda pendentes em oportunidades concluídas.
|
||||
- Mapa operacional passa a inferir `Venda`, `Produção`, `Validação física` e `Fatura` a partir de fase terminal/fatura atual ligada, evitando `Fatura ○` quando existe fatura emitida.
|
||||
- O chip da decisão seguinte usa o `action_code` da task pendente real quando existe, evitando mostrar `SEND_INVOICE` em tarefas de follow-up.
|
||||
- O detalhe da oportunidade ignora follow-ups pendentes antigos no contador operacional quando a oportunidade já está fechada.
|
||||
|
||||
Sem envio automático e sem alteração de dados por defeito, exceto quando o script é executado com `--apply`.
|
||||
36
RELEASE_NOTES_v4928_1_5_37.md
Normal file
36
RELEASE_NOTES_v4928_1_5_37.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# v4928.1.5.37 — terminal flow guard + Odoo sync panel
|
||||
|
||||
Combina a correção `v4928.1.5.36_terminal_opportunity_flow_guard` com a nova área `v1.5.37_odoo_sync_panel`.
|
||||
|
||||
## Inclui de v1.5.36
|
||||
|
||||
- Oportunidades em fase terminal deixam de recomendar follow-ups como próxima ação.
|
||||
- Follow-ups pendentes podem ser fechados em lote quando a oportunidade já está concluída.
|
||||
- O fluxo operacional passa a marcar fatura como concluída quando existe fatura atual emitida.
|
||||
- O chip da decisão seguinte usa o `action_code` real da task pendente.
|
||||
|
||||
## Novo em v1.5.37
|
||||
|
||||
- Novo painel **Estado Odoo** no detalhe da oportunidade.
|
||||
- Botão **Sincronizar Odoo agora** por oportunidade, equivalente ao fluxo manual de Jasmin.
|
||||
- Painel mostra venda Odoo ligada, cliente/valor Odoo, estado físico, entregas/pickings e produção/preparação.
|
||||
- Lista vendas Odoo ligadas/candidatas a partir de `reconciliation_items`.
|
||||
- Ação para associar uma venda Odoo candidata à oportunidade e atualizar `operation_links`.
|
||||
- Partial HTMX `/opportunities/{opportunity_id}/partials/odoo-status`.
|
||||
- Auditoria read-only `scripts/audit_odoo_opportunity_sync.py` para oportunidades avançadas sem venda/estado físico Odoo sincronizado.
|
||||
|
||||
## Segurança operacional
|
||||
|
||||
- O painel não chama Odoo ao abrir a oportunidade; a sincronização é manual para evitar lentidão e efeitos colaterais.
|
||||
- A associação de candidato Odoo reutiliza a reconciliação existente e não cria documentos fiscais.
|
||||
- O botão de sincronização apenas lê Odoo e atualiza `operation_links`/estado físico ClientFlow.
|
||||
|
||||
## Validação recomendada
|
||||
|
||||
```bash
|
||||
python -m compileall -q app scripts tests
|
||||
python -m pytest -q
|
||||
python scripts/audit_system_health.py; echo "EXIT_CODE=$?"
|
||||
python scripts/audit_deep_system.py --window-hours 72 --sample-limit 25; echo "EXIT_CODE=$?"
|
||||
python scripts/audit_odoo_opportunity_sync.py; echo "EXIT_CODE=$?"
|
||||
```
|
||||
33
RELEASE_NOTES_v4928_1_5_38.md
Normal file
33
RELEASE_NOTES_v4928_1_5_38.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# v4928.1.5.38 — Proforma ORC Attachment Guard
|
||||
|
||||
## Objetivo
|
||||
|
||||
Alinhar o Communication Composer com a regra operacional da BLIF/ClientFlow:
|
||||
|
||||
> No ClientFlow, a pró-forma é representada no Jasmin por um orçamento `ORC.*`.
|
||||
|
||||
## Alterações
|
||||
|
||||
- `SEND_PROFORMA` passa a esperar explicitamente documento `quotation`/`ORC.*` Jasmin.
|
||||
- O template de pró-forma passa a chamar o documento de `orçamento/proforma`.
|
||||
- O prompt OpenAI/file_search passa a receber a regra: pró-forma = orçamento Jasmin ORC.*.
|
||||
- O LLM fica proibido de dizer que “a proposta formal será enviada” quando já existe ORC selecionado/anexado.
|
||||
- A validação de segurança bloqueia respostas `SEND_PROFORMA` sem ORC selecionado.
|
||||
- A geração de rascunho para documentos passa a exigir PDF/anexo disponível também para `SEND_PROFORMA` e `SEND_QUOTE`, não apenas `SEND_INVOICE`.
|
||||
- A UI da task passa a distinguir melhor:
|
||||
- documento de pró-forma;
|
||||
- orçamento Jasmin ORC;
|
||||
- PDF disponível;
|
||||
- selecionado/anexado ao envio.
|
||||
- O botão de concluir task fiscal (`SEND_PROFORMA`/`SEND_INVOICE`) fica bloqueado quando o cliente fiscal está por confirmar.
|
||||
- O endpoint de conclusão também valida fiscalmente no servidor antes de concluir tarefas fiscais.
|
||||
|
||||
## Validação
|
||||
|
||||
- `python -m compileall -q app scripts tests`
|
||||
- `python -m pytest -q`
|
||||
- Resultado local: `314 passed`
|
||||
|
||||
## Nota
|
||||
|
||||
Esta versão não altera dados automaticamente. Apenas corrige regras de composer, validação e UI.
|
||||
15
RELEASE_NOTES_v4928_1_5_39.md
Normal file
15
RELEASE_NOTES_v4928_1_5_39.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# v4928.1.5.39 — Odoo panel import + draft attachment selection hotfix
|
||||
|
||||
Hotfix read/write behavior unchanged except for draft/document selection persistence.
|
||||
|
||||
## Fixes
|
||||
|
||||
- Fixes opportunity Odoo panel error `name 'engine' is not defined` by importing DB engine/text explicitly.
|
||||
- Fixes SEND_PROFORMA draft revision/send validation where older persisted drafts showed an ORC selected in compact UI but posted no `selected_document_ids`.
|
||||
- Centralizes effective selected-document fallback in task UI endpoints so generate/save/revise/send use the same ORC/invoice default.
|
||||
- Keeps ClientFlow business rule: proforma = Jasmin ORC.* quotation document.
|
||||
|
||||
## Validation
|
||||
|
||||
- `python -m compileall -q app scripts tests`
|
||||
- targeted import smoke tests
|
||||
70
RELEASE_NOTES_v4928_1_5_4.md
Normal file
70
RELEASE_NOTES_v4928_1_5_4.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# ClientFlow v4928.1.5.4 — LLM-first Reply Assistant
|
||||
|
||||
## Objetivo
|
||||
|
||||
Corrige a direção da versão anterior para evitar evolução excessivamente rule-based. As regras determinísticas passam a ser guardrails objetivos e o LLM/OpenRouter, quando ativo, passa a interpretar a intenção comercial/técnica e a gerar o rascunho com conhecimento BLIF.
|
||||
|
||||
## Principais alterações
|
||||
|
||||
- Adicionado modo `CLIENTFLOW_REPLY_LLM_FIRST_ENABLED=true`.
|
||||
- Novo template neutro `LLM_BUSINESS_REPLY` para respostas geradas por LLM.
|
||||
- Novo `classify_reply_guardrail()` apenas para guardrails objetivos:
|
||||
- bounces/email devolvido;
|
||||
- respostas automáticas/out-of-office;
|
||||
- pedidos RGPD/unsubscribe;
|
||||
- atualização de contacto.
|
||||
- O `classify_reply_intent()` antigo continua disponível como fallback determinístico quando LLM está desligado.
|
||||
- Novo `MessageCleaner` reforçado para remover:
|
||||
- histórico citado;
|
||||
- headers de email;
|
||||
- assinaturas;
|
||||
- disclaimers;
|
||||
- rodapés que contaminavam suporte/IVA.
|
||||
- Prompt OpenRouter reformulado para interpretar primeiro a necessidade real do cliente.
|
||||
- Contexto LLM passa a incluir:
|
||||
- mensagem limpa;
|
||||
- cliente;
|
||||
- oportunidade;
|
||||
- documentos selecionados;
|
||||
- conhecimento BLIF relevante;
|
||||
- catálogo estruturado de produtos/acessórios;
|
||||
- tipos de resposta permitidos.
|
||||
- Normalização LLM inclui:
|
||||
- `intent`;
|
||||
- `reply_type`;
|
||||
- `requires_attachment`;
|
||||
- `confidence`;
|
||||
- `customer_need`;
|
||||
- `recommended_next_action`;
|
||||
- `knowledge_used`;
|
||||
- `warnings`.
|
||||
- Se o LLM falhar em modo LLM-first, a tarefa fica bloqueada para revisão manual, em vez de sugerir uma resposta genérica.
|
||||
- O script de auditoria passa a mostrar intenção/necessidade/confiança do LLM.
|
||||
|
||||
## Segurança mantida
|
||||
|
||||
- O LLM não envia mensagens automaticamente.
|
||||
- O operador continua a rever/enviar.
|
||||
- O validador continua a bloquear promessas proibidas de instalação, anexos/documentos errados e preços suspeitos.
|
||||
- Sem OpenRouter ativo, o sistema regressa a fallback seguro/determinístico.
|
||||
|
||||
## Configuração
|
||||
|
||||
```env
|
||||
CLIENTFLOW_REPLY_LLM_ENABLED=false
|
||||
CLIENTFLOW_REPLY_LLM_FIRST_ENABLED=true
|
||||
CLIENTFLOW_REPLY_LLM_MODEL=
|
||||
OPENROUTER_API_KEY=
|
||||
```
|
||||
|
||||
Para auditar com LLM:
|
||||
|
||||
```bash
|
||||
python scripts/audit_task_reply_suggestions.py --status pending --use-llm --format markdown --out /tmp/task_reply_audit_llm.md
|
||||
```
|
||||
|
||||
## Validação
|
||||
|
||||
```text
|
||||
249 passed
|
||||
```
|
||||
15
RELEASE_NOTES_v4928_1_5_40.md
Normal file
15
RELEASE_NOTES_v4928_1_5_40.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# v4928.1.5.40 — scheduled follow-up visibility guard
|
||||
|
||||
## Objetivo
|
||||
Evitar que follow-ups agendados para uma data futura apareçam no Centro de trabalho como "A fazer agora" imediatamente após a task anterior ser concluída.
|
||||
|
||||
## Alterações
|
||||
- O Centro de trabalho exclui tasks `FOLLOW_UP_*` com `due_at > now()` da lista principal.
|
||||
- Follow-ups futuros continuam criados como `pending`, mas ficam apenas agendados até vencerem.
|
||||
- O total "A fazer agora" passa a refletir apenas itens acionáveis agora.
|
||||
- A lógica de atraso passa a usar `due_at` quando existe, em vez de apenas `created_at`.
|
||||
- Quando `due_at <= now()`, o follow-up volta a aparecer como trabalho humano normal.
|
||||
|
||||
## Validação
|
||||
- `python -m compileall -q app scripts tests`: OK
|
||||
- `python -m pytest -q`: 314 passed
|
||||
30
RELEASE_NOTES_v4928_1_5_5.md
Normal file
30
RELEASE_NOTES_v4928_1_5_5.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# ClientFlow v4928.1.5.5 — Audit LLM Auth Hotfix
|
||||
|
||||
## Objetivo
|
||||
|
||||
Corrige um problema no script `scripts/audit_task_reply_suggestions.py` em que, quando executado com `--use-llm`, o próprio script podia injetar uma chave placeholder `OPENROUTER_API_KEY=not-used-by-task-reply-audit` antes de `app.config` carregar o `.env`.
|
||||
|
||||
Como variáveis de ambiente têm precedência sobre o ficheiro `.env`, essa placeholder podia ocultar a chave real e fazer as chamadas OpenRouter falharem com HTTP 401 / autenticação ausente.
|
||||
|
||||
## Alterações
|
||||
|
||||
- `--use-llm` deixa de definir `OPENROUTER_API_KEY` placeholder.
|
||||
- A placeholder só é usada quando o LLM está desligado.
|
||||
- Se uma placeholder antiga existir no ambiente do processo, é removida antes de importar `app.config`.
|
||||
- Mantém o modo sem LLM compatível com auditorias read-only sem chave OpenRouter real.
|
||||
|
||||
## Teste recomendado
|
||||
|
||||
```bash
|
||||
python scripts/audit_task_reply_suggestions.py \
|
||||
--status pending \
|
||||
--route vendas \
|
||||
--use-llm \
|
||||
--limit 5 \
|
||||
--format markdown \
|
||||
--out /tmp/task_reply_audit_llm_vendas.md
|
||||
|
||||
grep -n "LLM:" /tmp/task_reply_audit_llm_vendas.md | head -20
|
||||
```
|
||||
|
||||
O esperado é aparecer `LLM: used` ou metadata de intenção/confiança gerada, em vez de `LLM: fallback` com erro `Missing Authentication header`.
|
||||
29
RELEASE_NOTES_v4928_1_5_52_document_selection.md
Normal file
29
RELEASE_NOTES_v4928_1_5_52_document_selection.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# v4928.1.5.52 — Seleção granular de documentos por oportunidade
|
||||
|
||||
Correção operacional para casos em que o mesmo cliente tem compras próximas e a reconciliação associa orçamento/fatura/venda ao processo errado.
|
||||
|
||||
## Problema
|
||||
|
||||
Antes, o operador só conseguia desassociar Jasmin/Odoo em bloco. Quando uma oportunidade continha uma fatura correta e um orçamento de outra compra, ou linhas importadas misturadas, não havia forma segura de escolher quais documentos pertenciam à oportunidade.
|
||||
|
||||
## Alterações
|
||||
|
||||
- Adicionadas ações por documento Jasmin na ficha da oportunidade:
|
||||
- **Definir principal**;
|
||||
- **Histórico**;
|
||||
- **Desassociar este**;
|
||||
- Atualizar nº / PDF continuam disponíveis.
|
||||
- A desassociação de um documento é local ao ClientFlow:
|
||||
- não apaga nada no Jasmin;
|
||||
- remove só a ligação local do documento à oportunidade;
|
||||
- remove apenas linhas importadas desse documento quando existe referência de origem;
|
||||
- devolve candidatos de reconciliação desse documento para revisão.
|
||||
- Candidatos Jasmin passam a permitir **Associar adicional** mesmo quando já existe documento atual, para casos em que orçamento/fatura pertencem à mesma compra.
|
||||
- Mantém auditoria via `opportunity_events`.
|
||||
|
||||
## Validação
|
||||
|
||||
```text
|
||||
PYTHONPATH=. pytest -q tests
|
||||
314 passed
|
||||
```
|
||||
24
RELEASE_NOTES_v4928_1_5_55.md
Normal file
24
RELEASE_NOTES_v4928_1_5_55.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# v4928.1.5.55 — Opportunity vertical cards layout
|
||||
|
||||
Reorganiza a página da oportunidade mantendo as funcionalidades atuais.
|
||||
|
||||
## Mudanças
|
||||
|
||||
- Remove a navegação duplicada por tabs na oportunidade.
|
||||
- Mantém a página em duas colunas:
|
||||
- esquerda: Contexto e evidência;
|
||||
- direita: Operação e ações humanas.
|
||||
- Transforma Cliente, Documentos, Produtos, Odoo, Mensagens, Timeline e Técnico em cards verticais normais.
|
||||
- Adiciona card operacional dedicado para “Associar cliente fiscal”.
|
||||
- Mantém os anchors internos e compatibilidade dos testes de regressão.
|
||||
|
||||
## Sem mudanças
|
||||
|
||||
- Sem migration.
|
||||
- Sem alteração de contratos externos.
|
||||
- Sem alteração de lógica Jasmin/Odoo/Chatwoot.
|
||||
- Sem alteração de endpoints.
|
||||
|
||||
## Validação
|
||||
|
||||
`PYTHONPATH=. pytest -q tests` → 319 passed.
|
||||
11
RELEASE_NOTES_v4928_1_5_56.md
Normal file
11
RELEASE_NOTES_v4928_1_5_56.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# v4928.1.5.56 — Opportunity Operation Dedup + Customer New Route
|
||||
|
||||
## Fixes
|
||||
- Removes the duplicated rendered card where **O que fazer agora?** and **Tarefa ativa** repeated the same pending task/action in the opportunity operation sidebar.
|
||||
- Keeps the active task action inside **O que fazer agora?** with the same button/target, avoiding redundant operator decisions.
|
||||
- Adds an explicit `/customers/new` page so the **Criar cliente** action no longer falls into `/customers/{customer_id}` and shows `Identificador de cliente inválido.`.
|
||||
- Updates fiscal association search link to use `/customers?q=...`, matching the customers filter parameter.
|
||||
|
||||
## Tests
|
||||
- Added regression tests for the non-duplicated operation panel and explicit customer creation route.
|
||||
- Full test suite: `322 passed`.
|
||||
34
RELEASE_NOTES_v4928_1_5_57.md
Normal file
34
RELEASE_NOTES_v4928_1_5_57.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# v4928.1.5.57 — Fluxo comercial flexível e condições de pagamento
|
||||
|
||||
## Objetivo
|
||||
|
||||
Reduzir rigidez do fluxo da oportunidade sem perder os dois percursos reais:
|
||||
|
||||
1. pagamento antes do envio;
|
||||
2. pagamento após entrega.
|
||||
|
||||
## Alterações
|
||||
|
||||
- O dropdown da oportunidade passa a mostrar uma lista curta de fases comerciais.
|
||||
- Estados detalhados como fatura, pagamento, Odoo, produção e envio continuam visíveis nos cards de contexto, mas deixam de dominar a alteração manual de fase.
|
||||
- Novo card **Condições comerciais** na coluna Operação.
|
||||
- Condição de pagamento persistida em `opportunities.metadata`:
|
||||
- `before_shipping` — antes do envio;
|
||||
- `after_delivery` — após entrega;
|
||||
- `agreement` — conforme acordo;
|
||||
- `undefined` — a definir.
|
||||
- Condição de entrega persistida em `opportunities.metadata`.
|
||||
- UI mostra aviso contextual:
|
||||
- pagamento pós-entrega não bloqueia preparação/envio;
|
||||
- pagamento antes do envio bloqueia expedição sem confirmação.
|
||||
|
||||
## Compatibilidade
|
||||
|
||||
- Sem migrations.
|
||||
- Sem alteração de tabela.
|
||||
- Sem alterar lógica de Odoo/Jasmin/Chatwoot.
|
||||
- Estados antigos continuam válidos e aceites no backend.
|
||||
|
||||
## Validação
|
||||
|
||||
- `325 passed`
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user