Import ClientFlow production v4928.1.5.132.4

This commit is contained in:
plx
2026-07-29 13:11:01 +00:00
parent 6445044ac6
commit 261d342057
405 changed files with 48373 additions and 1401 deletions

View File

@@ -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=

View 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.

View 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
View 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
```

View 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
```

View 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.

View 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
```

View 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
```

View 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.

View 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.

View 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
View 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
View 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
```

View 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
```

View 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
```

View 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.

View 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"
```

View File

@@ -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
View 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

View 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
```

View 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
```

View 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

View 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.

View 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
```

View 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.

View 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`.

View 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.

View 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`

View 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`.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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`.

View File

@@ -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.

View File

@@ -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
```

View File

@@ -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
```

View File

@@ -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`.

View File

@@ -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.

View 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.

View 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.

View 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

View 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

View 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.

View 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
```

View 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.

View 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.

View File

@@ -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”.

View File

@@ -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.

View 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.

View 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.

View 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`.

View 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.

View 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.

View 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;
- 3160 dias;
- 6190 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
```

View 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.

View 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.

View 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
```

View File

@@ -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
```

View File

@@ -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.

View File

@@ -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**.

View 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
```

View 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.

View 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.

View File

@@ -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

View File

@@ -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

View 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.

View File

@@ -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

View 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.

View 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`

View File

@@ -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.

View 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
```

View 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.

View 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.

View 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`

View 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
```

View 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

View 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.

View 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

View 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
```

View 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
```

View 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`.

View 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`

View 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`

View 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.

View 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

View 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.

View 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`

View 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.

View 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.

View 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.

View 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`.

View 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=$?"
```

View 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.

View 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

View 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
```

View 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

View 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`.

View 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
```

View 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.

View 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`.

View 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