# Dados, migração e operação da unificação ## Modelo e dicionário da extensão O esquema executável está em `database/migrations/2026_09_27_140000_investment_unification.php` e `2026_09_27_150000_investor_workspace.php`. Todas as relações têm chaves externas com eliminação restringida. Identificadores territoriais são UUID; oportunidades e utilizadores conservam os seus identificadores numéricos. | Tabela | Chave / relação | Conteúdo e regra | | ----------------------------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `investment_reference_options` | `id`; único `(group,code)` | Rótulo, actividade, ordem, obrigatoriedade de especificação. Códigos estáveis; desactivar não elimina referências históricas. | | `opportunity_profiles` | PK/FK `opportunity_id` | Educação, modelo de negócio, requisitos, riscos, data-alvo, fonte, versão. Texto destinado à ficha pública após publicação. Não colocar aqui notas privadas. | | `opportunity_clusters` | PK `(opportunity_id,cluster_id)` | Uma oportunidade pode pertencer a vários clusters. | | `opportunity_corridors` | PK `(opportunity_id,corridor_id)` | Vários corredores por oportunidade. | | `opportunity_projects` | PK `(opportunity_id,project_id)` | Vários projectos âncora por oportunidade. | | `opportunity_locations` | PK `(opportunity_id,geo_location_id)` | Ligações às localizações existentes. Geometrias continuam em `geo_geometries`, PostGIS SRID 4326. | | `opportunity_facets` | PK `(opportunity_id,option_id)` | Subsector, parceria, impacto, educação, infraestrutura, competências e necessidades configuráveis. | | `investment_submissions` | UUID; dono `user_id`; recibo único `interest_request_id` | `payload` encriptado, versão, estado draft/submitted. No máximo um destino: oportunidade ou dossier; interesse geral admite nenhum. Submissão exige recibo e rascunho não o admite. | | `investment_specific_needs` | `submission_id`; opções FK | Categoria, tipo, quantidade decimal não negativa, unidade, prioridade, prazo. Descrição, localização e demais detalhes são encriptados. | | `investment_submission_attachments` | UUID; submissão/documento FK | Ficheiro privado em `documents`; nome, tipo, tamanho e checksum canónicos. | | `investment_submission_events` | `submission_id`, actor, versão | Criação, gravação, anexo e submissão. Guarda actos e versões; não é um arquivo de cópias integrais dos rascunhos. | | `opportunity_favourites` | PK `(user_id,opportunity_id)` | Favoritos privados, sem duplicações. Retirada de publicação oculta-os. | | `investment_accounts` | PK/FK `user_id` | Perfil de investimento encriptado e versionado; complementa nome/email da conta. | | `investment_company_declarations` | UUID; dono `user_id` | Dados empresariais declarados, encriptados e versionados; até vinte por utilizador. Não altera `organizations` ou `memberships`, nem comprova representação. | | `investment_messages` | UUID; submissão/actor; chave única `(submission_id,actor_id,client_key)` | Conversa encriptada depois da submissão. Repetição com mesmo texto devolve ID; reutilização da chave com outro texto é conflito. | | `investment_notifications` | UUID; utilizador/submissão; evento único por destinatário | Confirmação, estado e mensagem, conteúdo encriptado, data de leitura. Notificações internas, sem envio de email nesta extensão. | | `investment_submission_revisions` | único `(submission_id,version)` | Snapshot encriptado do formulário, necessidades e IDs dos anexos em cada nova versão. Consulta por titular/gestor, sem restauro automático. | Relações principais: `users → investment_submissions → investment_specific_needs`; `investment_submissions → investment_submission_attachments → documents`; `investment_submissions → interest_requests → outbox_events → CRM`; `opportunities ↔ clusters/corridors/projects/locations` através dos quatro pivots. Dossiers continuam ligados às entidades territoriais originais, sem serem copiados para `opportunities`. O `payload` admite nome, email, telefone, país, organização, função, experiência, capacidade declarada, perfil, sector, objectivo, participação, parceria, montante/moeda, horizonte, localização, referências territoriais, especificação de Outro e consentimento. Campos desconhecidos são recusados. As necessidades admitem descrição, localização, observações, qualificação, especialidade, experiência, certificações, disponibilidade, instituição, modalidade/duração de formação e estado declarado. Os dados quantitativos não são indicadores territoriais verificados. ## Regras de visibilidade e segurança - A pesquisa exige oportunidade publicada, data de publicação atingida e ausência de eliminação lógica. Relações territoriais públicas exigem dossier publicado; localizações e geometrias exigem classificação pública. - O mapa devolve apenas pontos publicados; não estima coordenadas nem consulta serviços externos. A lista textual contém os mesmos locais. - Editar contexto devolve a oportunidade a rascunho. A versão recebida tem de coincidir sob bloqueio transaccional; conflito devolve 409. - Só o titular altera a sua manifestação. Gestores autorizados podem lê-la para atendimento. Outro investidor recebe 404. Após submissão, alterações são recusadas e repetir o envio devolve o mesmo protocolo. - A submissão, o recibo e o evento de integração são gravados atomicamente pelo serviço existente `InterestSubmission`. Nenhum envio externo SuiteCRM é activado por esta extensão. - Anexos PDF/TXT: até 10 MiB cada, dez por manifestação. Armazenamento privado; acesso revalida dono/gestor, estado, directório e SHA-256. Os anexos não integram o catálogo público. - `payload` e detalhes usam encriptação Laravel. Preservar `APP_KEY` com o backup e restringir o acesso; trocar a chave sem plano torna estes campos ilegíveis. - Auditoria guarda actor, identificador, acto e versão, sem copiar perguntas, contactos ou texto privado. A retenção e eliminação de dados pessoais ainda exigem uma política operacional implementada. - As revisões integrais privadas ficam separadas da auditoria. Quem deixa de ser gestor perde acesso a pedidos e notificações de terceiros. Mensagens só são permitidas em manifestações submetidas e não alteram a versão do formulário. - APIs privadas usam sessão web, email verificado, CSRF e limites de pedidos. Registo público não atribui gestão. Papéis técnico/validador/promotor separados ainda não foram introduzidos nesta extensão. ## Migração local executada em 27/09/2026 1. Backup privado `storage/app/private/unification-backup-20260927-134405/database.dump` e manifesto SHA-256. 2. Restauro numa base isolada `unification_restore_20260927134405_test`, PostgreSQL de QA na porta 55441; comparação de contagens das 76 tabelas existentes antes de migrar. 3. Aplicação exclusiva da migration `2026_09_27_140000_investment_unification` e de `InvestmentReferenceSeeder`. São doze tabelas adicionais. O seed usa `insertOrIgnore`: não substitui opções editadas. 4. Não se executou `DatabaseSeeder`, não se importaram exemplos remotos e não se criaram oportunidades, empresas ou coordenadas fictícias na base local. Segunda extensão: migration `2026_09_27_150000_investor_workspace`, após backup/restauro verificado de 88 tabelas em `storage/app/private/workspace-backup-20260927-184429/`. Resultado: **93 tabelas**; as cinco novas ficaram vazias. Reconciliação após a segunda migration: apenas `migrations` mudou (33 → 34); contagens das restantes 87 tabelas preservadas. O seed de instalação local passou também a incluir as opções de investimento; não foi reexecutado globalmente na base operacional. O backup integral anterior, `C:\laragon\backups\portal-investidor-20260927-101224`, tem instruções em `RESTAURO.md`. Representa uma versão anterior à extensão das sete áreas públicas; não misturar silenciosamente esse código com a base nova. ## Aplicação noutro ambiente Reconciliar primeiro código, migrations aplicadas e conteúdo da outra máquina. Gerar backup integral de código, base, ficheiros privados e chave, e ensaiar restauro isolado. Preservar IDs/origens remotos numa tabela de correspondência antes de qualquer importação; comparar contagens, unicidade, FKs, checksums e conteúdo por categoria. Exemplos demonstrativos ficam excluídos do catálogo real. A comparação pública não prova equivalência das bases. Com o código compatível e o backup conferido: ```sh php artisan migrate --path=database/migrations/2026_09_27_140000_investment_unification.php --force php artisan db:seed --class=InvestmentReferenceSeeder --force php artisan migrate --path=database/migrations/2026_09_27_150000_investor_workspace.php --force npm run build php artisan optimize:clear ``` Executar os testes apenas com `APP_ENV=testing` e base isolada terminada em `_test`. Nunca usar `migrate:fresh` na base operacional. ## Reversão e monitorização O `down()` recusa remover tabelas com dados, incluindo as opções semeadas. Para reverter código, conservar as tabelas aditivas e retirar os novos acessos. Para restaurar a base integral, suspender escritas e trabalhadores, guardar primeiro os dados recebidos depois do backup, restaurar numa base nova e reconciliar antes de mudar a ligação. Não substituir a base activa durante a verificação. O ensaio desta entrega prova restauro do dump anterior; não prova reversão de produção com tráfego concorrente. Monitorizar erros HTTP 5xx, conflitos 409, recusas 403/404, limites 429, fila/outbox pendente e falhas de entrega, integridade dos anexos, espaço em disco, backups e latência de pesquisa/submissão. Não colocar nomes, emails, conteúdo de anexos ou formulários em métricas. Medir p50/p95/p99 no destino antes de definir SLA; medições locais de poucos registos não são ensaio de carga. ## Go / No-Go | Item | Situação | | ----------------------------------------------------------- | -------------------------------------------------------------------------------------------- | | Autorização dos requisitos | Expressamente concedida pelo proprietário; não requer nova aprovação genérica. | | Extensão local e opções | Aplicadas após backup/restauro. | | QA automatizado e navegador | Evidências em `validation.md`; conferir limitações de cada ensaio. | | Conteúdo e geografia | Publica-se apenas o existente autorizado; completar dados com fontes reais. | | Consolidação da outra máquina | Adiada pelo proprietário para fase futura; fora do âmbito da execução local actual. | | WCAG 2.2 AA | Verificações parciais; falta auditoria completa com tecnologia assistiva. | | Área pessoal, empresas declaradas, mensagens e notificações | Implementadas e testadas localmente; sem atribuição automática de representação empresarial. | | Perfis especializados e cadastro territorial completo | Cobertura parcial; ver matriz de rastreabilidade. | | SuiteCRM real, carga, recuperação no destino | Não ensaiados nesta entrega. | | Formação e produção | Plano preparado; sessões e entrada em produção não executadas. | Resultado: disponível para utilização e avaliação local. A conclusão integral do prompt e a prontidão de produção não estão demonstradas pelos testes desta extensão.