# PROJECT — Rent and Charge (Weekly and Arrears) Produto de cobrança semanal de aluguel e atrasos (arrears) para hostel/residência Granby Lifehouse (quartos, SAGE, staff). **Atualizado:** 2026-09-19 · **Versão:** **006.2026** **SPEC infra:** `SPEC-VM500-RENTCHARGE.md` **SPEC AR / Credit Book:** `SPEC-ACCOUNT-RECEIVABLES.md` **SPEC Txn / Ticket:** `SPEC-TRANSACTION-IDS.md` **SPEC MAP WEEK:** `SPEC-PAYMENTS-MAP-WEEK-PRINT.md` **Sessões:** `SPEC-SESSION-2026-09-18.md` · `SPEC-SESSION-2026-09-19.md` **App:** https://rentcharge.itecnologys.com · Forgejo: https://git.itecnologys.com/myvexx/rently ## O que o sistema faz (escopo actual) - Lista residentes / quartos (padrão SAGE: filtros por coluna, `#`, Room estreita, badges NP / Name dup / Room conflict) - Valor semanal devido via **charge bands** (RENT_35/40/41/45/65, FB_85/91, BANK_ACCOUNT, FLEXIBLE_FB) - **Pagamentos:** fonte única = exports SAGE *Customer Invoices Due* (não o Excel de arrears) - Totais do mês / ano; statements HTML/PDF; debit letter; agreements - UI: Residents, Reporting (mesmo layout), Settings, ficha do residente com grelha de pagamentos - **Account Receivables:** cash / credit sale / settlement (meals, keys, electricity); Credit Book com **over-settlement**; ROI Finance + Credit consumption; Period dashboard (due vs paid vs % + other balances) - Utilizadores: registo público com código de e-mail, recuperação de senha, sessões revogáveis e auditoria no menu Admin - Infra: VM500 BIG + Traefik CT120 + Cloudflare Tunnel ## Stack - Backend FastAPI + SQLite (`arrears.db`) - Frontend Next.js 15 - Tudo do projeto: `/home/mini/rentcharge` (código, venv, sage-payments, config, archive) - Host: BIG VM 500 — **não** big1 --- ## Regras de negócio — ocupação de quarto ### RN-01 — Um quarto físico = um residente 1. O mesmo `room_no` físico **não pode aparecer duas vezes** na lista de ocupação. 2. Um quarto só pode ter **um** residente activo. 3. A dívida pertence à **pessoa** (SAGE ID), nunca ao quarto. ### RN-02 — Validação de ocupação pelo último pagamento (**cash SA**) 1. Prova operacional = **último pagamento cash** (SAGE Type **SA**, category `cash` > 0). 2. **RENTSUPP e refunds não contam** como pagamento de renda para esta regra. 3. Em conflito de quarto, prevalece quem tem o cash mais recente. ### RN-03 — Not Present (NP##) vs nome em duplicidade - **A:** não pagou / saiu ou mudou → `NP01`, `NP02`, … + `last_room_no`. - **B:** nome duplicado → badge para revisão humana; sem merge automático. API: `POST /api/residents/reconcile-occupancy`. --- ## Regras de negócio — pagamentos SAGE (2026-09-05/06) ### RN-PAY-01 — Fonte única - Exports SAGE **Customer Invoices Due** são a **única** fonte de pagamentos. - O Excel de arrears (`JUN 2026` / `Total Arrears-2025`) **não grava** pagamentos (`excel_import._upsert_payment` = no-op). - Endpoint: `POST /api/import/sage-payments` (`replace_all=true` apaga pagamentos anteriores). - Código: `backend/app/services/sage_import.py`. ### RN-PAY-02 — Semana e data - Semana = **segunda → domingo** (ISO). - `period_start` / `period_end` = Mon–Sun da `payment_date`. - Reports usam a **data de pagamento** da linha SAGE. ### RN-PAY-03 — Type SA = cash (pago) | SAGE | Campo BD | Conta como pago? | |------|----------|------------------| | Type **SA** + Details CASH/CHQ/… | `category=cash`, `cash_paid=abs(Amount)` | **Sim** | | Type **SA** + Details **RENTSUPP** | `category=rentsupp`, `rentsupp=abs` | **Não** (arquivo / relatório próprio) | | Refund / Type SP / Details *REFUND* | `category=refund`, `refund_amount=abs` | **Não** (não subtrai do pago) | Amount no ficheiro SAGE é **negativo** → armazenar valor absoluto. ### RN-PAY-04 — ID único - Coluna SAGE **No** → `payments.txn_no` (único; índice `ux_payments_txn_no`). - Também: `txn_ref`, `sage_type`, `details`, `payment_date`, `counts_as_paid`. - Reimport não duplica pelo mesmo `txn_no`. ### RN-PAY-05 — Match residente - Match obrigatório por **SAGE A/C** ↔ `residents.sage_id`. - Ex.: **Mapule Uwaene** = A/C **481** · band **RENT_35**. - **Patrick McNamara** = A/C **272** · B15 · band FB_85. ### RN-PAY-06 — Due vs pago (accounting) - Due semanal = charge band (`get_cap`); excess cash **não credita** semanas futuras. - `summarize_payments` agrega **cash por semana** antes do cap; RENTSUPP/refund ignorados no pago. - Statement / lista de pagamentos: **default só cash**; filtro UI para rentsupp / refund / all. ### Ficheiros fonte (operador) `E:\Reports Week\PAYMENTS-REPORTS-BY-WEEK\2026\` Import consolidado (sem duplicar): 1. `Payments_week_01_to_52_from_2025.xlsx` 2. `Payments_week_2026-total_partial.xlsx` (até ~30/08/2026) Cópia VM: `/home/mini/rentcharge/sage-payments/reimport/`. --- ## UI (padrão SAGE) — estado 2026-09-06 | Ecrã | Padrão | |------|--------| | Residents `/` | Command bar, views, filtros por coluna, pager, export | | Reporting `/reports` | Mesmo layout que Residents | | Ficha Payments | Grelha SAGE; colunas **ID · Ref · SAGE · Details · Status · Amount**; search + filtros; Type default **Cash (SA)** | --- ## Utilizadores e autenticação A especificação completa está em `AUTH-USERS-SPEC.md`. - `/login` oferece **Register new user**. - O novo utilizador valida o cadastro com um código de seis dígitos enviado por e-mail e entra imediatamente após a validação. - Recuperação de senha só usa e-mail verificado e token longo, de uso único. - Sessões são mantidas no servidor, expiram por inatividade/tempo absoluto e podem ser revogadas. - O menu **Admin** reúne contas, sessões (IP, SO, browser, duração e features) e eventos de segurança. - O cookie do browser é `Secure`, `HttpOnly` e `SameSite=Strict`; o frontend não persiste credenciais em `localStorage`. --- ## Fora de escopo (por agora) - Cloudflare Access na borda - Merge automático de residentes só por nome - Alterar `/etc/network/interfaces` do Proxmox - Contar RENTSUPP como renda paga