@@ -0,0 +1,71 @@
|
||||
# Script skills_edu — execução local de 30/09/2026
|
||||
|
||||
Executado o anexo de tabelas recebido em 30/09/2026, adaptado aos mestres existentes como exigido no próprio documento. O prompt funcional anterior foi cancelado e não foi retomado. Esta entrega é de estrutura relacional; não activa formulários, matching, publicação, acesso a documentos ou integrações.
|
||||
|
||||
## Resultado
|
||||
|
||||
- PostgreSQL **17.11**, pgcrypto/PostGIS já geridos pelas migrations existentes; nenhuma extensão reinstalada.
|
||||
- Seis migrations Laravel registadas: `2026_09_30_100100_skills_education_1` até `2026_09_30_100600_skills_education_6`.
|
||||
- **24 tabelas, 83 FK e 111 índices** no schema `skills_edu`. Todas as FK têm cobertura B-tree completa; índices parciais adicionais mantidos.
|
||||
- Novas tabelas vazias. Nenhuma pessoa, competência, curso, procura ou oportunidade inventada/importada.
|
||||
- Backup com restauro de **210 tabelas**: `storage/app/private/skills-education-backup-20260930-034646/`. A reconciliação posterior não encontrou diferenças, descontando os seis registos novos na tabela Laravel `migrations`.
|
||||
- Evidência e checksums SQL: `applied-verification.json` no mesmo directório privado.
|
||||
|
||||
## Adaptação aos mestres
|
||||
|
||||
As seguintes tabelas são extensões com **PK também FK obrigatória** para o mestre. Não geram um segundo identificador da entidade. Nomes/códigos e outros campos indicados continuam a ser lidos no mestre; não foram copiados.
|
||||
|
||||
| Extensão em skills_edu | Mestre em public | Informação mantida no mestre |
|
||||
|---|---|---|
|
||||
| skill_details | skills | UUID, código e nome PT |
|
||||
| occupation_details | occupations | UUID, código e nome PT |
|
||||
| program_details | courses | UUID, código, título PT, instituição/provider e estado |
|
||||
| human_resource_need_details | labour_demands | UUID, projecto, localização, ocupação, quantidade, qualificação, período, fonte e estado |
|
||||
| educational_opportunity_details | education_investment_proposals | UUID da proposta; título, descrição, valor e moeda continuam na oportunidade ligada por opportunity_id |
|
||||
| qualification_document_details | documents | UUID, nome do ficheiro, MIME, storage_key e SHA-256 |
|
||||
| consent_details | consents | UUID, finalidade, estado, concessão e retirada |
|
||||
|
||||
Os estados destas entidades seguem o mestre existente: por exemplo, consentimento retirado usa `withdrawn`, não uma segunda coluna `revoked`. A unicidade de códigos de competências/ocupações/cursos conserva as regras actuais dos mestres, incluindo soft delete. A versão do grupo documental na extensão refere-se ao comprovativo; `documents.version_no` continua a ser a versão do registo mestre.
|
||||
|
||||
O consentimento deve ter `public.consents.subject_ref` igual ao UUID do perfil. O trigger da extensão valida esta ligação e que a expiração é posterior à concessão. Não converte consentimento de outra finalidade nem muda o mestre.
|
||||
|
||||
| Referência do anexo | Mestre e tipo confirmados |
|
||||
|---|---|
|
||||
| user_master_id e campos de actores *_by/*_user_id | public.users.id — bigint |
|
||||
| sector_master_id | public.sectors.id — bigint |
|
||||
| company_master_id / organisation_master_id | public.organizations.id — UUID |
|
||||
| institution_master_id | public.training_providers.id — UUID |
|
||||
| program_master_id | public.courses.id — UUID |
|
||||
| project_master_id | public.projects.id — UUID |
|
||||
| cluster_master_id | public.economic_clusters.id — UUID |
|
||||
| corridor_master_id | public.development_corridors.id — UUID |
|
||||
| zee_master_id / zfi_master_id | public.investment_zones.id — UUID |
|
||||
| location_master_id | public.geo_locations.id — UUID |
|
||||
|
||||
As FK novas para mestres usam DELETE RESTRICT. Relações internas mantêm CASCADE/SET NULL/RESTRICT definidos no anexo. Não há conversão bigint–UUID. A escolha ZEE versus ZFI exige ainda validação de categoria na futura aplicação: a FK confirma a existência da zona, não a sua categoria.
|
||||
|
||||
## Migrations e segurança
|
||||
|
||||
SQL adaptado em `database/sql/skills-education/01.sql` a `06.sql`; execução por `App\Support\SkillsEducationMigration`. Cada migration é transaccional no PostgreSQL, adquire bloqueio consultivo, valida versão/extensões/tipos dos mestres e exige o bloco anterior. A primeira recusa um schema preexistente não registado. A repetição via Artisan terminou em `Nothing to migrate`; não se deve repetir o SQL bruto por fora do ledger.
|
||||
|
||||
O DOWN verifica tabelas vazias e remove somente as tabelas/funções/schema novos em ordem inversa, com RESTRICT. Recusa perda de dados e não apaga mestres nem extensões PostgreSQL. O rollback integral e a reaplicação foram executados na base isolada. Em produção/local com dados, preparar arquivo e reconciliação antes de qualquer reversão.
|
||||
|
||||
O anexo não define novas colunas geométricas; a localização aponta para o mestre geográfico existente. Não foram criadas coordenadas nem alterada a sua visibilidade. Perfis e necessidades conservam os valores privados por defeito.
|
||||
|
||||
A auditoria recusa UPDATE, DELETE e TRUNCATE, incluindo comandos sem linhas afectadas. Isto complementa o REVOKE do anexo; um administrador da BD continua capaz de alterar DDL. Cifragem de contactos, scanner, autorização de downloads e verificação humana são obrigações da futura aplicação, não capacidades fornecidas pelo DDL.
|
||||
|
||||
## Verificação
|
||||
|
||||
- Suite completa: **183 testes, 1871 verificações aprovadas**.
|
||||
- PHPStan e Pint aprovados.
|
||||
- Testes específicos: tipos/FK, extensão sem mestre recusada, utilizador inexistente, valores privados, intervalo inválido, auditoria imutável, cobertura de índices, rollback vazio, reaplicação e recusa de rollback com dados.
|
||||
- O teste de rollback das zonas passou a reverter primeiro as extensões dependentes. O reset PHPUnit do schema fica restrito às bases conhecidas de teste em localhost:55441; não ocorre no ambiente local operacional.
|
||||
- Não houve alterações de API ou frontend, nem commit Git.
|
||||
|
||||
Para inspeccionar o resultado sem modificar dados:
|
||||
|
||||
```powershell
|
||||
php tools/verify-skills-education.php
|
||||
```
|
||||
|
||||
Para novos ambientes, executar a cadeia Laravel normal após as migrations dos mestres: `php artisan migrate`. `tools/prepare-skills-education.py` documenta a transformação inicial do anexo; não deve ser usado para reescrever migrations já aplicadas. Correcções futuras exigem novas migrations.
|
||||
@@ -0,0 +1,71 @@
|
||||
# Matriz de competências — diagnóstico do novo prompt
|
||||
|
||||
> Actualização de 30/09/2026: o anexo posterior `ce3b9157-6463-4c3f-af50-6d53cba78a23` determina assumir todas as fases aprovadas e solicita reconciliação transversal do modelo de dados. A pendência de autorização genérica abaixo foi ultrapassada. Ver [reconciliação do capítulo 22](../reference-schema-reconciliation.md) para o âmbito e estado técnico actuais; este diagnóstico é histórico, não um pedido renovado de autorização.
|
||||
|
||||
Data: 30/09/2026. Fonte: anexo `bf1d310a-d3a8-48fc-953f-0648622a6c85/Pasted text.txt`. Este pedido retoma o âmbito funcional com uma nova especificação; não retoma automaticamente o prompt anteriormente cancelado.
|
||||
|
||||
A autorização do utilizador para executar está registada na conversa. Não se solicita novamente autorização genérica. O novo documento exige diagnóstico antes de codificar e determina: «Não iniciar desenvolvimento se não houver mapeamento aos RF aplicáveis, proprietário funcional, base legal/finalidade de tratamento, matriz de permissões, critérios de aceitação, plano de testes, estratégia de migração/rollback, definição de evidências, integração com o CRM, tratamento de consentimento e decisão Go/No-Go». Este diagnóstico não substitui esses elementos por aprovações inventadas.
|
||||
|
||||
## Estado observado no código
|
||||
|
||||
- `routes/web.php`: seis rotas internas Education, incluindo cadastro, procura/capacidade, revisão, análise e preparação de oportunidade. Não constituem a API REST versionada requerida.
|
||||
- `EducationController`: pesquisa por texto, estado e localização; catálogos de competências, ocupações, instituições e cursos; não fornece o conjunto de filtros contextuais do novo pedido.
|
||||
- `EducationEnabled` e `config/education.php`: uma flag global; não existem flags independentes para os novos fluxos.
|
||||
- `EducationGapCalculator`: conserva desconhecido, expirado e incomparável; não converte vagas formativas em trabalhadores disponíveis.
|
||||
- `education/index.tsx` e `education/analysis.tsx`: gestão interna; não são painéis individuais, institucionais e de investidores.
|
||||
- As seis migrations `2026_09_30_100100`–`100600` fornecem as extensões `skills_edu`, descritas em [skills-education-schema.md](skills-education-schema.md). A existência das tabelas não fornece serviços de candidatura, documentos, consentimento ou correspondência.
|
||||
- `labour_demands` exige campos preenchidos e admite apenas submitted/reviewed/rejected. Não suporta rascunho incompleto nem arquivo. Uma implementação de CA-01 exige evolução explícita deste contrato; não se deve criar outra tabela concorrente de procura.
|
||||
- A integração `app/Integrations/SuiteCrm` existe. A sua existência não prova contrato nem autorização para transmitir candidatos.
|
||||
- Os testes `EducationTest`, `EducationGapCalculatorTest` e `SkillsEducationSchemaTest` cobrem partes existentes. Não comprovam os novos CT-01–CT-16. Nenhuma nova execução de testes funcionais foi realizada neste diagnóstico documental.
|
||||
|
||||
## Conservar–Melhorar–Substituir–Descontinuar
|
||||
|
||||
| Decisão | Componentes | Justificação |
|
||||
|---|---|---|
|
||||
| Conservar | users, organizations, sectors, projects, economic_clusters, development_corridors, investment_zones, geo_locations | Identidades mestre; não converter bigint em UUID nem duplicar taxonomias. |
|
||||
| Conservar | skills, occupations, courses, training_providers, consents, documents e extensões skills_edu | Reutilizar PK/FK e estados do mestre; novos dados por migrations aditivas. |
|
||||
| Conservar | Fluxos internos, revisão independente, cálculo de lacunas e oportunidade canónica | Compatibilidade; capacidade de formação não equivale a oferta laboral. |
|
||||
| Melhorar | labour_demands e serviços de procura | Rascunhos, arquivo, várias competências, contexto, versões e autorização por organização. |
|
||||
| Melhorar | Interfaces e pesquisa | Painéis por actor, filtros combinados, PT/EN, acessibilidade e precisão territorial autorizada. |
|
||||
| Melhorar | Consentimento, auditoria e documentos | Finalidades separadas, revogação, scanner real, quarentena e autorização em cada leitura. |
|
||||
| Substituir | Nenhum componente nesta fase | Não há evidência que justifique reconstrução integral. |
|
||||
| Descontinuar | Nenhum contrato nesta fase | Preservar consumidores e dados existentes. |
|
||||
|
||||
## Plano por ficheiro — proposto, ainda não executado
|
||||
|
||||
| Ordem | Caminhos | Alteração e verificação necessária |
|
||||
|---|---|---|
|
||||
| 1 | `docs/investmoz/education/requirements.csv`, nova matriz funcional | Relacionar RF-CAN, CA/CT, RF-EDU/CRM/EII/GEO e A.31 com entidades, APIs, ecrãs, evidências e responsáveis. Não atribuir IDs normativos sem conferir a fonte. |
|
||||
| 2 | `config/education.php`, `app/Http/Middleware/EducationEnabled.php` | Flags independentes; desligar sem apagar dados. Testar acesso negado e contratos actuais. |
|
||||
| 3 | Nova migration Laravel posterior a `100600` | Evoluir rascunho/arquivo, concorrência e escopos conforme desenho aprovado; não reescrever SQL aplicado. Testar ida/volta em base isolada e recusa de perda de dados. |
|
||||
| 4 | Novos serviços em `app/Services/Education/`, requests e policies | Escritas transaccionais, segregação, organização vigente, validação contextual e auditoria sem conteúdo pessoal. |
|
||||
| 5 | Novos controllers e rotas versionadas | Perfis, necessidades, instituições, consentimentos e documentos; paginação, limites, conflitos de versão e respostas sem enumeração. Manter rotas internas. |
|
||||
| 6 | `resources/js/pages/education/` e componentes partilhados | Formulários por actor, rascunho/retoma, confirmação, revisão e estados; PT/EN, teclado e móvel. |
|
||||
| 7 | Serviços de pesquisa/correspondência e jobs | Autorização antes de resultados; condições aprovadas antes da pontuação; versão e explicação; revogação verificada também na execução de jobs. Sem encaminhamento automático. |
|
||||
| 8 | `app/Integrations/SuiteCrm/`, contratos e reconciliação | Só campos e finalidades aprovados; deduplicação, idempotência e recuperação; alternativa manual enquanto contrato externo pendente. |
|
||||
| 9 | `tests/Feature/`, `tests/Unit/`, ferramentas de navegador e contratos | CT-01–CT-16: fluxos, fronteiras, acesso, documentos maliciosos, revogação, concorrência, PT/EN, recuperação e carga com linha de base real. |
|
||||
|
||||
## Regras de desenho propostas para revisão
|
||||
|
||||
Proprietário consulta e edita o próprio rascunho; operador institucional precisa de vínculo vigente à instituição verificada e autorização do candidato; revisor não pode validar a própria submissão; investidor só pesquisa perfis validados, consentidos e no âmbito permitido. Contactos e documentos exigem autorização própria, não herdada da pesquisa. Gestão global não comprova mandato institucional.
|
||||
|
||||
Separar consentimentos de pesquisa, correspondência, partilha de contactos e transmissão ao CRM. Não reutilizar consentimento comercial. Revogação deve impedir a próxima leitura e execução de tarefa dependente; tempos de propagação externa, retenção e backups precisam de política aprovada. Esta proposta não declara base legal nem prazo de retenção.
|
||||
|
||||
Sem mudança da ligação principal de dados nesta etapa. Antes de futuras migrations: backup/restauro, contagens e checksums dos mestres, teste isolado, aplicação aditiva e reconciliação. Reversão operacional por flags, preservando registos; reversão estrutural apenas sem perda de dados. Sem deploy nem commit nesta entrega.
|
||||
|
||||
## Gate de diagnóstico
|
||||
|
||||
**Não concluído. Não iniciar desenvolvimento funcional sob este prompt até resolver os bloqueadores materiais expressamente definidos nele.**
|
||||
|
||||
| Elemento | Evidência disponível / falta concreta |
|
||||
|---|---|
|
||||
| Autorização para executar | Recebida; não precisa de repetição. |
|
||||
| Rastreabilidade | Há `requirements.csv`, `requirements-audit.csv` e especificação anterior. Há referências a A.31, mas não foi identificada nesta pesquisa a secção normativa integral com versão e correspondência ao novo anexo. |
|
||||
| Âmbito | O novo pedido inclui indivíduos, instituições e correspondência; a decisão antiga D-EDU-06 excluía dados individuais. Actualizar formalmente o registo de âmbito com o novo pedido, sem tratar a autorização de desenvolvimento como consentimento dos candidatos. |
|
||||
| Responsabilidade e tratamento | Faltam identificação do responsável funcional e política aplicável de finalidade, retenção, menores e destinatários. Papéis genéricos não são designação de pessoas/entidades. |
|
||||
| Correspondência | Faltam regras, pesos e conjunto de referência aprovados; não adoptar exemplos fictícios como dados operacionais. |
|
||||
| Desempenho | Falta linha de base da Fase Zero; não criar limites arbitrários para declarar CT-14 aprovado. |
|
||||
| Integração | Falta contrato específico de candidatos/consentimento com CRM e confirmação da aplicabilidade de AsyncAPI. |
|
||||
| Aceite | UAT com quatro perfis e decisões dos responsáveis não foram realizados; nenhuma aprovação fabricada. |
|
||||
|
||||
Próxima entrada necessária: localização da versão normativa de A.31 e do pacote de decisões da Fase Zero, incluindo os responsáveis e políticas acima. Se esses documentos ainda não existirem, é necessário resolver a divergência entre a ordem de implementar e a proibição explícita de iniciar desenvolvimento contida neste anexo.
|
||||
@@ -0,0 +1,23 @@
|
||||
# Reexecução do prompt — anexos D/E — 29/09/2026
|
||||
|
||||
1. **Fase e estado:** correcção incremental dos comandos de validação e do bloqueio de leitura canónica, aplicada localmente. O módulo completo e os anexos D/E continuam parcialmente implementados.
|
||||
2. **Diagnóstico:** os nomes históricos são `001_mne_schema.sql` e `002_mne_existing_territories.sql`, não os nomes ilustrados no novo anexo. Os SHA-256 de 001–004 foram comparados com os SQL conservados nas quatro evidências de aplicação e coincidem. Não foram renomeados, divididos ou reescritos ficheiros já aplicados.
|
||||
3. **Plano aplicado:** adicionar simulação transaccional ao backfill; dar código de saída não-zero à reconciliação bloqueada; criar 005 para recusar activação/rollout canónico; testar com papel sem superuser.
|
||||
4. **Ficheiros:** criados `005_mne_compatibility_guards.sql` e respectivo DOWN; alterados `BackfillMneCompatibility`, `ReconcileMneCompatibility` e `tools/test-mne-compatibility.php`. Documentação actualizada. Nenhum ficheiro removido; nenhum commit Git criado.
|
||||
5. **Migrations/reversão:** 005 acrescenta somente uma constraint aos controlos; exige 004 e recusa um controlo canónico já activo em vez de o desligar silenciosamente. Usa transacção e advisory lock próprio. DOWN remove apenas essa constraint. UP repetido, DOWN e reaplicação passaram em QA. Depois de 005, não reaplicar 004 como se fosse uma migration pendente: a sua verificação de estrutura detectará a constraint adicional.
|
||||
6. **Impacto:** nenhuma mudança de API, interface, integração externa ou autoridade de escrita. `public` permanece mestre. Nenhuma pessoa ou negócio canónico criado.
|
||||
7. **Testes:** teste SQL de compatibilidade aprovado, incluindo dry-run sem persistência e activação canónica recusada; 11 testes MNE / 91 verificações aprovados. Papel sintético NOLOGIN/NOSUPERUSER conseguiu inserir auditoria e teve 12 mutações de históricos e duas operações DDL recusadas. Papel e dados de teste revertidos integralmente. Não equivale à mudança das credenciais runtime reais.
|
||||
8. **Evidência:** backup integral com restauro de 210 tabelas em `storage/app/private/mne-integrity-backup-20260929-194732/`; aplicação em `storage/app/private/mne-guards-20260929-194806/`; contagens de 97 tabelas anteriores `mne`/`investmoz` preservadas. Execução local de dry-run: 1 identidade, 12 sectores, 0 negócios, rollback confirmado. Reconciliação com `--fail-on-difference` devolveu 1, conforme esperado.
|
||||
9. **Lacunas:** continuam a faltar convergência de todas as colunas do anexo D, histórico persistente de migrations SQL, instalador da cadeia com ordem/idempotência completas, testes de concorrência em duas sessões, navegador, SLA/notificações e deduplicação. O runtime real ainda é superuser, a identidade permanece pendente e não existem negócios reais reconciliados. Não se considera a cadeia 001–005 integralmente certificada.
|
||||
10. **Rastreabilidade:** D.3/E.1 dry-run → comando de backfill → teste de rollback; E.1 fail-on-difference → comando de reconciliação → saída local 1; D.1 canonical_read desactivado → 005 → dois testes negativos; E.2 privilégios → papel isolado → 14 recusas. Separação efectiva dos papéis de operação permanece pendente.
|
||||
11. **Decisão:** Go condicionado para este incremento técnico local; **No-Go** para leitura/escrita canónica, publicação ou produção. Os controlos shadow/canonical_read locais continuam desligados.
|
||||
12. **Próxima fase proposta, não iniciada neste incremento:** completar o registo e o executor da cadeia SQL preservando os checksums históricos, seguido da separação efectiva de owner/migrator/runtime e dos testes de concorrência. As aprovações de desenvolvimento existentes não são consideradas prova de identidade ou reconciliação dos dados.
|
||||
|
||||
Comandos disponíveis:
|
||||
|
||||
```powershell
|
||||
php artisan mne:backfill-compatibility --batch=100 --dry-run
|
||||
php artisan mne:reconcile-compatibility --fail-on-difference
|
||||
```
|
||||
|
||||
O dry-run executa as mesmas validações numa transacção exterior e reverte tudo; pode adquirir locks durante a simulação. Sem `--dry-run`, continua a gravar apenas vínculos/checkpoints/rejeições. `--fail-on-difference` é estrito: enquanto os gates de activação estiverem abertos, devolve falha mesmo com contagens e checksums iguais.
|
||||
@@ -0,0 +1,25 @@
|
||||
# Integridade do schema canónico — 29/09/2026
|
||||
|
||||
## Diagnóstico e âmbito entregue
|
||||
|
||||
A aplicação continua a usar `public.existing_businesses` e os serviços de intake/revisão privados. As 70 tabelas `mne` são um modelo físico separado, ainda sem ligação aos fluxos da aplicação. A presença das tabelas não significa que o mercado completo esteja implementado.
|
||||
|
||||
Foi aplicado `migrations/003_mne_integrity.sql` ao PostgreSQL 17 local. Acrescenta 81 índices onde nenhuma chave de índice B-tree completo cobre o prefixo da chave estrangeira. Índices parciais não asseguram cobertura de todos os registos. Não cria tabelas, copia negócios ou altera dados.
|
||||
|
||||
Os históricos `audit_events`, `business_status_history`, `document_versions` e `negotiation_events` passam a recusar UPDATE, DELETE e TRUNCATE, incluindo operações sem linhas afectadas. Novas evidências continuam a ser inseridas. Correcções devem ser novos eventos. Os triggers não impedem um administrador da base de dados de os desactivar; a separação de papéis operacionais permanece necessária.
|
||||
|
||||
## Validação e reversibilidade
|
||||
|
||||
- Teste reproduzível: `tools/test-mne-integrity.php`, exclusivamente em `portal_services_test`, porta 55441, `APP_ENV=testing`.
|
||||
- Criação isolada de 001 e 002; aplicação dupla de 003 sem índices duplicados; todas as FK com cobertura; 12 mutações recusadas; DOWN seguido de reaplicação; rollback integral da transacção de teste.
|
||||
- Backup completo e restauro isolado com contagens reconciliadas de 204 tabelas: `storage/app/private/mne-integrity-backup-20260929-185518/`.
|
||||
- Aplicação local com contagens preservadas de 91 tabelas `mne`/`investmoz`: `storage/app/private/mne-integrity-20260929-185534/`.
|
||||
- Reversão deste incremento: `migrations/003_mne_integrity_down.sql`. Remove apenas os triggers, a função e os índices identificados como pertencentes a 003. Não executar o DOWN integral 001 para reverter este incremento.
|
||||
|
||||
Nenhuma mudança de UI, API ou eventos externos; o OpenAPI mantém o contrato privado existente. Não foi executada a suite completa da aplicação neste incremento SQL. Nenhum commit Git foi criado.
|
||||
|
||||
## Trabalho ainda necessário
|
||||
|
||||
A fase de modelo e integração continua incompleta. Antes de adoptar o schema `mne` nos serviços é necessário resolver a identidade dos utilizadores (`public.users.id` bigint versus `mne.parties.user_id` UUID), o vínculo dos sectores e a migração reconciliada dos negócios existentes, sem criar cópias concorrentes. Publicação com duas aprovações, documentos/scanner, catálogo anonimizado, CRM, sala segura, negociação e facturação continuam pendentes conforme a matriz de implementação. Este incremento não constitui aprovação de produção nem entrega integral do prompt.
|
||||
|
||||
Incremento seguinte iniciado e aplicado: [ponte de compatibilidade 004](compatibility.md), com vínculos, backfill e repositório legado. A integração canónica completa continua pendente; consultar os gates e resultados nesse documento.
|
||||
@@ -0,0 +1,56 @@
|
||||
# Ponte de compatibilidade — 29/09/2026
|
||||
|
||||
Actualização: [reexecução dos anexos D/E](annex-de-execution.md), com migration 005 aplicada, dry-run, saída estrita de reconciliação e testes de privilégios numa conta isolada não-superuser. As limitações de activação abaixo mantêm-se.
|
||||
|
||||
## Estado da entrega
|
||||
|
||||
Incremento aplicado e testado localmente. **O anexo D ainda não está integralmente concluído.** `public` continua como autoridade de leitura e escrita dos formulários. Não há escrita dupla, migração de negócios, publicação nem activação de leitura canónica.
|
||||
|
||||
## Inventário confirmado
|
||||
|
||||
`public.users.id` e `public.sectors.id` são bigint; `public.existing_businesses.id`, `mne.parties.id` e `mne.existing_businesses.id` são UUID. O formulário privado não possui campo sectorial. A associação territorial de `mne.locations` continua a usar os identificadores inteiros de `investmoz`, sem nova taxonomia.
|
||||
|
||||
`004_mne_compatibility.sql` cria seis tabelas: identity_links, sector_links, business_links, compatibility_checkpoints, compatibility_rejections e compatibility_controls. Todas as novas FK têm cobertura por índices completos das constraints UNIQUE. O script verifica pré-condições, usa bloqueio consultivo, recusa tabelas de ponte com definição divergente e pode ser reaplicado. Não altera os ficheiros 001–003.
|
||||
|
||||
A coluna UUID `mne.parties.user_id` é mantida para reversibilidade, mas a nova constraint exige NULL. Contas autenticadas passam a ser resolvidas exclusivamente através de identity_links. A migration recusa dados antigos nesse campo em vez de tentar converter bigint em UUID. Pessoas podem existir sem conta; IdentityResolver só devolve uma ligação activa, vigente, a uma pessoa não arquivada. O resolver não autentica nem atribui permissões.
|
||||
|
||||
## Aplicação e backfill
|
||||
|
||||
BusinessRepository, LegacyBusinessRepository e CompatibleBusinessRepository estão ligados aos pontos de consulta dos controladores e dos quatro serviços privados. Políticas, bloqueios de escrita, versões e payloads HTTP existentes são preservados. Criação e gravação continuam nos modelos legados.
|
||||
|
||||
MneBusinessRepository é, por enquanto, um adaptador **apenas para comparação de cinco campos**: código, modalidade, estado, classificação e versão. Não implementa ainda a hidratação completa de um negócio nem substitui a autorização legada. SectorResolver v1 devolve UUIDs estáveis de ligação à taxonomia existente; não copia nomes ou cria sectores canónicos concorrentes.
|
||||
|
||||
```powershell
|
||||
php artisan mne:backfill-compatibility --batch=100
|
||||
php artisan mne:reconcile-compatibility
|
||||
```
|
||||
|
||||
Usar um PHP com extensão pgsql. O backfill faz transacção por lote, bloqueio consultivo, IDs determinísticos, checksums e checkpoint do último lote não vazio. Cada nova execução percorre novamente a origem, para detectar também alterações anteriores ao checkpoint. Dados modificados invalidam a reconciliação. Vínculos sem destino canónico ficam pendentes, com código, causa e responsável `data-steward` na fila de excepções. A resolução não é aceite automaticamente. O backfill não cria pessoas ou negócios canónicos.
|
||||
|
||||
Resultado local: **1 vínculo de identidade pendente, 12 vínculos sectoriais activos, 0 negócios**, nenhuma chave em falta nem checksum divergente na execução. Há uma excepção de identidade não resolvida. A inexistência de negócios não conta como validação de uma migração real.
|
||||
|
||||
## Comparação sombra e reversão operacional
|
||||
|
||||
A comparação só é agendada após autorização de consulta da ficha. Requer `INVESTMOZ_MNE_COMPATIBILITY_SHADOW=true` e o controlo `shadow` em compatibility_controls activado com referência de evidência e percentagem. Ambos estão desligados na instalação local. O consumidor é `php artisan queue:work database --queue=mne-shadow`; filas síncronas são recusadas. O evento contém apenas resultado e latência, sem payload, identificador ou mensagem de excepção. Desligar o controlo interrompe novos agendamentos; trabalhos já enfileirados continuam somente de leitura.
|
||||
|
||||
Comparação parcial igual não abre o gate de leitura canónica. `canonical_read` está reservado, sem adaptador de resposta completo habilitado. Alterar a flag não transfere leitura ou escrita. Não se apresentam métricas de fallback canónico como se essa capacidade já existisse.
|
||||
|
||||
O DOWN 004 recusa qualquer vínculo, checkpoint, rejeição, controlo activo ou pessoa dependente. Esta é a situação local após backfill: para reverter a estrutura, é necessário arquivar/reconciliar primeiro essas dependências. Para continuar a operar pelo legado não é necessário executar DOWN. O script não remove dados de negócios nem históricos.
|
||||
|
||||
## Evidência
|
||||
|
||||
- Backup/restauro verificado de 204 tabelas antes de 004: `storage/app/private/mne-integrity-backup-20260929-191334/`.
|
||||
- Aplicação: `storage/app/private/mne-compatibility-20260929-191400/`; contagens de 91 tabelas anteriores preservadas; schema mne passa de 70 para 76 tabelas.
|
||||
- `tools/test-mne-compatibility.php`: 001–004 em base isolada, repetição, DOWN/reaplicação, backfill repetido, negócio sintético, acesso proprietário/terceiro, unicidade, FK, identidade revogada, sector desconhecido/alterado e recusa de estrutura divergente. Transacção integralmente revertida.
|
||||
- Suite completa: 178 testes / 1820 verificações aprovados antes do teste adicional de isolamento da sombra. Esse teste adicional passou com 5 verificações.
|
||||
- PHPStan aprovado. Contrato OpenAPI privado preservado. Sem alterações de interface; navegador não validado neste incremento.
|
||||
|
||||
## Gates ainda não satisfeitos
|
||||
|
||||
1. Correspondência canónica da identidade pendente e resolução formal das excepções.
|
||||
2. Campo/relação sectorial nos negócios e reconciliação real de proprietários, estados e conteúdo completo.
|
||||
3. Adaptador de leitura canónica completo, comparação integral, rollout e fallback testados.
|
||||
4. Separação owner/migrator/runtime e testes de privilégios: a ligação local actual é superuser. Os triggers existem, mas esta conta pode desactivá-los. Nenhuma garantia de privilégio mínimo é declarada.
|
||||
5. Concorrência com duas sessões, jornadas de navegador nos dois modos, notificações/SLA e deduplicação ponta a ponta.
|
||||
|
||||
Permanecem bloqueadas a transferência de escrita, a publicação e a produção. Não foi criado commit Git.
|
||||
@@ -0,0 +1,187 @@
|
||||
{
|
||||
"proponent_type": {
|
||||
"step": 1,
|
||||
"pt": "Tipo de proponente",
|
||||
"en": "Applicant type",
|
||||
"type": "select",
|
||||
"max": 30,
|
||||
"options": {
|
||||
"person": "Pessoa \/ Person",
|
||||
"organization": "Organização \/ Organization"
|
||||
}
|
||||
},
|
||||
"proponent_name": {
|
||||
"step": 1,
|
||||
"pt": "Nome ou denominação",
|
||||
"en": "Name",
|
||||
"type": "text",
|
||||
"max": 200
|
||||
},
|
||||
"tax_identifier": {
|
||||
"step": 1,
|
||||
"pt": "NUIT ou identificador",
|
||||
"en": "Tax identifier",
|
||||
"type": "text",
|
||||
"max": 50
|
||||
},
|
||||
"contact_email": {
|
||||
"step": 1,
|
||||
"pt": "Email de contacto",
|
||||
"en": "Contact email",
|
||||
"type": "email",
|
||||
"max": 254
|
||||
},
|
||||
"contact_phone": {
|
||||
"step": 1,
|
||||
"pt": "Telefone",
|
||||
"en": "Phone",
|
||||
"type": "tel",
|
||||
"max": 40
|
||||
},
|
||||
"country": {
|
||||
"step": 1,
|
||||
"pt": "País (código de duas letras)",
|
||||
"en": "Country (two-letter code)",
|
||||
"type": "text",
|
||||
"max": 2
|
||||
},
|
||||
"acting_capacity": {
|
||||
"step": 2,
|
||||
"pt": "Qualidade em que actua",
|
||||
"en": "Acting capacity",
|
||||
"type": "select",
|
||||
"max": 30,
|
||||
"options": {
|
||||
"holder": "Titular \/ Holder",
|
||||
"representative": "Representante \/ Representative"
|
||||
}
|
||||
},
|
||||
"declared_holder": {
|
||||
"step": 2,
|
||||
"pt": "Titular declarado",
|
||||
"en": "Declared holder",
|
||||
"type": "text",
|
||||
"max": 200
|
||||
},
|
||||
"mandate_reference": {
|
||||
"step": 2,
|
||||
"pt": "Referência do mandato",
|
||||
"en": "Mandate reference",
|
||||
"type": "text",
|
||||
"max": 500
|
||||
},
|
||||
"mandate_valid_until": {
|
||||
"step": 2,
|
||||
"pt": "Validade do mandato",
|
||||
"en": "Mandate expiry",
|
||||
"type": "date",
|
||||
"max": 10
|
||||
},
|
||||
"legitimacy_declaration": {
|
||||
"step": 2,
|
||||
"pt": "Declaro legitimidade para esta submissão",
|
||||
"en": "I declare authority to submit",
|
||||
"type": "select",
|
||||
"max": 3,
|
||||
"options": {
|
||||
"yes": "Sim \/ Yes",
|
||||
"no": "Não \/ No"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"step": 3,
|
||||
"pt": "Título proposto",
|
||||
"en": "Proposed title",
|
||||
"type": "text",
|
||||
"max": 200
|
||||
},
|
||||
"description": {
|
||||
"step": 3,
|
||||
"pt": "Descrição reservada",
|
||||
"en": "Private description",
|
||||
"type": "textarea",
|
||||
"max": 10000
|
||||
},
|
||||
"declared_reason": {
|
||||
"step": 3,
|
||||
"pt": "Motivo e objectivo",
|
||||
"en": "Reason and objective",
|
||||
"type": "textarea",
|
||||
"max": 2000
|
||||
},
|
||||
"valid_until": {
|
||||
"step": 3,
|
||||
"pt": "Validade declarada",
|
||||
"en": "Declared expiry",
|
||||
"type": "date",
|
||||
"max": 10
|
||||
},
|
||||
"asking_value": {
|
||||
"step": 4,
|
||||
"pt": "Valor pedido",
|
||||
"en": "Asking value",
|
||||
"type": "decimal",
|
||||
"max": 24
|
||||
},
|
||||
"currency": {
|
||||
"step": 4,
|
||||
"pt": "Moeda (código de três letras)",
|
||||
"en": "Currency (three-letter code)",
|
||||
"type": "text",
|
||||
"max": 3
|
||||
},
|
||||
"value_nature": {
|
||||
"step": 4,
|
||||
"pt": "Natureza do valor",
|
||||
"en": "Value basis",
|
||||
"type": "select",
|
||||
"max": 30,
|
||||
"options": {
|
||||
"asking": "Pedido \/ Asking",
|
||||
"indicative": "Indicativo \/ Indicative",
|
||||
"appraised": "Avaliado, sujeito a prova \/ Appraised, evidence required"
|
||||
}
|
||||
},
|
||||
"equity_percentage": {
|
||||
"step": 4,
|
||||
"pt": "Participação disponível (%)",
|
||||
"en": "Available equity (%)",
|
||||
"type": "decimal",
|
||||
"max": 8
|
||||
},
|
||||
"funds_use": {
|
||||
"step": 4,
|
||||
"pt": "Utilização dos fundos",
|
||||
"en": "Use of funds",
|
||||
"type": "textarea",
|
||||
"max": 2000
|
||||
},
|
||||
"location_notes": {
|
||||
"step": 5,
|
||||
"pt": "Localização declarada (reservada)",
|
||||
"en": "Declared location (private)",
|
||||
"type": "textarea",
|
||||
"max": 2000
|
||||
},
|
||||
"asset_notes": {
|
||||
"step": 6,
|
||||
"pt": "Resumo dos activos e direitos declarados",
|
||||
"en": "Declared assets and rights summary",
|
||||
"type": "textarea",
|
||||
"max": 2000
|
||||
},
|
||||
"source_reference": {
|
||||
"step": 7,
|
||||
"pt": "Referência da fonte",
|
||||
"en": "Source reference",
|
||||
"type": "textarea",
|
||||
"max": 2000
|
||||
},
|
||||
"document_reference": {
|
||||
"step": 7,
|
||||
"pt": "Referências documentais (não são ficheiros validados)",
|
||||
"en": "Document references (not validated files)",
|
||||
"type": "textarea",
|
||||
"max": 2000
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,13 @@
|
||||
# Estado actual — Mercado de Negócios Existentes
|
||||
|
||||
29/09/2026 — [Formulário privado por oito etapas](wizard-implementation.md) disponível com autosave, retoma, sector e confirmação no servidor. Testado por API e navegador; os anexos A–C completos continuam pendentes.
|
||||
|
||||
29/09/2026 — [Anexos D/E: incremento 005](annex-de-execution.md) aplicado. Simulação do backfill, reconciliação com saída de falha e bloqueio SQL de activação canónica testados; a entrega integral continua pendente.
|
||||
|
||||
29/09/2026 — [Ponte de compatibilidade 004](compatibility.md) aplicada: seis tabelas de vínculo/controlo, backfill, resolvers e interface de repositório nos serviços privados. Legado permanece autoridade; identidade pendente, leitura canónica completa e privilégios mínimos ainda impedem a activação.
|
||||
|
||||
29/09/2026 — [Integridade do schema canónico](canonical-integrity.md): aplicado incremento SQL com 81 índices e protecção de quatro históricos; teste isolado de idempotência/reversão aprovado. As 70 tabelas `mne` ainda não alimentam a interface privada. A integração do modelo e as fases seguintes continuam incompletas.
|
||||
|
||||
Actualização 29/09/2026: [revisão granular com evidência, validade e invalidação por alteração](verification-evidence.md) implementada na ficha privada. Suite: 173 testes / 1785 verificações aprovadas. Os limites e contagens abaixo descrevem a entrega anterior; o módulo integral continua incompleto.
|
||||
|
||||
28/09/2026. A instrução mais recente autoriza executar todas as fases e avançar automaticamente. Substitui a paragem entre fases do prompt inicial. Não presume evidências jurídicas, nomeação de revisores, publicação ou integração externa efectiva.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "InvestMoz — private existing-business intake",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.0",
|
||||
"description": "Internal preparation and review only. No publication, certification, legal transfer, NDA, payment or external CRM operation."
|
||||
},
|
||||
"servers": [
|
||||
@@ -165,37 +165,241 @@
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"maxLength": 200
|
||||
"maxLength": 200,
|
||||
"description": "Proposed title"
|
||||
},
|
||||
"description": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 10000
|
||||
"maxLength": 10000,
|
||||
"description": "Private description"
|
||||
},
|
||||
"declared_reason": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 2000
|
||||
"maxLength": 2000,
|
||||
"description": "Reason and objective"
|
||||
},
|
||||
"source_reference": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 2000
|
||||
"maxLength": 2000,
|
||||
"description": "Source reference"
|
||||
},
|
||||
"valid_until": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"format": "date"
|
||||
"maxLength": 10,
|
||||
"format": "date",
|
||||
"description": "Declared expiry"
|
||||
},
|
||||
"proponent_type": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 30,
|
||||
"enum": [
|
||||
"person",
|
||||
"organization",
|
||||
null
|
||||
],
|
||||
"description": "Applicant type"
|
||||
},
|
||||
"proponent_name": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 200,
|
||||
"description": "Name"
|
||||
},
|
||||
"tax_identifier": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 50,
|
||||
"description": "Tax identifier"
|
||||
},
|
||||
"contact_email": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 254,
|
||||
"description": "Contact email"
|
||||
},
|
||||
"contact_phone": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 40,
|
||||
"description": "Phone"
|
||||
},
|
||||
"country": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 2,
|
||||
"description": "Country (two-letter code)"
|
||||
},
|
||||
"acting_capacity": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 30,
|
||||
"enum": [
|
||||
"holder",
|
||||
"representative",
|
||||
null
|
||||
],
|
||||
"description": "Acting capacity"
|
||||
},
|
||||
"declared_holder": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 200,
|
||||
"description": "Declared holder"
|
||||
},
|
||||
"mandate_reference": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 500,
|
||||
"description": "Mandate reference"
|
||||
},
|
||||
"mandate_valid_until": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 10,
|
||||
"format": "date",
|
||||
"description": "Mandate expiry"
|
||||
},
|
||||
"legitimacy_declaration": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 3,
|
||||
"enum": [
|
||||
"yes",
|
||||
"no",
|
||||
null
|
||||
],
|
||||
"description": "I declare authority to submit"
|
||||
},
|
||||
"asking_value": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 24,
|
||||
"description": "Asking value"
|
||||
},
|
||||
"currency": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 3,
|
||||
"description": "Currency (three-letter code)"
|
||||
},
|
||||
"value_nature": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 30,
|
||||
"enum": [
|
||||
"asking",
|
||||
"indicative",
|
||||
"appraised",
|
||||
null
|
||||
],
|
||||
"description": "Value basis"
|
||||
},
|
||||
"equity_percentage": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 8,
|
||||
"description": "Available equity (%)"
|
||||
},
|
||||
"funds_use": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 2000,
|
||||
"description": "Use of funds"
|
||||
},
|
||||
"location_notes": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 2000,
|
||||
"description": "Declared location (private)"
|
||||
},
|
||||
"asset_notes": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 2000,
|
||||
"description": "Declared assets and rights summary"
|
||||
},
|
||||
"document_reference": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 2000,
|
||||
"description": "Document references (not validated files)"
|
||||
},
|
||||
"wizard_version": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"enum": [
|
||||
"1",
|
||||
null
|
||||
]
|
||||
},
|
||||
"wizard_step": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"pattern": "^[1-8]$"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sector_id": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"description": "Existing active public.sectors identifier; required on wizard submission."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -516,41 +720,245 @@
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"maxLength": 200
|
||||
"maxLength": 200,
|
||||
"description": "Proposed title"
|
||||
},
|
||||
"description": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 10000
|
||||
"maxLength": 10000,
|
||||
"description": "Private description"
|
||||
},
|
||||
"declared_reason": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 2000
|
||||
"maxLength": 2000,
|
||||
"description": "Reason and objective"
|
||||
},
|
||||
"source_reference": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 2000
|
||||
"maxLength": 2000,
|
||||
"description": "Source reference"
|
||||
},
|
||||
"valid_until": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"format": "date"
|
||||
"maxLength": 10,
|
||||
"format": "date",
|
||||
"description": "Declared expiry"
|
||||
},
|
||||
"proponent_type": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 30,
|
||||
"enum": [
|
||||
"person",
|
||||
"organization",
|
||||
null
|
||||
],
|
||||
"description": "Applicant type"
|
||||
},
|
||||
"proponent_name": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 200,
|
||||
"description": "Name"
|
||||
},
|
||||
"tax_identifier": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 50,
|
||||
"description": "Tax identifier"
|
||||
},
|
||||
"contact_email": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 254,
|
||||
"description": "Contact email"
|
||||
},
|
||||
"contact_phone": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 40,
|
||||
"description": "Phone"
|
||||
},
|
||||
"country": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 2,
|
||||
"description": "Country (two-letter code)"
|
||||
},
|
||||
"acting_capacity": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 30,
|
||||
"enum": [
|
||||
"holder",
|
||||
"representative",
|
||||
null
|
||||
],
|
||||
"description": "Acting capacity"
|
||||
},
|
||||
"declared_holder": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 200,
|
||||
"description": "Declared holder"
|
||||
},
|
||||
"mandate_reference": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 500,
|
||||
"description": "Mandate reference"
|
||||
},
|
||||
"mandate_valid_until": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 10,
|
||||
"format": "date",
|
||||
"description": "Mandate expiry"
|
||||
},
|
||||
"legitimacy_declaration": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 3,
|
||||
"enum": [
|
||||
"yes",
|
||||
"no",
|
||||
null
|
||||
],
|
||||
"description": "I declare authority to submit"
|
||||
},
|
||||
"asking_value": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 24,
|
||||
"description": "Asking value"
|
||||
},
|
||||
"currency": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 3,
|
||||
"description": "Currency (three-letter code)"
|
||||
},
|
||||
"value_nature": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 30,
|
||||
"enum": [
|
||||
"asking",
|
||||
"indicative",
|
||||
"appraised",
|
||||
null
|
||||
],
|
||||
"description": "Value basis"
|
||||
},
|
||||
"equity_percentage": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 8,
|
||||
"description": "Available equity (%)"
|
||||
},
|
||||
"funds_use": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 2000,
|
||||
"description": "Use of funds"
|
||||
},
|
||||
"location_notes": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 2000,
|
||||
"description": "Declared location (private)"
|
||||
},
|
||||
"asset_notes": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 2000,
|
||||
"description": "Declared assets and rights summary"
|
||||
},
|
||||
"document_reference": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 2000,
|
||||
"description": "Document references (not validated files)"
|
||||
},
|
||||
"wizard_version": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"enum": [
|
||||
"1",
|
||||
null
|
||||
]
|
||||
},
|
||||
"wizard_step": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"pattern": "^[1-8]$"
|
||||
}
|
||||
}
|
||||
},
|
||||
"version_no": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"sector_id": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"description": "Existing active public.sectors identifier; required on wizard submission."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -641,6 +1049,13 @@
|
||||
"review_consent": {
|
||||
"type": "boolean",
|
||||
"description": "Must be true for submission. Stored in encrypted review evidence. Does not authorize publication."
|
||||
},
|
||||
"confirmation_code": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"description": "Exact business code required for wizard_version=1 submission, in addition to internal review consent."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1359,37 +1774,241 @@
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"maxLength": 200
|
||||
"maxLength": 200,
|
||||
"description": "Proposed title"
|
||||
},
|
||||
"description": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 10000
|
||||
"maxLength": 10000,
|
||||
"description": "Private description"
|
||||
},
|
||||
"declared_reason": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 2000
|
||||
"maxLength": 2000,
|
||||
"description": "Reason and objective"
|
||||
},
|
||||
"source_reference": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 2000
|
||||
"maxLength": 2000,
|
||||
"description": "Source reference"
|
||||
},
|
||||
"valid_until": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"format": "date"
|
||||
"maxLength": 10,
|
||||
"format": "date",
|
||||
"description": "Declared expiry"
|
||||
},
|
||||
"proponent_type": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 30,
|
||||
"enum": [
|
||||
"person",
|
||||
"organization",
|
||||
null
|
||||
],
|
||||
"description": "Applicant type"
|
||||
},
|
||||
"proponent_name": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 200,
|
||||
"description": "Name"
|
||||
},
|
||||
"tax_identifier": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 50,
|
||||
"description": "Tax identifier"
|
||||
},
|
||||
"contact_email": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 254,
|
||||
"description": "Contact email"
|
||||
},
|
||||
"contact_phone": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 40,
|
||||
"description": "Phone"
|
||||
},
|
||||
"country": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 2,
|
||||
"description": "Country (two-letter code)"
|
||||
},
|
||||
"acting_capacity": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 30,
|
||||
"enum": [
|
||||
"holder",
|
||||
"representative",
|
||||
null
|
||||
],
|
||||
"description": "Acting capacity"
|
||||
},
|
||||
"declared_holder": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 200,
|
||||
"description": "Declared holder"
|
||||
},
|
||||
"mandate_reference": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 500,
|
||||
"description": "Mandate reference"
|
||||
},
|
||||
"mandate_valid_until": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 10,
|
||||
"format": "date",
|
||||
"description": "Mandate expiry"
|
||||
},
|
||||
"legitimacy_declaration": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 3,
|
||||
"enum": [
|
||||
"yes",
|
||||
"no",
|
||||
null
|
||||
],
|
||||
"description": "I declare authority to submit"
|
||||
},
|
||||
"asking_value": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 24,
|
||||
"description": "Asking value"
|
||||
},
|
||||
"currency": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 3,
|
||||
"description": "Currency (three-letter code)"
|
||||
},
|
||||
"value_nature": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 30,
|
||||
"enum": [
|
||||
"asking",
|
||||
"indicative",
|
||||
"appraised",
|
||||
null
|
||||
],
|
||||
"description": "Value basis"
|
||||
},
|
||||
"equity_percentage": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 8,
|
||||
"description": "Available equity (%)"
|
||||
},
|
||||
"funds_use": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 2000,
|
||||
"description": "Use of funds"
|
||||
},
|
||||
"location_notes": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 2000,
|
||||
"description": "Declared location (private)"
|
||||
},
|
||||
"asset_notes": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 2000,
|
||||
"description": "Declared assets and rights summary"
|
||||
},
|
||||
"document_reference": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 2000,
|
||||
"description": "Document references (not validated files)"
|
||||
},
|
||||
"wizard_version": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"enum": [
|
||||
"1",
|
||||
null
|
||||
]
|
||||
},
|
||||
"wizard_step": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"pattern": "^[1-8]$"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sector_id": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"description": "Existing active public.sectors identifier; required on wizard submission."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"passed": true,
|
||||
"observations": [
|
||||
{
|
||||
"scenario": "autosave_and_reload",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"scenario": "responsive_summary",
|
||||
"width": 390,
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"scenario": "responsive_summary",
|
||||
"width": 1440,
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"scenario": "private_submission",
|
||||
"passed": true
|
||||
}
|
||||
],
|
||||
"errors": []
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
# Formulário por etapas — entrega local, 29/09/2026
|
||||
|
||||
**Disponível em `/investidor/negocios`: criar rascunho e abrir a ficha. O prompt A–C completo ainda não está entregue.** Este incremento implementa a preparação privada por etapas, autosave, retoma, sector e confirmação. Não implementa ainda publicação, uploads seguros, manifestações MNE ou operação integral.
|
||||
|
||||
1. **Diagnóstico tecnológico:** Laravel 13.30.1, PHP de validação 8.5.11, React 19.2.8, TypeScript 5.9.3, Inertia, Vite Plus, Leaflet e PostgreSQL 17.11/PostGIS. Mantidos componentes e autenticação por sessão/email verificado. Filas database/Redis e disco S3 são configuráveis; conectividade Redis/S3/ClamAV e CRM não ensaiada neste incremento. Sem novas dependências.
|
||||
2. **Pressupostos:** formulário autenticado, classificação reservada e `public.existing_businesses` como autoridade. Identidade e titularidade são declarações pendentes. Referências documentais não são uploads, mandatos validados ou autorizações de transmissão. O preenchimento permite rascunhos incompletos; a submissão aplica validações adicionais. Clientes anteriores sem wizard_version conservam o contrato legado; converter todos os clientes exige uma migração de contrato posterior.
|
||||
3. **Mapa A–C:** ver matriz abaixo. Não há equivalência entre oito etapas visíveis e implementação de todos os requisitos dessas etapas.
|
||||
4. **Plano por fase:** responsável técnico da implementação: Codex. Incremento A1 executado: schema/validação, FK sectorial, wizard e testes. A2 pendente: localização normalizada/cartografia, contexto económico, anexos/scanner, consentimentos separados e acessibilidade integral. B pendente: manifestação MNE ligada a anúncio publicado, perfil e CRM idempotente. C pendente: aprovações, tarefas, SLA, alertas e dashboard. Dependências e gates mantidos em `implementation-status.md`; autorizações de desenvolvimento existentes preservadas.
|
||||
5. **Modelo PostgreSQL:** coluna nullable bigint `existing_businesses.sector_id`, FK RESTRICT para a taxonomia existente e índice B-tree completo. Dados declarativos privados ficam no payload cifrado já existente; activos/direitos continuam nas entidades tipadas existentes. Não foram criadas pessoas, sectores ou negócios no schema mne. Localização deste formulário é ainda descritiva, não georreferenciada.
|
||||
6. **Migration/modelos:** `2026_09_29_200000_existing_business_form_sector.php` aplicada localmente. DOWN remove a coluna somente se não houver associações sectoriais. Modelo ExistingBusiness existente preservado; `BusinessFormSchema` centraliza campos, rótulos PT/EN e validação. Snapshot/auditoria cifrados existentes conservados.
|
||||
7. **API:** mesmos nove endpoints privados `/api/v1/existing-business`. `sector_id` é adicional no draft; `confirmation_code` exigido na submissão de wizard_version=1. GET da ficha fornece formSchema e sectores activos. Erros 403/409/422 e ausência de publicação preservados. OpenAPI actualizado para 1.2.0; schema exportável por `tools/export-business-form-schema.php`.
|
||||
8. **Permissões:** proprietário verificado cria/edita rascunho ou pedido de informação; revisor designado, independente e com MFA consulta submissões. Terceiros recusados. Revisores recebem os novos campos apenas após a policy de consulta. Autosave usa as mesmas autorizações e versões da gravação manual.
|
||||
9. **Estados:** rascunho → submetido; revisão/pedido de informação/retirada existentes. Confirmação explícita por código e consentimento de análise interna no servidor. Repetir submissão com versão anterior dá 409 sem duplicar histórico; ainda não há comprovativo idempotente por chave de pedido. As duas aprovações e os demais estados de A–C continuam pendentes.
|
||||
10. **Serviços/eventos/Jobs:** reutilizados ExistingBusinessDrafts, ExistingBusinessIntake, objectos, snapshots e ponte legada. Nenhum Job externo, tarefa de triagem, notificação ou envio CRM novo. O consentimento actual é de revisão interna; não é o catálogo completo de consentimentos versionados do anexo.
|
||||
11. **React:** `business-wizard.tsx` oferece oito etapas, progresso, PT/EN nos controlos principais, autosave após três segundos, retoma no servidor, resumo e confirmação. Navegação/fecho avisam sobre alterações não gravadas; conflitos não substituem silenciosamente dados. Representante revela campos de mandato. Activos/direitos reutilizam BusinessObjects; estes componentes existentes ainda não estão integralmente traduzidos. Criação/consulta administrativa por representação de terceiros permanece pendente.
|
||||
12. **Código por ficheiro:** novos `app/Services/BusinessFormSchema.php`, migration sectorial, `resources/js/pages/investment/business-wizard.tsx`, `tests/Feature/ExistingBusinessWizardTest.php`, exportador de schema e ferramentas de navegador. Alterados controlador, Drafts, Intake, business.tsx, businesses.tsx, business-types.ts e gerador OpenAPI. Código completo nesses ficheiros; sem ficheiros removidos nem commit Git.
|
||||
13. **Testes:** suite completa executada: 180 testes / 1845 verificações. Depois do controlo adicional por modalidade e do teste de rollback, suite específica do wizard: 2 testes / 30 verificações aprovados. Cobertura inclui cifragem, terceiro recusado, retoma, sector, dados inválidos, conflito de versão, mandato, activo obrigatório, confirmação, submissão duplicada e rollback. Navegador: autosave/recarga, resumo em 390/1440 px e submissão, sem excepções JS. Isto não é uma auditoria WCAG completa.
|
||||
14. **Comandos:** abaixo. Não foi necessário instalar pacotes Composer/npm. Backend requer PHP com pgsql. Migration local aplicada após backup/restauro, usando `--path` específico. Os scripts de navegador e fixtures só usam QA em 55441/18027, nunca a base local operacional.
|
||||
15. **Resultados:** PHPStan, Pint, TypeScript, lint do wizard e build aprovados. Backup com restauro de 210 tabelas: `storage/app/private/mne-integrity-backup-20260929-200103/`. Evidência do navegador: `wizard-browser-validation.json`. Auditoria de dependências e ensaios de serviços externos não executados neste incremento.
|
||||
16. **Riscos residuais:** restantes campos e relações A; identidade normalizada; coordenadas/geometria; CRM MNE; scanner/upload; redis em operação; consentimentos separados; comprovativo/notificações; SLA; aprovações/publicação; tradução integral; testes de acessibilidade, carga e serviços externos. A conta de BD local ainda é superuser. Nenhuma alegação de prontidão para produção.
|
||||
17. **Go/No-Go:** Go para testar preparação privada local; **No-Go para entrega integral A–C ou Go-Live**. Os fluxos incompletos não foram expostos como funcionais.
|
||||
|
||||
| Requisito | Implementação actual | Estado |
|
||||
|---|---|---|
|
||||
| A: proponente/representação | Campos cifrados, mandato condicional, declaração e validação | Parcial; sem verificação de identidade/documentos |
|
||||
| A: modalidade/sector/valor | Taxonomia existente, FK, limites, moeda e natureza na submissão | Parcial; faltam intervalos e estruturas completas |
|
||||
| A: localização/contexto | Descrição privada | Geografia estruturada e ligações pendentes |
|
||||
| A: activos/direitos | Editor tipado existente; objecto aplicável exigido por modalidade | Parcial |
|
||||
| A: fontes/documentos | Referências declaradas | Upload/scanner pendentes |
|
||||
| A: rascunho/retoma/resumo | Autosave, versão, oito etapas, confirmação no servidor | Testado |
|
||||
| B: interesse/CRM | Serviço geral existente preservado | Integração MNE pendente |
|
||||
| C: revisão | Revisão privada e histórico existentes | Tarefas/SLA/dupla aprovação pendentes |
|
||||
|
||||
```powershell
|
||||
php artisan migrate --path=database/migrations/2026_09_29_200000_existing_business_form_sector.php
|
||||
php tools/export-business-form-schema.php
|
||||
node tools/existing-business-openapi.mjs
|
||||
php vendor/bin/phpunit --filter ExistingBusinessWizard --do-not-cache-result
|
||||
npm.cmd run types:check
|
||||
npm.cmd run build
|
||||
```
|
||||
|
||||
Em ambiente QA isolado (`APP_ENV=testing`, base `portal_services_test`, porta 55441): `php tools/business-wizard-browser-seed.php`, seguido de `node tools/business-wizard-browser.mjs`, com servidor QA 18027 e navegador CDP 9224. As fixtures sintéticas ficam apenas nessa base de teste.
|
||||
@@ -0,0 +1,55 @@
|
||||
# Reconciliação do DDL de referência — 30/09/2026
|
||||
|
||||
Origem: anexo `ce3b9157-6463-4c3f-af50-6d53cba78a23/Pasted text.txt`, capítulo 22. A instrução «Assumir que todas as fase estão aprovadas» substitui a pendência de autorização de fases do diagnóstico anterior. Não se exige nova aprovação genérica. O requisito de concluir a correspondência antes do DDL continua aplicável.
|
||||
|
||||
## Evidência executada
|
||||
|
||||
`tools/inventory-reference-schema.php` consulta PostgreSQL numa transacção REPEATABLE READ, READ ONLY. Inventaria tabelas e vistas, colunas/tipos/defaults, constraints, índices, triggers e contagens, sem exportar registos pessoais nem credenciais.
|
||||
|
||||
Resultado: **237 tabelas, 2.343 colunas, 806 índices, 1.171 constraints**. Inventário privado: `storage/app/private/reference-reconciliation-20260930-042147/inventory.json`; SHA-256 `bbd0cd93d590be0d4121efad6009e66f1a545c280f2491fd845831070712d723`.
|
||||
|
||||
O total inclui schemas legados e tabelas de extensões. Não significa 237 entidades aplicacionais distintas. A evidência é um inventário, não um backup restaurável nem certificado de qualidade dos dados.
|
||||
|
||||
## Matriz de correspondência
|
||||
|
||||
As decisões abaixo seleccionam objectos reais. «Estender» não significa que a alteração já foi aplicada. Nenhuma equivalência parcial é declarada cobertura integral.
|
||||
|
||||
| Conceito proposto | Objecto existente / evidência no repositório | Cobertura e lacunas | Decisão técnica |
|
||||
|---|---|---|---|
|
||||
| Fontes / data_sources | `public.geo_sources` (migration territorial_registry); `public.investment_legal_sources` (investment_zone_foundation); `public.ai_sources`; `investmoz.fontes_dados`; `mne.source_records` | Há cinco mecanismos especializados, com identidades, validações e consumidores diferentes. A fonte jurídica não é automaticamente autorização para IA nem proveniência cartográfica. Não existe correspondência transversal comprovada. | Mapear por compatibilidade, preservando tipo e schema na identidade. Não criar `data_sources` nem fundir registos por URL apenas. A definição do adaptador e da referência transversal ainda falta. |
|
||||
| Diplomas / legal_instruments | `public.investment_legal_sources`; modelo InvestmentLegalSource; serviço InvestmentZones | Diploma, artigo, entidade competente, URL, vigência, versão, proprietário e revisor já existem. Tipo, número estruturado e publicação não têm colunas próprias. | Estender este mestre com os campos efectivamente necessários; não duplicar diplomas em legal_instruments. Documentos legais do portal conservam a referência ao ficheiro. |
|
||||
| Organizações / organisations | `public.organizations`, `memberships`; migration investmoz_identity; `mne.organizations` separado | Mestre público UUID; pertenças existentes. MNE não está integrado como autoridade. Tax identifier, contactos e site não devem ser copiados silenciosamente entre módulos. | Reutilizar public.organizations; mapear MNE apenas por vínculo comprovado. Preservar IDs e contas bigint. |
|
||||
| Áreas administrativas | `public.geo_locations`, `geo_territory_profiles`, `geo_names`; seis tabelas `investmoz.provincia` a `aldeia_quarteirao`; TerritorialRegistry | Mestre aplicacional UUID e cadastro administrativo inteiro coexistem: 1.990 geo_locations e 21.410 unidades nas seis tabelas. Códigos/níveis precisam de correspondência, não de conversão por posição ou nome. | Mapear por compatibilidade; não criar administrative_areas. Preservar a hierarquia completa, IDs e precisão. |
|
||||
| Entidades territoriais | `economic_clusters`, `development_corridors`, `investment_zones`, `value_chains`, `value_chain_links`; migrations clusters/corridors/zone_foundation | Tipos especializados e relações já existem. Subcluster/nó territorial não têm um mestre identificado; não confundir elo de cadeia com nó geográfico. | Reutilizar mestres especializados e estender relações explícitas; não criar territorial_entities como segundo cadastro. |
|
||||
| Investimentos | `investment_items`, `investment_item_projects`, `investment_item_opportunities`, `projects`, `opportunities`, `existing_businesses`; InvestmentIdentity | 6 items ligados a 6 oportunidades, 16 projectos e 0 negócios. O item é identidade de ligação, não cópia completa da ficha. IDs UUID e bigint têm contratos distintos. | Reutilizar InvestmentIdentity e os mestres. Não criar investments; não cadastrar projectos em massa só para preencher a ponte. |
|
||||
| Relações | `cluster_projects`, `corridor_projects`, `corridor_clusters`, `opportunity_*`, `investment_zone_links`, `investment_zone_relations` | FK explícitas; relações de zona já têm fonte, vigência, validação, associação, prioridade e justificação. Nem todos os pivots têm essa cobertura. | Estender somente pivots deficientes; manter os actuais. Não criar investment_relations polimórfica concorrente. |
|
||||
| Dependências | `complementary_project_links`, migration investmoz_clusters | Direcção anchor→complementary, tipos dependency/enabler/supplier/market, auto-relação recusada. Não foi identificado controlo de ciclos indirectos ou concorrentes. | Estender esta tabela e o serviço de escrita para dependency; não tratar supplier/market como dependência acíclica. Falta implementar/testar controlo de ciclos. |
|
||||
| Geometrias | `geo_geometries`, `project_locations`, `investment_zone_geometries`; migrations geography/zone_foundation | PostGIS 4326, 2D, não vazio, válido, tipo consistente e limites geográficos já impostos. Zonas distinguem perímetro oficial/influência/site e fonte legal. | Reutilizar. Não criar spatial_features nem duplicar geometria em cada entidade. Pertença jurídica nunca inferida por ST_Intersects. |
|
||||
| Instalações | `existing_business_objects`; `mne.assets`, `real_estates`, `real_estate_units`, `improvements`; respectivas migrations e serviços | Existem activos/instalações de negócios, mas não um inventário operacional de lotes/capacidade de zonas. Payload privado não é uma tabela de reservas. | Criar apenas a relação/entidade operacional de instalação territorial ausente, após distinguir instalação de activo patrimonial. Não copiar os activos MNE. Ainda não implementada. |
|
||||
| Regras regulatórias | `investment_legal_sources`, regras de publicação em InvestmentZones, `mne.restrictions`, `portal_resources` | Diplomas, restrições patrimoniais e recursos existem; não foi encontrado cadastro de regras territoriais versionadas dos cinco tipos do anexo. | Criar regras territoriais referenciando o diploma existente, com FK explícitas; preservar restrições patrimoniais. Ainda não implementado. |
|
||||
| Documentos | `public.documents`, pivots `*_documents`, portal_resources; `mne.documents` e qualificações skills_edu | Mestre público tem UUID, chave, MIME, checksum, classificação e versão; domínios mantêm ligações próprias. Novos campos de vigência/título/fonte exigem mapeamento. | Reutilizar documents e pivots; não criar outro documents nem copiar ficheiros. MNE conserva contrato enquanto não reconciliado. |
|
||||
| Indicadores | Contagens calculadas em EducationController; `skills_edu.skill_gaps`; conteúdo de investment_dossiers | Existem métricas calculadas, mas não série genérica com definição, unidade, natureza, período, fonte e aprovação. Lacuna de competências não equivale a qualquer indicador. | Criar série de indicadores apenas para essa lacuna, com proprietário por FK explícita. Não persistir cópia das contagens dos dashboards. Ainda não implementado. |
|
||||
| Manifestações | `interest_requests`, `interest_submission_receipts`, leads, contacts, consents, outbox_events; InterestSubmission e ConvertInterestToLead | Fluxo de interesse e CRM existente; consentimentos e recibos separados. Manifestação não é acto administrativo nem candidatura. | Reutilizar fluxo e IDs; estender contexto/SLA onde faltar, sem expressions_of_interest paralelo. |
|
||||
| Versões | version_no dos mestres; investment_zone_revisions, geo_territory_revisions, existing_business_revisions, investment_submission_revisions | Snapshots especializados existem; version_no isolado não permite reconstrução. Não há histórico completo de todos os mestres. | Reutilizar históricos por domínio e estender cobertura; não criar entity_versions que duplique snapshots existentes. |
|
||||
| Auditoria | `public.audit_events`, PortalAudit, trigger imz_audit_events_immutable; auditorias especializadas mne/skills_edu | Auditoria pública já recusa UPDATE/DELETE/TRUNCATE por statement, incluindo zero linhas. Conta proprietária pode alterar DDL. | Reutilizar; preservar tipos e segregação de dados. Não substituir UUID por bigserial nem criar outro audit_events público. |
|
||||
|
||||
## Decisões e limites
|
||||
|
||||
Responsável pela autorização das fases: utilizador, instrução de 30/09/2026. Decisões técnicas: reconciliação acima, baseada no inventário e no código. Esta identificação não inventa revisores humanos, mandatos públicos, aprovação jurídica ou testes não executados.
|
||||
|
||||
Não executar o bloco BEGIN…COMMIT do anexo: criaria mestres paralelos e `IF NOT EXISTS` ocultaria divergências, por exemplo em `documents` e `audit_events`. Não eliminar os schemas existentes ou aplicar DROP CASCADE. Nenhuma migration nova foi preparada nesta etapa: ainda faltam contratos de compatibilidade e desenho detalhado das extensões/criações identificadas. O gate técnico de fecho da reconciliação está, por isso, **incompleto**, apesar da autorização de todas as fases.
|
||||
|
||||
## Reconciliação, testes e rollback a executar com as migrations
|
||||
|
||||
1. Guardar inventário e backup restaurável de todos os schemas; restaurar isoladamente e reconciliar contagens. O inventário acima não substitui este passo.
|
||||
2. Congelar por migration os objectos/colunas/restrições novos e a decisão desta matriz que os justifica. Não reescrever migrations já aplicadas.
|
||||
3. Validar FK, unicidade e duplicados lógicos com relatório de excepções; não eliminar ou fundir por semelhança de nome.
|
||||
4. Verificar geometrias por validade, vazio, SRID e igualdade geométrica quando houver transformação. Manter geometrias restritas fora de respostas públicas.
|
||||
5. Exercitar dependências directas/indirectas, updates, restauro lógico e duas sessões concorrentes. A auto-relação actualmente recusada não prova ausência de ciclos.
|
||||
6. Exercitar autorização, datas, versões, consentimento, idempotência e contratos de URL/API. Testes devem usar dados sintéticos numa base isolada, nunca limpar a base operacional.
|
||||
7. Comparar contagens/checksums antes/depois e IDs dos mestres. Aceitar somente as diferenças explicitadas no plano da migration.
|
||||
8. DOWN remove apenas objectos introduzidos, com RESTRICT, recusando perda de novos dados. Consolidar dados exige mapa reversível de IDs e restauro testado; não foi realizada consolidação nesta etapa.
|
||||
|
||||
## Estado real da entrega
|
||||
|
||||
Executados: inventário real e análise dos 16 conceitos, com decisões de reaproveitamento e lacunas identificadas. Não executados: novas migrations, consolidação, testes de concorrência, reserva de instalações, motor regulatório, séries de indicadores, alterações de UI ou deploy. O pacote completo pedido ainda não está concluído. A aprovação das fases não está pendente; o trabalho restante é técnico.
|
||||
Reference in New Issue
Block a user