docs: RentCharge 006.2026 — SPEC-VM500-RENTCHARGE.md

This commit is contained in:
roger 2026-09-19 11:22:37 +00:00
parent 2c0effd739
commit e0d586a846

View file

@ -1,68 +1,253 @@
# SPEC-VM500 ? Rent and Charge (Weekly and Arrears)
**Fonte local:** `C:\\LAPTOP\\rentcharge\\docs` ? sync Obsidian CT130.
**Data:** 2026-09-05
**Status:** infra de acesso + DNS + Traefik (placeholder HTTP). App ainda nao deployado.
**Host da VM:** BIG (`big-itecnologys-wardrobe`) ? NAO e big1.
## Objetivo
Ambiente na VM 500 PROJETOS para o produto **RenteCharge Weekly and Arrears** (controle de aluguel semanal / debitos). Pasta na VM: `/home/mini/RenteCharge Weekly&Arrears`. Codigo `rent-arrears-web` ainda no PC Windows.
## Topologia (obrigatorio hop)
Windows (Tailscale) -> **big** `100.90.196.58:22` -> LAN `10.10.10.100` (VM 500).
Este PC **nao** roteia `10.10.10.100` direto. BIG e BIG1 nao se conectam.
## VM 500
| Campo | Valor |
|--------|--------|
| VMID | 500 |
| Hostname | PROJETOS |
| SO | Debian 12 |
| IP LAN | 10.10.10.100/24 gw 10.10.10.1 vmbr1 |
| CPU/RAM/disco | 2 vCPU / 4 GB / 25 GB |
| Users | `admin` (sudo NOPASSWD), `mini` (docker) |
| Senha SSH | 805353 (admin e mini) |
| Chave | `id_ed25519` PROXMOX-HERTZNER-3 em authorized_keys de admin e mini |
| SSH config Windows | `Host vm500` ProxyJump `big-proxmox` User admin IdentityFile id_ed25519 |
## DNS Cloudflare (zona itecnologys.com)
| Nome | Tipo | Destino | Proxy |
|------|------|---------|-------|
| rentcharge.itecnologys.com | A | 100.90.196.58 (Tailscale do big) | DNS only |
| rentecharge.itecnologys.com | CNAME | rentcharge.itecnologys.com | DNS only |
Nao aponta para 10.10.10.100. Hostname nao aceita `&`.
## Traefik (BIG CT120 web-traefik, 10.10.10.120)
Caminho HTTP:
`Host: rentcharge.itecnologys.com` (ou rentecharge)
-> nginx host big `:80` (`myvexx-proxy` server_name)
-> Traefik `http://10.10.10.120:80`
-> `http://10.10.10.100:80`
Arquivo: `/opt/traefik/dynamic/services.yml` (router `rentcharge` / service `rentcharge-svc`).
Backup: `services.yml.bak-rentcharge-20260905`.
Traefik so entrypoint `:80` (sem TLS neste CT).
Teste no big: HTTP 200 placeholder.
## Placeholder na VM 500
`python3 -m http.server 80` em `/var/www/html` (apt nginx falhou: VM sem saida HTTPS para deb.debian.org). Pagina: "Rent and Charge / VM500 PROJETOS - Traefik OK".
## Instabilidade SSH deste Windows
Porta 22 Tailscale do big = `SSH-2.0-Tailscale`, nao OpenSSH. `ssh.exe` timeout no KEX; paramiko funciona. WAN 51.171.219.235:22/80/443 fechados.
## Checklist agente
1. Hop so no **big**, nunca big1 para esta VM.
2. Nao alterar `/etc/network/interfaces` do Proxmox sem 3 confirmacoes.
3. Proximo: deploy do app Rent Charge no lugar do http.server:80.
# SPEC-VM500 — Rent and Charge (Weekly and Arrears)
**Fonte local:** `C:\LAPTOP\rentcharge\docs` — sync Obsidian CT130.
**Data:** 2026-09-09 (rev. Account Receivables / Credit Book / Period dashboard — noite 08–09)
**Status:** app completo na VM500; HTTPS público via Cloudflare Tunnel; login validado; pagamentos SAGE importados; **AR Credit Book live**.
**Host da VM:** BIG (`big-itecnologys-wardrobe`) — **não** é big1.
**Produto / escopo funcional:** ver `PROJECT-RENTCHARGE.md` (ocupação NP + **RN-PAY** + **SPEC-ACCOUNT-RECEIVABLES.md**).
## Objetivo
Ambiente na VM 500 PROJETOS para o produto **RenteCharge Weekly and Arrears** (controle de aluguel semanal / débitos). **Única pasta do projeto:** `/home/mini/rentcharge` (código, venv, SAGE imports, config, archive e bare git).
## Topologia (obrigatório hop)
Windows (Tailscale) → **big** `100.90.196.58:22` → LAN `10.10.10.100` (VM 500).
Este PC **não** roteia `10.10.10.100` direto. BIG e BIG1 não se conectam.
Caminho **público HTTPS** (2026-09-05):
Internet → Cloudflare (TLS `*.itecnologys.com`) → tunnel `39cffaba-…` no BIG → Traefik CT120 `10.10.10.120:80` → VM500 (`:3000` UI, `:8000` `/api`).
Caminho **público HTTPS myvexx / Rently** (em curso 2026-09-06):
Internet → Cloudflare (TLS `*.myvexx.com`) → tunnel myvexx `7233d455-…` (`config-mycloud.yml` no BIG) → Traefik CT120 → VM500.
Hostname: `rently.myvexx.com`. Traefik + CORS já prontos; ingress DNS no BIG ainda pendente (SSH publickey). Ver `docs/ops/RENTLY-MYVEXX-TUNNEL.md`.
Caminho **privado Tailscale** (preservado):
`rentcharge-ts.itecnologys.com` → A `100.90.196.58` DNS-only → nginx BIG `:80` → Traefik CT120 → VM500.
## VM 500
| Campo | Valor |
|--------|--------|
| VMID | 500 |
| Hostname | PROJETOS |
| SO | Debian 12 |
| IP LAN | 10.10.10.100/24 gw 10.10.10.1 vmbr1 |
| CPU/RAM/disco | 2 vCPU / 4 GB / 25 GB |
| Users | `admin` (sudo NOPASSWD), `mini` (docker) |
| Senha SSH | 805353 (admin e mini) |
| Chave | `id_ed25519` PROXMOX-HERTZNER-3 em authorized_keys de admin e mini |
| SSH config Windows | `Host vm500` ProxyJump `big-proxmox` User admin IdentityFile id_ed25519 |
## DNS Cloudflare (zona itecnologys.com)
Zona ID: `7aab8fcc135ed992f284b1f5fbf57065`
Conta da zona: `37fd43032acca512b575ebe20853bf6d` (Admin@itecnologys.com)
| Nome | Tipo | Destino | Proxy |
|------|------|---------|-------|
| rentcharge.itecnologys.com | CNAME | `39cffaba-b611-4c50-8297-41a48d220870.cfargotunnel.com` | **Proxied** |
| rentcharge-ts.itecnologys.com | A | 100.90.196.58 (Tailscale do BIG) | DNS only |
| rentecharge.itecnologys.com | CNAME | rentcharge.itecnologys.com | DNS only |
Backup pré-alteração DNS: `/root/cloudflare-backups/rentcharge-dns-20260905T185905Z.json` (no BIG).
Não aponta para `10.10.10.100`. Hostname não aceita `&`.
**Por que CNAME para o túnel (não A Tailscale):** o padrão dos serviços já SSL no BIG (`fleet.myvexx.com`, `projetos.myvexx.com`, `mycloud.myvexx.com`) é Cloudflare Tunnel. Um CNAME `*.cfargotunnel.com` só funciona se o túnel estiver na **mesma conta Cloudflare da zona**. A zona `itecnologys.com` usa o túnel `39cffaba`; o túnel myvexx `7233d455` é de outra conta e **não** pode publicar este domínio.
## SSL / Cloudflare Tunnel (BIG)
Dois `cloudflared` convivem no BIG. Não se cruzam com Tailscale (redes distintas). Único conflito possível: porta de métricas — já isolada.
| Serviço | Túnel | Conta CF | Config | Métricas | Função |
|---------|-------|----------|--------|----------|--------|
| `cloudflared.service` | `7233d455-2e1f-45b3-bf3a-4bfad26b2f4a` | myvexx (`2d504d7a…`) | `/etc/cloudflared/config-mycloud.yml` | `127.0.0.1:20241` | **Não alterar.** fleet, projetos, mycloud, etc. |
| `cloudflared-itecnologys.service` | `39cffaba-b611-4c50-8297-41a48d220870` | itecnologys (`37fd4303…`) | `/etc/cloudflared/config-itecnologys.yml` | `127.0.0.1:20242` | Só `rentcharge.itecnologys.com` |
Ingress do túnel itecnologys:
```
hostname: rentcharge.itecnologys.com
service: http://10.10.10.120:80
originRequest.httpHostHeader: rentcharge.itecnologys.com
catch-all: http_status:404
```
Credencial: `/root/.cloudflared/39cffaba-b611-4c50-8297-41a48d220870.json`
Unit: `/etc/systemd/system/cloudflared-itecnologys.service` (enabled, Restart=always)
Certificado público observado: CN `itecnologys.com`, SAN `itecnologys.com` + `*.itecnologys.com`, emissor Google Trust Services WE1.
Validação: `https://rentcharge.itecnologys.com/login` HTTP 200; `https://rentcharge.itecnologys.com/api/health` → `{"status":"ok","app":"Granby Rent Arrears"}`.
Origin certs `/root/.cloudflared/cert.pem` e `cert-mycloud.pem` **não** autenticam a API de gestão (`tunnel list` → unauthorized). Operar o túnel com a credencial JSON continua válido. DNS do rentcharge foi aplicado pela API Cloudflare (token de zona), não por `cloudflared tunnel route dns`.
## Traefik (BIG CT120 web-traefik, 10.10.10.120)
Caminho HTTP interno (inalterado):
`Host: rentcharge.itecnologys.com` (ou rentecharge)
→ nginx host BIG `:80` (`myvexx-proxy` server_name) **ou** Cloudflare Tunnel
→ Traefik `http://10.10.10.120:80`
→ UI `http://10.10.10.100:3000` e API `http://10.10.10.100:8000` (`/api`)
Arquivo: `/opt/traefik/dynamic/services.yml` (router `rentcharge` / service `rentcharge-svc`).
Backup: `services.yml.bak-rentcharge-20260905`.
Traefik só entrypoint `:80` (TLS na borda Cloudflare, não neste CT).
`cloudflared` no CT120 está **inativo/disabled**; o túnel itecnologys corre no **host BIG**, não no CT120.
Nginx BIG `:443` (`myvexx-proxy-https`) **não** inclui `rentcharge.itecnologys.com`; SSL público não depende desse vhost.
## Aplicação na VM 500
| Componente | Porta | Serviço |
|------------|-------|---------|
| Next.js frontend | 3000 | `rentcharge-frontend.service` |
| FastAPI backend | 8000 | `rentcharge-backend.service` |
Os dois usam `systemd`, boot automático, `Restart=always`.
Frontend WorkingDirectory: `/home/mini/rentcharge/frontend`.
Backend WorkingDirectory: `/home/mini/rentcharge/backend`.
Python: `/home/mini/rentcharge/venv/bin/python`.
Node: `/home/admin/node/bin/node`.
O frontend usa API no **mesmo domínio** (`/api`); o Traefik separa para 3000/8000.
Banco live: `/home/mini/rentcharge/backend/data/arrears.db`.
Segredo JWT: `/home/mini/rentcharge/config/rentcharge.env` (`SECRET_KEY` apenas; symlink legado `/etc/rentcharge.env`), permissão `600`, não versionar.
Deploy anterior arquivado em `/home/mini/rentcharge/archive/`.
Backup pré-deploy do banco: ver `archive/` / `backend/data/*.bak*`.
Backup pré-troca de senha: `/home/mini/rentcharge/backend/data/arrears.db.bak-20260905T190523Z`.
Backups pagamentos SAGE: `arrears.db.bak-clear-amounts-*`, `arrears.db.bak-sage-dedupe-*`, `arrears.db.bak-before-sage-*`.
### Pagamentos SAGE (fonte única — 2026-09-05/06)
| Item | Valor |
|------|--------|
| Import | `POST /api/import/sage-payments` · `backend/app/services/sage_import.py` |
| Formato | SAGE Customer Invoices Due (Amount negativo; Type SA = cash) |
| Pasta Windows | `E:\Reports Week\PAYMENTS-REPORTS-BY-WEEK\2026\` |
| Pasta VM | `/home/mini/rentcharge/sage-payments/reimport/` |
| Unicidade | `payments.txn_no` (SAGE No) + índice único |
| Default UI | só `category=cash`; filtro rentsupp/refund/all |
| Excel arrears | **não** escreve payments |
Detalhe de regras: `PROJECT-RENTCHARGE.md` § RN-PAY-01..06.
### Login (produção)
| Campo | Valor |
|--------|--------|
| Utilizador | `admin` |
| Palavra-passe | `(rotated — see vault / password manager; never store in Git)` |
| Endpoint | `POST /api/auth/login-json` |
A password de produção **não** deve constar nesta spec. O default em `backend/app/config.py` só aplica no primeiro arranque se o user ainda não existir; após bootstrap, gerir a password fora do Git.
### Módulo de utilizadores (2026-09-06)
Especificação funcional e de segurança: `AUTH-USERS-SPEC.md`.
- Código isolado: `backend/app/modules/users/`
- Cadastro público: `/signup`, acessível por **Register new user** em `/login`
- Verificação: código de seis dígitos por e-mail; 10 minutos, uso único, máximo de cinco erros
- Recuperação: token aleatório longo, hash no SQLite e revogação das sessões após uso
- Sessões: cookie HttpOnly + registo server-side com IP, User-Agent, SO, browser, login, last seen e encerramento
- Auditoria Admin: `/users/sessions` e `/users/activity`
- Banco: tabelas `email_verification_codes`, `auth_sessions`, `session_feature_access`, `auth_events`
- Migração é aditiva e preserva `admin`, `teresa` e `natalie`
**Deploy 2026-09-07:** backend e frontend reiniciados em conjunto; backup
`arrears.db.bak-before-auth-module-20260907-090632`; migração e cookie login
validados. SMTP autenticado **configurado e validado 2026-09-19** (`mail.myvexx.com:465`, `admin@myvexx.com`) em `/home/mini/rentcharge/config/rentcharge.env`.
### Correção crítica: login no browser (2026-09-05)
Causa: `.env.local` com `NEXT_PUBLIC_API_URL=http://127.0.0.1:8000` tinha precedência sobre `.env.production` (`NEXT_PUBLIC_API_URL=` vazio). O JS compilado pedia a API ao **localhost do visitante**, não ao servidor. Curl no BIG/VM devolvia 200; o formulário falhava.
Correção:
- `.env.local` → `/home/mini/rentcharge/frontend/.env.local.disabled-20260905T190745Z`
- Rebuild `npm run build` + `systemctl restart rentcharge-frontend`
- Build atual: **zero** referências a `127.0.0.1:8000`; login usa `/api/auth/login-json` relativo
**Regra:** nunca deixar `.env.local` no frontend de produção. Rebuild só com `.env.production` (`NEXT_PUBLIC_API_URL` vazio).
## Produto (construído nesta fase)
### Charge bands
Além das bandas de rent / full board, existem:
- **Paid by bank account** (`KIND_BANK`) — `uses_resident_amount`: weekly due = `weekly_rent` / `imported_weekly_amount` do residente
- **Flexible full board** (`KIND_FLEXIBLE_FB`) — idem, com flag full board
Lógica: `backend/app/services/charge_bands.py`, `accounting.get_cap`, schema `ChargeBandOut.uses_resident_amount`.
### SAGE ID
Import Excel normaliza SAGE ID (float/int/string; ignora VACANT). Backfill em residentes existentes por quarto/nome sem sobrescrever IDs já válidos. Unicidade em create/update. Visível na lista e na ficha do residente.
### UI (padrão SAGE)
- Header em dois níveis (`AppNav`): barra de company group + barra branca com logo e navegação
- **Residents** e **Reporting**: mesmo layout (command bar, views, filtros por coluna, pager, export) — sem cards KPI no Reporting
- Ficha residente **Payments**: grelha SAGE; colunas separadas ID / Ref / SAGE / Details / Status / Amount; search + filtros; Type default Cash (SA)
- Coluna `#`, Room estreita, badges NP / Name dup / Room conflict; botão Reconcile rooms
### Regras de negócio — ocupação + pagamentos
Detalhe canónico em `PROJECT-RENTCHARGE.md`. Resumo obrigatório para agentes:
| ID | Regra |
|----|--------|
| **RN-01** | Um quarto físico = um residente. Dívida na pessoa (SAGE), não no quarto. |
| **RN-02** | Ocupante validado = último **cash SA** (> 0). RENTSUPP/refund **não** contam. |
| **RN-03A** | Saiu / mudou sem ocupar → **NP01…** + `last_room_no`. |
| **RN-03B** | Nome duplicado → badge; sem merge automático. |
| **RN-PAY-01..06** | SAGE = única fonte de payments; SA=cash; RENTSUPP arquivado; refund não subtrai; `txn_no` único; semana Mon–Sun. |
Estado de implementação: **RN-01..03 + RN-PAY + UI Reporting/Payments aplicados na VM500** (2026-09-06).
**Account Receivables / Credit Book / Period dashboard / ROI Finance A4 pack:** ver `SPEC-ACCOUNT-RECEIVABLES.md` (2026-09-09) — R-CB01…R-CB05, ROI Finance remit/credit net, over-settlement, **Print ROI Finance pack (A4)** com logo SA + Brinks bags + 3 assinaturas.
### Cartas impressas
Templates HTML em `backend/app/templates/` (`letter_debt.html`, `letter_agreement.html`, `statement.html`). Layout visual das cartas de reconhecimento/demonstrativo ainda em evolução com os exemplos do operador.
## Instabilidade SSH deste Windows
Porta 22 Tailscale do big = `SSH-2.0-Tailscale`, não OpenSSH. `ssh.exe` timeout no KEX; paramiko funciona. WAN 51.171.219.235:22/80/443 fechados neste contexto. Acesso público do **app** é pelo túnel Cloudflare, não por abrir WAN.
## Checklist agente
1. Hop só no **big**, nunca big1 para esta VM.
2. **Nunca** alterar `/etc/network/interfaces` do Proxmox sem **três** confirmações explícitas.
3. Gerir a aplicação com `systemctl status|restart rentcharge-backend rentcharge-frontend`.
4. Não iniciar processos manuais nas portas 3000 ou 8000.
5. Não tocar em `cloudflared.service` / `config-mycloud.yml` (myvexx). Só o unit `cloudflared-itecnologys`.
6. Rebuild do frontend: confirmar que `.env.local` está disabled; `NEXT_PUBLIC_API_URL` vazio.
7. DNS público do rentcharge = CNAME proxied para o túnel `39cffaba`; acesso Tailscale pelo hostname `rentcharge-ts`.
8. Aplicação agora está na internet com senha curta; Cloudflare Access na borda ainda **não** está ligado.
9. Antes de alterar lista de residentes / rooms / payments: ler RN-01..RN-03 e **RN-PAY** em `PROJECT-RENTCHARGE.md`.
10. Reimport SAGE: usar consolidados em `E:\Reports Week\...` / `/home/mini/rentcharge/sage-payments/reimport/`; dedupe por `txn_no`.
11. Antes de alterar Account Receivables / Credit Book / ROI Finance: ler `SPEC-ACCOUNT-RECEIVABLES.md` (over-settlement, remit, R-CB*).
## Comandos rápidos
```bash
# BIG
systemctl status cloudflared cloudflared-itecnologys
ss -ltnp | grep -E '20241|20242'
# VM500 (via ProxyJump)
systemctl status rentcharge-backend rentcharge-frontend
curl -sS http://127.0.0.1:8000/api/health
curl -sS -X POST http://127.0.0.1:8000/api/auth/login-json \
-H 'Content-Type: application/json' \
--data '{"username":"admin","password":"<REDACTED>"}'
```