Release v4928.1.4.2 stable
This commit is contained in:
109
docs/CLIENTFLOW_V31_NOTES.md
Normal file
109
docs/CLIENTFLOW_V31_NOTES.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# ClientFlow v3.1 — correções Jasmin/Packlink
|
||||
|
||||
Esta versão é incremental sobre a v3 e corrige os pontos encontrados nos testes remotos.
|
||||
|
||||
## Alterações principais
|
||||
|
||||
### Produtos
|
||||
|
||||
Agora existe separação entre:
|
||||
|
||||
- `sku`: código interno/Odoo;
|
||||
- `jasmin_sales_item`: artigo de venda no Jasmin.
|
||||
|
||||
Exemplo:
|
||||
|
||||
```text
|
||||
sku = ODOO-1
|
||||
jasmin_sales_item = CARREGADOR_MONO_7KW
|
||||
```
|
||||
|
||||
As tabelas recebem colunas novas de forma aditiva:
|
||||
|
||||
```sql
|
||||
ALTER TABLE products ADD COLUMN IF NOT EXISTS jasmin_sales_item TEXT;
|
||||
ALTER TABLE opportunity_items ADD COLUMN IF NOT EXISTS jasmin_sales_item TEXT;
|
||||
ALTER TABLE order_items ADD COLUMN IF NOT EXISTS jasmin_sales_item TEXT;
|
||||
```
|
||||
|
||||
Quando uma linha é adicionada à oportunidade a partir do catálogo, `opportunity_items.jasmin_sales_item` é copiado do produto.
|
||||
|
||||
A criação de orçamento Jasmin usa esta prioridade:
|
||||
|
||||
```text
|
||||
metadata.jasmin_sales_item
|
||||
opportunity_items.jasmin_sales_item
|
||||
JASMIN_DEFAULT_SALES_ITEM
|
||||
sku
|
||||
```
|
||||
|
||||
Assim o SKU Odoo deixa de ser usado como artigo Jasmin, exceto como fallback.
|
||||
|
||||
### Documentos Jasmin
|
||||
|
||||
Depois de criar orçamento/fatura, o serviço faz um `GET` ao Jasmin para tentar preencher:
|
||||
|
||||
- `document_type`
|
||||
- `serie`
|
||||
- `series_number`
|
||||
- `document_number`
|
||||
- `total_amount`
|
||||
- `currency`
|
||||
|
||||
Isto melhora a tabela `commercial_documents` e a visualização na oportunidade.
|
||||
|
||||
### UI da oportunidade
|
||||
|
||||
- O formulário **Adicionar produto** saiu da área técnica e passou para o cartão **Produtos**.
|
||||
- Os botões **Criar orçamento** e **Converter em fatura** mostram mensagem de feedback informando que o pedido foi enviado para a outbox.
|
||||
- A tabela de produtos mostra SKU/Odoo e artigo Jasmin separadamente.
|
||||
|
||||
### Timers systemd
|
||||
|
||||
Foram adicionados exemplos em `deploy/systemd/`:
|
||||
|
||||
- `clientflow-outbox-jasmin.service`
|
||||
- `clientflow-outbox-jasmin.timer`
|
||||
- `clientflow-outbox-packlink.service`
|
||||
- `clientflow-outbox-packlink.timer`
|
||||
|
||||
Instalação Jasmin:
|
||||
|
||||
```bash
|
||||
sudo cp deploy/systemd/clientflow-outbox-jasmin.* /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now clientflow-outbox-jasmin.timer
|
||||
systemctl list-timers | grep clientflow
|
||||
```
|
||||
|
||||
O timer Jasmin processa outbox a cada 1 minuto.
|
||||
|
||||
## Verificações após deploy
|
||||
|
||||
```bash
|
||||
cd /mnt/ssd/home/plx/clientflow_backend
|
||||
source .venv/bin/activate
|
||||
python -m py_compile app/*.py scripts/*.py
|
||||
python -m pytest -q
|
||||
sudo systemctl restart clientflow-api
|
||||
sudo systemctl status clientflow-api --no-pager
|
||||
```
|
||||
|
||||
## Migração prática de produtos existentes
|
||||
|
||||
Depois do deploy, ir a `/products/{id}` e preencher **Artigo Jasmin** para cada produto usado em documentos.
|
||||
|
||||
Para corrigir via SQL/Python um produto específico:
|
||||
|
||||
```bash
|
||||
python - <<'PY'
|
||||
from sqlalchemy import text
|
||||
from app.db import engine
|
||||
with engine.begin() as conn:
|
||||
conn.execute(text("""
|
||||
UPDATE products
|
||||
SET jasmin_sales_item = 'CARREGADOR_MONO_7KW', updated_at = now()
|
||||
WHERE sku = 'ODOO-1'
|
||||
"""))
|
||||
PY
|
||||
```
|
||||
78
docs/CLIENTFLOW_V32_NOTES.md
Normal file
78
docs/CLIENTFLOW_V32_NOTES.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# ClientFlow v3.2 — HTMX, validações e operação sem terminal
|
||||
|
||||
Esta versão é incremental em cima da v3.1. O objetivo é tornar o fluxo Jasmin/Packlink mais visível e operável pela UI, sem reescrever o dashboard inteiro.
|
||||
|
||||
## Principais alterações
|
||||
|
||||
### HTMX progressivo na oportunidade
|
||||
|
||||
A página de oportunidade passa a usar HTMX em blocos específicos:
|
||||
|
||||
- cartão **Documentos Jasmin**;
|
||||
- cartão **Produtos**;
|
||||
- ações de outbox associadas à oportunidade.
|
||||
|
||||
O comportamento esperado é:
|
||||
|
||||
```text
|
||||
[Criar orçamento]
|
||||
→ cria item pending na outbox
|
||||
→ atualiza apenas o cartão Documentos Jasmin
|
||||
→ mostra estado pending/sent/failed
|
||||
→ polling atualiza o cartão a cada 10 segundos
|
||||
```
|
||||
|
||||
Isto reduz a sensação de que “não aconteceu nada” depois de clicar num botão.
|
||||
|
||||
### Validação antes de criar orçamento
|
||||
|
||||
Antes de criar outbox `jasmin.create_quotation`, o sistema valida:
|
||||
|
||||
- oportunidade com cliente local associado;
|
||||
- cliente com nome fiscal;
|
||||
- cliente com NIF;
|
||||
- morada/código postal/cidade quando ainda não existe `jasmin_customer_party_key`;
|
||||
- pelo menos uma linha de produto ativa;
|
||||
- todas as linhas com `jasmin_sales_item` preenchido;
|
||||
- preços unitários maiores que zero.
|
||||
|
||||
Se faltar algo, a UI mostra o erro diretamente no cartão Jasmin e não cria outbox inválida.
|
||||
|
||||
### Produtos por HTMX
|
||||
|
||||
No cartão **Produtos** da oportunidade:
|
||||
|
||||
- adicionar produto atualiza apenas o cartão;
|
||||
- remover produto atualiza apenas o cartão;
|
||||
- total é recalculado no bloco;
|
||||
- produtos sem `jasmin_sales_item` aparecem como bloqueio visual para orçamento.
|
||||
|
||||
### Outbox visível na oportunidade
|
||||
|
||||
O cartão **Documentos Jasmin** mostra as ações recentes Jasmin relacionadas com a oportunidade:
|
||||
|
||||
- `create_quotation`;
|
||||
- `convert_quotation_to_invoice`;
|
||||
- estado `pending`, `sent`, `failed`;
|
||||
- último erro;
|
||||
- botão **Reprocessar** para itens `failed`/`pending`.
|
||||
|
||||
### Filtro de produtos sem Artigo Jasmin
|
||||
|
||||
Em `/products`, o filtro de estado ganhou a opção:
|
||||
|
||||
```text
|
||||
Ativos sem Artigo Jasmin
|
||||
```
|
||||
|
||||
Serve para encontrar rapidamente produtos que ainda bloqueiam emissão de orçamento Jasmin.
|
||||
|
||||
## Continuação recomendada
|
||||
|
||||
Para v3.3:
|
||||
|
||||
1. Obter PDF de orçamento/fatura.
|
||||
2. Mostrar link/número real do documento quando o GET Jasmin devolver `documentType`, `serie` e `seriesNumber`.
|
||||
3. Adicionar ações HTMX em `/integrations` para testar Jasmin/Packlink.
|
||||
4. Implementar painel de Packlink semelhante ao painel Jasmin.
|
||||
5. Refatorar `admin_dashboard.py` para `app/admin_ui/pages/` por etapas.
|
||||
120
docs/CLIENTFLOW_V33_NOTES.md
Normal file
120
docs/CLIENTFLOW_V33_NOTES.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# ClientFlow v3.3 — HTMX manual e operação Jasmin mais estável
|
||||
|
||||
Esta versão é incremental sobre a v3.2 e foca-se em remover o comportamento visual estranho causado pelo auto-refresh do cartão **Documentos Jasmin**.
|
||||
|
||||
## Alterações principais
|
||||
|
||||
### 1. Documentos Jasmin sem auto-refresh
|
||||
|
||||
Foi removido o polling HTMX automático do cartão de Documentos Jasmin.
|
||||
|
||||
Antes:
|
||||
|
||||
```html
|
||||
hx-trigger="every 10s"
|
||||
```
|
||||
|
||||
Agora:
|
||||
|
||||
- o cartão não se reconstrói sozinho;
|
||||
- existe botão **Atualizar estado**;
|
||||
- os botões **Criar orçamento** e **Converter em fatura** continuam a atualizar o cartão uma vez após o clique;
|
||||
- o operador controla quando quer atualizar o estado.
|
||||
|
||||
Fluxo esperado:
|
||||
|
||||
```text
|
||||
[Criar orçamento]
|
||||
→ cria item na outbox
|
||||
→ mostra feedback imediato
|
||||
→ timer/worker processa
|
||||
→ operador clica [Atualizar estado]
|
||||
→ vê orçamento criado ou erro
|
||||
```
|
||||
|
||||
### 2. Feedback visível após ações Jasmin
|
||||
|
||||
Os botões continuam a devolver mensagens visíveis no cartão:
|
||||
|
||||
- Pedido de orçamento enviado para a outbox Jasmin.
|
||||
- Pedido de fatura enviado para a outbox Jasmin.
|
||||
- Erros de validação antes de criar a outbox.
|
||||
|
||||
### 3. Outbox Jasmin visível na oportunidade
|
||||
|
||||
O cartão de Documentos Jasmin continua a mostrar as ações da outbox associadas à oportunidade:
|
||||
|
||||
- action type;
|
||||
- estado;
|
||||
- erro resumido;
|
||||
- botão **Reprocessar** para itens pendentes/falhados.
|
||||
|
||||
### 4. Validação antes de criar orçamento
|
||||
|
||||
Mantém-se a validação preventiva:
|
||||
|
||||
- cliente associado;
|
||||
- NIF válido;
|
||||
- nome fiscal;
|
||||
- morada/código postal/cidade quando o cliente ainda não existe no Jasmin;
|
||||
- pelo menos uma linha de produto;
|
||||
- todas as linhas com `jasmin_sales_item`;
|
||||
- preço unitário maior que zero.
|
||||
|
||||
### 5. SKU/Odoo separado de artigo Jasmin
|
||||
|
||||
Mantém-se a regra introduzida na v3.1/v3.2:
|
||||
|
||||
```text
|
||||
products.sku = código interno/Odoo
|
||||
products.jasmin_sales_item = artigo Jasmin
|
||||
opportunity_items.sku = código interno/Odoo
|
||||
opportunity_items.jasmin_sales_item = artigo Jasmin usado no orçamento
|
||||
```
|
||||
|
||||
A criação de orçamento usa `jasmin_sales_item`, não o SKU Odoo.
|
||||
|
||||
## Deploy
|
||||
|
||||
Aplicar como nas versões anteriores:
|
||||
|
||||
```bash
|
||||
rsync -avz --delete \
|
||||
--exclude ".env" \
|
||||
--exclude ".venv/" \
|
||||
--exclude "venv/" \
|
||||
--exclude "__pycache__/" \
|
||||
--exclude ".git/" \
|
||||
--exclude "*.db" \
|
||||
--exclude "*.log" \
|
||||
--exclude "uploads/" \
|
||||
./ plx@alarmsys:/mnt/ssd/home/plx/clientflow_backend/
|
||||
```
|
||||
|
||||
Depois no remoto:
|
||||
|
||||
```bash
|
||||
cd /mnt/ssd/home/plx/clientflow_backend
|
||||
source .venv/bin/activate
|
||||
python -m py_compile app/*.py scripts/*.py
|
||||
sudo systemctl restart clientflow-api
|
||||
```
|
||||
|
||||
## Nota operacional
|
||||
|
||||
O botão **Atualizar estado** não processa a outbox. Ele apenas recarrega o cartão.
|
||||
|
||||
Para processar automaticamente, manter ativo o timer systemd da outbox Jasmin:
|
||||
|
||||
```bash
|
||||
systemctl list-timers | grep clientflow-outbox-jasmin
|
||||
```
|
||||
|
||||
Ou processar manualmente:
|
||||
|
||||
```bash
|
||||
JASMIN_OUTBOX_ENABLED=true \
|
||||
OUTBOX_TARGET_SYSTEM=jasmin \
|
||||
OUTBOX_DRY_RUN=false \
|
||||
python scripts/process_outbox.py
|
||||
```
|
||||
60
docs/CLIENTFLOW_V34_NOTES.md
Normal file
60
docs/CLIENTFLOW_V34_NOTES.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# ClientFlow v3.4 — Documentos, PDFs e Outbox Operacional
|
||||
|
||||
Esta versão é incremental sobre a v3.3 e foca-se em reduzir a necessidade de usar o terminal para operar integrações.
|
||||
|
||||
## Alterações principais
|
||||
|
||||
### Documentos Jasmin
|
||||
|
||||
- Adicionados botões no cartão **Documentos Jasmin** da oportunidade:
|
||||
- **Atualizar nº**: volta a consultar o Jasmin e atualiza número/série/valor do documento local.
|
||||
- **PDF**: tenta obter o PDF do orçamento ou da fatura diretamente via API Jasmin.
|
||||
- Mantém atualização manual; não há auto-refresh contínuo do cartão.
|
||||
|
||||
### Outbox operacional
|
||||
|
||||
- Página `/outbox` remodelada para uso operacional.
|
||||
- Filtros por estado: todas, pendentes, enviadas, falhadas e ignoradas.
|
||||
- Filtros por sistema: Jasmin, Packlink, Chatwoot, Mautic e Odoo.
|
||||
- Ações na UI:
|
||||
- Reprocessar
|
||||
- Ignorar
|
||||
- Ver detalhe/payload/erro
|
||||
- Novo estado `ignored` suportado na outbox.
|
||||
|
||||
### Produtos / Jasmin
|
||||
|
||||
- Página `/products/validate-jasmin` para validar se os artigos configurados em `products.jasmin_sales_item` existem no Jasmin.
|
||||
- Botão **Validar artigos Jasmin** no catálogo de produtos.
|
||||
- Mantém a separação:
|
||||
- `products.sku` = SKU interno/Odoo
|
||||
- `products.jasmin_sales_item` = artigo Jasmin usado em orçamentos/faturas
|
||||
|
||||
## Validações mantidas
|
||||
|
||||
Antes de criar orçamento Jasmin, continua a validar:
|
||||
|
||||
- Cliente associado à oportunidade
|
||||
- NIF do cliente
|
||||
- Nome fiscal
|
||||
- Morada/código postal/cidade quando for necessário criar cliente no Jasmin
|
||||
- Pelo menos um produto ativo na oportunidade
|
||||
- Todos os produtos com `jasmin_sales_item`
|
||||
- Preços unitários positivos
|
||||
|
||||
## Notas operacionais
|
||||
|
||||
- A obtenção de PDF depende dos endpoints de impressão do Jasmin estarem disponíveis no tenant.
|
||||
- Se o PDF falhar, a página devolve erro textual sem afetar o documento já criado.
|
||||
- A outbox Jasmin deve ser processada por timer systemd ou manualmente com `scripts/process_outbox.py`.
|
||||
|
||||
## Validação técnica
|
||||
|
||||
Executado na geração desta versão:
|
||||
|
||||
```bash
|
||||
python -m py_compile app/*.py scripts/*.py
|
||||
python -m pytest -q
|
||||
```
|
||||
|
||||
Resultado: `4 passed`.
|
||||
82
docs/CLIENTFLOW_V3_CUSTOMERS_DOCUMENTS.md
Normal file
82
docs/CLIENTFLOW_V3_CUSTOMERS_DOCUMENTS.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# ClientFlow v3 — Clientes, documentos e envios
|
||||
|
||||
Esta versão separa responsabilidades para evitar que a página da oportunidade fique demasiado pesada.
|
||||
|
||||
## Princípio de UI
|
||||
|
||||
- **Clientes**: dados fiscais, NIF, morada, contactos e ligação Jasmin.
|
||||
- **Oportunidades**: estado da potencial compra, produtos, documentos associados e envios.
|
||||
- **Documentos**: orçamentos e faturas em `commercial_documents`.
|
||||
- **Envios**: registos Packlink em `shipments`.
|
||||
|
||||
## Páginas principais
|
||||
|
||||
### `/customers`
|
||||
|
||||
Lista clientes locais normalizados, com pesquisa por nome, NIF, email, telefone ou `jasmin_customer_party_key`.
|
||||
|
||||
Permite criar uma ficha mínima de cliente com nome e NIF.
|
||||
|
||||
### `/customers/{id}`
|
||||
|
||||
Ficha completa de cliente:
|
||||
|
||||
- nome fiscal;
|
||||
- NIF;
|
||||
- email;
|
||||
- telefone;
|
||||
- morada fiscal;
|
||||
- código postal;
|
||||
- cidade;
|
||||
- país;
|
||||
- Jasmin `customerPartyKey`;
|
||||
- Jasmin ID;
|
||||
- oportunidades associadas;
|
||||
- documentos Jasmin;
|
||||
- envios Packlink.
|
||||
|
||||
### `/opportunities/{id}`
|
||||
|
||||
A oportunidade mantém apenas um resumo compacto do cliente e um seletor para associar uma ficha local.
|
||||
|
||||
Botões operacionais:
|
||||
|
||||
- **Criar orçamento**: cria sempre um novo orçamento Jasmin.
|
||||
- **Converter em fatura**: converte o orçamento ativo/mais recente em fatura.
|
||||
|
||||
## Modelo de dados
|
||||
|
||||
Novas tabelas/colunas usadas nesta versão:
|
||||
|
||||
- `customers`
|
||||
- `commercial_documents`
|
||||
- `commercial_document_lines`
|
||||
- `shipments`
|
||||
- `opportunities.local_customer_id`
|
||||
|
||||
O campo antigo `opportunities.customer_id` é mantido para compatibilidade com dados legados/Chatwoot. A ligação nova e normalizada usa `opportunities.local_customer_id`.
|
||||
|
||||
## Fluxo Jasmin validado
|
||||
|
||||
1. Cliente associado à oportunidade.
|
||||
2. `find_or_create_customer_for_opportunity()` usa primeiro o cliente local.
|
||||
3. NIF é enviado para Jasmin sem prefixo `PT`.
|
||||
4. Se o cliente não existir no Jasmin, cria cliente com `partyKey = CF{NIF}`.
|
||||
5. Cria orçamento `ORC / ORC2026`.
|
||||
6. Para faturar, chama `POST /billing/invoices/fromQuotation/{quotationId}` com body `{}`.
|
||||
|
||||
## Regras importantes
|
||||
|
||||
- Não enviar `electronicMail` nem `telephone` vazios para Jasmin.
|
||||
- OData Jasmin deve usar `$top <= 100`.
|
||||
- Packlink usa API key no header `Authorization`, sem `Bearer`.
|
||||
- Para cotação Packlink em Portugal, normalizar CP para 4 dígitos.
|
||||
|
||||
## Estratégia de refactor
|
||||
|
||||
Esta v3 ainda não reescreve o `admin_dashboard.py` totalmente. A prioridade foi criar a camada de domínio e a navegação operacional correta. O próximo passo recomendado é extrair progressivamente:
|
||||
|
||||
- `app/admin_ui/pages/customers.py`
|
||||
- `app/admin_ui/pages/opportunities.py`
|
||||
- `app/admin_ui/pages/integrations.py`
|
||||
- `app/admin_ui/components.py`
|
||||
72
docs/CLIENTFLOW_V40_NOTES.md
Normal file
72
docs/CLIENTFLOW_V40_NOTES.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# ClientFlow v4.0 — estabilização operacional + refactor modular incremental
|
||||
|
||||
Esta versão junta a estabilização prevista para v3.5 com o arranque do refactor v4.
|
||||
|
||||
## Incluído
|
||||
|
||||
### Operação segura
|
||||
|
||||
- Migrações SQL versionadas em `migrations/`.
|
||||
- Script `scripts/apply_migrations.py`.
|
||||
- Script `scripts/check_clientflow_health.py`.
|
||||
- Script `scripts/install_systemd_timers.sh`.
|
||||
- Índices adicionais para outbox, clientes, documentos, envios e produtos.
|
||||
- Colunas de segurança na outbox: `locked_at`, `lock_owner`, `ignored_at`.
|
||||
- API interna read-only:
|
||||
- `/api/internal/health`
|
||||
- `/api/internal/outbox/summary`
|
||||
- `/api/internal/documents/summary`
|
||||
|
||||
### Refactor modular incremental
|
||||
|
||||
- Novo pacote `app/admin_ui/`.
|
||||
- `app/main.py` passa a importar o router admin através de `app.admin_ui.router`.
|
||||
- O router ainda usa `app.admin_dashboard` internamente para manter compatibilidade.
|
||||
- Helpers HTML comuns em `app/admin_ui/components.py`.
|
||||
- Estrutura de destino criada para modularizar:
|
||||
- `app/customers/`
|
||||
- `app/opportunities/`
|
||||
- `app/commercial/`
|
||||
- `app/logistics/`
|
||||
- `app/integrations/`
|
||||
- `app/outbox/`
|
||||
|
||||
## Deploy recomendado
|
||||
|
||||
```bash
|
||||
cd /mnt/ssd/home/plx/clientflow_backend
|
||||
source .venv/bin/activate
|
||||
python scripts/apply_migrations.py --dry-run
|
||||
python scripts/apply_migrations.py
|
||||
python -m py_compile app/*.py scripts/*.py
|
||||
python scripts/check_clientflow_health.py
|
||||
sudo systemctl restart clientflow-api
|
||||
```
|
||||
|
||||
## Timers systemd
|
||||
|
||||
Jasmin:
|
||||
|
||||
```bash
|
||||
sudo ./scripts/install_systemd_timers.sh /mnt/ssd/home/plx/clientflow_backend
|
||||
```
|
||||
|
||||
Packlink, só quando o fluxo de envios estiver validado:
|
||||
|
||||
```bash
|
||||
ENABLE_PACKLINK_TIMER=true sudo -E ./scripts/install_systemd_timers.sh /mnt/ssd/home/plx/clientflow_backend
|
||||
```
|
||||
|
||||
## Próximo passo após v4.0
|
||||
|
||||
Migrar gradualmente rotas de `app/admin_dashboard.py` para:
|
||||
|
||||
```text
|
||||
app/admin_ui/pages/customers.py
|
||||
app/admin_ui/pages/opportunities.py
|
||||
app/admin_ui/pages/products.py
|
||||
app/admin_ui/pages/outbox.py
|
||||
app/admin_ui/pages/integrations.py
|
||||
```
|
||||
|
||||
Sem mudar URLs públicas.
|
||||
34
docs/CLIENTFLOW_V41_NOTES.md
Normal file
34
docs/CLIENTFLOW_V41_NOTES.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# ClientFlow v4.1 — Correções de deploy e scripts operacionais
|
||||
|
||||
Esta versão é incremental sobre a v4.0 e corrige o problema encontrado no remoto:
|
||||
|
||||
```text
|
||||
ModuleNotFoundError: No module named 'app'
|
||||
```
|
||||
|
||||
## Alterações
|
||||
|
||||
- `scripts/apply_migrations.py` passa a resolver automaticamente a raiz do projeto.
|
||||
- `scripts/check_clientflow_health.py` passa a resolver automaticamente a raiz do projeto.
|
||||
- `scripts/process_outbox.py` também passa a mudar para a raiz do projeto antes de carregar `.env`.
|
||||
- `scripts/test_jasmin_connection.py` e `scripts/test_packlink_connection.py` passam a carregar `.env` a partir da raiz do projeto mesmo quando executados de outra pasta.
|
||||
|
||||
## Comandos depois do deploy
|
||||
|
||||
```bash
|
||||
cd /mnt/ssd/home/plx/clientflow_backend
|
||||
source .venv/bin/activate
|
||||
|
||||
python scripts/apply_migrations.py --dry-run
|
||||
python scripts/apply_migrations.py
|
||||
python scripts/check_clientflow_health.py
|
||||
|
||||
sudo systemctl restart clientflow-api
|
||||
sudo systemctl status clientflow-api --no-pager
|
||||
```
|
||||
|
||||
Já não deve ser necessário executar com `PYTHONPATH=.`.
|
||||
|
||||
## Nota
|
||||
|
||||
O `.env` continua excluído do `rsync` e deve permanecer apenas no servidor remoto.
|
||||
59
docs/CLIENTFLOW_V42_NOTES.md
Normal file
59
docs/CLIENTFLOW_V42_NOTES.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# ClientFlow v4.2 — Operação Comercial e Logística
|
||||
|
||||
Esta versão melhora a maturidade operacional do ClientFlow sem alterar a arquitetura principal.
|
||||
|
||||
## Alterações principais
|
||||
|
||||
- Nova página `/operations` / `/operacoes` para operação diária.
|
||||
- Nova página `/system/health` para saúde operacional.
|
||||
- Timeline da oportunidade passa a juntar eventos, produtos, documentos, outbox e envios.
|
||||
- API interna adiciona:
|
||||
- `/api/internal/operations/summary`
|
||||
- `/api/internal/system/health`
|
||||
- Navegação passa a separar:
|
||||
- Operações
|
||||
- Outbox
|
||||
- Integrações
|
||||
- A página de operações destaca:
|
||||
- oportunidades abertas
|
||||
- outbox pendente/falhada
|
||||
- faturas emitidas hoje
|
||||
- orçamentos abertos
|
||||
- envios pendentes
|
||||
- clientes incompletos
|
||||
- produtos sem Artigo Jasmin
|
||||
|
||||
## Objetivo
|
||||
|
||||
Reduzir a dependência do terminal para operação diária. A equipa deve conseguir abrir `/operations` e saber rapidamente o que precisa de ação.
|
||||
|
||||
## Rotas novas
|
||||
|
||||
```text
|
||||
/operations
|
||||
/operacoes
|
||||
/system/health
|
||||
/api/internal/operations/summary
|
||||
/api/internal/system/health
|
||||
```
|
||||
|
||||
## Notas de deploy
|
||||
|
||||
Depois do rsync:
|
||||
|
||||
```bash
|
||||
cd /mnt/ssd/home/plx/clientflow_backend
|
||||
source .venv/bin/activate
|
||||
python scripts/apply_migrations.py --dry-run
|
||||
python scripts/apply_migrations.py
|
||||
python scripts/check_clientflow_health.py
|
||||
sudo systemctl restart clientflow-api
|
||||
```
|
||||
|
||||
## Próximas melhorias sugeridas
|
||||
|
||||
- Packlink com cotação antes de criar envio.
|
||||
- Guardar PDFs localmente.
|
||||
- Backups/restore via scripts próprios.
|
||||
- Duplicados de clientes por NIF.
|
||||
- Sincronização de preços Jasmin vs Odoo.
|
||||
42
docs/CLIENTFLOW_V42_REVIEW_UI_NOTES.md
Normal file
42
docs/CLIENTFLOW_V42_REVIEW_UI_NOTES.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# ClientFlow v4.2 — revisão operacional e UI
|
||||
|
||||
Esta revisão mantém a arquitetura da v4.2, mas corrige pontos de estabilidade e torna a UI mais operacional.
|
||||
|
||||
## Correções aplicadas
|
||||
|
||||
- `OUTBOX_DRY_RUN=true` deixou de marcar itens como `sent`; agora passa o item para `dry_run`.
|
||||
- Integrações com `*_OUTBOX_ENABLED=false` deixam de ficar apenas em `skipped` silencioso; agora ficam `blocked` com erro claro.
|
||||
- A idempotência de `jasmin.create_quotation` passou a ser determinística por oportunidade: `jasmin:quotation:{opportunity_id}`.
|
||||
- `integration_outbox` ganhou estados visíveis e reprocessáveis: `pending`, `processing`, `sent`, `failed`, `blocked`, `dry_run`, `ignored`, `cancelled`.
|
||||
- O schema base passa a garantir colunas de controlo da outbox (`locked_at`, `lock_owner`, `ignored_at`) mesmo em instalações novas.
|
||||
- Webhooks Chatwoot exigem `CLIENTFLOW_WEBHOOK_SECRET` em `ENV=prod`, `production` ou `staging`.
|
||||
- Admin UI e API interna podem ser protegidas com `CLIENTFLOW_ADMIN_TOKEN`.
|
||||
|
||||
## Melhorias UI
|
||||
|
||||
- Página da oportunidade ganhou painel **Integrações da oportunidade** com contadores:
|
||||
- pendentes;
|
||||
- falhadas;
|
||||
- bloqueadas;
|
||||
- dry-run.
|
||||
- A oportunidade passa a mostrar ações Jasmin/Packlink/Chatwoot/Mautic ligadas à oportunidade, com último erro e reprocessamento.
|
||||
- `/outbox` ganhou filtros para os novos estados operacionais.
|
||||
- Badges visuais foram atualizados para `processing`, `blocked`, `dry_run` e `cancelled`.
|
||||
|
||||
## Validação
|
||||
|
||||
Executado:
|
||||
|
||||
```bash
|
||||
python -m compileall app scripts tests
|
||||
pytest -q
|
||||
```
|
||||
|
||||
Resultado: `12 passed`.
|
||||
|
||||
## Ainda recomendado para próxima ronda
|
||||
|
||||
- Substituir o token simples por login/sessão + CSRF nos POSTs.
|
||||
- Refatorar `admin_dashboard.py`, que continua demasiado grande.
|
||||
- Confirmar endpoints reais de PDF Jasmin no tenant de produção.
|
||||
- Criar testes funcionais com Postgres de teste e mocks para Jasmin/Packlink.
|
||||
89
docs/CLIENTFLOW_V451_UI_ALIGNMENT.md
Normal file
89
docs/CLIENTFLOW_V451_UI_ALIGNMENT.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# ClientFlow v4.5.1 — UI Alignment
|
||||
|
||||
Esta atualização alinha a UI real instalada com o mockup aprovado para a organização v4.5 Operational Core.
|
||||
|
||||
## Objetivo
|
||||
|
||||
Manter a lógica e os endpoints da v4.5, mas tornar a interface mais próxima do mockup:
|
||||
|
||||
- sidebar clara e moderna;
|
||||
- topbar com pesquisa global;
|
||||
- cartões KPI mais legíveis;
|
||||
- tabelas com linhas em formato de cartões;
|
||||
- melhor responsividade em mobile;
|
||||
- distinção visual mais clara entre Dashboard, Centro de trabalho, Comunicações, Oportunidades, Outbox e Integrações.
|
||||
|
||||
## Escopo
|
||||
|
||||
Esta versão é essencialmente visual.
|
||||
|
||||
Não altera:
|
||||
|
||||
- migrações da BD;
|
||||
- lógica Jasmin;
|
||||
- lógica Packlink;
|
||||
- outbox worker;
|
||||
- webhooks;
|
||||
- serviços de email;
|
||||
- endpoints principais.
|
||||
|
||||
## Páginas afetadas
|
||||
|
||||
Como o layout base é partilhado, a melhoria visual afeta a UI admin em geral:
|
||||
|
||||
- `/`
|
||||
- `/operations`
|
||||
- `/communications`
|
||||
- `/opportunities`
|
||||
- `/outbox`
|
||||
- `/integrations`
|
||||
- `/products`
|
||||
- `/customers`
|
||||
|
||||
## Atualização em campo
|
||||
|
||||
Não é necessário aplicar migração nova.
|
||||
|
||||
Procedimento recomendado:
|
||||
|
||||
```bash
|
||||
cd /home/ricar/Transferências/clientflow_backend_v451_ui_alignment
|
||||
|
||||
rsync -avz --delete --dry-run \
|
||||
--exclude ".env" \
|
||||
--exclude ".venv/" \
|
||||
--exclude "venv/" \
|
||||
--exclude "__pycache__/" \
|
||||
--exclude ".git/" \
|
||||
--exclude "*.db" \
|
||||
--exclude "*.log" \
|
||||
--exclude "uploads/" \
|
||||
./ plx@alarmsys:/mnt/ssd/home/plx/clientflow_backend/
|
||||
```
|
||||
|
||||
Se o dry-run estiver correto:
|
||||
|
||||
```bash
|
||||
rsync -avz --delete \
|
||||
--exclude ".env" \
|
||||
--exclude ".venv/" \
|
||||
--exclude "venv/" \
|
||||
--exclude "__pycache__/" \
|
||||
--exclude ".git/" \
|
||||
--exclude "*.db" \
|
||||
--exclude "*.log" \
|
||||
--exclude "uploads/" \
|
||||
./ plx@alarmsys:/mnt/ssd/home/plx/clientflow_backend/
|
||||
```
|
||||
|
||||
No servidor:
|
||||
|
||||
```bash
|
||||
cd /mnt/ssd/home/plx/clientflow_backend
|
||||
source .venv/bin/activate 2>/dev/null || true
|
||||
PYTHONPATH=. python -m compileall app scripts tests
|
||||
pytest -q
|
||||
sudo systemctl restart clientflow-api
|
||||
sudo systemctl status clientflow-api --no-pager
|
||||
journalctl -u clientflow-api -n 80 --no-pager
|
||||
```
|
||||
19
docs/CLIENTFLOW_V452_UI_ICONS_FIX.md
Normal file
19
docs/CLIENTFLOW_V452_UI_ICONS_FIX.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# ClientFlow v4.5.2 — UI Icons Fix
|
||||
|
||||
Patch visual pequeno sobre a v4.5.1.
|
||||
|
||||
## O que corrige
|
||||
|
||||
- Os cartões KPI da v4.5.1 mostravam caixas azul-claro vazias onde deveriam existir ícones.
|
||||
- A v4.5.2 adiciona ícones/fallback visuais nos KPI cards através de CSS seguro.
|
||||
- Adiciona Bootstrap Icons por CDN para uso progressivo nos templates.
|
||||
- Corrige um pequeno bloco duplicado no rodapé da sidebar.
|
||||
|
||||
## O que não altera
|
||||
|
||||
- Base de dados
|
||||
- Migrações
|
||||
- Jasmin
|
||||
- Packlink
|
||||
- Workers/outbox
|
||||
- Webhooks
|
||||
49
docs/CLIENTFLOW_V453_UI_POLISH.md
Normal file
49
docs/CLIENTFLOW_V453_UI_POLISH.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# ClientFlow v4.5.3 — Semantic Icons + UI Polish
|
||||
|
||||
Esta microversão corrige o acabamento visual da v4.5.2.
|
||||
|
||||
## Objetivo
|
||||
|
||||
Substituir os ícones genéricos dos cards KPI por ícones semânticos, mantendo a lógica operacional e as integrações intactas.
|
||||
|
||||
## Alterações
|
||||
|
||||
- Atualização do badge visual para `v4.5.3 UI`.
|
||||
- Sidebar passa a usar Bootstrap Icons semânticos.
|
||||
- Cards KPI passam a renderizar um elemento `.cf-kpi-icon` com ícone Bootstrap.
|
||||
- Ícones automáticos por tema:
|
||||
- oportunidades/pipeline: funil;
|
||||
- documentos/orçamentos: documento;
|
||||
- finanças/pagamentos: euro;
|
||||
- tasks: checklist;
|
||||
- comunicações/email: envelope;
|
||||
- erros/bloqueios: alerta;
|
||||
- envios/logística: camião;
|
||||
- clientes: cartão de cliente;
|
||||
- produtos/Jasmin: caixa/artigo;
|
||||
- integrações/sistema: puzzle.
|
||||
- Tons visuais por tipo: verde, laranja, vermelho e roxo quando aplicável.
|
||||
- Mantém Bootstrap 5 + Bootstrap Icons via CDN.
|
||||
|
||||
## Não altera
|
||||
|
||||
- Base de dados.
|
||||
- Migrações.
|
||||
- Jasmin.
|
||||
- Packlink.
|
||||
- Outbox worker.
|
||||
- Webhooks.
|
||||
- Lógica de documentos comerciais.
|
||||
|
||||
## Validação
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m compileall app scripts tests
|
||||
PYTHONPATH=. pytest -q
|
||||
```
|
||||
|
||||
Resultado esperado:
|
||||
|
||||
```text
|
||||
19 passed
|
||||
```
|
||||
110
docs/CLIENTFLOW_V45_OPERATIONAL_CORE.md
Normal file
110
docs/CLIENTFLOW_V45_OPERATIONAL_CORE.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# ClientFlow v4.5 — Operational Core
|
||||
|
||||
Esta versão junta a proposta v4.3 + v4.4 numa atualização única e incremental para o sistema em campo.
|
||||
|
||||
## Objetivo
|
||||
|
||||
Transformar a UI num sistema operacional diário:
|
||||
|
||||
```text
|
||||
Email / Chatwoot / pedido recebido
|
||||
→ Comunicação classificada
|
||||
→ Centro de trabalho
|
||||
→ Cliente / oportunidade
|
||||
→ Task humana ou Outbox automática
|
||||
→ Documento / envio
|
||||
→ Timeline
|
||||
```
|
||||
|
||||
## O que muda
|
||||
|
||||
### Dashboard
|
||||
|
||||
O `/` passa a ser uma visão geral: métricas, alertas e ligação direta ao Centro de trabalho. Deixa de ser a página principal para execução item a item.
|
||||
|
||||
### Centro de trabalho
|
||||
|
||||
O `/operations` passa a juntar itens acionáveis:
|
||||
|
||||
- tasks humanas pendentes;
|
||||
- comunicações por tratar;
|
||||
- outbox pending/failed/blocked;
|
||||
- clientes/produtos/documentos que bloqueiam o fluxo.
|
||||
|
||||
### Comunicações
|
||||
|
||||
Nova área:
|
||||
|
||||
```text
|
||||
/communications
|
||||
/comunicacoes
|
||||
```
|
||||
|
||||
Guarda emails/mensagens classificados com ligação opcional a cliente, oportunidade e task.
|
||||
|
||||
Estados suportados:
|
||||
|
||||
```text
|
||||
new
|
||||
classified
|
||||
needs_review
|
||||
linked
|
||||
task_created
|
||||
done
|
||||
ignored
|
||||
```
|
||||
|
||||
### Oportunidade
|
||||
|
||||
A página da oportunidade passa a incluir navegação por secções:
|
||||
|
||||
```text
|
||||
Resumo
|
||||
Produtos
|
||||
Documentos
|
||||
Tasks
|
||||
Comunicações
|
||||
Outbox
|
||||
Timeline
|
||||
Técnico
|
||||
```
|
||||
|
||||
### Tasks contextualizadas
|
||||
|
||||
A tabela `tasks` recebe colunas para contexto operacional:
|
||||
|
||||
```text
|
||||
communication_id
|
||||
document_id
|
||||
shipment_id
|
||||
outbox_id
|
||||
priority
|
||||
assigned_to
|
||||
```
|
||||
|
||||
### Timeline unificada
|
||||
|
||||
Nova tabela `timeline_events`, integrada na timeline da oportunidade.
|
||||
|
||||
## Instalação
|
||||
|
||||
1. Fazer backup da base de dados.
|
||||
2. Aplicar código v4.5.
|
||||
3. Reiniciar serviço.
|
||||
4. Confirmar que o startup executou `init_db()`.
|
||||
5. Opcional: aplicar manualmente `migrations/006_v45_operational_core.sql`.
|
||||
6. Validar no browser:
|
||||
|
||||
```text
|
||||
/
|
||||
/operations
|
||||
/communications
|
||||
/opportunities/{id}
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
- Não muda para React/Vue.
|
||||
- Mantém FastAPI + HTML/HTMX.
|
||||
- Não remove rotas antigas.
|
||||
- A automação crítica continua por outbox e deve continuar a exigir validação humana quando aplicável.
|
||||
83
docs/CLIENTFLOW_V45_UPDATE_GUIDE.md
Normal file
83
docs/CLIENTFLOW_V45_UPDATE_GUIDE.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# Guia de atualização — ClientFlow v4.5 Operational Core
|
||||
|
||||
## Antes de atualizar
|
||||
|
||||
1. Fazer backup da base de dados PostgreSQL.
|
||||
2. Guardar o `.env` atual.
|
||||
3. Confirmar que os timers/serviços atuais estão estáveis:
|
||||
|
||||
```bash
|
||||
systemctl status clientflow-api.service
|
||||
systemctl list-timers | grep clientflow
|
||||
```
|
||||
|
||||
## Instalação recomendada
|
||||
|
||||
```bash
|
||||
# exemplo; ajustar diretórios ao servidor real
|
||||
cd /opt/clientflow
|
||||
systemctl stop clientflow-api.service
|
||||
cp -a clientflow_backend clientflow_backend_backup_$(date +%Y%m%d_%H%M)
|
||||
unzip clientflow_backend_v45_operational_core.zip -d /opt/clientflow/clientflow_backend_v45
|
||||
cp /opt/clientflow/clientflow_backend/.env /opt/clientflow/clientflow_backend_v45/.env
|
||||
cd /opt/clientflow/clientflow_backend_v45
|
||||
python -m compileall app scripts tests
|
||||
pytest -q
|
||||
systemctl start clientflow-api.service
|
||||
```
|
||||
|
||||
Se o serviço usa um caminho fixo, trocar o symlink/current release conforme o teu deploy atual.
|
||||
|
||||
## Migração de base de dados
|
||||
|
||||
A v4.5 é aditiva. O `init_db()` cria/atualiza:
|
||||
|
||||
```text
|
||||
communications
|
||||
timeline_events
|
||||
tasks.communication_id
|
||||
tasks.document_id
|
||||
tasks.shipment_id
|
||||
tasks.outbox_id
|
||||
tasks.priority
|
||||
tasks.assigned_to
|
||||
```
|
||||
|
||||
Também existe migração SQL explícita:
|
||||
|
||||
```bash
|
||||
psql "$DATABASE_URL" -f migrations/006_v45_operational_core.sql
|
||||
```
|
||||
|
||||
## Validação pós-instalação
|
||||
|
||||
Abrir:
|
||||
|
||||
```text
|
||||
/
|
||||
/operations
|
||||
/communications
|
||||
/opportunities
|
||||
/outbox
|
||||
/integrations
|
||||
```
|
||||
|
||||
Verificar:
|
||||
|
||||
```text
|
||||
Dashboard mostra apenas visão geral.
|
||||
Centro de trabalho mostra fila operacional.
|
||||
Comunicações abre sem erro.
|
||||
Oportunidade mostra tabs: Resumo, Produtos, Documentos, Tasks, Comunicações, Outbox, Timeline, Técnico.
|
||||
Outbox continua a processar sem marcar dry_run como sent.
|
||||
```
|
||||
|
||||
## Rollback
|
||||
|
||||
Como a migração é aditiva, o rollback de código pode ser feito voltando para a pasta/ZIP anterior. As tabelas e colunas novas podem ficar na BD sem afetar a versão anterior.
|
||||
|
||||
## Limitações conhecidas
|
||||
|
||||
- A v4.5 cria a estrutura de Comunicações, mas o conector real de email/classificação deve inserir dados nessa tabela.
|
||||
- A confirmação de pagamentos e ações críticas continuam humanas.
|
||||
- Permissões e CSRF completos continuam recomendados para uma ronda posterior se o sistema estiver exposto fora da rede controlada.
|
||||
22
docs/CLIENTFLOW_V461_CUSTOMER_MISMATCH_WARNING.md
Normal file
22
docs/CLIENTFLOW_V461_CUSTOMER_MISMATCH_WARNING.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# ClientFlow v4.6.1 — Customer mismatch warning tuning
|
||||
|
||||
Esta correção reduz falsos positivos no aviso “contacto e cliente fiscal não coincidem”.
|
||||
|
||||
O caso típico era uma oportunidade com contacto curto/truncado, por exemplo:
|
||||
|
||||
- Contacto/origem: `Riotec elec`
|
||||
- Cliente fiscal: `Riotec - Electricidade, Aquecimento e Controle, Lda`
|
||||
|
||||
A heurística anterior comparava strings completas e gerava alerta. A nova heurística compara tokens significativos e aceita abreviações/prefixos como o mesmo cliente provável.
|
||||
|
||||
## Desativar completamente o aviso
|
||||
|
||||
Se o aviso continuar a criar ruído operacional, adiciona no `.env`:
|
||||
|
||||
```env
|
||||
CLIENTFLOW_CUSTOMER_MISMATCH_WARNING_ENABLED=false
|
||||
```
|
||||
|
||||
Depois reinicia o serviço.
|
||||
|
||||
A validação real antes de criar documentos continua a depender dos dados fiscais do cliente associado e das validações Jasmin/Packlink.
|
||||
32
docs/CLIENTFLOW_V462_DISABLE_CUSTOMER_MISMATCH_WARNING.md
Normal file
32
docs/CLIENTFLOW_V462_DISABLE_CUSTOMER_MISMATCH_WARNING.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# ClientFlow v4.6.2 — Customer warning off by default + opportunity layout tuning
|
||||
|
||||
Esta versão desativa por defeito o aviso de divergência entre contacto/origem e cliente fiscal associado.
|
||||
|
||||
## Motivo
|
||||
|
||||
Na operação real é comum a conversa chegar por um contacto abreviado ou pessoal, mas o documento fiscal pertencer a uma empresa. Exemplos válidos:
|
||||
|
||||
- `Riotec elec` → `Riotec - Electricidade, Aquecimento e Controle, Lda`
|
||||
- `Bruno Oliveira` → `Nortuflex`
|
||||
|
||||
Nestes casos o aviso gerava ruído e podia atrasar a operação.
|
||||
|
||||
## Comportamento novo
|
||||
|
||||
Por defeito:
|
||||
|
||||
```env
|
||||
CLIENTFLOW_CUSTOMER_MISMATCH_WARNING_ENABLED=false
|
||||
```
|
||||
|
||||
O aviso pode ser reativado no `.env` se necessário:
|
||||
|
||||
```env
|
||||
CLIENTFLOW_CUSTOMER_MISMATCH_WARNING_ENABLED=true
|
||||
```
|
||||
|
||||
## Ajuste visual
|
||||
|
||||
A página da oportunidade passa para uma coluna mais cedo em ecrãs/zoom de laptop, evitando que o painel lateral comprima a área principal.
|
||||
|
||||
Não há migração de base de dados.
|
||||
43
docs/CLIENTFLOW_V462_WORKFLOW_ACTIONS_CLEANUP.md
Normal file
43
docs/CLIENTFLOW_V462_WORKFLOW_ACTIONS_CLEANUP.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# ClientFlow v4.6.2 — Workflow Actions Cleanup
|
||||
|
||||
Correção pequena focada em operação diária, sem migrações de base de dados e sem alterações em Jasmin/Packlink.
|
||||
|
||||
## Alterações
|
||||
|
||||
- Remove o aviso operacional por divergência de nome entre contacto e cliente fiscal.
|
||||
- Contacto pessoal e empresa fiscal podem ser diferentes e estar corretos.
|
||||
- A validação crítica deve ficar em NIF, dados fiscais obrigatórios, documento emitido e associação fiscal, não em semelhança de nomes.
|
||||
- Adiciona a ação `MARK_NO_INTEREST`.
|
||||
- Usada quando o cliente indica que não tem interesse, não tem frota/veículos elétricos, não necessita ou a oferta não se aplica.
|
||||
- Adiciona regra determinística para mensagens inequívocas, por exemplo:
|
||||
- “não temos veículos elétricos”
|
||||
- “não temos na nossa frota veículos elétricos”
|
||||
- “não estamos interessados”
|
||||
- “não se aplica”
|
||||
- `MARK_NO_INTEREST` cria trabalho pendente em vendas para o operador confirmar/fechar o caso.
|
||||
- Se existir oportunidade aberta, a task pode ficar ligada à oportunidade.
|
||||
- Ao concluir a task, a oportunidade pode avançar para `LOST`.
|
||||
- Se não existir oportunidade aberta, `MARK_NO_INTEREST` não cria uma oportunidade nova só para a fechar.
|
||||
- Ajusta CSS da ficha da oportunidade para reduzir quebra de layout em zoom/larguras intermédias.
|
||||
|
||||
## Exemplo
|
||||
|
||||
Mensagem:
|
||||
|
||||
> Agradecemos a informação, mas não temos na nossa frota veículos elétricos.
|
||||
|
||||
Resultado esperado:
|
||||
|
||||
- `action_code = MARK_NO_INTEREST`
|
||||
- `route = vendas`
|
||||
- `status = pending`
|
||||
- ação: “Marcar sem interesse”
|
||||
|
||||
## Não altera
|
||||
|
||||
- Base de dados
|
||||
- Migrações
|
||||
- Jasmin
|
||||
- Packlink
|
||||
- Outbox worker
|
||||
- Webhooks Chatwoot
|
||||
27
docs/CLIENTFLOW_V463_OPERATIONS_QUEUE_CLEANUP.md
Normal file
27
docs/CLIENTFLOW_V463_OPERATIONS_QUEUE_CLEANUP.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# ClientFlow v4.6.3 — Operations Queue Cleanup
|
||||
|
||||
Esta versão é um hotfix operacional, sem migração de base de dados.
|
||||
|
||||
## Alterações
|
||||
|
||||
- `MARK_NO_INTEREST` deixa de fechar oportunidades como `LOST`.
|
||||
- Novo estado de oportunidade: `NO_INTEREST` / “Sem interesse”.
|
||||
- `LOST` fica reservado para oportunidades perdidas por preço, funcionalidades, desistência ou concorrência.
|
||||
- O Centro de trabalho deixa de contar/mostrar outbox `failed` cujo erro indique que foi “limpo manualmente” ou “resolvido manualmente”.
|
||||
- Textos técnicos como “Resposta LLM inválida para action_code” passam a aparecer como “Classificação da mensagem falhou”.
|
||||
- “Emails por tratar” passa a “Mensagens para revisão”.
|
||||
- “Sem cliente” passa a “Mensagens sem cliente”.
|
||||
- Documentos no Centro de trabalho passam a focar documentos que podem exigir ação.
|
||||
- Novo script: `scripts/ignore_manually_cleaned_outbox.py`.
|
||||
|
||||
## Uso do script
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python scripts/ignore_manually_cleaned_outbox.py --dry-run
|
||||
PYTHONPATH=. python scripts/ignore_manually_cleaned_outbox.py
|
||||
```
|
||||
|
||||
## Regra conceptual
|
||||
|
||||
- `NO_INTEREST`: cliente sem interesse atual após divulgação/campanha, sem necessidade ou sem frota elétrica.
|
||||
- `LOST`: oportunidade comercial real perdida depois de negociação, por preço, funcionalidades, desistência ou concorrência.
|
||||
44
docs/CLIENTFLOW_V465_OPERATIONS_WORK_QUEUE.md
Normal file
44
docs/CLIENTFLOW_V465_OPERATIONS_WORK_QUEUE.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# ClientFlow v4.6.5 — Operations Work Queue
|
||||
|
||||
Objetivo: transformar `/operations` numa lista única de trabalho do operador, em vez de um mini-dashboard técnico.
|
||||
|
||||
## Decisão de produto
|
||||
|
||||
- Chatwoot continua a ser a inbox.
|
||||
- Dashboard mostra estado e gargalos globais.
|
||||
- Operations mostra apenas trabalho humano acionável.
|
||||
- Outbox, documentos, produtos e clientes só devem aparecer em Operations quando bloqueiam uma ação concreta.
|
||||
|
||||
## Alterações principais
|
||||
|
||||
- `/operations` passa a mostrar 4 contadores curtos: a fazer agora, atrasadas, bloqueadas e rever associação.
|
||||
- Adicionados filtros operacionais: Todas, Vendas, Financeiro, Logística, Revisão, Bloqueadas e Concluídas hoje.
|
||||
- A fila principal passa a ser composta por cartões de trabalho com:
|
||||
- cliente/contacto;
|
||||
- oportunidade/processo;
|
||||
- próxima ação;
|
||||
- origem;
|
||||
- estado/fila;
|
||||
- botões principais.
|
||||
- Removidas da vista principal as tabelas técnicas de outbox, documentos recentes, produtos sem Jasmin e clientes incompletos.
|
||||
- Outbox continua a entrar na fila apenas quando representa erro/bloqueio que exige ação humana.
|
||||
|
||||
## Botão principal por tipo de ação
|
||||
|
||||
Exemplos:
|
||||
|
||||
- `SEND_QUOTE` → Preparar orçamento
|
||||
- `SEND_INFO` → Preparar resposta
|
||||
- `SEND_PROFORMA` → Preparar pró-forma
|
||||
- `SEND_INVOICE` → Emitir fatura
|
||||
- `CONFIRM_PAYMENT` → Confirmar pagamento
|
||||
- `PREPARE_ORDER` → Preparar encomenda
|
||||
- `CREATE_SHIPMENT` → Criar envio
|
||||
- `REVIEW_MANUALLY` → Rever mensagem
|
||||
- `ASSOCIATE_CUSTOMER` → Associar cliente
|
||||
- `MARK_NO_INTEREST` → Marcar sem interesse
|
||||
- `REMOVE_FROM_LIST` → Remover da lista
|
||||
|
||||
## Sem migração
|
||||
|
||||
Esta versão não altera base de dados, Jasmin, Packlink nem workers. É uma alteração de apresentação e agregação operacional.
|
||||
16
docs/CLIENTFLOW_V466_SAFE_OPPORTUNITY_LINKING.md
Normal file
16
docs/CLIENTFLOW_V466_SAFE_OPPORTUNITY_LINKING.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# ClientFlow v4.6.6 — Safe Opportunity Linking
|
||||
|
||||
Objetivo: reduzir o risco de associar automaticamente uma mensagem/task do Chatwoot à oportunidade ou cliente fiscal errado.
|
||||
|
||||
## Alterações principais
|
||||
|
||||
- `conversation_id` passa a ser a correspondência forte para associar task a oportunidade.
|
||||
- `contact_id` do Chatwoot passa a ser correspondência fraca: só associa automaticamente se existir exatamente uma oportunidade aberta recente para esse contacto.
|
||||
- Se houver mais de uma oportunidade aberta recente para o mesmo `contact_id`, a task é marcada para revisão de associação e não é criada/atualizada uma nova oportunidade automaticamente.
|
||||
- A página de detalhe da task deixa de usar `contact_id` do Chatwoot como se fosse ID local de cliente.
|
||||
- O link “Ver cliente” passa a aparecer apenas quando existe cliente fiscal local seguro.
|
||||
- No Centro de trabalho, uma associação ambígua aparece como ação humana: “Associar oportunidade”.
|
||||
|
||||
## Regra operacional
|
||||
|
||||
Chatwoot `contact_id` identifica o contacto/conversa, não necessariamente o cliente fiscal. O cliente fiscal continua a ser confirmado na oportunidade antes de orçamento/fatura/envio.
|
||||
26
docs/CLIENTFLOW_V467_CHATWOOT_AUTOCOMPLETE_GUARDRAILS.md
Normal file
26
docs/CLIENTFLOW_V467_CHATWOOT_AUTOCOMPLETE_GUARDRAILS.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# ClientFlow v4.6.7 — Chatwoot Auto-complete Guardrails
|
||||
|
||||
Esta versão endurece o comportamento de auto-completar tarefas quando chega uma mensagem `outgoing` do Chatwoot.
|
||||
|
||||
## Objetivo
|
||||
|
||||
Manter o Chatwoot como inbox, mas evitar que uma resposta enviada feche tarefas que exigem validação humana.
|
||||
|
||||
## Alterações
|
||||
|
||||
- `CONFIRM_PAYMENT` deixa de estar nos action codes auto-completados por defeito.
|
||||
- `MARK_NO_INTEREST` e `REVIEW_MANUALLY` também não são auto-completados por resposta outgoing.
|
||||
- Se a task tiver `opportunity_linking_status=ambiguous`, a resposta outgoing não fecha a tarefa.
|
||||
- Tarefas auto-completadas registam evento de timeline como `task_auto_completed` quando existe oportunidade associada.
|
||||
|
||||
## Continua configurável
|
||||
|
||||
É possível sobrescrever a lista via:
|
||||
|
||||
```env
|
||||
CHATWOOT_AUTO_COMPLETE_ACTION_CODES=SEND_INFO,SEND_QUOTE,SUPPORT
|
||||
```
|
||||
|
||||
## Regra operacional
|
||||
|
||||
O sistema pode fechar automaticamente tasks em que a resposta ao cliente é a própria ação. Não deve fechar tasks que confirmam pagamento, validam associação ou exigem decisão fiscal/operacional.
|
||||
59
docs/CLIENTFLOW_V468_OPERATIONAL_SAFETY_CONSOLIDATION.md
Normal file
59
docs/CLIENTFLOW_V468_OPERATIONAL_SAFETY_CONSOLIDATION.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# ClientFlow v4.6.8 — Operational Safety Consolidation
|
||||
|
||||
Esta versão consolida as proteções iniciadas em v4.6.5–v4.6.7 e prepara o caminho para v4.7/v4.8.
|
||||
|
||||
## 1. Auto-complete com política hard-deny
|
||||
|
||||
Mesmo que `CHATWOOT_AUTO_COMPLETE_ACTION_CODES` seja configurado manualmente, estas ações não são fechadas por mensagem outgoing:
|
||||
|
||||
- `CONFIRM_PAYMENT`
|
||||
- `CONFIRM_PAYMENT_AND_PREPARE_SHIPMENT`
|
||||
- `MARK_NO_INTEREST`
|
||||
- `REVIEW_MANUALLY`
|
||||
- `PREPARE_ORDER`
|
||||
- `CREATE_SHIPMENT`
|
||||
- `REMOVE_FROM_LIST`
|
||||
|
||||
O default fica limitado a ações em que responder no Chatwoot é a execução da tarefa:
|
||||
|
||||
- `SEND_INFO`
|
||||
- `SEND_QUOTE`
|
||||
- `SEND_PROFORMA`
|
||||
- `SEND_INVOICE`
|
||||
- `SUPPORT`
|
||||
|
||||
## 2. Outbox com claim transacional
|
||||
|
||||
`scripts/process_outbox.py` passa a usar `claim_pending_outbox()`.
|
||||
|
||||
O worker marca linhas como `processing` dentro da mesma transação, usando:
|
||||
|
||||
```sql
|
||||
FOR UPDATE SKIP LOCKED
|
||||
```
|
||||
|
||||
Isto reduz o risco de dois workers processarem a mesma ação externa.
|
||||
|
||||
## 3. Health operacional
|
||||
|
||||
`/api/internal/system/health` passa a expor métricas operacionais:
|
||||
|
||||
- tasks pendentes
|
||||
- tasks concluídas nas últimas 24h
|
||||
- auto-completes nas últimas 24h
|
||||
- tasks com oportunidade ambígua
|
||||
- outbox em processing
|
||||
- outbox processing stale
|
||||
- outbox failed/blocked
|
||||
- configuração efetiva do auto-complete Chatwoot
|
||||
|
||||
## 4. Navegação mais limpa
|
||||
|
||||
`/tasks` continua disponível para diagnóstico/listagem, mas sai da navegação principal. A entrada diária recomendada é `/operations`.
|
||||
|
||||
## Validação
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m compileall app scripts tests
|
||||
PYTHONPATH=. pytest -q
|
||||
```
|
||||
99
docs/CLIENTFLOW_V46_CHATWOOT_WORKFLOW_HARDENING.md
Normal file
99
docs/CLIENTFLOW_V46_CHATWOOT_WORKFLOW_HARDENING.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# ClientFlow v4.6 — Chatwoot Workflow Hardening
|
||||
|
||||
## Objetivo
|
||||
|
||||
Esta atualização não cria uma segunda inbox. O Chatwoot continua a ser a caixa de entrada e o ClientFlow passa a tratar melhor o trabalho que nasce das mensagens do Chatwoot.
|
||||
|
||||
Fluxo alvo:
|
||||
|
||||
```text
|
||||
Chatwoot → webhook → classificação → task/opportunity → Centro de trabalho → timeline
|
||||
```
|
||||
|
||||
## Alterações principais
|
||||
|
||||
### 1. `REVIEW_MANUALLY` passa a ser trabalho pendente
|
||||
|
||||
Antes, decisões `REVIEW_MANUALLY` ficavam como `skipped`. Isto escondia mensagens que precisavam de operador.
|
||||
|
||||
Agora:
|
||||
|
||||
```text
|
||||
REVIEW_MANUALLY → route=rever → status=pending → priority=alta
|
||||
```
|
||||
|
||||
### 2. `REMOVE_FROM_LIST` passa a ser operacional
|
||||
|
||||
Antes podia ficar tratado como revisão/skip. Agora fica pendente e orientado para marketing:
|
||||
|
||||
```text
|
||||
REMOVE_FROM_LIST → route=marketing → status=pending
|
||||
```
|
||||
|
||||
### 3. Só spam e “sem ação” ficam `skipped`
|
||||
|
||||
```text
|
||||
IGNORE_SPAM → skipped
|
||||
NO_ACTION → skipped
|
||||
```
|
||||
|
||||
### 4. Centro de trabalho mostra contexto Chatwoot
|
||||
|
||||
Os itens operacionais vindos do Chatwoot passam a trazer:
|
||||
|
||||
```text
|
||||
source_system
|
||||
conversation_id
|
||||
contact_id
|
||||
request_text
|
||||
chatwoot_url
|
||||
```
|
||||
|
||||
A UI mostra botão para abrir a conversa no Chatwoot quando `CHATWOOT_PUBLIC_URL`/`CHATWOOT_BASE_URL` e `CHATWOOT_ACCOUNT_ID` estão configurados.
|
||||
|
||||
### 5. Comunicações deixam de ser navegação principal
|
||||
|
||||
A rota técnica `/communications` continua disponível para diagnóstico, mas deixa de ser a inbox principal. O operador deve responder e gerir conversas no Chatwoot.
|
||||
|
||||
### 6. Timeline para tasks associadas à oportunidade
|
||||
|
||||
Quando uma task criada pelo Chatwoot está ligada a uma oportunidade, é registado evento de timeline `task_created`.
|
||||
|
||||
### 7. Script para reabrir revisões antigas
|
||||
|
||||
Para corrigir as tasks recentes que já ficaram `skipped` antes da v4.6:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python scripts/reopen_chatwoot_review_tasks.py --dry-run
|
||||
PYTHONPATH=. python scripts/reopen_chatwoot_review_tasks.py --days 7
|
||||
```
|
||||
|
||||
Por defeito só olha para os últimos 7 dias.
|
||||
|
||||
## Passos pós-instalação recomendados
|
||||
|
||||
```bash
|
||||
cd /mnt/ssd/home/plx/clientflow_backend
|
||||
source .venv/bin/activate 2>/dev/null || true
|
||||
|
||||
PYTHONPATH=. python -m compileall app scripts tests
|
||||
PYTHONPATH=. pytest -q
|
||||
|
||||
PYTHONPATH=. python scripts/reopen_chatwoot_review_tasks.py --dry-run
|
||||
PYTHONPATH=. python scripts/reopen_chatwoot_review_tasks.py --days 7
|
||||
|
||||
sudo systemctl restart clientflow-api
|
||||
sudo systemctl status clientflow-api --no-pager
|
||||
```
|
||||
|
||||
## Validação funcional
|
||||
|
||||
Verificar:
|
||||
|
||||
```text
|
||||
/tasks?status=pending&route=rever
|
||||
/operations
|
||||
/opportunities
|
||||
```
|
||||
|
||||
E confirmar que novas mensagens Chatwoot classificadas como revisão aparecem como pendentes.
|
||||
30
docs/CLIENTFLOW_V471_ADMIN_SIDEBAR_SCROLL_FIX.md
Normal file
30
docs/CLIENTFLOW_V471_ADMIN_SIDEBAR_SCROLL_FIX.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# ClientFlow v4.7.1 — Admin Sidebar Scroll Fix
|
||||
|
||||
Hotfix visual para a navegação lateral introduzida na v4.7.
|
||||
|
||||
## Problema
|
||||
|
||||
Em ecrãs com pouca altura, ao abrir o submenu **Admin**, os itens técnicos no final do menu podiam ficar fora da área visível sem scroll próprio no sidebar.
|
||||
|
||||
## Correção
|
||||
|
||||
- O sidebar desktop passa a ter `overflow-y: auto` e `max-height: 100vh`.
|
||||
- O scroll fica contido no menu lateral com `overscroll-behavior: contain`.
|
||||
- Foi adicionado `scrollbar-gutter: stable` para evitar saltos visuais quando o scrollbar aparece.
|
||||
- O footer e blocos de navegação deixam de encolher de forma imprevisível.
|
||||
- Em mobile, o menu continua horizontal com `overflow-x: auto` e sem scroll vertical no sidebar.
|
||||
|
||||
## Impacto
|
||||
|
||||
Não altera rotas, regras de negócio, base de dados, Chatwoot, outbox nem lógica de oportunidades.
|
||||
|
||||
## Validação
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m compileall app scripts tests
|
||||
PYTHONPATH=. pytest -q
|
||||
```
|
||||
|
||||
## Rollback
|
||||
|
||||
Reverter apenas `app/admin_ui/styles.py` para a versão v4.7.
|
||||
47
docs/CLIENTFLOW_V472_DOMAIN_ROUTES.md
Normal file
47
docs/CLIENTFLOW_V472_DOMAIN_ROUTES.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# ClientFlow v4.7.2 — Rotas por domínio
|
||||
|
||||
Esta versão separa o registo de rotas da UI administrativa por domínio, reduzindo a responsabilidade de `app/admin_dashboard.py` e preparando a extração gradual de view models/componentes na v4.7.3.
|
||||
|
||||
## Objetivo
|
||||
|
||||
- Manter comportamento e URLs existentes.
|
||||
- Não alterar regras Chatwoot, auto-complete, oportunidades, outbox ou DB.
|
||||
- Criar módulos reais em `app/admin_ui/pages/`.
|
||||
|
||||
## Estrutura
|
||||
|
||||
```text
|
||||
app/admin_ui/router.py
|
||||
app/admin_ui/pages/
|
||||
dashboard.py
|
||||
operations.py
|
||||
opportunities.py
|
||||
tasks.py
|
||||
customers.py
|
||||
products.py
|
||||
orders.py
|
||||
finance.py
|
||||
integrations.py
|
||||
communications.py
|
||||
outbox.py
|
||||
events.py
|
||||
runs.py
|
||||
queues.py
|
||||
conversations.py
|
||||
system.py
|
||||
```
|
||||
|
||||
## Compatibilidade
|
||||
|
||||
As rotas públicas continuam iguais, incluindo aliases em português como `/operacoes`, `/oportunidades`, `/clientes`, `/produtos`, `/encomendas`, `/financeiro` e `/integracoes`.
|
||||
|
||||
## Validação
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m compileall app scripts tests
|
||||
PYTHONPATH=. pytest -q
|
||||
```
|
||||
|
||||
## Migração de base de dados
|
||||
|
||||
Não requer migração de base de dados.
|
||||
74
docs/CLIENTFLOW_V473_HTMX_PARTIALS.md
Normal file
74
docs/CLIENTFLOW_V473_HTMX_PARTIALS.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# ClientFlow v4.7.3 — HTMX Partials & View Models
|
||||
|
||||
## Objetivo
|
||||
|
||||
A v4.7.3 consolida a primeira camada HTMX depois da separação de rotas da v4.7.2.
|
||||
|
||||
Não altera regras de negócio, base de dados, Chatwoot, auto-complete, associação de oportunidades ou outbox worker.
|
||||
|
||||
## O que muda
|
||||
|
||||
### Centro de trabalho
|
||||
|
||||
`/operations` passa a ter refresh parcial da lista de trabalho:
|
||||
|
||||
- endpoint: `/operations/partials/work-items`
|
||||
- target: `#operations-work-items`
|
||||
- filtros com `hx-get`
|
||||
- `hx-push-url` para manter URL partilhável
|
||||
- indicador `#operations-loading`
|
||||
|
||||
### Tasks
|
||||
|
||||
`/tasks` passa a ter refresh parcial da lista:
|
||||
|
||||
- endpoint: `/tasks/partials/list`
|
||||
- target: `#tasks-list`
|
||||
- tabs e formulário com `hx-get`
|
||||
- `hx-push-url` para manter URL partilhável
|
||||
- indicador `#tasks-loading`
|
||||
|
||||
### Oportunidades
|
||||
|
||||
A v4.7.3 mantém e documenta os partials já usados no detalhe da oportunidade:
|
||||
|
||||
- `/opportunities/{id}/partials/products`
|
||||
- `/opportunities/{id}/partials/jasmin-documents`
|
||||
|
||||
Estes partials continuam a ser usados pelos forms HTMX de produtos e documentos.
|
||||
|
||||
## View models e labels
|
||||
|
||||
Foram criados:
|
||||
|
||||
- `app/admin_ui/labels.py`
|
||||
- `app/admin_ui/htmx.py`
|
||||
- `app/admin_ui/view_models/operations.py`
|
||||
|
||||
A intenção é retirar labels e preparação de dados das rotas antes de avançar para mais partials.
|
||||
|
||||
## Critério de segurança
|
||||
|
||||
A v4.7.3 é uma versão de UI/estrutura. Não muda:
|
||||
|
||||
- schema da base de dados
|
||||
- workflow Chatwoot
|
||||
- auto-complete
|
||||
- regras de oportunidade/cliente
|
||||
- worker de outbox
|
||||
- integrações Jasmin/Packlink/Odoo
|
||||
|
||||
## Validação
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m compileall app scripts tests
|
||||
PYTHONPATH=. pytest -q
|
||||
```
|
||||
|
||||
## Próximo passo
|
||||
|
||||
A próxima versão pode consolidar:
|
||||
|
||||
- partial do quadro de oportunidades
|
||||
- partial da outbox operacional
|
||||
- componentes HTML para `work_item_card`, `task_table` e `opportunity_card`
|
||||
101
docs/CLIENTFLOW_V474_HTMX_COMPLETION.md
Normal file
101
docs/CLIENTFLOW_V474_HTMX_COMPLETION.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# ClientFlow v4.7.4 — HTMX Completion & Operator UX
|
||||
|
||||
## Objetivo
|
||||
|
||||
Completar a experiência HTMX nas áreas operacionais, sem alterar regras de negócio, schema de base de dados, Chatwoot auto-complete, associação de oportunidades ou worker da outbox.
|
||||
|
||||
## O que mudou
|
||||
|
||||
### Oportunidades
|
||||
|
||||
Novo partial:
|
||||
|
||||
```text
|
||||
/opportunities/partials/board
|
||||
```
|
||||
|
||||
A página `/opportunities` passou a filtrar o quadro por HTMX, com atualização do container:
|
||||
|
||||
```text
|
||||
#opportunities-board
|
||||
```
|
||||
|
||||
Filtros principais:
|
||||
|
||||
```text
|
||||
Todas
|
||||
Novas
|
||||
Orçamento enviado
|
||||
Pró-forma enviada
|
||||
Pagamento pendente
|
||||
Enviadas
|
||||
Bloqueadas
|
||||
```
|
||||
|
||||
### Tasks
|
||||
|
||||
Novo partial:
|
||||
|
||||
```text
|
||||
/tasks/{task_id}/partials/detail
|
||||
```
|
||||
|
||||
As ações principais do detalhe de task passam a poder atualizar o painel:
|
||||
|
||||
```text
|
||||
#task-detail-panel
|
||||
```
|
||||
|
||||
Ações com HTMX:
|
||||
|
||||
```text
|
||||
Marcar como feita
|
||||
Ignorar tarefa
|
||||
Reclassificar
|
||||
Preparar pró-forma
|
||||
Preparar envio
|
||||
Preparar recolha
|
||||
```
|
||||
|
||||
### Outbox
|
||||
|
||||
Novo partial:
|
||||
|
||||
```text
|
||||
/outbox/partials/table
|
||||
```
|
||||
|
||||
A página `/outbox` passa a atualizar filtros e ações manuais no container:
|
||||
|
||||
```text
|
||||
#outbox-table
|
||||
```
|
||||
|
||||
Ações com HTMX:
|
||||
|
||||
```text
|
||||
Reprocessar
|
||||
Ignorar
|
||||
```
|
||||
|
||||
## Compatibilidade
|
||||
|
||||
As rotas antigas continuam disponíveis. Sem HTMX, os forms continuam com `method="post"` e `action`, mantendo fallback por redirect.
|
||||
|
||||
## Não muda
|
||||
|
||||
```text
|
||||
Não requer migração de base de dados.
|
||||
Não altera regras Chatwoot.
|
||||
Não altera auto-complete.
|
||||
Não altera associação oportunidade/cliente.
|
||||
Não altera outbox worker.
|
||||
Não altera integrações Jasmin/Packlink/Odoo/Mautic.
|
||||
```
|
||||
|
||||
## Validação
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m compileall app scripts tests
|
||||
PYTHONPATH=. pytest -q
|
||||
```
|
||||
95
docs/CLIENTFLOW_V47_ADMIN_UI_REFACTOR.md
Normal file
95
docs/CLIENTFLOW_V47_ADMIN_UI_REFACTOR.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# ClientFlow v4.7 — Refactor UI e Centro de Trabalho do Operador
|
||||
|
||||
## Objetivo
|
||||
|
||||
A v4.7 reorganiza a UI administrativa sem alterar regras de negócio, schema da base de dados ou fluxo Chatwoot/auto-complete. A versão parte da v4.6.8 e foca-se em manutenção, clareza para o operador e redução do ficheiro `app/admin_dashboard.py`.
|
||||
|
||||
## Modelo operacional
|
||||
|
||||
- **Chatwoot** = conversa e inbox.
|
||||
- **Centro de trabalho / Operations** = trabalho diário do operador.
|
||||
- **Oportunidades** = contexto comercial e próxima ação.
|
||||
- **Admin** = diagnóstico técnico, outbox, eventos, runs e comunicações internas.
|
||||
|
||||
## Alterações principais
|
||||
|
||||
### Navegação
|
||||
|
||||
O menu principal fica focado no operador:
|
||||
|
||||
- Dashboard
|
||||
- Centro de trabalho
|
||||
- Oportunidades
|
||||
- Clientes
|
||||
- Produtos
|
||||
- Encomendas
|
||||
- Financeiro
|
||||
- Integrações
|
||||
|
||||
As áreas técnicas passam para o grupo **Admin**:
|
||||
|
||||
- Tasks
|
||||
- Comunicações
|
||||
- Outbox
|
||||
- Eventos
|
||||
- Runs
|
||||
- Filas
|
||||
- System health
|
||||
- Configuração
|
||||
|
||||
As rotas antigas continuam disponíveis por URL direto.
|
||||
|
||||
### Estrutura de código
|
||||
|
||||
Foram extraídos módulos para `app/admin_ui/`:
|
||||
|
||||
- `styles.py` — CSS da UI admin.
|
||||
- `navigation.py` — modelo e renderização da navegação.
|
||||
- `layout.py` — shell HTML comum.
|
||||
- `components.py` — componentes reutilizáveis, incluindo KPI cards.
|
||||
|
||||
`app/admin_dashboard.py` mantém as rotas públicas, mas perde CSS/layout/nav embutidos e fica significativamente menor.
|
||||
|
||||
### Oportunidades
|
||||
|
||||
Os cards de oportunidade com tarefas pendentes passam a expor a intenção operacional:
|
||||
|
||||
- **Concluir tarefa pendente** quando existe trabalho humano aberto.
|
||||
- **Ver oportunidade** quando não há tarefas pendentes.
|
||||
|
||||
Isto aproxima a board do conceito de centro de trabalho.
|
||||
|
||||
### Sem migração
|
||||
|
||||
A v4.7 não requer migração de base de dados.
|
||||
|
||||
## Validação
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m compileall app scripts tests
|
||||
PYTHONPATH=. pytest -q
|
||||
```
|
||||
|
||||
## Rollback
|
||||
|
||||
Como não há migração de DB, o rollback é reposição do diretório anterior e restart do serviço.
|
||||
|
||||
```bash
|
||||
sudo systemctl restart clientflow-api
|
||||
```
|
||||
|
||||
|
||||
## Atualização v4.7.2 — Rotas por domínio
|
||||
|
||||
A v4.7.2 move o registo das rotas administrativas para módulos por domínio em `app/admin_ui/pages/`, mantendo os URLs existentes e sem alterar regras de negócio.
|
||||
|
||||
Módulos principais:
|
||||
|
||||
- `dashboard.py` — página inicial.
|
||||
- `operations.py` — centro de trabalho.
|
||||
- `opportunities.py` — oportunidades, documentos comerciais e ações comerciais.
|
||||
- `tasks.py` — listagem, detalhe e ações de tasks.
|
||||
- `customers.py`, `products.py`, `orders.py`, `finance.py`, `integrations.py`.
|
||||
- `communications.py`, `outbox.py`, `events.py`, `runs.py`, `queues.py`, `system.py` para áreas técnicas/admin.
|
||||
|
||||
Esta alteração não requer migração de base de dados.
|
||||
43
docs/CLIENTFLOW_V4801_ADMIN_MENU_COLLAPSED.md
Normal file
43
docs/CLIENTFLOW_V4801_ADMIN_MENU_COLLAPSED.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# ClientFlow v4.8.1 — Admin Menu Collapsed Default
|
||||
|
||||
Hotfix visual em cima da v4.8.0.
|
||||
|
||||
## Objetivo
|
||||
|
||||
O submenu **Admin** deixa de aparecer expandido por defeito nas páginas operacionais.
|
||||
|
||||
Na navegação principal, o operador vê apenas:
|
||||
|
||||
- Dashboard
|
||||
- Centro de trabalho
|
||||
- Oportunidades
|
||||
- Clientes
|
||||
- Produtos
|
||||
- Encomendas
|
||||
- Financeiro
|
||||
- Integrações
|
||||
- Admin
|
||||
|
||||
O submenu Admin abre apenas quando:
|
||||
|
||||
1. o utilizador clica em Admin; ou
|
||||
2. a página atual pertence à área Admin, por exemplo `/outbox`, `/tasks`, `/communications`, `/system/health`.
|
||||
|
||||
## Impacto
|
||||
|
||||
Não altera:
|
||||
|
||||
- base de dados
|
||||
- Chatwoot
|
||||
- auto-complete
|
||||
- outbox worker
|
||||
- regras de oportunidade
|
||||
- regras LLM
|
||||
- integrações
|
||||
|
||||
## Validação
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m compileall app scripts tests
|
||||
PYTHONPATH=. pytest -q
|
||||
```
|
||||
109
docs/CLIENTFLOW_V480_OPERATIONAL_AUTOMATION_AUDIT_RECOVERY.md
Normal file
109
docs/CLIENTFLOW_V480_OPERATIONAL_AUTOMATION_AUDIT_RECOVERY.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# ClientFlow v4.8.0 — Operational Automation, Audit & Recovery
|
||||
|
||||
Esta versão avança de uma UI HTMX estável para segurança operacional: recuperação de outbox presa, auditoria de ações manuais e confirmações para ações sensíveis.
|
||||
|
||||
## Objetivo
|
||||
|
||||
- Recuperar ou expor itens de outbox presos em `processing`.
|
||||
- Registar ações críticas do operador como eventos auditáveis.
|
||||
- Evitar cliques acidentais em ações sensíveis.
|
||||
- Melhorar `/system/health` e `/operations` com métricas operacionais reais.
|
||||
|
||||
## Outbox stale recovery
|
||||
|
||||
Novas funções:
|
||||
|
||||
- `outbox_stale_minutes()`
|
||||
- `recover_stale_processing_outbox()`
|
||||
|
||||
Novos ENV:
|
||||
|
||||
```env
|
||||
OUTBOX_STALE_PROCESSING_MINUTES=30
|
||||
OUTBOX_STALE_RECOVERY_MODE=manual_only
|
||||
OUTBOX_RECOVER_STALE_BEFORE_PROCESS=true
|
||||
```
|
||||
|
||||
Modos disponíveis:
|
||||
|
||||
- `manual_only`: marca `processing` antigo como `stale` para decisão humana.
|
||||
- `mark_failed`: marca como `failed` com erro explicativo.
|
||||
- `retry_pending`: devolve para `pending` e incrementa retry.
|
||||
|
||||
O worker `scripts/process_outbox.py` chama a recuperação antes de fazer claim de novos itens, por defeito em modo `manual_only`.
|
||||
|
||||
Também foi criado:
|
||||
|
||||
```bash
|
||||
python scripts/recover_stale_outbox.py
|
||||
```
|
||||
|
||||
## Auditoria operacional
|
||||
|
||||
Novo módulo:
|
||||
|
||||
```text
|
||||
app/operator_audit_service.py
|
||||
```
|
||||
|
||||
Regista eventos `operator_action` em `business_events` sem criar nova tabela. Quando existe `task_id`, também espelha em `task_events`.
|
||||
|
||||
Ações auditadas:
|
||||
|
||||
- concluir task manualmente
|
||||
- concluir task com nota
|
||||
- ignorar task
|
||||
- reclassificar task
|
||||
- reprocessar outbox
|
||||
- marcar outbox como sent/failed/ignored
|
||||
- recuperação de outbox stale
|
||||
|
||||
## Confirmações para ações sensíveis
|
||||
|
||||
Foram adicionados `hx-confirm` nas ações HTMX sensíveis:
|
||||
|
||||
- concluir task
|
||||
- ignorar task
|
||||
- reclassificar task
|
||||
- reprocessar outbox
|
||||
- ignorar outbox
|
||||
- marcar outbox como failed
|
||||
|
||||
## Saúde operacional
|
||||
|
||||
`/system/health` passa a destacar:
|
||||
|
||||
- tasks pendentes
|
||||
- tasks concluídas 24h
|
||||
- tasks auto-completed 24h
|
||||
- tasks com associação ambígua
|
||||
- outbox processing
|
||||
- outbox stale
|
||||
- outbox failed/blocked/stale
|
||||
- ações do operador nas últimas 24h
|
||||
- configuração de stale recovery
|
||||
|
||||
## Operations
|
||||
|
||||
O Centro de trabalho ganha filtros de foco operacional:
|
||||
|
||||
- A fazer
|
||||
- Bloqueadas
|
||||
- Ambíguas
|
||||
- Atrasadas
|
||||
- Vendas
|
||||
- Financeiro
|
||||
- Logística
|
||||
- Revisão
|
||||
- Concluídas hoje
|
||||
|
||||
## Base de dados
|
||||
|
||||
Não há nova tabela obrigatória. A auditoria usa `business_events` e `task_events`, já existentes.
|
||||
|
||||
## Validação
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m compileall app scripts tests
|
||||
PYTHONPATH=. pytest -q
|
||||
```
|
||||
76
docs/CLIENTFLOW_V482_PRODUCTION_STABILIZATION.md
Normal file
76
docs/CLIENTFLOW_V482_PRODUCTION_STABILIZATION.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# ClientFlow v4.8.2 — Production Stabilization & Guided Operations
|
||||
|
||||
Esta versão fecha o ciclo de atualização com melhorias pequenas e focadas na operação diária. Não adiciona novas integrações, não muda regras LLM, não altera auto-complete e não requer migração de base de dados.
|
||||
|
||||
## Objetivo
|
||||
|
||||
Reduzir erros reais do operador:
|
||||
|
||||
- cliente fiscal errado;
|
||||
- documento emitido sem dados mínimos;
|
||||
- envio preparado sem morada/telefone;
|
||||
- outbox falhada sem explicação operacional;
|
||||
- operador sem próximo passo claro.
|
||||
|
||||
## O que muda
|
||||
|
||||
### Cliente fiscal e contacto Chatwoot separados
|
||||
|
||||
Operations, oportunidades e detalhe de task passam a mostrar explicitamente:
|
||||
|
||||
- Cliente fiscal;
|
||||
- Contacto Chatwoot;
|
||||
- conversa/contacto de origem quando disponível.
|
||||
|
||||
### Prontidão antes de documento ou envio
|
||||
|
||||
A UI mostra uma checklist mínima:
|
||||
|
||||
- cliente fiscal associado;
|
||||
- NIF;
|
||||
- email de faturação;
|
||||
- morada fiscal;
|
||||
- código postal;
|
||||
- localidade;
|
||||
- telefone para envio quando aplicável.
|
||||
|
||||
Quando falta informação, a UI mostra que a ação deve aguardar correção dos dados.
|
||||
|
||||
### Próximo passo e bloqueios atuais
|
||||
|
||||
A ficha de oportunidade passa a destacar:
|
||||
|
||||
- próxima ação;
|
||||
- bloqueios atuais;
|
||||
- dados fiscais/contacto;
|
||||
- prontidão para documentos;
|
||||
- prontidão para envio.
|
||||
|
||||
### Outbox em linguagem operacional
|
||||
|
||||
A outbox passa a mostrar uma leitura operacional do erro, por exemplo:
|
||||
|
||||
> Motivo provável: Cliente fiscal sem NIF válido ou dados fiscais incompletos.
|
||||
|
||||
O detalhe do item continua a incluir o payload e o erro técnico para diagnóstico.
|
||||
|
||||
### Health para operação
|
||||
|
||||
`/system/health` passa a mostrar estado simples:
|
||||
|
||||
- OK;
|
||||
- Atenção;
|
||||
- Crítico.
|
||||
|
||||
Também mostra contadores de oportunidades sem cliente fiscal, clientes fiscais incompletos em oportunidades ativas, produtos sem código externo e último webhook Chatwoot.
|
||||
|
||||
## Validação
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m compileall app scripts tests
|
||||
PYTHONPATH=. pytest -q
|
||||
```
|
||||
|
||||
## Sem migração
|
||||
|
||||
Esta versão é apenas UI/health/readability. Não requer alteração de schema.
|
||||
85
docs/CLIENTFLOW_V483_OPERATIONS_GUARDRAILS.md
Normal file
85
docs/CLIENTFLOW_V483_OPERATIONS_GUARDRAILS.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# ClientFlow v4.8.3 — Operations Card Simplification & Opportunity Guardrails
|
||||
|
||||
Esta versão fecha o ciclo de estabilização operacional após a v4.8.2. O foco é reduzir ruído em `/operations` e impedir que mensagens sem intenção comercial criem oportunidades falsas.
|
||||
|
||||
## Objetivos
|
||||
|
||||
- Tornar os cards de Operations mais leves para o operador.
|
||||
- Nunca mostrar `contact_id` do Chatwoot como se fosse cliente fiscal.
|
||||
- Mostrar bloqueios apenas quando bloqueiam a próxima ação da jornada.
|
||||
- Criar oportunidades apenas quando existe intenção comercial real.
|
||||
- Evitar oportunidades para bounces, spam, Mail Delivery Subsystem, unsubscribe e revisão falhada.
|
||||
|
||||
## Regras de criação de oportunidade
|
||||
|
||||
Criar oportunidade automaticamente apenas para ações comerciais claras, como:
|
||||
|
||||
- `SEND_QUOTE`
|
||||
- `SEND_PROFORMA`
|
||||
- `SEND_INVOICE`
|
||||
- `CONFIRM_PAYMENT`
|
||||
- `PREPARE_ORDER`
|
||||
- `CREATE_SHIPMENT`
|
||||
|
||||
`SEND_INFO` só cria oportunidade se houver intenção comercial explícita no assunto/corpo/nota, como orçamento, cotação, preço, comprar, encomendar, pró-forma, fatura ou pagamento.
|
||||
|
||||
Nunca cria oportunidade para:
|
||||
|
||||
- `REVIEW_MANUALLY`
|
||||
- `REMOVE_FROM_LIST`
|
||||
- `IGNORE_SPAM`
|
||||
- `NO_ACTION`
|
||||
- `SUPPORT`
|
||||
- `MARK_NO_INTEREST` sem oportunidade existente
|
||||
- Mail Delivery Subsystem / mailer-daemon / postmaster
|
||||
- returned mail / undelivered mail / delivery status notification
|
||||
|
||||
## Bloqueios por fase da jornada
|
||||
|
||||
`Cliente fiscal por associar` só aparece como bloqueio quando a próxima ação exige dados fiscais, documento, pagamento ou envio.
|
||||
|
||||
Não aparece como bloqueio em fases iniciais como:
|
||||
|
||||
- rever mensagem
|
||||
- responder informação simples
|
||||
- suporte simples
|
||||
- marketing/remover da lista
|
||||
- mensagem automática/bounce
|
||||
|
||||
## Cards de Operations
|
||||
|
||||
O card principal passa a mostrar apenas:
|
||||
|
||||
- título simples
|
||||
- contexto curto
|
||||
- próxima ação
|
||||
- bloqueio atual, se existir
|
||||
- botões principais
|
||||
|
||||
Campos técnicos foram movidos para `Ver detalhes`:
|
||||
|
||||
- fila
|
||||
- estado
|
||||
- origem
|
||||
- contacto Chatwoot
|
||||
- cliente fiscal quando existir ou for necessário
|
||||
|
||||
## Limpeza de oportunidades antigas
|
||||
|
||||
Foi adicionado script conservador:
|
||||
|
||||
```bash
|
||||
python scripts/cleanup_non_commercial_opportunities.py --limit 100
|
||||
```
|
||||
|
||||
Por defeito é dry-run. Para aplicar:
|
||||
|
||||
```bash
|
||||
python scripts/cleanup_non_commercial_opportunities.py --apply
|
||||
```
|
||||
|
||||
Só fecha oportunidades abertas com valor 0, sem documentos comerciais e sem envios, que parecem ter sido criadas por mensagens automáticas/bounce.
|
||||
|
||||
## Sem migração
|
||||
|
||||
Esta versão não cria tabelas nem exige migração de base de dados.
|
||||
30
docs/CLIENTFLOW_V484_FISCAL_LINK_CONSISTENCY.md
Normal file
30
docs/CLIENTFLOW_V484_FISCAL_LINK_CONSISTENCY.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# ClientFlow v4.8.4 — Fiscal Link Consistency Hotfix
|
||||
|
||||
## Objetivo
|
||||
|
||||
Corrigir inconsistências entre a ficha da task, a fila Operations e os cards de oportunidade quando a oportunidade já tem cliente fiscal associado.
|
||||
|
||||
## Problema corrigido
|
||||
|
||||
Em alguns casos a task mostrava corretamente o cliente fiscal ligado pela oportunidade, por exemplo `Elegantlegacy Lda`, mas o card em Operations continuava a mostrar `Cliente fiscal por associar`. Isto acontecia porque a fila operacional lia o cliente fiscal a partir de `tasks.customer_id`, enquanto o detalhe da task usava `opportunities.local_customer_id`.
|
||||
|
||||
## Alterações
|
||||
|
||||
- Operations passa a procurar primeiro o cliente fiscal da oportunidade (`opportunities.local_customer_id`).
|
||||
- `tasks.customer_id` fica apenas como fallback quando for um UUID local válido/resolvido.
|
||||
- O contact_id do Chatwoot nunca é usado como cliente fiscal.
|
||||
- Operations passa a receber também NIF, email, morada, código postal e localidade do cliente fiscal.
|
||||
- Bloqueios de Operations passam a mostrar dados fiscais em falta quando há cliente fiscal associado, em vez de dizer apenas “cliente fiscal por associar”.
|
||||
- O detalhe da task passa a carregar morada/código postal/localidade do cliente fiscal.
|
||||
- A secção “Dados em falta” da task passa a incluir falhas fiscais críticas, para não contradizer a “Prontidão fiscal da tarefa”.
|
||||
|
||||
## Sem migração
|
||||
|
||||
Esta versão não altera schema de base de dados.
|
||||
|
||||
## Validação
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m compileall app scripts tests
|
||||
PYTHONPATH=. pytest -q
|
||||
```
|
||||
74
docs/CLIENTFLOW_V485_BOUNCE_IGNORE_CARD_UX.md
Normal file
74
docs/CLIENTFLOW_V485_BOUNCE_IGNORE_CARD_UX.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# ClientFlow v4.8.5 — Bounce Ignore & Card UX Cleanup
|
||||
|
||||
Objetivo: fechar o ciclo de estabilização operacional com uma correção pequena e conservadora.
|
||||
|
||||
## O que muda
|
||||
|
||||
### 1. Emails bounce/NDR não entram no fluxo operacional
|
||||
|
||||
Mensagens automáticas como Office 365 / Exchange NDR, Mail Delivery Subsystem, postmaster e mailer-daemon deixam de criar trabalho no ClientFlow.
|
||||
|
||||
Exemplos tratados como bounce/NDR:
|
||||
|
||||
- `Undeliverable:`
|
||||
- `Your message couldn't be delivered`
|
||||
- `Recipient wasn't found`
|
||||
- `Unknown To address`
|
||||
- `Delivery Status Notification`
|
||||
- `Non-Delivery Report`
|
||||
- `Remote Server returned`
|
||||
- `550 5.1.1`
|
||||
- `5.1.10`
|
||||
|
||||
Comportamento:
|
||||
|
||||
- não cria task;
|
||||
- não cria oportunidade;
|
||||
- não cria cliente fiscal;
|
||||
- não aparece em Operations;
|
||||
- não exige cliente fiscal.
|
||||
|
||||
O email continua disponível na inbox original, como Chatwoot, Thunderbird ou servidor de email.
|
||||
|
||||
### 2. Cards de Operations mais leves
|
||||
|
||||
O card principal passa a priorizar informação útil para decidir:
|
||||
|
||||
- identidade legível do contacto/cliente;
|
||||
- contexto curto;
|
||||
- próxima ação;
|
||||
- bloqueio atual apenas quando existe;
|
||||
- botões principais.
|
||||
|
||||
IDs técnicos do Chatwoot deixam de ser título principal. Quando não existe nome/email/telefone útil, o card mostra `Contacto sem identificação`, e não `Contacto Chatwoot #123`.
|
||||
|
||||
O bloco `Ver detalhes` só aparece quando houver informação realmente útil, como cliente fiscal, oportunidade ligada, bloqueio ou contexto de integração.
|
||||
|
||||
### 3. Cards de oportunidades mais simples
|
||||
|
||||
O card da oportunidade deixa de mostrar informação de baixo valor no primeiro nível, como valor `0,00 €`, data/hora completa e contadores técnicos.
|
||||
|
||||
O CTA passa a ser específico sempre que possível:
|
||||
|
||||
- `Preparar resposta`
|
||||
- `Emitir pró-forma`
|
||||
- `Emitir fatura`
|
||||
- `Confirmar pagamento`
|
||||
- `Ver tarefa pendente`
|
||||
- `Ver oportunidade`
|
||||
|
||||
## O que não muda
|
||||
|
||||
- Sem migração de base de dados.
|
||||
- Sem nova integração.
|
||||
- Sem nova lógica LLM.
|
||||
- Sem alteração ao outbox worker.
|
||||
- Sem alteração ao auto-complete Chatwoot.
|
||||
- Sem alteração às regras documentais/fiscais já existentes.
|
||||
|
||||
## Validação
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m compileall app scripts tests
|
||||
PYTHONPATH=. pytest -q
|
||||
```
|
||||
35
docs/CLIENTFLOW_V486_OPPORTUNITY_BOARD_LAYOUT_CLEANUP.md
Normal file
35
docs/CLIENTFLOW_V486_OPPORTUNITY_BOARD_LAYOUT_CLEANUP.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# ClientFlow v4.8.6 — Opportunity Board Layout Cleanup
|
||||
|
||||
Esta versão corrige a legibilidade do quadro de oportunidades.
|
||||
|
||||
## Objetivo
|
||||
|
||||
Evitar sobreposição de chips, botões e texto nos cards de oportunidades, especialmente em ecrãs com menos largura útil ou zoom diferente de 100%.
|
||||
|
||||
## Alterações
|
||||
|
||||
- O quadro de oportunidades passou a usar colunas com largura mínima.
|
||||
- Quando não há espaço suficiente, o quadro usa scroll horizontal em vez de comprimir os cards.
|
||||
- Os cards deixam de repetir o estado da etapa dentro do próprio card.
|
||||
- A informação visível no card fica limitada a:
|
||||
- identificação do cliente/contacto;
|
||||
- assunto;
|
||||
- próxima ação;
|
||||
- bloqueio relevante, se existir;
|
||||
- botão principal.
|
||||
- Os chips redundantes de estado/prioridade deixam de ocupar o topo do card.
|
||||
|
||||
## Sem alteração funcional
|
||||
|
||||
- Não altera regras de negócio.
|
||||
- Não altera Chatwoot.
|
||||
- Não altera criação de oportunidades.
|
||||
- Não altera auto-complete.
|
||||
- Não altera base de dados.
|
||||
|
||||
## Validação
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m compileall app scripts tests
|
||||
PYTHONPATH=. pytest -q
|
||||
```
|
||||
28
docs/CLIENTFLOW_V487_OPERATIONS_WORKBENCH_LAYOUT.md
Normal file
28
docs/CLIENTFLOW_V487_OPERATIONS_WORKBENCH_LAYOUT.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# ClientFlow v4.8.7 — Operations Workbench Layout
|
||||
|
||||
## Objetivo
|
||||
|
||||
Limpar o Centro de trabalho para uso diário: a página passa a funcionar como uma fila compacta de ações com painel lateral de contexto.
|
||||
|
||||
## O que mudou
|
||||
|
||||
- `/operations` deixa de apresentar uma sequência de cards grandes como layout principal.
|
||||
- A fila à esquerda mostra apenas identidade, contexto curto, próxima ação e bloqueio real.
|
||||
- O painel lateral mostra a mensagem, cliente/contacto, bloqueios e botões de ação.
|
||||
- A seleção de uma ação usa HTMX em `/operations/partials/item-detail`.
|
||||
- Informação técnica fica recolhida em "Detalhes técnicos".
|
||||
|
||||
## O que não mudou
|
||||
|
||||
- Não requer migração de base de dados.
|
||||
- Não altera regras de negócio.
|
||||
- Não altera auto-complete do Chatwoot.
|
||||
- Não altera criação de oportunidades.
|
||||
- Não altera o worker da outbox.
|
||||
|
||||
## Validação
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m compileall app scripts tests
|
||||
PYTHONPATH=. pytest -q
|
||||
```
|
||||
39
docs/CLIENTFLOW_V488_OPERATIONS_CLASSIC_CARDS.md
Normal file
39
docs/CLIENTFLOW_V488_OPERATIONS_CLASSIC_CARDS.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# ClientFlow v4.8.8 — Operations Classic Cards
|
||||
|
||||
Esta versão ajusta o `Centro de trabalho` depois do teste da v4.8.7.
|
||||
|
||||
A v4.8.7 introduziu uma fila compacta com painel lateral fixo. A experiência ficou menos familiar e menos apelativa para uso diário. A v4.8.8 volta a uma apresentação mais parecida com a versão anterior: lista vertical de cards, simples e direta.
|
||||
|
||||
## O que muda
|
||||
|
||||
- O Centro de trabalho volta a usar cards verticais.
|
||||
- Remove o painel lateral fixo da v4.8.7; volta a uma experiência sem painel lateral fixo.
|
||||
- Mantém os cards mais limpos das versões recentes.
|
||||
- Mantém `Ver detalhes` apenas quando há informação útil.
|
||||
- Mantém filtros HTMX em `/operations/partials/work-items`.
|
||||
|
||||
## O que fica fora do primeiro nível
|
||||
|
||||
- IDs técnicos do Chatwoot como título principal.
|
||||
- Estado técnico repetido.
|
||||
- Origem Chatwoot repetida.
|
||||
- Cliente fiscal quando ainda não é necessário para a ação.
|
||||
- Bloqueios que não impedem a próxima ação.
|
||||
|
||||
## O que continua visível
|
||||
|
||||
Cada card deve mostrar rapidamente:
|
||||
|
||||
- Quem é o contacto ou cliente.
|
||||
- Qual é o contexto curto.
|
||||
- Qual é a próxima ação.
|
||||
- Qual é o bloqueio real, se existir.
|
||||
- Qual é o botão principal.
|
||||
|
||||
## Compatibilidade
|
||||
|
||||
Não requer migração de base de dados.
|
||||
Não altera regras de negócio.
|
||||
Não altera Chatwoot auto-complete.
|
||||
Não altera outbox worker.
|
||||
Não altera criação de oportunidades.
|
||||
35
docs/CLIENTFLOW_V489_OPERATIONS_CARD_POLISH.md
Normal file
35
docs/CLIENTFLOW_V489_OPERATIONS_CARD_POLISH.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# ClientFlow v4.8.9 — Operations Card Polish
|
||||
|
||||
Esta versão é um hotfix de UX sobre a v4.8.8.
|
||||
|
||||
## Objetivo
|
||||
|
||||
Manter o Centro de trabalho no layout clássico de cards, mas remover o ruído visual do primeiro nível.
|
||||
|
||||
## Alterações
|
||||
|
||||
- Remove completamente o bloco **Ver detalhes** dos cards de Operations.
|
||||
- Mantém informação técnica em páginas próprias, como task, oportunidade e admin.
|
||||
- Reforça a hierarquia de ações: um botão principal e botões secundários menos fortes.
|
||||
- Melhora espaçamento, separadores internos e alinhamento dos cards.
|
||||
|
||||
## O que não muda
|
||||
|
||||
- Não requer migração de base de dados.
|
||||
- Não altera Chatwoot.
|
||||
- Não altera auto-complete.
|
||||
- Não altera outbox worker.
|
||||
- Não altera criação de oportunidades.
|
||||
- Não altera regras de negócio.
|
||||
|
||||
## Regra de UI
|
||||
|
||||
O card de Operations deve mostrar apenas:
|
||||
|
||||
1. Quem é.
|
||||
2. O que aconteceu.
|
||||
3. Qual é a próxima ação.
|
||||
4. Bloqueio real, se existir.
|
||||
5. Botão principal.
|
||||
|
||||
Os detalhes técnicos devem ficar fora da fila operacional.
|
||||
48
docs/CLIENTFLOW_V490_OPERATIONS_NOISE_CLEANUP.md
Normal file
48
docs/CLIENTFLOW_V490_OPERATIONS_NOISE_CLEANUP.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# ClientFlow v4.9.0 — Operations Noise Cleanup
|
||||
|
||||
Esta versão fecha a higiene da fila do **Centro de trabalho**. O objetivo é retirar da fila diária mensagens que pertencem à inbox/email, mas não ao fluxo operacional do ClientFlow.
|
||||
|
||||
## O que muda
|
||||
|
||||
- Mensagens de `postmaster`, `mailer-daemon`, `Mail Delivery Subsystem`, `Mail Delivery System`, Office 365 / Exchange NDR, `Undeliverable`, `Returned mail`, `Delivery Status Notification`, `Unknown To address` e erros semelhantes deixam de aparecer no Centro de trabalho.
|
||||
- Tasks novas de `REVIEW_MANUALLY`, `REMOVE_FROM_LIST`, `MARK_NO_INTEREST`, `IGNORE_SPAM`, `NO_ACTION` e `IGNORE_BOUNCE` não entram como prioridade alta por defeito.
|
||||
- A contagem visível de “A fazer agora” passa a refletir melhor o que aparece realmente na fila, em vez de contar lixo antigo ainda pendente.
|
||||
- Foi adicionado um detector partilhado em `app/operation_noise.py` para manter as regras de ruído consistentes entre triagem, Operations e limpeza.
|
||||
|
||||
## Limpeza de dados antigos
|
||||
|
||||
A versão inclui o script:
|
||||
|
||||
```bash
|
||||
python scripts/cleanup_operations_noise.py --limit 200
|
||||
```
|
||||
|
||||
Por defeito é dry-run. Para aplicar:
|
||||
|
||||
```bash
|
||||
python scripts/cleanup_operations_noise.py --apply
|
||||
```
|
||||
|
||||
O script marca tasks pendentes claramente técnicas/sistema/bounce como `skipped`. Não apaga mensagens, eventos brutos nem conversas Chatwoot.
|
||||
|
||||
## Depois da limpeza
|
||||
|
||||
É normal a fila cair bastante, por exemplo de dezenas de itens para apenas trabalho real:
|
||||
|
||||
- faturas a emitir;
|
||||
- orçamentos a preparar;
|
||||
- respostas comerciais reais;
|
||||
- bloqueios de outbox/documentos;
|
||||
- revisões com identidade/contexto útil.
|
||||
|
||||
## Sem migração
|
||||
|
||||
Não requer migração de base de dados.
|
||||
|
||||
## O que não muda
|
||||
|
||||
- Não altera Chatwoot.
|
||||
- Não altera auto-complete.
|
||||
- Não altera outbox worker.
|
||||
- Não altera regras de criação de oportunidades comerciais reais.
|
||||
- Não adiciona nova integração ou LLM.
|
||||
46
docs/CLIENTFLOW_V4912_ODOO_FULFILMENT_RECONCILIATION.md
Normal file
46
docs/CLIENTFLOW_V4912_ODOO_FULFILMENT_RECONCILIATION.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# ClientFlow v4.9.12 — Fiscal Customer Reconciliation & Odoo Fulfilment
|
||||
|
||||
## Objetivo
|
||||
|
||||
A reconciliação passa a reconstruir melhor o processo operacional quando a evidência vem do Odoo.
|
||||
|
||||
Regra base:
|
||||
|
||||
- ClientFlow: o **Cliente fiscal** é a referência central da oportunidade.
|
||||
- Jasmin: procurar documentos por **NIF**.
|
||||
- Odoo: quando não há NIF/VAT fiável, procurar por **nome fiscal normalizado** e, depois de confirmado, guardar mapeamento `odoo partner_id -> cliente fiscal ClientFlow`.
|
||||
|
||||
## O que muda
|
||||
|
||||
- O sync Odoo importa linhas da venda, produtos, quantidades entregues/faturadas.
|
||||
- O sync Odoo procura entregas/stock pickings associadas à venda.
|
||||
- A timeline candidata pode mostrar:
|
||||
- venda Odoo encontrada;
|
||||
- linhas/produtos importados;
|
||||
- entrega Odoo concluída;
|
||||
- fatura por emitir.
|
||||
- Ao ligar um processo à oportunidade, são gravados:
|
||||
- `operation_links` para venda Odoo;
|
||||
- `operation_links` para estado físico Odoo;
|
||||
- eventos de timeline reconstruída.
|
||||
- Itens ignorados automaticamente pela limpeza de janela voltam a abrir quando regressam à janela sincronizada.
|
||||
|
||||
## Segurança
|
||||
|
||||
Esta versão não confirma pagamentos, não emite faturas, não fecha oportunidades e não altera Odoo/Jasmin/Packlink. Apenas lê evidência, sugere ligação e reconstrói timeline depois de confirmação do operador.
|
||||
|
||||
## Exemplo Elegantlegacy
|
||||
|
||||
Odoo:
|
||||
|
||||
- venda `S00274`;
|
||||
- cliente `ELEGANTLEGACY, LDA`;
|
||||
- entrega `WH/OUT/00295` concluída;
|
||||
- `invoice_status = to invoice`.
|
||||
|
||||
ClientFlow deve sugerir:
|
||||
|
||||
- ligar à oportunidade `Elegantlegacy Lda`;
|
||||
- estado reconstruído: venda/entrega Odoo encontrada;
|
||||
- próxima ação: emitir fatura.
|
||||
|
||||
77
docs/CLIENTFLOW_V4913_APPLY_RECONSTRUCTED_PROCESS.md
Normal file
77
docs/CLIENTFLOW_V4913_APPLY_RECONSTRUCTED_PROCESS.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# ClientFlow v4.9.13 — Apply Reconstructed Process to Opportunity Pipeline
|
||||
|
||||
## Objetivo
|
||||
|
||||
A Reconciliação já conseguia reconstruir um processo a partir de evidências externas, por exemplo:
|
||||
|
||||
- venda Odoo `S00274`;
|
||||
- linhas/produtos da venda;
|
||||
- produções Odoo concluídas;
|
||||
- delivery `WH/OUT/00295` concluído;
|
||||
- `invoice_status = to invoice`.
|
||||
|
||||
A v4.9.13 fecha a etapa seguinte: quando o operador liga o processo reconstruído a uma oportunidade, o ClientFlow deixa de criar apenas uma ligação solta e passa a aplicar o estado reconstruído à oportunidade.
|
||||
|
||||
## O que muda
|
||||
|
||||
Ao confirmar **Ligar processo à oportunidade**, o sistema passa a:
|
||||
|
||||
1. ligar os reconciliation items à oportunidade;
|
||||
2. criar eventos na timeline da oportunidade;
|
||||
3. criar/atualizar links operacionais Odoo;
|
||||
4. importar linhas Odoo para os produtos da oportunidade;
|
||||
5. atualizar o valor da oportunidade se ainda estiver a zero;
|
||||
6. atualizar a fase visual para o estado reconstruído mais avançado;
|
||||
7. garantir task `SEND_INVOICE` quando a venda/entrega está feita e a fatura está por emitir.
|
||||
|
||||
## Caso Elegantlegacy
|
||||
|
||||
Para a venda Odoo `S00274`, o resultado esperado depois de ligar à oportunidade é:
|
||||
|
||||
- Valor: `537,00 €`;
|
||||
- Produtos:
|
||||
- Wallbox 7.4KW;
|
||||
- Wallbox 11KW;
|
||||
- Wallbox 22KW;
|
||||
- Odoo:
|
||||
- venda `S00274` criada;
|
||||
- produção concluída;
|
||||
- estado físico `shipped`;
|
||||
- validação física/entrega concluída;
|
||||
- Timeline:
|
||||
- venda/encomenda encontrada no Odoo;
|
||||
- linhas/produtos importados;
|
||||
- entrega Odoo concluída;
|
||||
- fatura por emitir;
|
||||
- Próxima ação: `SEND_INVOICE`.
|
||||
|
||||
A linha de transporte Odoo com valor zero fica no payload operacional, mas não é importada como linha comercial da oportunidade.
|
||||
|
||||
## Janela da Reconciliação
|
||||
|
||||
A página `/reconciliation` passa a aceitar janela via query string:
|
||||
|
||||
```text
|
||||
/reconciliation?days=7
|
||||
/reconciliation?days=30
|
||||
```
|
||||
|
||||
A UI mostra botões rápidos:
|
||||
|
||||
- Hoje;
|
||||
- 3 dias;
|
||||
- 7 dias;
|
||||
- 30 dias.
|
||||
|
||||
As ações de sincronização e limpeza respeitam a janela ativa.
|
||||
|
||||
## Segurança
|
||||
|
||||
Esta versão continua conservadora:
|
||||
|
||||
- não confirma pagamentos automaticamente;
|
||||
- não emite faturas automaticamente;
|
||||
- não fecha oportunidades;
|
||||
- não altera Odoo/Jasmin/Packlink;
|
||||
- apenas aplica evidências externas depois da confirmação do operador.
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# ClientFlow v4.9.17 — Reconciliation Cross-Source Grouping
|
||||
|
||||
Esta versão corrige um problema na Reconciliação em que evidências do mesmo processo apareciam separadas quando cada sistema fornecia identificadores diferentes.
|
||||
|
||||
## Problema
|
||||
|
||||
Exemplo real:
|
||||
|
||||
- Jasmin tem orçamento para `ACZCO BRAGA ENERGY, LDA` com NIF `517249200`.
|
||||
- Odoo tem venda `S00279` para `ACZCO BRAGA ENERGY, LDA`, mas sem NIF.
|
||||
|
||||
Antes, a Reconciliação podia mostrar o orçamento como processo candidato com `1 evidência` e a venda Odoo apenas como sugestão/elemento separado.
|
||||
|
||||
## Correção
|
||||
|
||||
A construção de processos candidatos passa a usar todos os sinais de identidade disponíveis no item:
|
||||
|
||||
- NIF
|
||||
- email
|
||||
- nome fiscal normalizado
|
||||
|
||||
Se uma evidência Jasmin tem NIF + nome e uma evidência Odoo tem o mesmo nome, ambas são agrupadas no mesmo processo candidato. O NIF continua a ser a chave de maior confiança do grupo.
|
||||
|
||||
## Resultado esperado
|
||||
|
||||
O caso deve aparecer como um único processo:
|
||||
|
||||
- Orçamento Jasmin encontrado
|
||||
- Venda/encomenda Odoo encontrada
|
||||
- Linhas/produtos Odoo importados
|
||||
- Entrega Odoo concluída, se existir
|
||||
- Fatura por emitir, se `invoice_status = to invoice`
|
||||
|
||||
A ação sugerida deve ser coerente com o estado mais avançado: por exemplo, `SEND_INVOICE` quando existe venda/entrega Odoo por faturar.
|
||||
|
||||
## Segurança
|
||||
|
||||
A versão não liga automaticamente nada. Continua a ser necessário o operador confirmar a ligação ou reconstrução.
|
||||
124
docs/CLIENTFLOW_V491_OPERATIONAL_RECONCILIATION.md
Normal file
124
docs/CLIENTFLOW_V491_OPERATIONAL_RECONCILIATION.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# ClientFlow v4.9.1 — Operational Reconciliation & External Intake
|
||||
|
||||
Esta versão adiciona uma área de **Reconciliação** para organizar informação que existe fora do ClientFlow antes de a transformar em processo comercial.
|
||||
|
||||
## Objetivo
|
||||
|
||||
O ClientFlow passa a preparar informação solta de:
|
||||
|
||||
- documentos Jasmin criados fora do ClientFlow;
|
||||
- faturas/orçamentos sem oportunidade;
|
||||
- comprovativos de pagamento recebidos manualmente;
|
||||
- pedidos vindos de WhatsApp, telefone, email direto ou presencial;
|
||||
- futuras vendas Odoo sem oportunidade.
|
||||
|
||||
A regra principal é:
|
||||
|
||||
```text
|
||||
Sincronizar/detetar automaticamente.
|
||||
Criar candidato de reconciliação.
|
||||
Operador confirma ligar, criar oportunidade ou ignorar.
|
||||
```
|
||||
|
||||
## O que mudou
|
||||
|
||||
### Nova página
|
||||
|
||||
```text
|
||||
/reconciliation
|
||||
/reconciliacao
|
||||
```
|
||||
|
||||
A página mostra:
|
||||
|
||||
- itens abertos de reconciliação;
|
||||
- documentos sem ligação;
|
||||
- comprovativos por associar;
|
||||
- ações para criar oportunidade, ligar oportunidade existente ou ignorar.
|
||||
|
||||
### Novas tabelas aditivas
|
||||
|
||||
A versão cria schema adicional no arranque:
|
||||
|
||||
```text
|
||||
reconciliation_items
|
||||
payment_proofs
|
||||
```
|
||||
|
||||
É uma alteração aditiva: não remove nem altera dados existentes.
|
||||
|
||||
### Entrada manual externa
|
||||
|
||||
A página permite registar pedidos vindos de canais fora do Chatwoot:
|
||||
|
||||
```text
|
||||
WhatsApp
|
||||
Telefone
|
||||
Email direto
|
||||
Presencial
|
||||
Outro
|
||||
```
|
||||
|
||||
Ao registar, o sistema cria:
|
||||
|
||||
- oportunidade;
|
||||
- task com a próxima ação;
|
||||
- evento de timeline.
|
||||
|
||||
Não cria IDs falsos de Chatwoot.
|
||||
|
||||
### Comprovativos de pagamento
|
||||
|
||||
Um comprovativo pode ser guardado e ligado a uma oportunidade.
|
||||
|
||||
Importante:
|
||||
|
||||
```text
|
||||
Comprovativo recebido não confirma pagamento.
|
||||
```
|
||||
|
||||
Quando está ligado a uma oportunidade, cria task `CONFIRM_PAYMENT` para validação humana.
|
||||
|
||||
### Sincronização local
|
||||
|
||||
Script novo:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python scripts/sync_reconciliation_candidates.py
|
||||
```
|
||||
|
||||
Ele procura documentos Jasmin locais em `commercial_documents` sem `opportunity_id` e cria candidatos de reconciliação.
|
||||
|
||||
## Fluxo operacional recomendado
|
||||
|
||||
```text
|
||||
Documento/Comprovativo/Pedido externo
|
||||
↓
|
||||
Reconciliação
|
||||
↓
|
||||
Operador decide:
|
||||
- ligar a oportunidade existente
|
||||
- criar oportunidade
|
||||
- ignorar
|
||||
↓
|
||||
ClientFlow cria/atualiza oportunidade e task
|
||||
```
|
||||
|
||||
## O que esta versão não faz
|
||||
|
||||
- Não confirma pagamentos automaticamente.
|
||||
- Não emite faturas automaticamente.
|
||||
- Não apaga nem substitui documentos Jasmin.
|
||||
- Não fecha oportunidades sozinha.
|
||||
- Não usa LLM para reorganizar sem confirmação humana.
|
||||
|
||||
## Validação
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m compileall app scripts tests
|
||||
PYTHONPATH=. pytest -q
|
||||
```
|
||||
|
||||
## Rollback
|
||||
|
||||
O código pode ser revertido normalmente. As tabelas novas são aditivas e podem ficar sem afetar o fluxo antigo.
|
||||
81
docs/CLIENTFLOW_V4920_MULTI_PURCHASE_RECONCILIATION.md
Normal file
81
docs/CLIENTFLOW_V4920_MULTI_PURCHASE_RECONCILIATION.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# ClientFlow v4.9.20 — Multi-Purchase Reconciliation
|
||||
|
||||
## Objetivo
|
||||
|
||||
A reconciliação deixa de assumir que `um cliente fiscal = um processo`.
|
||||
|
||||
A regra passa a ser:
|
||||
|
||||
- **Cliente fiscal** identifica quem é a entidade: NIF, nome fiscal normalizado, email ou mapeamento externo confirmado.
|
||||
- **Compra/processo** identifica o ciclo comercial específico: orçamento, venda, pró-forma, fatura, comprovativo, entrega ou histórico associado a essa compra.
|
||||
- **Oportunidade** representa uma compra/processo concreto, não todo o histórico do cliente.
|
||||
|
||||
## Problema corrigido
|
||||
|
||||
Clientes com documentos soltos e várias compras podiam ser agrupados numa única timeline apenas porque partilhavam NIF ou nome fiscal.
|
||||
|
||||
Exemplo de risco:
|
||||
|
||||
- ACZCO BRAGA ENERGY, LDA / NIF 517249200
|
||||
- ORC.ORC2026.154 / 638,60 €
|
||||
- S00279 / 638,60 €
|
||||
- ORC.ORC2026.160 / 190,00 €
|
||||
- FA2026.80 histórica
|
||||
|
||||
Antes, estes itens podiam aparecer como um único processo candidato.
|
||||
|
||||
Agora a reconciliação faz duas passagens:
|
||||
|
||||
1. Agrupa por cliente fiscal.
|
||||
2. Divide o cliente em compras/processos separados.
|
||||
|
||||
## Regras de separação por compra
|
||||
|
||||
São âncoras de compra:
|
||||
|
||||
- Venda Odoo (`odoo_sale_order`)
|
||||
- Orçamento Jasmin (`jasmin_quotation`)
|
||||
- Pró-forma Jasmin (`jasmin_proforma`)
|
||||
- Fatura Jasmin (`jasmin_invoice`)
|
||||
|
||||
Duas âncoras só são fundidas no mesmo processo quando há evidência forte:
|
||||
|
||||
- mesma referência externa; ou
|
||||
- tipos documentais diferentes com valor igual/aproximado e datas próximas; ou
|
||||
- ponte Jasmin ↔ Odoo próxima quando o valor Jasmin não foi importado, mantendo vendas Odoo diferentes sempre separadas.
|
||||
|
||||
Duas vendas Odoo diferentes nunca são fundidas automaticamente.
|
||||
Duas cotações Jasmin diferentes nunca são fundidas automaticamente só por terem o mesmo cliente.
|
||||
|
||||
## Itens soltos
|
||||
|
||||
Comprovativos, envios e outros itens sem âncora própria são atribuídos a uma compra apenas quando o match é inequívoco por valor/data/referência/produto.
|
||||
|
||||
Caso contrário, ficam como item/processo separado para revisão manual.
|
||||
|
||||
## UI
|
||||
|
||||
A página de Reconciliação passou a explicar explicitamente:
|
||||
|
||||
> O sistema agrupa primeiro por cliente fiscal e depois separa por compra/processo.
|
||||
|
||||
Nos cartões de processo, a chave passa a distinguir:
|
||||
|
||||
- Cliente fiscal: NIF / nome fiscal / email
|
||||
- Compra/processo: S00279, ORC.ORC2026.154, FA2026.80, etc.
|
||||
|
||||
## Testes adicionados
|
||||
|
||||
- Duas vendas Odoo do mesmo cliente geram dois processos candidatos.
|
||||
- Um orçamento Jasmin é atribuído à venda Odoo correta por valor/data.
|
||||
- Duas cotações Jasmin do mesmo cliente ficam em dois processos diferentes.
|
||||
- Cotação e fatura com mesmo valor e data próxima podem formar uma compra.
|
||||
- Comprovativo é associado apenas quando o match com a venda é inequívoco.
|
||||
|
||||
## Validação
|
||||
|
||||
Suite completa:
|
||||
|
||||
```text
|
||||
164 passed
|
||||
```
|
||||
52
docs/CLIENTFLOW_V4921_RESET_RECONCILIATION.md
Normal file
52
docs/CLIENTFLOW_V4921_RESET_RECONCILIATION.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# ClientFlow v4.9.21 — Reset seguro da reconciliação gerada
|
||||
|
||||
## Objetivo
|
||||
|
||||
Permitir limpar candidatos de reconciliação gerados por sincronização externa e correr novamente o processo com o método multi-compra.
|
||||
|
||||
A limpeza é limitada por defeito a:
|
||||
|
||||
- `source_system IN ('jasmin', 'odoo', 'packlink')`
|
||||
- `status IN ('open', 'needs_review', 'ignored')`
|
||||
- `opportunity_id IS NULL`
|
||||
|
||||
Não apaga oportunidades, documentos comerciais, comprovativos de pagamento, vendas Odoo, documentos Jasmin, envios Packlink, operation links ou eventos de oportunidade.
|
||||
|
||||
## Comandos
|
||||
|
||||
Pré-visualizar:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python scripts/reset_reconciliation_generated.py
|
||||
```
|
||||
|
||||
Aplicar:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python scripts/reset_reconciliation_generated.py --apply
|
||||
```
|
||||
|
||||
Sincronizar novamente:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python scripts/sync_external_reconciliation.py --all --days 30 --limit 200
|
||||
PYTHONPATH=. python scripts/sync_reconciliation_candidates.py
|
||||
```
|
||||
|
||||
Para voltar à janela operacional curta:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python scripts/sync_external_reconciliation.py --all --days 3 --limit 100
|
||||
```
|
||||
|
||||
## Backup
|
||||
|
||||
Ao aplicar, o script cria antes uma tabela de backup:
|
||||
|
||||
```text
|
||||
reconciliation_items_reset_backup_YYYYMMDD_HHMMSS
|
||||
```
|
||||
|
||||
## Nota
|
||||
|
||||
Se já existirem oportunidades ou ligações erradas criadas pela reconciliação antiga, elas não são removidas por este script. Devem ser revistas em separado, porque já podem ter eventos, tarefas e operação associada.
|
||||
38
docs/CLIENTFLOW_V4922_REBUILD_RECONCILIATION_UI.md
Normal file
38
docs/CLIENTFLOW_V4922_REBUILD_RECONCILIATION_UI.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# ClientFlow v4.9.22 — Rebuild Reconciliação pela UI
|
||||
|
||||
A página de Reconciliação passa a ter uma ação operacional para reconstruir a janela ativa.
|
||||
|
||||
## Botão
|
||||
|
||||
`Apagar e correr novamente`
|
||||
|
||||
O botão executa:
|
||||
|
||||
1. Apaga apenas candidatos gerados por `jasmin`, `odoo` e `packlink`.
|
||||
2. Limita aos estados `open`, `needs_review` e `ignored`.
|
||||
3. Protege sempre `opportunity_id IS NULL`.
|
||||
4. Cria uma tabela de backup antes do delete.
|
||||
5. Volta a sincronizar APIs externas para a mesma janela ativa.
|
||||
|
||||
## Segurança
|
||||
|
||||
Não apaga:
|
||||
|
||||
- clientes
|
||||
- oportunidades
|
||||
- documentos reais Jasmin/Odoo/Packlink
|
||||
- itens já ligados a oportunidades
|
||||
- comprovativos manuais
|
||||
- tarefas ou operações
|
||||
|
||||
## Endpoint
|
||||
|
||||
`POST /reconciliation/rebuild`
|
||||
|
||||
Recebe `days` pela UI e mantém a mesma janela ativa: 1, 3, 7 ou 30 dias.
|
||||
|
||||
## Função de serviço
|
||||
|
||||
`reset_generated_reconciliation_items(...)`
|
||||
|
||||
É a versão reutilizável e segura do script `scripts/reset_reconciliation_generated.py`.
|
||||
32
docs/CLIENTFLOW_V4924_RECONCILIATION_PROCESS_REVIEW.md
Normal file
32
docs/CLIENTFLOW_V4924_RECONCILIATION_PROCESS_REVIEW.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# ClientFlow v4.9.24 — Reconciliation Process Review
|
||||
|
||||
Esta versão evolui a Reconciliação para uma área de revisão de processos, não apenas de matching de documentos.
|
||||
|
||||
## Alterações principais
|
||||
|
||||
- Cartões de processo candidato passam a mostrar **Motivos** e **Riscos**.
|
||||
- Cada candidato recebe um estado visual de revisão: `pronto`, `rever` ou `conflito`.
|
||||
- A UI permite marcar processos/itens como `needs_review` ou `historical`.
|
||||
- A tabela `reconciliation_decisions` guarda decisões do operador para auditoria e aprendizagem futura.
|
||||
- O resumo da página passa a contar `needs_review`, `conflict` e `historical`.
|
||||
|
||||
## Regras preservadas
|
||||
|
||||
- A sincronização externa continua a não criar oportunidades automaticamente.
|
||||
- O botão de reconstrução continua a apagar apenas candidatos gerados sem oportunidade ligada.
|
||||
- Cliente fiscal continua separado de processo/compra.
|
||||
- Oportunidades continuam a ser criadas/ligadas apenas por ação explícita do operador.
|
||||
|
||||
## Objetivo operacional
|
||||
|
||||
Cada cartão deve responder rapidamente:
|
||||
|
||||
1. Quem é o cliente fiscal?
|
||||
2. Que compra/processo é este?
|
||||
3. Porque é que o sistema agrupou estes documentos?
|
||||
4. Que riscos existem antes de aplicar?
|
||||
5. Qual a ação certa: ligar, criar, rever ou arquivar como histórico?
|
||||
|
||||
## Validação
|
||||
|
||||
Suite completa: `173 passed`.
|
||||
98
docs/CLIENTFLOW_V4925_FISCAL_ENRICHMENT_WORKER.md
Normal file
98
docs/CLIENTFLOW_V4925_FISCAL_ENRICHMENT_WORKER.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# ClientFlow v4.9.25 — Fiscal Enrichment Worker
|
||||
|
||||
Esta versão adiciona uma camada autónoma antes da reconciliação:
|
||||
|
||||
```text
|
||||
Oportunidades abertas sem cliente fiscal
|
||||
→ enriquecimento fiscal por NIF/email/domínio/nome
|
||||
→ sugestão ou auto-associação segura
|
||||
→ sincronização Jasmin/Odoo
|
||||
→ reconciliação por cliente fiscal + processo de compra
|
||||
```
|
||||
|
||||
## Objetivo
|
||||
|
||||
Melhorar a ligação entre oportunidades internas, Jasmin e Odoo evitando que a
|
||||
reconciliação dependa de nomes de contacto ou texto livre.
|
||||
|
||||
## Configuração
|
||||
|
||||
Adicionar ao `.env` quando a API externa estiver disponível:
|
||||
|
||||
```env
|
||||
EXTERNAL_COMPANY_LOOKUP_ENABLED=true
|
||||
EXTERNAL_COMPANY_LOOKUP_BASE_URL=http://127.0.0.1:8000
|
||||
EXTERNAL_COMPANY_LOOKUP_API_KEY=...
|
||||
EXTERNAL_COMPANY_LOOKUP_TIMEOUT=10
|
||||
EXTERNAL_COMPANY_LOOKUP_AUTO_THRESHOLD=95
|
||||
```
|
||||
|
||||
## Novas tabelas
|
||||
|
||||
```text
|
||||
external_company_cache
|
||||
fiscal_customer_suggestions
|
||||
fiscal_enrichment_runs
|
||||
```
|
||||
|
||||
A cache evita chamadas repetidas à API externa. As sugestões guardam a decisão
|
||||
pendente/aceite/rejeitada. Os runs permitem auditoria do worker periódico.
|
||||
|
||||
## Worker
|
||||
|
||||
```bash
|
||||
python scripts/enrich_fiscal_customers.py --incremental --limit 100
|
||||
```
|
||||
|
||||
Para apenas criar sugestões sem auto-associação:
|
||||
|
||||
```bash
|
||||
python scripts/enrich_fiscal_customers.py --incremental --no-auto-apply
|
||||
```
|
||||
|
||||
## Pipeline completo
|
||||
|
||||
```bash
|
||||
python scripts/run_reconciliation_pipeline.py --days 7 --limit 100
|
||||
```
|
||||
|
||||
Ordem:
|
||||
|
||||
```text
|
||||
1. enriquecer oportunidades sem cliente fiscal
|
||||
2. seed de clientes fiscais Jasmin/Odoo
|
||||
3. sincronizar documentos/vendas/envios
|
||||
4. mostrar processos candidatos na reconciliação
|
||||
```
|
||||
|
||||
## UI
|
||||
|
||||
Na página de Reconciliação foi adicionado:
|
||||
|
||||
```text
|
||||
[Enriquecer oportunidades]
|
||||
```
|
||||
|
||||
Na ficha de oportunidade foi adicionado:
|
||||
|
||||
```text
|
||||
Sugestões fiscais
|
||||
[Enriquecer cliente fiscal]
|
||||
[Associar]
|
||||
[Rejeitar]
|
||||
```
|
||||
|
||||
## Regra de auto-associação
|
||||
|
||||
A auto-associação só acontece se:
|
||||
|
||||
```text
|
||||
- oportunidade ainda não tem cliente fiscal
|
||||
- API devolve NIF + nome fiscal
|
||||
- confiança >= threshold
|
||||
- match_type forte: nif_exato, contacto_email_exato, email_principal_exato,
|
||||
email_exato_empresa_inferida, email_principal_dominio, website_dominio
|
||||
- não existe conflito com cliente fiscal já associado
|
||||
```
|
||||
|
||||
Outros casos ficam como sugestão para operador.
|
||||
21
docs/CLIENTFLOW_V4926_6_NOTES.md
Normal file
21
docs/CLIENTFLOW_V4926_6_NOTES.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# ClientFlow v4926.6 — Jasmin quotation to opportunity details
|
||||
|
||||
Esta versão faz com que oportunidades criadas/ligadas a partir de documentos Jasmin deixem de ficar vazias.
|
||||
|
||||
## Incluído
|
||||
|
||||
- Importa documentos Jasmin de reconciliação para `commercial_documents`.
|
||||
- Recria linhas em `commercial_document_lines`.
|
||||
- Copia linhas comerciais para `opportunity_items` quando o payload Jasmin contém linhas.
|
||||
- Atualiza `opportunities.value_amount` a partir do total Jasmin quando ainda está vazio/zero.
|
||||
- Atualiza `product_interest` com resumo das linhas.
|
||||
- Aplica o mesmo comportamento em:
|
||||
- criar oportunidade a partir de item único de reconciliação;
|
||||
- criar oportunidade a partir de processo/candidato agrupado;
|
||||
- ligar processo de reconciliação a oportunidade existente.
|
||||
- Mantém a integração conservadora: documentos soltos continuam a exigir ação do operador.
|
||||
|
||||
## Testes
|
||||
|
||||
- `compile_ok`
|
||||
- `7 passed` em testes estáticos v4926.5/v4926.6.
|
||||
30
docs/CLIENTFLOW_V4926_7_1_NOTES.md
Normal file
30
docs/CLIENTFLOW_V4926_7_1_NOTES.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# ClientFlow v4.9.26.7.1 — stale email identity review cleanup
|
||||
|
||||
## Context
|
||||
|
||||
The SEMENTENERGIAS opportunity had been corrected at fiscal-customer level, but the technical fiscal-association panel still displayed a stale email identity extraction where `pt` was treated as a company mention and an old PLANETOPTION suggestion was shown.
|
||||
|
||||
## Changes
|
||||
|
||||
- Applies current company-mention guardrails when reading already-stored `email_identity_extractions`.
|
||||
- Filters invalid company mentions such as `pt`, `com`, `www`, `http`, domains and TLDs even if they were saved before the guardrail existed.
|
||||
- Prevents conflict calculation when there is no valid explicit company mention.
|
||||
- Prevents internal customer suggestion display when the only mention is invalid/stale.
|
||||
- Fixes identity confidence display: `0.95` is now shown as `95%`, not `1%`.
|
||||
- Hides stale accepted suggestions that duplicate the currently linked fiscal customer without NIF.
|
||||
- Improves `scripts/cleanup_invalid_email_identity_suggestions.py`:
|
||||
- no longer writes to non-existent `resolution_note` column;
|
||||
- can reject invalid suggestions;
|
||||
- can rewrite stored email identity extractions with current filters.
|
||||
|
||||
## Recommended cleanup for affected opportunity
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python scripts/cleanup_invalid_email_identity_suggestions.py \
|
||||
--opportunity-id <OPPORTUNITY_ID> \
|
||||
--include-accepted \
|
||||
--fix-extractions \
|
||||
--apply
|
||||
```
|
||||
|
||||
Then restart the service and reload the opportunity detail.
|
||||
17
docs/CLIENTFLOW_V4926_7_NOTES.md
Normal file
17
docs/CLIENTFLOW_V4926_7_NOTES.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# ClientFlow v4926.7 — fiscal identity guardrails + duplicate NIF handling
|
||||
|
||||
## Fixes
|
||||
|
||||
- Rejects invalid company mentions extracted from email identity, including bare TLD/domain fragments such as `pt`, `com`, `www`, and full domains like `sementenergias.pt`.
|
||||
- Prevents short fragments such as `pt` from matching inside company names like `PLANETOPTION, LDA`.
|
||||
- Reduces false fiscal conflicts caused by invalid email identity mentions.
|
||||
- Adds friendly duplicate-NIF handling when editing customers. Editing a customer with a NIF that already belongs to another customer now returns a business conflict instead of a raw PostgreSQL unique violation.
|
||||
|
||||
## Scripts
|
||||
|
||||
- `scripts/cleanup_invalid_email_identity_suggestions.py` rejects pending/accepted email-identity suggestions created from invalid lookup values such as `pt`.
|
||||
- `scripts/inspect_customer_tax_id_conflict.py` lists customers and linked counts for a duplicate NIF conflict.
|
||||
|
||||
## Operator notes
|
||||
|
||||
For the SEMENTENERGIAS case, refresh email identity extraction after installing and reject old invalid suggestions created from `pt`.
|
||||
40
docs/CLIENTFLOW_V4926_8_1_NOTES.md
Normal file
40
docs/CLIENTFLOW_V4926_8_1_NOTES.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# ClientFlow v4926.8.1 — Jasmin candidate association + fiscal suggestion cleanup
|
||||
|
||||
## Objetivo
|
||||
|
||||
Corrige dois problemas observados no detalhe de oportunidade:
|
||||
|
||||
1. Quando já existe um orçamento Jasmin para o cliente, a oportunidade mostrava "Ainda sem documentos Jasmin" e não dava uma forma clara de associar o orçamento existente antes de criar novo.
|
||||
2. Sugestões fiscais antigas `accepted` podiam continuar visíveis com `NIF —`, mesmo quando o cliente fiscal atual já tinha NIF correto.
|
||||
|
||||
## Alterações
|
||||
|
||||
### Documentos Jasmin
|
||||
|
||||
- O painel `Documentos Jasmin` passa a procurar documentos Jasmin compatíveis em `reconciliation_items` por:
|
||||
- `customer_id` interno;
|
||||
- NIF fiscal;
|
||||
- NIF dentro do payload Jasmin;
|
||||
- email;
|
||||
- domínio no payload;
|
||||
- nome fiscal exato.
|
||||
- Se encontrar candidatos ainda não importados, mostra aviso: associar documento existente antes de criar novo.
|
||||
- Novo botão por candidato: `Associar e importar`.
|
||||
- Novo endpoint:
|
||||
- `POST /opportunities/{opportunity_id}/jasmin/link-candidate/{item_id}`
|
||||
- A ação liga o `reconciliation_item` à oportunidade e reaproveita o backfill Jasmin para importar:
|
||||
- `commercial_documents`;
|
||||
- `commercial_document_lines`;
|
||||
- `opportunity_items`;
|
||||
- `value_amount`;
|
||||
- `product_interest`.
|
||||
|
||||
### Sugestões fiscais
|
||||
|
||||
- `list_fiscal_suggestions_for_opportunity` passa a fazer `LEFT JOIN customers` para preencher `suggested_nif`/`suggested_name` com o cliente interno quando a sugestão antiga tem NIF vazio.
|
||||
- A UI deixa de mostrar sugestões `accepted` que apenas confirmam o cliente fiscal já ligado à oportunidade.
|
||||
- Isto remove ruído como `SEMENTENERGIAS accepted NIF —` quando a ficha fiscal atual já mostra `NIF 516356216`.
|
||||
|
||||
## Validação
|
||||
|
||||
- `python -m compileall -q app scripts`
|
||||
52
docs/CLIENTFLOW_V4926_8_2_NOTES.md
Normal file
52
docs/CLIENTFLOW_V4926_8_2_NOTES.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# ClientFlow v4926.8.2
|
||||
|
||||
## Objetivo
|
||||
|
||||
Evitar que uma oportunidade associe/importa um orçamento Jasmin antigo, fechado, convertido ou anulado quando existe um orçamento/proforma aberto mais recente para o mesmo cliente fiscal.
|
||||
|
||||
## Alterações
|
||||
|
||||
- Classificação de ciclo de vida dos documentos Jasmin a partir de `payload.record`:
|
||||
- `documentStatusDescription`
|
||||
- `documentStatus`
|
||||
- `documentLineStatusDescription`
|
||||
- `statusWasCompleted`
|
||||
- `isDeleted`
|
||||
- `isDraft`
|
||||
- Candidatos Jasmin agora são ordenados por:
|
||||
1. aberto/válido primeiro;
|
||||
2. quotation/proforma antes de invoice;
|
||||
3. data/série mais recente;
|
||||
4. score de match fiscal.
|
||||
- Documentos fechados/convertidos/anulados aparecem apenas como auditoria e o botão fica desativado.
|
||||
- `link_and_import_jasmin_candidate_async` rejeita importação de candidato não aberto/válido.
|
||||
- Novo botão na UI: **Sincronizar Jasmin** para buscar documentos recentes antes de escolher candidato.
|
||||
- Novo botão na UI: **Substituir atual**, para trocar um orçamento/proforma importado por engano por um candidato aberto mais recente.
|
||||
- Novo script de inspeção:
|
||||
- `scripts/inspect_jasmin_candidates_for_opportunity.py`
|
||||
- Novo script de reparação:
|
||||
- `scripts/replace_jasmin_document_for_opportunity.py`
|
||||
|
||||
## Operação segura
|
||||
|
||||
Para diagnosticar:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python scripts/inspect_jasmin_candidates_for_opportunity.py \
|
||||
--opportunity-id <OPPORTUNITY_ID> \
|
||||
--sync \
|
||||
--days 30
|
||||
```
|
||||
|
||||
Para substituir por um candidato válido:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python scripts/replace_jasmin_document_for_opportunity.py \
|
||||
--opportunity-id <OPPORTUNITY_ID> \
|
||||
--item-id <RECONCILIATION_ITEM_ID> \
|
||||
--dry-run
|
||||
|
||||
PYTHONPATH=. python scripts/replace_jasmin_document_for_opportunity.py \
|
||||
--opportunity-id <OPPORTUNITY_ID> \
|
||||
--item-id <RECONCILIATION_ITEM_ID>
|
||||
```
|
||||
44
docs/CLIENTFLOW_V4926_8_3_NOTES.md
Normal file
44
docs/CLIENTFLOW_V4926_8_3_NOTES.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# ClientFlow v4926.8.3 — Jasmin current document consistency
|
||||
|
||||
## Objetivo
|
||||
|
||||
Corrige inconsistências observadas em oportunidades que já têm orçamento Jasmin importado:
|
||||
|
||||
- não mostrar orçamentos antigos ainda abertos como candidatos principais quando já existe um orçamento Jasmin mais recente associado;
|
||||
- não sugerir `Criar orçamento Jasmin` no cockpit quando a oportunidade já tem `commercial_documents` Jasmin;
|
||||
- reduzir risco de duplicar orçamentos por engano.
|
||||
|
||||
## Regras novas
|
||||
|
||||
### Current document wins
|
||||
|
||||
Se a oportunidade já tem um documento Jasmin `quotation` ou `proforma`, candidatos encontrados em `reconciliation_items` com data/série menor ou igual ao documento atual são ocultados por defeito.
|
||||
|
||||
Candidatos mais recentes continuam a aparecer como substituição possível.
|
||||
|
||||
### Workflow awareness
|
||||
|
||||
O plano de ação passa a considerar também `commercial_documents`, não apenas `operation_links`:
|
||||
|
||||
- `commercial_documents.system='jasmin' AND document_kind='quotation'` conta como orçamento existente;
|
||||
- `document_kind='proforma'` conta como pró-forma;
|
||||
- `document_kind='invoice'` conta como fatura.
|
||||
|
||||
Assim o cockpit deixa de mostrar `Depois: Criar orçamento Jasmin` quando já existe orçamento importado.
|
||||
|
||||
### Botão criar orçamento
|
||||
|
||||
Quando já existe documento Jasmin, o botão principal deixa de ser `Criar orçamento` e passa para uma ação secundária:
|
||||
|
||||
`Novo orçamento adicional`
|
||||
|
||||
com confirmação explícita.
|
||||
|
||||
## Validação recomendada
|
||||
|
||||
Na oportunidade SEMENTENERGIAS:
|
||||
|
||||
- ORC.ORC2026.158 deve continuar importado;
|
||||
- ORC.ORC2026.137 não deve aparecer como candidato acionável por defeito;
|
||||
- cockpit deve deixar de mostrar `Depois: Criar orçamento Jasmin`;
|
||||
- botão de orçamento deve ser secundário e com confirmação se já existir documento.
|
||||
25
docs/CLIENTFLOW_V4926_8_4_NOTES.md
Normal file
25
docs/CLIENTFLOW_V4926_8_4_NOTES.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# ClientFlow v4926.8.4 — Opportunity evidence consistency polish
|
||||
|
||||
## Objetivo
|
||||
|
||||
Reduzir pequenas incoerências visuais no detalhe de oportunidade quando já existe documento Jasmin importado, produtos e tarefa de pagamento.
|
||||
|
||||
## Alterações
|
||||
|
||||
- Mostra aviso suave quando existe tarefa de confirmar pagamento, mas a evidência documental atual ainda é apenas orçamento Jasmin.
|
||||
- Mantém o processo desbloqueado; o aviso é informativo e não impede ação do operador.
|
||||
- Quando não existem mensagens indexadas mas a oportunidade tem `conversation_id`, a UI mostra que a conversa Chatwoot existe mas ainda não está ligada localmente, em vez de dizer simplesmente “sem comunicações”.
|
||||
- Quando não há eventos de timeline registados, a UI mostra eventos derivados de documentos/produtos atuais, evitando timeline vazia em oportunidades já importadas.
|
||||
- Normaliza status numérico de documentos Jasmin:
|
||||
- `1` passa a aparecer como `Aberto` em vez de `1`.
|
||||
- outros estados numéricos básicos ficam com labels legíveis.
|
||||
|
||||
## Impacto esperado
|
||||
|
||||
No caso SEMENTENERGIAS:
|
||||
|
||||
- `ORC.ORC2026.158` continua como documento atual.
|
||||
- Valor/produtos mantêm-se coerentes.
|
||||
- A task “Confirmar pagamento” continua válida, mas a UI avisa se ainda só existe orçamento.
|
||||
- Mensagens Chatwoot deixam de parecer inexistentes quando existe conversa #498.
|
||||
- Timeline deixa de ficar vazia quando há documento/produtos importados.
|
||||
83
docs/CLIENTFLOW_V4926_8_NOTES.md
Normal file
83
docs/CLIENTFLOW_V4926_8_NOTES.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# ClientFlow v4926.8 — UI maintenance actions
|
||||
|
||||
Esta versão consolida as correções v4926.6/v4926.7 e adiciona ações de manutenção diretamente na UI da oportunidade.
|
||||
|
||||
## Incluído
|
||||
|
||||
### 1. Reimportar detalhes Jasmin pela UI
|
||||
|
||||
No painel **Documentos Jasmin** foi adicionado o botão:
|
||||
|
||||
- `Reimportar detalhes`
|
||||
|
||||
A ação chama:
|
||||
|
||||
- `POST /opportunities/{opportunity_id}/jasmin/reimport-details`
|
||||
|
||||
E executa o mesmo motor validado pelo script de backfill:
|
||||
|
||||
- documentos comerciais
|
||||
- linhas do documento
|
||||
- produtos/linhas da oportunidade
|
||||
- valor total da oportunidade
|
||||
- product_interest
|
||||
|
||||
A importação é idempotente e atualiza linhas já existentes com preço zero.
|
||||
|
||||
### 2. Serviço reutilizável de backfill Jasmin
|
||||
|
||||
Novo módulo:
|
||||
|
||||
- `app/jasmin_backfill_service.py`
|
||||
|
||||
Função principal:
|
||||
|
||||
- `backfill_jasmin_opportunity_details_async(...)`
|
||||
|
||||
O objetivo é deixar de depender apenas do terminal para corrigir oportunidades antigas criadas a partir de reconciliação Jasmin.
|
||||
|
||||
### 3. Limpeza de identidade fiscal inválida pela UI
|
||||
|
||||
No bloco técnico de identidade extraída foi adicionado o botão:
|
||||
|
||||
- `Limpar identidade inválida`
|
||||
|
||||
A ação chama:
|
||||
|
||||
- `POST /opportunities/{opportunity_id}/email-identity/cleanup-invalid`
|
||||
|
||||
E remove/corrige estado antigo causado por tokens inválidos como:
|
||||
|
||||
- `pt`
|
||||
- `com`
|
||||
- `net`
|
||||
- `www`
|
||||
- domínios isolados
|
||||
|
||||
### 4. Serviço reutilizável de cleanup de identidade
|
||||
|
||||
Novo módulo:
|
||||
|
||||
- `app/email_identity_cleanup_service.py`
|
||||
|
||||
Função principal:
|
||||
|
||||
- `cleanup_invalid_email_identity_state(...)`
|
||||
|
||||
Rejeita sugestões inválidas e corrige `email_identity_extractions.company_mentions` quando já estavam guardadas antes dos filtros novos.
|
||||
|
||||
## Como testar
|
||||
|
||||
### Reimportar Jasmin pela UI
|
||||
|
||||
1. Abrir uma oportunidade com documento Jasmin.
|
||||
2. Ir ao painel **Documentos Jasmin**.
|
||||
3. Clicar **Reimportar detalhes**.
|
||||
4. Confirmar que documentos, produtos e valor ficam preenchidos.
|
||||
|
||||
### Limpar identidade fiscal antiga
|
||||
|
||||
1. Abrir uma oportunidade com identidade antiga inválida.
|
||||
2. Ir ao painel técnico.
|
||||
3. Clicar **Limpar identidade inválida**.
|
||||
4. Confirmar que tokens como `pt` deixam de aparecer como empresa mencionada.
|
||||
21
docs/CLIENTFLOW_V4927_1_HOTFIX.md
Normal file
21
docs/CLIENTFLOW_V4927_1_HOTFIX.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# ClientFlow v4927.1 — Hotfix arranque produção
|
||||
|
||||
## Problema
|
||||
|
||||
Depois da v4927, o nginx podia devolver `502 Bad Gateway` porque a aplicação falhava no arranque durante `init_db()`.
|
||||
|
||||
A causa provável era a criação do índice único `ux_commercial_documents_primary_role` em bases reais já existentes. Quando as colunas `role` e `is_primary` eram adicionadas com `DEFAULT 'current'` e `DEFAULT TRUE`, documentos antigos do mesmo tipo ficavam todos marcados como primários. Se existissem vários orçamentos/faturas da mesma oportunidade, a criação do índice único falhava e o processo uvicorn/gunicorn terminava.
|
||||
|
||||
## Correção
|
||||
|
||||
Antes de criar o índice único, o schema faz uma normalização defensiva:
|
||||
|
||||
- mantém apenas o documento mais recente como `is_primary = TRUE` por oportunidade/sistema/tipo/role;
|
||||
- move os restantes para `role = 'historical'`;
|
||||
- marca os restantes como `is_primary = FALSE` e `is_active = FALSE`.
|
||||
|
||||
## Validação
|
||||
|
||||
- testes existentes continuam a passar;
|
||||
- a alteração é aditiva e segura para bases existentes;
|
||||
- reduz o risco de `502` no arranque após deploy.
|
||||
34
docs/CLIENTFLOW_V4927_2_HOTFIX.md
Normal file
34
docs/CLIENTFLOW_V4927_2_HOTFIX.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# ClientFlow v4927.2 — Hotfix arranque 502
|
||||
|
||||
## Problema
|
||||
|
||||
Em bases reais, a migração de `commercial_documents` podia encontrar vários documentos legados marcados implicitamente como `role='current'` e `is_primary=true` para a mesma oportunidade/sistema/tipo.
|
||||
|
||||
A versão v4927.1 tentava normalizar antes de criar o índice único, mas em produção continuou a existir duplicação suficiente para o PostgreSQL falhar em:
|
||||
|
||||
```sql
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_commercial_documents_primary_role
|
||||
```
|
||||
|
||||
Quando este erro acontece dentro de `init_db()`, a aplicação FastAPI/uvicorn não arranca e o nginx devolve `502 Bad Gateway`.
|
||||
|
||||
## Correção
|
||||
|
||||
A v4927.2 remove a criação do índice `UNIQUE` durante o arranque e substitui por índice não único:
|
||||
|
||||
```sql
|
||||
DROP INDEX IF EXISTS ux_commercial_documents_primary_role;
|
||||
CREATE INDEX IF NOT EXISTS idx_commercial_documents_primary_role ...;
|
||||
```
|
||||
|
||||
A regra de um documento principal por fase continua a ser aplicada pela normalização inicial e pelos serviços de importação/promoção de documentos, mas deixa de bloquear o arranque em bases com histórico inconsistente.
|
||||
|
||||
## Validação
|
||||
|
||||
```text
|
||||
203 passed
|
||||
```
|
||||
|
||||
## Nota
|
||||
|
||||
O índice único pode ser reintroduzido numa versão futura apenas depois de existir um relatório/migração explícita para resolver todos os duplicados legados.
|
||||
30
docs/CLIENTFLOW_V4927_ROADMAP_STEP1.md
Normal file
30
docs/CLIENTFLOW_V4927_ROADMAP_STEP1.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# ClientFlow v4.9.27 — Documentos ligados e próxima ação
|
||||
|
||||
Esta versão implementa a primeira etapa do roadmap de refactor incremental.
|
||||
|
||||
## Incluído
|
||||
|
||||
- `commercial_documents` passa a suportar papel operacional do documento:
|
||||
- `role`: `current`, `accepted`, `historical`, `cancelled`, `related`
|
||||
- `is_primary`: identifica o documento principal por fase/tipo
|
||||
- Ao importar documentos Jasmin por reconciliação, documentos anteriores do mesmo tipo são despromovidos para histórico quando entra um novo documento principal.
|
||||
- Novo serviço central `app/opportunity_next_action_service.py` para calcular a próxima ação da oportunidade.
|
||||
- A página da oportunidade usa a próxima ação recomendada pelo serviço, sem remover o comportamento antigo.
|
||||
- Correções de consistência:
|
||||
- prioridade real das tarefas incluída na listagem e respeitada na UI;
|
||||
- sugestões fiscais inválidas (`pt`, `com`, `www`, etc.) deixam de aparecer mesmo quando pendentes;
|
||||
- confiança fiscal formatada corretamente para valores `0..1` e `0..100`;
|
||||
- label `proforma` apresentado como `Pró-forma`;
|
||||
- índice auxiliar em `operation_links` para external_id.
|
||||
|
||||
## Fora desta versão
|
||||
|
||||
- Reconciliação decisional completa (`linked`, `historical`, `ignored`, `created_opportunity`).
|
||||
- Redesign completo da página da oportunidade.
|
||||
- Painel operacional baseado integralmente em tasks.
|
||||
- Remoção/destruição de índices antigos de `operation_links`.
|
||||
|
||||
## Validação
|
||||
|
||||
- `python -m pytest -q`
|
||||
- Resultado: `203 passed`
|
||||
25
docs/CLIENTFLOW_V4928_1_1_SYNTAX_HOTFIX.md
Normal file
25
docs/CLIENTFLOW_V4928_1_1_SYNTAX_HOTFIX.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# ClientFlow v4928.1.1 — syntax hotfix
|
||||
|
||||
Correção de arranque para a v4928.1.
|
||||
|
||||
## Problema
|
||||
|
||||
O backend falhava no import de `app.admin_dashboard` com:
|
||||
|
||||
```text
|
||||
SyntaxError: f-string expression part cannot include a backslash
|
||||
```
|
||||
|
||||
Causa: HTML opcional construído dentro de uma expressão de f-string usando escapes com `\`.
|
||||
|
||||
## Correção
|
||||
|
||||
O HTML opcional do `external_id` do documento comercial foi extraído para a variável `external_id_html` antes da f-string principal.
|
||||
|
||||
## Validação
|
||||
|
||||
```text
|
||||
python -m compileall -q app tests
|
||||
pytest -q
|
||||
207 passed
|
||||
```
|
||||
28
docs/CLIENTFLOW_V4928_1_2_UI_COHERENCE.md
Normal file
28
docs/CLIENTFLOW_V4928_1_2_UI_COHERENCE.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# ClientFlow v4928.1.2 — UI coherence freeze candidate
|
||||
|
||||
Correção curta antes do congelamento do sistema.
|
||||
|
||||
## Objetivo
|
||||
|
||||
Reduzir incoerências visíveis no audit GET, sem alterar fluxos de negócio nem introduzir constraints fortes na base de dados.
|
||||
|
||||
## Correções incluídas
|
||||
|
||||
- A oportunidade passa a ter apenas uma secção explícita de "Próxima ação" no topo.
|
||||
- O cockpit antigo passa a ser apresentado como "Fluxo operacional", evitando duas decisões principais concorrentes.
|
||||
- Se o cockpit detetar fatura Jasmin existente, impede a apresentação enganadora de "Criar orçamento Jasmin" nesse bloco.
|
||||
- Quando já existe documento emitido/ligado, bloqueios fiscais passam a ser apresentados como avisos para revisão administrativa/próximos documentos.
|
||||
- O resumo da oportunidade usa a label "Valor principal" quando o valor vem de documento principal.
|
||||
- Documentos Jasmin ignorados deixam de expor NIFs de outros clientes na zona de auditoria da oportunidade.
|
||||
- O seletor de produtos deixa de mostrar preços por defeito para reduzir ruído monetário no detalhe.
|
||||
- Conteúdo de tarefas proveniente de emails técnicos/postmaster deixa de expor marcadores como `Exception:` no HTML, evitando falsos positivos no audit.
|
||||
|
||||
## Validação
|
||||
|
||||
- `python3 -m compileall -q app`
|
||||
- `pytest -q`
|
||||
- Resultado local: 207 testes passaram.
|
||||
|
||||
## Nota
|
||||
|
||||
Esta versão não altera autenticação, nginx, tokens, schema crítico nem reconciliação destrutiva.
|
||||
30
docs/CLIENTFLOW_V4928_1_3_UI_FREEZE_FIXES.md
Normal file
30
docs/CLIENTFLOW_V4928_1_3_UI_FREEZE_FIXES.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# ClientFlow v4928.1.3 — UI freeze fixes
|
||||
|
||||
Correções finais antes do freeze:
|
||||
|
||||
- Sanitiza marcadores técnicos também no partial HTMX de detalhe de task (`/tasks/{id}/partials/detail`).
|
||||
- Remove a frase operacional antiga `Documento deve aguardar correção destes dados` da UI de oportunidade.
|
||||
- Mantém a mensagem de prontidão como revisão administrativa quando faltam dados antes de novo documento.
|
||||
- Sem alterações destrutivas de schema ou migração obrigatória.
|
||||
|
||||
Validação local:
|
||||
|
||||
```bash
|
||||
python3 -m compileall -q app tests
|
||||
pytest -q
|
||||
# 207 passed
|
||||
```
|
||||
|
||||
Audit recomendado:
|
||||
|
||||
```bash
|
||||
python3 clientflow_get_audit_v5_deep.py \
|
||||
--base https://clientflow.blif.pt \
|
||||
--basic-user "$CLIENTFLOW_BASIC_USER" \
|
||||
--basic-pass "$CLIENTFLOW_BASIC_PASS" \
|
||||
--depth deep \
|
||||
--limit-opportunities 25 \
|
||||
--limit-tasks 50 \
|
||||
--save-html \
|
||||
--timeout 5
|
||||
```
|
||||
33
docs/CLIENTFLOW_V4928_1_LEGACY_COHERENCE.md
Normal file
33
docs/CLIENTFLOW_V4928_1_LEGACY_COHERENCE.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# ClientFlow v4928.1 — Correção de coerência para registos antigos
|
||||
|
||||
## Objetivo
|
||||
|
||||
Esta versão estabiliza a v4928 antes do freeze, focando apenas incoerências observadas em oportunidades antigas/reconstruídas.
|
||||
|
||||
## Correções incluídas
|
||||
|
||||
- Próxima ação deixa de sugerir criar orçamento quando já existe fatura Jasmin ligada.
|
||||
- `workflow_guard` passa a considerar fatura existente antes de propor orçamento.
|
||||
- Oportunidades com fatura e sem pagamento confirmado passam a sugerir aguardar/confirmar pagamento.
|
||||
- Migração leve no arranque: quando existe fatura principal, orçamentos/pró-formas anteriores da mesma oportunidade passam a histórico.
|
||||
- Oportunidades com fatura e sem task pendente são marcadas em `metadata.clientflow_record_mode = reconstructed_invoice_review`, sem alterar fase nem fechar processo.
|
||||
- Valor do resumo usa o documento principal quando existe, em vez de somar linhas históricas/entregues.
|
||||
- Produtos entregues/históricos deixam de contaminar o total operacional e ficam em detalhe recolhido.
|
||||
- Candidatos Jasmin de outro NIF deixam de aparecer na lista principal; ficam apenas contabilizados como ocultados.
|
||||
- Texto de documentos Jasmin passa a distinguir candidatos acionáveis, auditoria e ausência de candidatos.
|
||||
- UUID técnico do documento deixa de aparecer quando já existe número de documento legível.
|
||||
- Bloqueios fiscais após documento emitido passam a ser aviso para próximos documentos, não bloqueio de emissão.
|
||||
|
||||
## Segurança
|
||||
|
||||
A migração é não destrutiva:
|
||||
|
||||
- não apaga documentos;
|
||||
- não altera fases comerciais;
|
||||
- não cria constraints únicas fortes;
|
||||
- apenas ajusta `role`, `is_primary`, `is_active` de documentos já supersedidos por fatura;
|
||||
- adiciona metadados explicativos para UI.
|
||||
|
||||
## Validação
|
||||
|
||||
`207 passed`.
|
||||
101
docs/CLIENTFLOW_V4928_DECISIONAL_UI.md
Normal file
101
docs/CLIENTFLOW_V4928_DECISIONAL_UI.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# ClientFlow v4.9.28 — Reconciliação decisional e UI operacional
|
||||
|
||||
Esta versão constrói sobre a baseline estável v4.9.27.2 sem reintroduzir constraints únicas agressivas no arranque.
|
||||
|
||||
## Objetivo
|
||||
|
||||
Transformar a reconciliação e a oportunidade numa experiência orientada à decisão:
|
||||
|
||||
- oportunidade = processo de compra comercial;
|
||||
- documento Jasmin/Odoo = evidência/documento oficial ligado ao processo;
|
||||
- task = próxima ação humana;
|
||||
- reconciliação = decisão explícita sobre cada evidência.
|
||||
|
||||
## Alterações principais
|
||||
|
||||
### 1. Reconciliation decision service
|
||||
|
||||
Novo ficheiro:
|
||||
|
||||
```text
|
||||
app/reconciliation_decision_service.py
|
||||
```
|
||||
|
||||
Classifica itens e processos reconstruídos em buckets operacionais:
|
||||
|
||||
```text
|
||||
actionable -> ação recomendada
|
||||
review -> requer revisão
|
||||
historical -> histórico
|
||||
ignored -> ignorado
|
||||
resolved -> resolvido
|
||||
```
|
||||
|
||||
A classificação é read-only e não cria constraints na base de dados.
|
||||
|
||||
### 2. Reconciliação com decisão explícita
|
||||
|
||||
A página `/reconciliation` passa a mostrar um resumo decisional:
|
||||
|
||||
```text
|
||||
Ação recomendada
|
||||
Requer revisão
|
||||
Histórico
|
||||
Ignorados
|
||||
Resolvidos
|
||||
```
|
||||
|
||||
Cada item passa a mostrar também a decisão principal sugerida, por exemplo:
|
||||
|
||||
```text
|
||||
Ligar à oportunidade sugerida
|
||||
Criar ou ligar oportunidade
|
||||
Rever manualmente
|
||||
Sem ação operacional
|
||||
```
|
||||
|
||||
Foram adicionadas ações explícitas:
|
||||
|
||||
```text
|
||||
/reconciliation/{item_id}/needs-review
|
||||
/reconciliation/processes/ignore
|
||||
```
|
||||
|
||||
### 3. UI da oportunidade orientada à operação
|
||||
|
||||
A página da oportunidade passa a ter um bloco “Mapa operacional” com:
|
||||
|
||||
```text
|
||||
Cliente fiscal
|
||||
Documento principal
|
||||
Tasks
|
||||
Decisão seguinte
|
||||
```
|
||||
|
||||
As ações menos frequentes ficam em “Ações avançadas”.
|
||||
|
||||
### 4. Documentos Jasmin mais seguros
|
||||
|
||||
Quando a oportunidade já tem documento Jasmin atual, a UI deixa de apresentar “Associar e importar” como ação equivalente. Passa a mostrar “Substituir atual”, evitando duplicados acidentais.
|
||||
|
||||
O botão “Converter em fatura” só fica ativo se existir orçamento/pró-forma elegível.
|
||||
|
||||
### 5. Tasks com contexto reforçado
|
||||
|
||||
A task detail passa a repetir no painel de próxima ação os chips de fila, estado e prioridade para reforçar o contexto operacional.
|
||||
|
||||
## Segurança de deploy
|
||||
|
||||
Esta versão mantém a regra da v4.9.27.2:
|
||||
|
||||
```text
|
||||
Não criar índice único forte no arranque sobre dados legados.
|
||||
```
|
||||
|
||||
A consistência de “documento principal” é tratada pela aplicação e pela UI, não por constraint nova que possa bloquear o arranque.
|
||||
|
||||
## Validação
|
||||
|
||||
```text
|
||||
203 passed
|
||||
```
|
||||
130
docs/CLIENTFLOW_V492_EXTERNAL_API_SYNC.md
Normal file
130
docs/CLIENTFLOW_V492_EXTERNAL_API_SYNC.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# ClientFlow v4.9.2 — External API Sync for Reconciliation
|
||||
|
||||
Esta versão liga a área de Reconciliação às APIs externas, de forma conservadora.
|
||||
|
||||
## Objetivo
|
||||
|
||||
Preparar informação solta que existe fora do ClientFlow para ser analisada pelo operador:
|
||||
|
||||
- documentos Jasmin sem oportunidade;
|
||||
- faturas/pro-formas/orçamentos criados manualmente;
|
||||
- vendas/encomendas Odoo sem oportunidade;
|
||||
- envios Packlink sem oportunidade;
|
||||
- comprovativos e pedidos externos já suportados na v4.9.1.
|
||||
|
||||
## Regra de segurança
|
||||
|
||||
A sincronização externa **não cria oportunidades automaticamente** e **não confirma pagamentos**.
|
||||
|
||||
Fluxo:
|
||||
|
||||
```text
|
||||
API externa
|
||||
↓
|
||||
item de reconciliação
|
||||
↓
|
||||
operador decide:
|
||||
- ligar a oportunidade existente
|
||||
- criar oportunidade
|
||||
- ignorar
|
||||
```
|
||||
|
||||
## Novos componentes
|
||||
|
||||
```text
|
||||
app/external_reconciliation_sync.py
|
||||
scripts/sync_external_reconciliation.py
|
||||
```
|
||||
|
||||
A página `/reconciliation` recebeu botões para:
|
||||
|
||||
```text
|
||||
Sincronizar Jasmin
|
||||
Sincronizar Odoo
|
||||
Sincronizar Packlink
|
||||
Sincronizar APIs externas
|
||||
```
|
||||
|
||||
## Script
|
||||
|
||||
```bash
|
||||
cd /mnt/ssd/home/plx/clientflow_backend
|
||||
source .venv/bin/activate 2>/dev/null || true
|
||||
|
||||
PYTHONPATH=. python scripts/sync_external_reconciliation.py --all
|
||||
PYTHONPATH=. python scripts/sync_external_reconciliation.py --jasmin --limit 100
|
||||
PYTHONPATH=. python scripts/sync_external_reconciliation.py --odoo --days 90
|
||||
PYTHONPATH=. python scripts/sync_external_reconciliation.py --packlink
|
||||
```
|
||||
|
||||
## Configuração
|
||||
|
||||
A sincronização respeita os flags existentes:
|
||||
|
||||
```env
|
||||
JASMIN_ENABLED=true
|
||||
ODOO_ENABLED=true
|
||||
PACKLINK_ENABLED=true
|
||||
```
|
||||
|
||||
Se um sistema estiver desativado, o script reporta `skipped` e não falha.
|
||||
|
||||
## Frequência sugerida
|
||||
|
||||
```text
|
||||
Jasmin documentos 15 min
|
||||
Odoo vendas 15-30 min
|
||||
Packlink envios 30 min
|
||||
Reconciliação completa 1 vez por dia
|
||||
```
|
||||
|
||||
Exemplo systemd timer para execução geral:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=ClientFlow external reconciliation sync
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
WorkingDirectory=/mnt/ssd/home/plx/clientflow_backend
|
||||
Environment=PYTHONPATH=.
|
||||
ExecStart=/mnt/ssd/home/plx/clientflow_backend/.venv/bin/python scripts/sync_external_reconciliation.py --all
|
||||
```
|
||||
|
||||
Timer:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Run ClientFlow external reconciliation sync every 15 minutes
|
||||
|
||||
[Timer]
|
||||
OnBootSec=2min
|
||||
OnUnitActiveSec=15min
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
```
|
||||
|
||||
## O que não faz
|
||||
|
||||
```text
|
||||
Não apaga documentos.
|
||||
Não substitui orçamentos.
|
||||
Não emite faturas.
|
||||
Não confirma pagamentos.
|
||||
Não fecha oportunidades.
|
||||
Não cria oportunidades automaticamente.
|
||||
```
|
||||
|
||||
## Validação
|
||||
|
||||
Depois do deploy:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m compileall app scripts tests
|
||||
PYTHONPATH=. pytest -q
|
||||
PYTHONPATH=. python scripts/sync_external_reconciliation.py --all
|
||||
```
|
||||
|
||||
Depois abrir `/reconciliation` e confirmar os candidatos.
|
||||
39
docs/CLIENTFLOW_V493_JASMIN_RECENT_SYNC.md
Normal file
39
docs/CLIENTFLOW_V493_JASMIN_RECENT_SYNC.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# ClientFlow v4.9.3 — Jasmin Recent Sync Guardrails
|
||||
|
||||
Esta versão corrige o comportamento da sincronização Jasmin para reconciliação.
|
||||
|
||||
## Problema corrigido
|
||||
|
||||
Na v4.9.2, o comando:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python scripts/sync_external_reconciliation.py --jasmin --limit 2 --days 1
|
||||
```
|
||||
|
||||
aceitava `--days`, mas a sincronização Jasmin não aplicava essa janela temporal. Também usava `limit` por família documental, o que podia devolver 2 orçamentos + 2 faturas.
|
||||
|
||||
Resultado: documentos antigos de 2023/2025 apareciam como pendências abertas.
|
||||
|
||||
## Comportamento novo
|
||||
|
||||
- `--days` é aplicado ao sync Jasmin.
|
||||
- A API Jasmin é chamada com `$orderby=documentDate desc` e `$filter=documentDate ge <data>` quando possível.
|
||||
- Mesmo que o tenant/API não aceite o filtro OData, o ClientFlow aplica filtro local por `document_date`.
|
||||
- `--limit` passa a ser limite global para Jasmin, depois de juntar orçamentos e faturas recentes.
|
||||
- A página Reconciliação sincroniza Jasmin por defeito para os últimos 30 dias.
|
||||
|
||||
## Limpeza de itens antigos já criados
|
||||
|
||||
A v4.9.2 pode já ter criado candidatos antigos. Usar primeiro dry-run:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python scripts/cleanup_stale_reconciliation_items.py --jasmin --days 30
|
||||
```
|
||||
|
||||
Se a lista estiver correta:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python scripts/cleanup_stale_reconciliation_items.py --jasmin --days 30 --apply
|
||||
```
|
||||
|
||||
Isto marca os candidatos antigos como `ignored`. Não apaga documentos, oportunidades, mensagens ou registos externos.
|
||||
45
docs/CLIENTFLOW_V494_RECONCILIATION_RECENT_WINDOW.md
Normal file
45
docs/CLIENTFLOW_V494_RECONCILIATION_RECENT_WINDOW.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# ClientFlow v4.9.4 — Reconciliation Recent Window Cleanup
|
||||
|
||||
## Objetivo
|
||||
|
||||
A reconciliação deve ser uma fila curta de trabalho operacional, não uma importação histórica de anos anteriores.
|
||||
|
||||
Esta versão muda o comportamento por defeito para trabalhar apenas com os últimos 3 dias e adiciona uma ação de limpeza para retirar da fila aberta candidatos antigos já sincronizados.
|
||||
|
||||
## Alterações
|
||||
|
||||
- `sync_external_reconciliation.py` passa a usar `--days 3` por defeito.
|
||||
- O botão "Sincronizar APIs externas" usa os últimos 3 dias.
|
||||
- Jasmin, Odoo e Packlink passam a respeitar a janela recente de 3 dias por defeito.
|
||||
- A página `/reconciliation` mostra uma ação "Limpar fora dos 3 dias".
|
||||
- O script `cleanup_stale_reconciliation_items.py` passa a usar 3 dias por defeito e pode limpar todas as fontes, ou apenas uma fonte.
|
||||
|
||||
## Comandos úteis
|
||||
|
||||
Dry-run:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python scripts/cleanup_stale_reconciliation_items.py --days 3
|
||||
```
|
||||
|
||||
Aplicar:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python scripts/cleanup_stale_reconciliation_items.py --days 3 --apply
|
||||
```
|
||||
|
||||
Limpar só Odoo:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python scripts/cleanup_stale_reconciliation_items.py --source odoo --days 3 --apply
|
||||
```
|
||||
|
||||
Sincronizar apenas últimos 3 dias:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python scripts/sync_external_reconciliation.py --all --days 3 --limit 50
|
||||
```
|
||||
|
||||
## Segurança
|
||||
|
||||
A limpeza não apaga documentos no Jasmin, vendas no Odoo, envios Packlink ou oportunidades ClientFlow. Apenas marca candidatos antigos de reconciliação como `ignored` para não aparecerem na fila aberta.
|
||||
42
docs/CLIENTFLOW_V495_RECONCILIATION_OPERATION_SUGGESTIONS.md
Normal file
42
docs/CLIENTFLOW_V495_RECONCILIATION_OPERATION_SUGGESTIONS.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# ClientFlow v4.9.5 — Reconciliation Operation Suggestions
|
||||
|
||||
Esta versão melhora a página de Reconciliação para reduzir trabalho manual ao ligar documentos/vendas externas a processos já abertos no ClientFlow.
|
||||
|
||||
## Objetivo
|
||||
|
||||
Quando um item externo entra na Reconciliação, por exemplo um orçamento Jasmin ou uma venda Odoo, o sistema passa a procurar oportunidades/operações abertas que possam corresponder ao mesmo cliente, email, nome ou valor aproximado.
|
||||
|
||||
O sistema **não liga automaticamente**. Apenas sugere uma ligação pré-preenchida para o operador confirmar.
|
||||
|
||||
## O que muda
|
||||
|
||||
- Itens de reconciliação mostram uma sugestão quando existe operação/oportunidade aberta compatível.
|
||||
- A sugestão aparece com o botão `Ligar a esta operação`.
|
||||
- O campo manual `Outro ID oportunidade` continua disponível para casos em que a sugestão não é correta.
|
||||
- `Criar oportunidade` passa a ser usado quando não existe operação aberta correspondente.
|
||||
|
||||
## Segurança operacional
|
||||
|
||||
O modelo continua conservador:
|
||||
|
||||
- Não cria oportunidade automaticamente.
|
||||
- Não confirma pagamentos.
|
||||
- Não emite documentos.
|
||||
- Não liga documentos sem confirmação do operador.
|
||||
|
||||
## Sinais de correspondência
|
||||
|
||||
A sugestão usa sinais como:
|
||||
|
||||
- cliente fiscal ligado;
|
||||
- email exato;
|
||||
- nome de cliente/contacto;
|
||||
- valor aproximado;
|
||||
- existência de task pendente em Operations.
|
||||
|
||||
## Fluxo esperado
|
||||
|
||||
1. Sync externo cria item de reconciliação.
|
||||
2. Reconciliação procura operações abertas compatíveis.
|
||||
3. UI mostra sugestão pré-preenchida.
|
||||
4. Operador confirma `Ligar a esta operação` ou escolhe outro ID/cria nova oportunidade.
|
||||
19
docs/CLIENTFLOW_V496_RECONCILIATION_NIF_SUGGESTIONS.md
Normal file
19
docs/CLIENTFLOW_V496_RECONCILIATION_NIF_SUGGESTIONS.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# ClientFlow v4.9.6 — Reconciliation NIF Match Suggestions
|
||||
|
||||
Esta versão ajusta as sugestões da Reconciliação para o caso operacional mais simples e fiável: quando um item externo traz NIF e existe uma oportunidade aberta com cliente fiscal com o mesmo NIF.
|
||||
|
||||
## Regra principal
|
||||
|
||||
NIF exato passa a ser sinal forte suficiente para sugerir ligação.
|
||||
|
||||
Valor, data, nome e email continuam úteis, mas apenas reforçam ou ordenam a sugestão. Não devem impedir a sugestão quando o NIF coincide.
|
||||
|
||||
## Segurança
|
||||
|
||||
A ligação continua manual:
|
||||
|
||||
- o sistema sugere;
|
||||
- o operador confirma;
|
||||
- só depois o item é ligado à oportunidade.
|
||||
|
||||
A versão não cria oportunidades automaticamente, não confirma pagamentos, não emite documentos e não altera documentos externos.
|
||||
31
docs/CLIENTFLOW_V497_RECONCILIATION_PAGE_HOTFIX.md
Normal file
31
docs/CLIENTFLOW_V497_RECONCILIATION_PAGE_HOTFIX.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# ClientFlow v4.9.7 — Reconciliation Page Hotfix
|
||||
|
||||
Corrige falhas internas em `/reconciliation` quando a base de dados ainda não tem todas as colunas auxiliares usadas pelas sugestões automáticas.
|
||||
|
||||
## Alterações
|
||||
|
||||
- A página de Reconciliação passa a continuar a abrir mesmo que a pesquisa de sugestões falhe.
|
||||
- As sugestões por NIF ficam como melhoria opcional: se falharem, os itens continuam visíveis sem sugestões.
|
||||
- `ensure_reconciliation_schema()` adiciona guards aditivos para `customers.tax_id`, `customers.email` e `opportunities.local_customer_id` quando necessário.
|
||||
- Não há alteração destrutiva de dados.
|
||||
|
||||
## Validação
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m compileall app scripts tests
|
||||
PYTHONPATH=. pytest -q
|
||||
```
|
||||
|
||||
## Depois de instalar
|
||||
|
||||
Reiniciar a API e abrir:
|
||||
|
||||
```text
|
||||
/reconciliation
|
||||
```
|
||||
|
||||
Se a página abrir sem erro, reexecutar a sincronização recente:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python scripts/sync_external_reconciliation.py --all --days 3 --limit 50
|
||||
```
|
||||
@@ -0,0 +1,35 @@
|
||||
# ClientFlow v4.9.8 — Reconciliation Matching & Board Cleanup
|
||||
|
||||
## Objetivo
|
||||
|
||||
Melhorar a reconciliação para ser uma fila curta e útil, e limpar a board de oportunidades de ruído antigo.
|
||||
|
||||
## Alterações
|
||||
|
||||
- A janela "últimos 3 dias" passa a significar hoje + dois dias anteriores.
|
||||
- A página `/reconciliation` mostra por defeito apenas candidatos dentro da janela operacional recente.
|
||||
- A sincronização Odoo passa a enriquecer vendas com NIF, email e nome do `res.partner` quando disponível.
|
||||
- A ação manual "Outro ID oportunidade" deixa de ser a ação principal e passa para `Escolher outra oportunidade`.
|
||||
- A Reconciliação mostra primeiro sugestões e pesquisa de oportunidade.
|
||||
- A board de oportunidades passa a ocultar oportunidades de ruído técnico como `Mail Delivery Subsystem`, `postmaster`, `mailer-daemon`, bounces/NDR.
|
||||
- A coluna visual da board pode ser ajustada pela próxima ação pendente: fatura/pró-forma/pagamento aparecem em pagamento; encomenda/envio aparecem em operação/logística.
|
||||
|
||||
## O que não muda
|
||||
|
||||
- Não cria oportunidades automaticamente.
|
||||
- Não liga documentos automaticamente.
|
||||
- Não confirma pagamentos.
|
||||
- Não altera documentos Jasmin/Odoo/Packlink.
|
||||
- Não apaga dados externos.
|
||||
|
||||
## Comandos recomendados
|
||||
|
||||
```bash
|
||||
PYTHONPATH=. python -m compileall app scripts tests
|
||||
PYTHONPATH=. pytest -q
|
||||
PYTHONPATH=. python scripts/cleanup_stale_reconciliation_items.py --days 3 --apply
|
||||
PYTHONPATH=. python scripts/sync_external_reconciliation.py --jasmin --days 3 --limit 50
|
||||
PYTHONPATH=. python scripts/sync_external_reconciliation.py --odoo --days 3 --limit 50
|
||||
```
|
||||
|
||||
Evitar `--all` se Packlink ainda estiver com erro 401.
|
||||
33
docs/CLIENTFLOW_V499_PROCESS_TIMELINE_RECONSTRUCTION.md
Normal file
33
docs/CLIENTFLOW_V499_PROCESS_TIMELINE_RECONSTRUCTION.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# ClientFlow v4.9.9 — Process Timeline Reconstruction
|
||||
|
||||
Esta versão melhora a Reconciliação para deixar de tratar documentos/vendas/comprovativos como ligações soltas.
|
||||
|
||||
## Objetivo
|
||||
|
||||
Quando o ClientFlow encontra evidências externas, tenta agrupá-las por NIF, email ou nome e propõe um processo provável:
|
||||
|
||||
- orçamento Jasmin encontrado;
|
||||
- pró-forma/fatura Jasmin encontrada;
|
||||
- venda/encomenda Odoo encontrada;
|
||||
- comprovativo recebido;
|
||||
- envio Packlink encontrado.
|
||||
|
||||
A UI mostra uma timeline reconstruída e o operador decide se deve criar uma oportunidade reconstruída ou ligar todos os itens a uma oportunidade existente.
|
||||
|
||||
## Segurança
|
||||
|
||||
A versão não confirma pagamentos, não emite faturas, não fecha oportunidades e não altera documentos externos automaticamente.
|
||||
|
||||
## Fluxo
|
||||
|
||||
1. Sincronizar Jasmin/Odoo/Packlink.
|
||||
2. Abrir `/reconciliation`.
|
||||
3. Rever "Processos candidatos".
|
||||
4. Escolher uma ação:
|
||||
- ligar processo à sugestão;
|
||||
- ligar a outra oportunidade;
|
||||
- criar oportunidade reconstruída.
|
||||
|
||||
## Notas
|
||||
|
||||
Eventos reconstruídos ficam registados como `reconciliation_evidence_imported` e a criação como `opportunity_reconstructed_from_reconciliation`.
|
||||
135
docs/JASMIN_CLIENTFLOW.md
Normal file
135
docs/JASMIN_CLIENTFLOW.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# Integração Jasmin no ClientFlow
|
||||
|
||||
Esta versão adiciona a primeira integração Jasmin validada com testes reais em Jasmin 3.02.
|
||||
|
||||
## O que foi validado
|
||||
|
||||
- OAuth Client Credentials:
|
||||
- `https://identity.primaverabss.com/connect/token`
|
||||
- `grant_type=client_credentials`
|
||||
- `scope=application`
|
||||
- `GET /businessCore/productInfos/getVersions`
|
||||
- Clientes:
|
||||
- `GET /salesCore/customerParties/getCustomerByCompanyTaxId/{nif}`
|
||||
- `POST /salesCore/customerParties`
|
||||
- Produtos:
|
||||
- `GET /salesCore/salesItems/extension/odata?$top=50`
|
||||
- Orçamentos:
|
||||
- `POST /sales/quotations`
|
||||
- `documentType=ORC`
|
||||
- `serie=ORC2026`
|
||||
- Faturas:
|
||||
- `POST /billing/invoices/fromQuotation/{quotationId}` com body `{}`
|
||||
|
||||
## Regras importantes descobertas
|
||||
|
||||
1. O NIF deve ser pesquisado no Jasmin sem prefixo `PT`.
|
||||
2. O Jasmin rejeita `electronicMail` e `telephone` vazios; campos opcionais vazios são omitidos.
|
||||
3. OData tem limite de `$top=100`.
|
||||
4. Cliente novo pode usar `partyKey=CF{NIF}`.
|
||||
5. Converter orçamento em fatura exige `json={}`; sem body pode devolver `411 Length Required`.
|
||||
|
||||
## Novas tabelas
|
||||
|
||||
- `customers`
|
||||
- `commercial_documents`
|
||||
- `commercial_document_lines`
|
||||
- `shipments`
|
||||
|
||||
Estas tabelas permitem o pressuposto correto:
|
||||
|
||||
```text
|
||||
Cliente
|
||||
→ várias oportunidades
|
||||
→ vários orçamentos
|
||||
→ várias faturas
|
||||
```
|
||||
|
||||
## Fluxo na oportunidade
|
||||
|
||||
A página da oportunidade tem dois botões simples:
|
||||
|
||||
```text
|
||||
[Criar orçamento]
|
||||
[Converter em fatura]
|
||||
```
|
||||
|
||||
`Criar orçamento` faz internamente:
|
||||
|
||||
```text
|
||||
find_or_create_customer
|
||||
create_quotation
|
||||
```
|
||||
|
||||
`Converter em fatura` faz internamente:
|
||||
|
||||
```text
|
||||
pegar no orçamento ativo/mais recente
|
||||
POST /billing/invoices/fromQuotation/{quotationId} com {}
|
||||
grava a fatura ligada ao orçamento local
|
||||
```
|
||||
|
||||
## Variáveis `.env`
|
||||
|
||||
```env
|
||||
JASMIN_ENABLED=true
|
||||
JASMIN_OUTBOX_ENABLED=true
|
||||
JASMIN_BASE_URL=https://my.jasminsoftware.com
|
||||
JASMIN_PUBLIC_URL=https://my.jasminsoftware.com
|
||||
JASMIN_TOKEN_URL=https://identity.primaverabss.com/connect/token
|
||||
JASMIN_SCOPE=application
|
||||
JASMIN_ACCOUNT=...
|
||||
JASMIN_SUBSCRIPTION=...
|
||||
JASMIN_CLIENT_ID=...
|
||||
JASMIN_CLIENT_SECRET=...
|
||||
|
||||
JASMIN_COMPANY_KEY=CTULDA
|
||||
JASMIN_QUOTATION_TYPE=ORC
|
||||
JASMIN_QUOTATION_SERIE=ORC2026
|
||||
JASMIN_DEFAULT_PRICE_LIST=03
|
||||
JASMIN_DEFAULT_PAYMENT_METHOD=TRA
|
||||
JASMIN_DEFAULT_PAYMENT_TERM=00
|
||||
JASMIN_DEFAULT_DELIVERY_TERM=TRANSP
|
||||
JASMIN_DEFAULT_CURRENCY=EUR
|
||||
JASMIN_DEFAULT_COUNTRY=PT
|
||||
JASMIN_DEFAULT_CUSTOMER_GROUP=02
|
||||
JASMIN_DEFAULT_PARTY_TAX_SCHEMA=CONTINENTE
|
||||
JASMIN_DEFAULT_UNIT=UN
|
||||
JASMIN_DEFAULT_ITEM_TAX_SCHEMA=NORMAL
|
||||
JASMIN_DEFAULT_SALES_ITEM=CARREGADOR_MONO_7KW
|
||||
```
|
||||
|
||||
## Teste não destrutivo
|
||||
|
||||
```bash
|
||||
python scripts/test_jasmin_connection.py
|
||||
```
|
||||
|
||||
## Processar outbox Jasmin
|
||||
|
||||
Modo seguro:
|
||||
|
||||
```bash
|
||||
OUTBOX_TARGET_SYSTEM=jasmin JASMIN_OUTBOX_ENABLED=true OUTBOX_DRY_RUN=true python scripts/process_outbox.py
|
||||
```
|
||||
|
||||
Modo real:
|
||||
|
||||
```bash
|
||||
OUTBOX_TARGET_SYSTEM=jasmin JASMIN_OUTBOX_ENABLED=true OUTBOX_DRY_RUN=false python scripts/process_outbox.py
|
||||
```
|
||||
|
||||
## Dados necessários na oportunidade
|
||||
|
||||
Para criar cliente Jasmin novo, a oportunidade precisa de:
|
||||
|
||||
- nome do cliente
|
||||
- NIF em `metadata.customer_tax_id`, `metadata.nif` ou `metadata.customer.tax_id`
|
||||
- morada/código postal/cidade se disponível
|
||||
|
||||
Para criar orçamento, as linhas da oportunidade precisam de mapear para artigos Jasmin. O serviço usa por ordem:
|
||||
|
||||
1. `opportunity_items.metadata.jasmin_sales_item`
|
||||
2. `opportunity_items.metadata.jasmin_item_key`
|
||||
3. `opportunity_items.sku`
|
||||
4. `JASMIN_DEFAULT_SALES_ITEM`
|
||||
118
docs/PACKLINK_CLIENTFLOW.md
Normal file
118
docs/PACKLINK_CLIENTFLOW.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# Integração Packlink PRO no ClientFlow
|
||||
|
||||
## Estado validado
|
||||
|
||||
Foram validados estes pontos da API Packlink PRO:
|
||||
|
||||
- `GET /clients` com `Authorization: <PACKLINK_API_KEY>` funciona.
|
||||
- `GET /locations/postalcodes/PT/3650-219` funciona.
|
||||
- `GET /services` funciona para Portugal quando os códigos postais são normalizados para 4 dígitos na cotação, por exemplo `3650` → `4000`.
|
||||
- Serviço default validado:
|
||||
- `service_id=20571`
|
||||
- `carrier=Correos Express`
|
||||
- `service=Paq 24`
|
||||
- `departure_type=pick-up`
|
||||
- `destination_type=home`
|
||||
|
||||
## Configuração `.env`
|
||||
|
||||
```env
|
||||
PACKLINK_ENABLED=true
|
||||
PACKLINK_OUTBOX_ENABLED=true
|
||||
PACKLINK_BASE_URL=https://api.packlink.com/v1
|
||||
PACKLINK_PUBLIC_URL=https://pro.packlink.pt
|
||||
PACKLINK_API_KEY=...
|
||||
|
||||
PACKLINK_DEFAULT_SERVICE_ID=20571
|
||||
PACKLINK_DEFAULT_SERVICE=Paq 24
|
||||
PACKLINK_DEFAULT_CARRIER=Correos Express
|
||||
PACKLINK_SOURCE=PRO
|
||||
PACKLINK_PLATFORM=PRO
|
||||
PACKLINK_PLATFORM_COUNTRY=UN
|
||||
|
||||
PACKLINK_COLLECTION_TIME=09:00-14:00
|
||||
PACKLINK_COLLECTION_DAYS_AHEAD=1
|
||||
|
||||
PACKLINK_DEFAULT_PACKAGE_HEIGHT=10
|
||||
PACKLINK_DEFAULT_PACKAGE_WIDTH=20
|
||||
PACKLINK_DEFAULT_PACKAGE_LENGTH=30
|
||||
PACKLINK_DEFAULT_PACKAGE_WEIGHT=2
|
||||
|
||||
PACKLINK_SENDER_NAME=...
|
||||
PACKLINK_SENDER_SURNAME=.
|
||||
PACKLINK_SENDER_COMPANY=...
|
||||
PACKLINK_SENDER_STREET1=...
|
||||
PACKLINK_SENDER_STREET2=
|
||||
PACKLINK_SENDER_ZIP=3650-219
|
||||
PACKLINK_SENDER_CITY=Vila Nova de Paiva
|
||||
PACKLINK_SENDER_COUNTRY=PT
|
||||
PACKLINK_SENDER_PHONE=...
|
||||
PACKLINK_SENDER_EMAIL=...
|
||||
|
||||
PACKLINK_FALLBACK_PHONE=...
|
||||
PACKLINK_FALLBACK_EMAIL=...
|
||||
```
|
||||
|
||||
## Teste de ligação
|
||||
|
||||
```bash
|
||||
python scripts/test_packlink_connection.py
|
||||
```
|
||||
|
||||
## Processamento da outbox
|
||||
|
||||
Por segurança, o `process_outbox.py` corre em dry-run por defeito.
|
||||
|
||||
Teste sem criar envio real:
|
||||
|
||||
```bash
|
||||
OUTBOX_TARGET_SYSTEM=packlink \
|
||||
PACKLINK_OUTBOX_ENABLED=true \
|
||||
OUTBOX_DRY_RUN=true \
|
||||
python scripts/process_outbox.py
|
||||
```
|
||||
|
||||
Criação real de envio:
|
||||
|
||||
```bash
|
||||
OUTBOX_TARGET_SYSTEM=packlink \
|
||||
PACKLINK_OUTBOX_ENABLED=true \
|
||||
OUTBOX_DRY_RUN=false \
|
||||
python scripts/process_outbox.py
|
||||
```
|
||||
|
||||
## Dados necessários antes de criar envio
|
||||
|
||||
A oportunidade precisa de ter dados de entrega em `metadata.shipment`, `metadata.packlink` ou numa preparação de tarefa `prep_type=shipment`.
|
||||
|
||||
Campos aceites:
|
||||
|
||||
```json
|
||||
{
|
||||
"shipment": {
|
||||
"recipient_name": "Nome Cliente",
|
||||
"recipient_phone": "+351...",
|
||||
"recipient_email": "cliente@example.com",
|
||||
"delivery_address": "Rua Exemplo 1, 4000-001 Porto",
|
||||
"country": "PT"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Se a morada tiver código postal PT completo, o ClientFlow guarda a morada completa, mas normaliza para 4 dígitos apenas na cotação Packlink.
|
||||
|
||||
## Fluxo implementado
|
||||
|
||||
1. Operador clica em `Criar envio Packlink` na oportunidade.
|
||||
2. Se `PACKLINK_ENABLED=true` e não foi escrita referência manual, o ClientFlow cria um item `integration_outbox`:
|
||||
- `target_system=packlink`
|
||||
- `action_type=create_shipment`
|
||||
3. `scripts/process_outbox.py` processa o item.
|
||||
4. O Packlink devolve uma `reference`.
|
||||
5. O ClientFlow regista `operation_links` com `system=packlink`, `external_type=shipment`, `status=created`.
|
||||
|
||||
## Notas de segurança
|
||||
|
||||
- A API key não deve ser colocada no repositório.
|
||||
- O processamento real só deve correr com `OUTBOX_DRY_RUN=false` depois de confirmares o comportamento de pagamento/rascunho da tua conta Packlink PRO.
|
||||
- A criação de envio pode gerar custos na conta Packlink, dependendo da configuração de pagamento.
|
||||
21
docs/RENOMEAR_CCE_PARA_CLIENTFLOW.md
Normal file
21
docs/RENOMEAR_CCE_PARA_CLIENTFLOW.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Renomeação: CCE → ClientFlow
|
||||
|
||||
A partir desta fase, o nome do motor passa a ser:
|
||||
|
||||
```text
|
||||
ClientFlow
|
||||
```
|
||||
|
||||
O nome anterior CCE / Customer Context Engine fica apenas como referência histórica.
|
||||
|
||||
## Frase curta
|
||||
|
||||
ClientFlow é o motor de contexto e estado comercial que interpreta mensagens de clientes, normaliza o estado da conversa e alimenta Chatwoot, CRM e Mautic.
|
||||
|
||||
## Componentes
|
||||
|
||||
- ClientFlow Analyzer: chamada ao LLM.
|
||||
- ClientFlow Normalizer: regras de negócio.
|
||||
- ClientFlow State: estados finais.
|
||||
- ClientFlow API: endpoint `/analyze`.
|
||||
- ClientFlow Integrations: Chatwoot e Mautic.
|
||||
8
docs/env.odoo.example
Normal file
8
docs/env.odoo.example
Normal file
@@ -0,0 +1,8 @@
|
||||
# Odoo integration
|
||||
ODOO_ENABLED=true
|
||||
ODOO_BASE_URL=http://127.0.0.1:8069
|
||||
ODOO_PUBLIC_URL=https://odoo.blif.pt
|
||||
ODOO_DB=odoo19_prod
|
||||
ODOO_USERNAME=clientflow@blif.pt
|
||||
ODOO_API_KEY=coloca_aqui_a_api_key_do_utilizador_odoo
|
||||
ODOO_API_MODE=xmlrpc
|
||||
Reference in New Issue
Block a user