# Prompt 06 — Painel Cliente (escritório): condomínios, contatos, espaços comuns, assembleias, documentos ## Contexto Prompts 01–05 concluídos: autenticação completa, painel master funcionando, empresas sendo provisionadas com tenant próprio. **Leia `10-tracker-progresso.md` antes de começar.** Este prompt implementa o CRUD principal do menu **Cliente → Cadastro Condomínio**, que é o coração do sistema: condomínios, contatos (síndico/subsíndico/conselheiro), vínculo de funcionários, espaços comuns, assembleias e documentos. Todos os endpoints deste prompt exigem `exigirFuncionario` (Prompt 04) e operam sobre o `db_empresa_` do `empresa_id` do token (usar `TenantDataSourceProvider` do Prompt 03). ## Objetivo CRUD completo de `condominios`, `contatos` + `contato_condominio`, `funcionario_condominio`, `espacos_comuns`, `assembleias`, `documentos` (upload de arquivo). ## Especificação ### 1. Condomínios ``` POST /api/condominios body: {"nome", "cnpj", "endereco"} GET /api/condominios lista todos do tenant GET /api/condominios/{id} → resposta agregada: dados do condomínio + síndico atual (via ContatoCondominioDao) + contagem de assembleias futuras + contagem de avisos ativos (isso é o "checklist" que o funcionário usa como referência rápida) PUT /api/condominios/{id} ``` ### 2. Contatos (síndico/subsíndico/conselheiro) ``` POST /api/contatos body: {"nome", "telefone", "email"} → cria a pessoa (sem vínculo ainda) POST /api/condominios/{id}/contatos/vincular body: {"contato_id" (ou dados de um contato novo pra criar+vincular numa chamada só), "tipo": "sindico"|"subsindico"|"conselheiro", "data_inicio_mandato", "data_fim_mandato"} → grava em contato_condominio; se marcar tipo=sindico e já existir outro síndico ativo pro mesmo condomínio, perguntar/forçar: por padrão, encerrar automaticamente o mandato anterior (status='inativo', data_fim_mandato=hoje) e ativar o novo — deixar isso documentado no código como decisão de negócio GET /api/condominios/{id}/contatos lista todos os vínculos ativos daquele condomínio DELETE /api/condominios/{id}/contatos/{contatoId}?tipo=... (desvincula — não apaga o contato, só remove o vínculo daquele condomínio/tipo específico; um contato pode continuar vinculado a outros condomínios) ``` ### 3. Funcionários vinculados a condomínios ``` POST /api/condominios/{id}/funcionarios-vinculados body: {"funcionario_id"} (funcionario precisa existir em db_master, mesma empresa) GET /api/condominios/{id}/funcionarios-vinculados DELETE /api/condominios/{id}/funcionarios-vinculados/{funcionarioId} ``` ### 4. Espaços comuns ``` POST /api/condominios/{id}/espacos-comuns body: {"nome", "descricao", "valor_utilizacao_centavos", "indice_correcao"} GET /api/condominios/{id}/espacos-comuns PUT /api/condominios/{id}/espacos-comuns/{espacoId} → se o valor mudar, atualizar data_ultimo_reajuste=hoje automaticamente ``` ### 5. Assembleias ``` POST /api/condominios/{id}/assembleias body: {"tipo", "data_hora", "local", "pauta"} GET /api/condominios/{id}/assembleias (ordenar por data_hora desc) PUT /api/condominios/{id}/assembleias/{id} (inclui mudar status: agendada/realizada/cancelada) ``` ### 6. Documentos (upload) ``` POST /api/condominios/{id}/documentos multipart/form-data: arquivo + tipo → salvar o arquivo em um diretório local montado como volume Docker (ex: /app/uploads/ //-), gravar caminho_armazenamento com esse path relativo → validar extensão (permitir: pdf, jpg, jpeg, png, docx) e tamanho máximo (ex: 10MB) GET /api/condominios/{id}/documentos GET /api/documentos/{id}/download → serve o arquivo (Content-Disposition: attachment) ``` Adicionar o volume de uploads no `docker-compose.yml` do Prompt 01 (`./uploads:/app/uploads`). ### 7. Auditoria Toda operação de `POST`/`PUT`/`DELETE` deste prompt deve gravar uma linha em `log_auditoria` (tabela_afetada, registro_id, ação, dados_anteriores/dados_novos em JSON, funcionario_id do token). ## Critérios de aceite - [ ] Criar um condomínio, vincular um síndico e um subsíndico, e chamar `GET /api/condominios/{id}` mostra o síndico atual corretamente. - [ ] Vincular um segundo síndico ao mesmo condomínio encerra automaticamente o mandato do primeiro (`status='inativo'`). - [ ] O mesmo `contato` pode ser vinculado como síndico em dois condomínios diferentes simultaneamente. - [ ] Upload de um PDF funciona; upload de um `.exe` é rejeitado com erro claro; upload de 15MB é rejeitado. - [ ] `log_auditoria` recebe uma linha para cada operação de escrita feita neste prompt (testar com pelo menos 3 operações diferentes). - [ ] Todos os endpoints só retornam/alteram dados do `empresa_id` do token — tentar acessar `/api/condominios/{id}` de outra empresa retorna 404 (não 403 — não revelar que o ID existe em outro tenant). ## Ao concluir Atualizar `10-tracker-progresso.md`: Prompt 06 concluído, data, resumo. Confirmar Prompts 01–05 seguem marcados.