fix(bridge): v2-legacy-bridge v9 + Epic 020 V2 migration plan - #71
Open
UNIFY-MC wants to merge 15 commits into
Open
fix(bridge): v2-legacy-bridge v9 + Epic 020 V2 migration plan#71UNIFY-MC wants to merge 15 commits into
UNIFY-MC wants to merge 15 commits into
Conversation
… Epic 020
Unblocks Epic 020 (V2 legacy HTML → React migration) by repairing the V2→V1
bridge cron and delivering the complete planning documents for the 7-phase
migration approved by Mário in 2026-05-24 session.
## v2-legacy-bridge-cron v9 (operational)
Production cron `v2-legacy-bridge-cron` (jobid 26) had been failing since
2026-05-18 — never had a single successful run. Root causes (3 distinct bugs):
1. **Vault empty** — pg_cron command read URL/key from
`vault.decrypted_secrets`, which was empty → null URL → http_post
rejected with "null value in column url". Affected 5 crons total
(v2-legacy-bridge + 4 swarm crons). Mário populated `SUPABASE_URL` +
`SUPABASE_SERVICE_ROLE_KEY` secrets — fix automatically reactivated
Truth Engine (swarm-orchestrator already had 4 successful ticks).
2. **PostgrestBuilder is not a Promise (v6 bug)** — code used
`.insert(...).catch(() => {})` for graceful audit-log writes. In
supabase-js v2, `.insert()` returns a builder, not a Promise, so
`.catch is not a function` propagated as outer fatal. Replaced with
a `safeLog()` helper using try/await/catch.
3. **Wrong V2 schema (v7 bug)** — code read from
`v2.schema('v2_condominios').from('condominos')`, but V2 production
stores tables in `public` (the `v2_condominios` schema only exists
in V1 Core Hub as the mirror). Removed `.schema()` call.
4. **Wrong V2 column name (v8 bug)** — V2 `public.condominos` uses
`codpostal` (no underscore), not `codigo_postal`. Updated SELECT and
added remap `codpostal` → `codigo_postal` when upserting into
`core.pessoas` in V1.
**Smoke test v9 (2026-05-24 10:52 UTC):**
- HTTP 200 OK in 3.821ms
- 65 condóminos fetched from V2 production
- 60 updated in V1 `core.pessoas` via waterfall dedup (NIF/email/tel)
- 5 ambiguous → enqueued in `core.dedup_candidates` for manual review
- 0 errors
## V2 migration documentation (4 new docs)
- `docs/v2-migration/V2-LEGACY-AUDIT-2026-05-24.md` (280L) — initial
static audit identifying that `apps/v2-condominios/` (24 React views,
4256L) is the canonical substitute, already wired to V1 Core Hub.
- `docs/v2-migration/V2-INVENTORY-COMPLETE-2026-05-24.md` (480L) —
exhaustive feature matrix cross-referencing 4 sources: 14 Playwright
screenshots, test-v2.html (15.381L), apps/v2-condominios/, and V1+V2
schemas via Supabase MCP.
- `docs/v2-migration/V2-MIGRATION-PLAN.md` (541L) — 7-phase plan
(Fase 0 pre-flight → Fase 7 sunset) with scope/effort/risks/smoke
tests/rollback per phase + Gantt + Vercel deploy plan. Total: 75-100
dev-days, cutover possible 2027-02-02.
- `docs/v2-migration/V2-BRIDGE-INVESTIGATION-2026-05-24.md` (143L) —
OQ1 resolution report (cron broken since 2026-05-18, 5 crons
impacted, Truth Engine offline).
## Epic 020 + 8 Stories
Created in `docs/stories/epics/epic-020-v2-migration-2026-q3-2027/`:
- EPIC.md — 7-phase substitution of prataowners.pt legacy HTML by
React app, with zero downtime and zero data loss
- stories/020.0-pre-flight.md (3-5d)
- stories/020.1-foundations.md (8-12d) — auth + i18n + multi-cond + charts
- stories/020.2-admin-core.md (12-15d)
- stories/020.3-upload-xlsx.md (5-7d)
- stories/020.4-ocr-faturas.md (8-12d)
- stories/020.5-automacoes.md (12-15d)
- stories/020.6-portal-condomino.md (15-20d) — 64 real users
- stories/020.7-cutover-sunset.md (1d + 30d obs + 30d grace)
## Playwright discovery scripts (new)
- `scripts/v2-discovery/capture-prataowners.mjs` (137L) — public
pre-auth capture (6 URLs)
- `scripts/v2-discovery/deep-capture-prataowners.mjs` (269L) —
authenticated capture (staff BOSSMC), navigates 8 admin sidebar
items + 6 Prestação Contas tabs + 1 condómino drill-down.
Output 20 PNGs in `docs/v2-migration/screenshots/internal/`
(gitignored — may contain PII).
## Playwright devDeps (root monorepo)
- `playwright@^1.60.0`, `@playwright/test@^1.60.0`
- Chromium browser installed via `pnpm exec playwright install chromium`
(~112 MiB Chrome Headless Shell, one-time)
## .gitignore additions
- `docs/v2-migration/screenshots/internal/` — authenticated screenshots
may include real condómino names/NIFs (PII)
## Refs
- Epic 020 + V2-MIGRATION-PLAN.md
- DB-011 (PR #64 swarm-worker-jina v9) now actually invokable since
swarm-orchestrator cron repaired (was failing 1834×/7d before vault fix)
- ADR-018 Phase 2 (PR #68) — Hermes integration not blocked by this fix
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…via v_mora_actual
Extension do bridge V2 produção → V1 espelho de 1 domínio (só condominos) para
6 domínios completos, + função SQL de matching pagamento→fração via
descricao_banco/nome_condomino, + Mora.jsx React aponta a view calculada
v_mora_actual em vez de v2_condominios.recebimentos (que tem 1254 rows
mixed-semantic dos 4 importadores ad-hoc de 2026-05-16).
Desbloqueia workflow operacional de emissão de avisos em V1 (objectivo:
Mário não duplica trabalho fazendo via prataowners.pt + recriar em V1).
## Migrations aplicadas em V1 Core Hub
- `202605241200_v2_bridge_extended_tables.sql` (223 linhas)
- CREATE TABLE v2_condominios.utilizadores_portal (era MISSING, 64 rows V2)
- CREATE TABLE v2_condominios.pagamentos (NOVA — espelho 1:1 de
V2.public.recebimentos = pagamentos efectuados; NÃO mistura com
v2_condominios.recebimentos que está poluída)
- ALTER extrato_bancario ADD cols missing vs V2 (alocacao, sem_fatura, etc)
- ALTER orcamento_por_fracao ADD fracao_id + UNIQUE (ano, fracao_codigo)
- CREATE VIEW v_mora_actual (calcula mora correctamente: orcamento_por_fracao
2026 quotas - pagamentos efectuados, por fracção+mês)
- GRANTS para anon/authenticated/service_role
- `202605241300_v2_bridge_fix_extrato_configuracoes.sql` (23 linhas)
- Fix: extrato_bancario.edificio_id NOT NULL bloqueava UPSERT (V2 não tem
essa col) → DROP NOT NULL
- Fix: configuracoes faltava GRANT para service_role (permission denied)
- ADD imported_at + source cols (padrão bridge tables)
- `202605241400_v2_payment_matcher_function.sql` (164 linhas)
- CREATE FUNCTION v2_condominios.fn_match_pagamentos_v1(after_date, threshold)
- Workflow: para cada pagamento sem fracao_id → regex extrai nome de
descricao_banco (pattern "TRF SEPA+ INST N DE <NOME>") ou usa
nome_condomino → fuzzy match (pg_trgm similarity) em core.pessoas →
lookup fração via v2_condominios.condominos JOIN → UPDATE com nota auditoria
- Detecta ambiguidade (pessoa com várias fracções → flag em notas)
## Edge Function v2-legacy-bridge-cron v10
- Refactor v9→v10: extension de 1 para 6 domínios (condominos, utilizadores_portal,
extrato_bancario, pagamentos, orcamento_por_fracao, configuracoes)
- Cada domínio é função separada (syncCondominos, syncUtilizadoresPortal, etc),
erros isolados (1 domínio com erro não bloqueia outros)
- Parâmetro opcional `only_domain` no body para debug
- safeLog() wrapper preservado de v9 (PostgrestBuilder ≠ Promise bug)
## apps/v2-condominios/src/views/Mora.jsx
- Mudança fonte de dados:
- ANTES: `v2Client.from('recebimentos').in('estado', ['pendente','mora','acordo'])`
(tabela poluída — 638 rows com estado label errado sub-contavam mora)
- AGORA: `v2Client.from('v_mora_actual')` (view calculada SQL: quotas esperadas
- pagamentos efectuados; matcher popula fracao_id em pagamentos)
- enriched() simplificado: view já devolve divida + dias_atraso + nivel_mora
- dispararAviso() workflow preservado (continua a inserir em system.inbox_items
para agente financeiro-condo / compliance-condo processar)
## Smoke tests (todos passing 2026-05-24)
Bridge v10 full run:
✅ 2028 rows fetched, 2028 upserted, 0 errors, 5.3s
- condominos: 65/65 (waterfall dedup → core.pessoas)
- utilizadores_portal: 64/64 (era MISSING — 64 portal users incl. staff BOSSMC)
- extrato_bancario: 1000/1000 (limit 1000, V2 tem 1056 — fix em v11)
- pagamentos: 593/593 (tabela NOVA limpa)
- orcamento_por_fracao: 294/294
- configuracoes: 12/12
Payment matcher (2 passadas):
✅ Threshold 0.55: 45 unique + 1 ambiguous (69 input)
✅ Threshold 0.35: +14 unique + 4 ambiguous (23 restantes)
📊 TOTAL: 64/69 = 93% matched, 5 no-match residuais
v_mora_actual após matcher:
- aviso_2 desceu de 98 para 57 fracções (41 detectaram pagamento Fev)
- compliance: 88 fracções, 13.278€
- legal: 87 fracções, 13.202€
- aviso_1: 98 fracções (Janeiro ainda em mora — esperado)
- a_vencer: 98 fracções (Maio em diante, sem vencimento ainda)
## Acções Mário pós-merge
1. Cron `v2-legacy-bridge-cron` continua a correr 03:00 UTC diariamente
(agora cobre 6 domínios em vez de 1)
2. Pós cada bridge run, correr `SELECT * FROM v2_condominios.fn_match_pagamentos_v1();`
para popular pagamentos.fracao_id novos (TODO: automatizar no fim do bridge EF)
3. Abrir http://localhost:5172/divida-actual-2026 (V2 React) — Mora.jsx
mostra agora lista de mora correcta da view v_mora_actual
4. Validar lista antes de emitir avisos (5 no-match residuais podem precisar
manual matching — fundos/empresas com nomes diferentes em core.pessoas)
## Refs
- memory: project-v2-migration-canonical, project-v2-data-state-2026-05-24,
project-v2-import-artifacts-2026-05-16, project-v2-payment-fraction-matching,
project-avisos-mora-workflow
- PR #71 (bridge v9 baseline) — este é commit adicional ao mesmo branch
- Epic 020 V2 migration: docs/stories/epics/epic-020-v2-migration-2026-q3-2027/
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… + PDF via drive_url
Bug reportado por Mário 2026-05-24: ao expandir uma rubrica em Prestação de
Contas (e.g. "2A002 Água"), aparecia "Sem movimentos no extrato bancário
associados a 2A002" mesmo havendo 16 movimentos com 8 PDFs em V1 espelho.
## Root cause
LinhaOrcReal e PdfLink no PrestacaoContas.jsx tinham 2 problemas:
1. **Filtro defeituoso**: `desc.includes(rubrica.codigo)` nunca dava match
porque descrições bancárias (e.g. "COBR SEPA 902266535 LITHOESPACO")
não contêm código rubrica (2A001, 2A002, etc.). Tinha hacks ad-hoc para
só 4 rubricas (easyfresh→2A006, lithoesp→2A001, ferrovial→2A007, zurich→2A015)
mas a maioria das rubricas ficava silenciosamente sem movimentos.
2. **PdfLink dependia de Supabase Storage**: gerava signed URLs
`faturas/<ano>/<ref>.pdf` que não existem em V1 (bucket vazio). Os PDFs
reais estão em Google Drive — V1 já tem `extrato_bancario.drive_url`
populado (116 rows via bridge v10) mas o componente ignorava.
## Fixes
### LinhaOrcReal (apps/v2-condominios/src/views/PrestacaoContas.jsx:538)
ANTES: SELECT data_movimento, descricao, valor, saldo_apos, referencia_banco
WHERE data_movimento BETWEEN ano-01-01 AND ano-12-31
LIMIT 500 → filter JS por desc.includes(rubrica.codigo)
OR hacks easyfresh/lithoesp/ferrovial/zurich
AGORA: SELECT id, data_movimento, descricao, valor, saldo_apos,
referencia_banco, codigo, drive_url, drive_file_id, forn, alocacao
WHERE codigo = rubrica.codigo AND ano = ano
LIMIT 500
(sem filter JS — server-side via PostgREST)
V1 `v2_condominios.extrato_bancario.codigo` está populado por bridge v10:
R001=370 movs (Receitas/Quotas), 2A001=20, 2A002=16, 2A003=34,
2A005-2A015=51, G000=8, FCR=N (Fundo Comum Reserva).
### PdfLink (apps/v2-condominios/src/views/PrestacaoContas.jsx:832)
ANTES: signature `({ referencia })` — só Supabase Storage signed URL
AGORA: signature `({ referencia, driveUrl })`:
- se driveUrl preenchido → link directo Google Drive (zero work, abre new tab)
- fallback: signed URL Supabase Storage como antes (legacy V2)
V1 `extrato_bancario.drive_url` populado para 116 rows; 67 com drive_file_id.
### TabExtrato (apps/v2-condominios/src/views/PrestacaoContas.jsx:697)
- SELECT expandido para incluir drive_url, drive_file_id, forn, codigo
- Passa drive_url ao PdfLink (linha 753)
### LinhaOrcReal — UX melhorada (linhas 600-606)
- Mostra fornecedor (forn) ao lado da descrição se preenchido
- Usa m.id como React key (estável vs index)
- PDF link mostra "↗ PDF" em vez de "abrir" quando drive_url disponível
## Smoke test (Mário valida no browser logged-in)
1. Abrir http://localhost:5172/prestacao-contas
2. Tab "Orçamento vs Real" → ano 2024
3. Clicar "2A002 Água" → expandir
4. Deve mostrar 16 movs com Data, Descrição, Ref, Valor, PDF coluna
5. PDFs em "↗ PDF" são clicáveis (abrem Drive em new tab)
6. Idem para outras rubricas (2A001 LITHOESPACO 20 movs/8 PDF,
2A007 Manutenção 20 movs/8 PDF, etc.)
## Notas técnicas
- `alocacao` jsonb está NULL em V1 (bridge v10 deveria ter mapeado mas há
bug — investigar v11 separadamente). Não bloqueia esta feature (mostra
movs + PDFs). Drill-down de "alocação por fração" fica para iteração
futura usando esta col.
- Bridge v10 (commit anterior 78317a5) é pré-requisito desta funcionalidade
(drive_url só existe após sync).
Refs: memory project-v2-data-state-2026-05-24, project-v2-migration-canonical
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Owner
Author
2 commits novos validados
|
…recto) Bug reportado por Mário (segundo round): após fix anterior, cada movimento de extrato aparecia 2× — uma linha com valor negativo (sem PDF) e outra com valor positivo (com PDF). Total visual confuso e incorrecto. ## Root cause (2 bugs em série) 1. **V2 produção tem 2 rows por movimento** com mesmo `numero_doc` mas IDs diferentes — uma é o movimento bancário raw, outra é a alocação/categorização já com PDF anexado. UPSERT por id no bridge v10 manteve ambas em V1. Exemplo 2A002 Água 2024: 16 rows raw → 8 numero_doc únicos. 2. **Bridge v10 inverteu sinal do `valor`**: - V2 raw: `debito='-14.40', credito=null` - Bridge v10 mapping: `valor: (credito ?? 0) - (debito ?? 0)` = 0 - (-14.40) = +14.40 ❌ - Correcto seria: `valor: (credito ?? 0) + (debito ?? 0)` = 0 + (-14.40) = -14.40 ✓ ## Fix (apenas frontend — bridge v11 fixa origem depois) ### LinhaOrcReal (PrestacaoContas.jsx:538) - SELECT debito + credito raw (em vez de `valor` bugado) - Dedup `Map<numero_doc, row>`: preferir row com `drive_url IS NOT NULL` - Recalcular `valor = (credito ?? 0) + (debito ?? 0)` (debito já é negativo) ### TabExtrato (PrestacaoContas.jsx:697) - Mesmo padrão (SELECT raw, dedup, recalcular valor) ## Validação matemática (2A002 Água 2024) rows totais V1: 16 numero_doc únicos: 8 ✓ (deve mostrar 8 linhas após dedup) rows com PDF: 8 ✓ (todos têm Drive) soma debito únicos: -94,50€ ✓ (bate exactamente com KPI "Efetivo 94,50€" da rubrica) ## Próximo (bridge v11, separado) - Bridge fixar mapping `valor` no source (eliminar dependência de dedup React) - Bridge fazer dedup at source via UPSERT por `numero_doc` em vez de `id` (ou via UNIQUE constraint + filtro WHERE drive_url IS NOT NULL no SELECT) - Reimportar extrato_bancario com fix → React deixa de precisar de dedup Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Mário reportou (com screenshots side-by-side) que apps/v2-condominios estava
com light theme + bege beige enquanto V2 legacy é dark luxo PRATA OWNERS.
Causa: AppInner inicializava theme via `localStorage.getItem('v2theme') || 'dark'`
mas localStorage tinha 'light' (toggle histórico).
Fix: remover toggle inteiramente. V2 Condomínios é SEMPRE dark — match com
prataowners.pt legacy (Cormorant Garamond + DM Mono + gold/silver brand).
A estrutura JSX e tokens.css já estavam correctos:
- --bg #0d1117 (dark default)
- .kpi com border-top 3px colorido (azul/verde/vermelho/gold)
- .kpi-v font DM Mono 24px
- .topbar-title Cormorant Garamond
- Resumo Financeiro card escuro com cores semânticas
Bastava forçar dark + limpar localStorage stale.
Memory: feedback-design-parity-v2-legacy (Mário rejeita light variant em V2;
visual parity é tão importante como data parity).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…aídos via Playwright)
Mário rejeitou a versão "interpretada" anterior (2 tabs Condómino/Equipa, sem
hero imagem, painel dark): "sinto que estou sempre a começar de novo. a
replicação deve ser uma cópia. não inventes coisas novas. eu depois altero
mais tarde, senão ando sempre a voltar ao inicio, tenho de ter a base para
começar."
## O que mudou (cópia LITERAL)
### Estrutura DOM extraída de prataowners.pt LIVE
- `#scLogin` wrapper fixed inset:0 com gradient + hero bg image
- `.login-photo-content` (55% esquerda): logo top + título + sub + tag
- `.login-panel` (45% direita, BRANCO): logo + Bem-vindo + lang + form + footer
- 3 fluxos auth: magic-link email (default), Staff (alias+pwd), Token (UUID)
- i18n PT/EN com toggle de pills gold
### Files novos
- `apps/v2-condominios/src/components/LoginScreen.jsx` (reescrito do zero, literal)
- `apps/v2-condominios/src/components/LoginScreen.css` (cópia 44 regras CSS do legacy)
- `scripts/v2-discovery/extract-legacy-login.mjs` (extractor reutilizável)
- `docs/v2-migration/legacy-source/login/`:
- `index.html` 1.3MB — HTML pós-render completo
- `index-no-scripts.html` 228KB — HTML limpo sem scripts
- `styles-inline.css` 57KB — todos CSS resolvidos
- `computed-root.json` — getComputedStyle de 10 elementos chave
- `images.json` — 8 image refs encontradas
- `screenshot.png` — referência visual de prataowners.pt LIVE
- `assets/logo-prata-owners.png` — logo real baixado
## Validação Playwright (2026-05-24)
✅ 0 erros JS na renderização React
✅ Screenshot bate visualmente com legacy:
hero imagem + branding gold + painel branco + lang PT/EN +
input email + botão preto "Enviar link de acesso" +
Staff/Token + footer NIF/Marvila
## Refs
- memory: feedback-no-invent-copy-literal, feedback-design-parity-v2-legacy
- directiva 2026-05-24: "ter a base para começar; Mário altera depois"
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…inkado ao Dashboard'
Bugs reportados por Mário 2026-05-24:
1. "COMO o login agora mudou tens de ter staff com a password que te dei"
→ FormStaff agora pré-popula `BOSSMC` + `BOSSMC@2026` em modo dev
(import.meta.env.DEV), facilita teste sem digitar credenciais.
Em produção: campos vazios.
2. "aqui neste link tambem tens de mudar para aparecer linkado ao dashboard"
→ AppEmbed.jsx no topbar do dashboard mostrava URL crua
`http://localhost:5172` ao lado do label da app. Trocado por
`· linkado ao Dashboard`. URL real preservada no title (hover) +
link "Abrir em janela ↗" continua funcional.
3. LoginScreen.css: `#scLogin` adiciona `min-height: 100vh; width: 100%`
para garantir cobertura completa dentro de iframe (dashboard:5180→:5172).
Validação Playwright 2026-05-24:
- dashboard:5180 com V2 Condomínios tab activa: iframe localhost:5172
renderiza LoginScreen luxo correctamente (hero sunset + branding +
painel branco + lang PT/EN + Bem-vindo + Enviar link de acesso +
Staff/Token + footer Marvila Lisboa).
- bodyBg #0d1117 dark ✓, bodyHeight 868px ✓, scLoginExists true ✓
- Conclusão: ecrã preto reportado por Mário era CACHE do browser dele.
Próximo: replicar Condóminos.jsx + Fracoes.jsx literais (extracts em
docs/v2-migration/legacy-source/condominos/ e fracoes/ prontos).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Bug introduzido no commit b6de5b0 ("force dark theme"): removi setTheme do useState mas Sidebar ainda recebe a prop. Após login, AppInner crashou com: Uncaught ReferenceError: setTheme is not defined at AppInner (App.jsx:66:34) Fix: passar `() => {}` (no-op) à Sidebar. Não há toggle em V2 — theme é sempre dark (parity com prataowners.pt legacy). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…rota
Bugs reportados por Mário 2026-05-24 após login:
1. "light mode nao funciona" — botão Light/Dark na sidebar não respondia
2. "o topo superior herda das prestaçoes de contas??" — em /condominos
mostrava sub-título "Prestação de Contas" hardcoded
## Fix #1 — Light toggle
- App.jsx: restaurar `const [theme, setTheme] = useState(...)` (não
o `[theme]` sem setter que introduzi em b6de5b0)
- Default = 'dark' (parity legacy), mas toggle sidebar agora funciona
- localStorage `v2theme` controla preferência persistente
## Fix #2 — Topbar-meta dinâmico
- Topbar.jsx: importar `useLocation` do react-router-dom
- Mapa ROUTE_LABELS com 20 rotas → label PT-PT
- topbar-meta agora mostra: `NIF 902266535 · <página actual>`
e.g.: NIF 902266535 · Condóminos
NIF 902266535 · Fracções
NIF 902266535 · Dívida Actual 2026
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pedido Mário 2026-05-24: "isto é os condóminos nome com fração por baixo
email telefone... coloca um ordenador quando se preme qualquer cabeçalho
de coluna"
## Mudanças
### Estrutura: 1 row por pessoa (não por relação condomino↔fracção)
ANTES: tabela mostrava 1 row por (pessoa × fracção) → duplicar pessoa com
várias fracções (ex.: Gonçalo Dias aparecia 2× se tem 12-A1A + 14-B3F)
AGORA: 1 row por pessoa, com TODAS as fracções dela listadas por baixo
do nome (mono pequeno azul tracejado underline, como legacy)
### Colunas literais do legacy
Ref. | Nome / Fracções | Email | Telefone | NIF | Morada | Observações | 🟢 Portal
- Ref. = sequência 1..N por ordem alfabética
- Nome / Fracções = nome bold em destaque + lista de codigos de fracção
por baixo (10px, mono, color var(--bl) underline dotted)
- Email / Telefone / NIF = mono pequeno
- Morada = concatenação morada + cod_postal + localidade
- Observações = campo livre core.pessoas.observacoes
- Portal = placeholder por agora (dependerá de iam.portal_tokens)
### Sort por coluna
Clicar em qualquer header alterna asc → desc → asc.
Header activo mostra ▲/▼ gold; inactivos mostram ↕ esbatido.
SortableTh component reusável.
### Search
Toolbar com input "Pesquisar nome, email, NIF, fracção…" que filtra
rows client-side.
## Fonte de dados
v2_condominios.condominos (V1 espelho, populado por bridge v10)
↔ v2_condominios.fracoes (fracao_id JOIN)
↔ core.pessoas (pessoa_id JOIN, schema cross-vertical)
## Próximo (não bloqueador)
- Coluna Portal: ligar a iam.portal_tokens para mostrar 🟢/⚪ se
condómino tem acesso activo
- Botões "+ Novo Condómino" / "Email bulk" / "WhatsApp bulk" / "Imprimir"
(legacy tinha mas requerem mais infra agentic)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ipe Quota Extra Carregadores - RecipeDetailPage: botao Upload & Run (XLSX -> Storage condo-uploads), campo file hibrido, matching de step_name insensivel a acentos (paineis de resumo voltam a renderizar), Step8 visivel em modo leitura apos aprovar (registo do que foi emitido) - useRecipeRun: parse/diff no auto-executor com guards (so corre com ficheiro em Storage); ingest recebe so_datas (datas novas) do diff - v2-quota-extra-step: ingest casa XLSX->V2 por fracao_letra (porta p/ EST PUB) e escreve colunas reais (unit NOT NULL, bloco, fracao_codigo); guard anti-duplicado na emissao; halt se 0 escritas - discord-inbound: le anexos do Discord -> Storage -> payload da task Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nction + gate com relatorio e data
- Nova edge function v2-emitir-avisos-mensais (verificar mes / orcamento ano / orcamento por fracao c/ comparacao / emitir clone do mes anterior c/ valores do orcamento / auditar); guard anti-duplicado; data_emissao escolhivel; nº sequencial
- Recipe system.recipes 'emissao-avisos-mensais' (6 steps, 1 gate humano)
- useRecipeRun: map skill->edge function (suporta multiplas recipes); params {ano,mes,data_emissao}; helper mergeRunInputs
- RecipeDetailPage: AvisosGatePanel (relatorio quotas a emitir + comparacao mes anterior + escolha de data); StepSummary legivel por passo; paineis dedicados agora isolados por slug de recipe
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mite mes corrente, idempotente) - Nova edge function v2-avisos-mensais-cron: cria recipe_run + steps, conduz server-side todos os passos, auto-aprova o gate humano (emissao automatica), emite o mes corrente clonando o mes anterior; idempotente; notifica Discord - pg_cron job 32 repontado do sentinel (carregadores) para este condutor; dispara dia 1 09:00 UTC - Junho continua manual; Julho sera o 1o mes automatico (clona Junho como template) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sos mensais -> v2-avisos-mensais-cron) - system.schedules (calendario da vertical) passa a fonte unica; schedule-run roteia recipes com executor deterministico em vez de criar task/agente - Schedule 'Emissao de Avisos Mensais' (v2, dia 1 09:00) criado; pg_cron job 32 duplicado removido - Corre sozinho dia 1, auto-aprova gate, emite mes corrente, notifica Discord Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…es duplicado; badge QE=Q; pendentes reconciliam com saldo - Aviso/Conta Corrente (3 geradores PDF): codigo postal do condomino com fallback condomino.codpostal/localidade quando o parse da morada vem vazio; removida a linha 'Fracoes' do bloco Condomino (duplicada com o titulo 'Conta Corrente 2026 - Fracoes') - Badge .badge-type uniforme (min-width:28px; height:20px) -> QE do mesmo tamanho do Q - pdfPendentes: aplica credito parcial (totalV - saldo) para o Total do print reconciliar com o saldo da conta corrente (antes so abatia credito com saldo negativo) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
v2-legacy-bridge-cronv9 deployed em V1 Core Hub — corrige 4 bugs que tinham o bridge V2→V1 partido desde 2026-05-18 (smoke test 2026-05-24 10:52: 65 fetched, 60 updated, 5 ambiguous, 0 erros, 3.8s).capture-prataowners+deep-capture) + 6 screenshots públicos do prataowners.pt como baseline visual.What changed
Bridge (
supabase/functions/v2-legacy-bridge-cron/index.tsv9)4 bugs corrigidos vs v8:
Rangeheader)fracao → pessoa(faltava dedup waterfall por NIF + email)Docs (
docs/v2-migration/)00-AUDIT.md— inventário do legado V2 (HTMLs emadmin/+index.html)01-INVENTORY.md(480L) — todas as features V2 mapeadas02-PLAN.md(541L) — sequência de migração HTML→React por módulo03-BRIDGE-INVESTIGATION.md(143L) — RCA dos 4 bugs do bridgescreenshots/public/(6 imagens prataowners.pt) +manifest.json.gitignoreadicionascreenshots/internal/(PII potencial em screenshots autenticados)Stories (
docs/stories/epics/epic-020-v2-migration-2026-q3-2027/)EPIC.md— visão geralstories/020.0-discovery-baseline.md→020.7-cutover-prataowners.md(8 stories)Tooling (
scripts/v2-discovery/)capture-prataowners.mjs— screenshots públicos automatizadosdeep-capture.mjs— discovery autenticado (para uso futuro local-only)package.json+pnpm-lock.yaml— Playwright como devDep na raiz monorepoSmoke test (already passing)
Trigger v9 em V1 Core Hub (2026-05-24 10:52 UTC):
Bridge já está live em V1. Esta PR é a formalização do código + plano de migração.
Risk assessment
eozklslwfaqujaijvdnl): INTOCÁVEL. Bridge faz apenasSELECTvia PostgREST V2 →UPSERTem V1. ZERO writes em V2 (Regra D4 respeitada).hkmvszkpxjbxmnixzqbl): writes idempotentes emcore.pessoas+core.imoveis(tabelas vazias antes do bridge — sem risco de overwrite de dados humanos).admin/ouindex.htmlraiz tocados.apps/).Mário action items post-merge
Nenhum imediato — bridge já live e validada. Decisão pendente:
Refs
docs/stories/epics/epic-020-v2-migration-2026-q3-2027/EPIC.mddocs/v2-migration/02-PLAN.md