Permitir consulta pública dos ecrãs reais com dados privados protegidos
tests / ci (push) Canceled after 0s
tests / ci (push) Canceled after 0s
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Services\EvaluationAccess;
|
||||
use App\Services\PublicNavigation;
|
||||
use App\Services\RequisitionWorkflow;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -14,6 +15,9 @@ class EnterLocalEvaluation
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
$access = app(EvaluationAccess::class);
|
||||
if (app(PublicNavigation::class)->enabled()) {
|
||||
return $next($request);
|
||||
}
|
||||
if (! $access->dashboardEntry($request)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Services\PublicNavigation;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class PublicDevelopmentNavigation
|
||||
{
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
$navigation = app(PublicNavigation::class);
|
||||
if (! $navigation->enabled() || $request->user('web') !== null || ! in_array($request->method(), ['GET', 'HEAD'], true)) {
|
||||
return $next($request);
|
||||
}
|
||||
$path = '/'.$request->path();
|
||||
if ($request->path() === '/') {
|
||||
return redirect('/dashboard');
|
||||
}
|
||||
if (! isset(PublicNavigation::PAGES[$path])) {
|
||||
return $next($request);
|
||||
}
|
||||
$response = Inertia::render(PublicNavigation::PAGES[$path], $navigation->props($request))->toResponse($request);
|
||||
$response->headers->set('Cache-Control', 'private, no-store, max-age=0');
|
||||
$response->headers->set('X-Robots-Tag', 'noindex, nofollow');
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class PublicNavigation
|
||||
{
|
||||
public const PAGES = [
|
||||
'/dashboard' => 'dashboard', '/requisicoes' => 'requisitions/index', '/requisicoes/criar' => 'requisitions/form',
|
||||
'/fornecedores' => 'suppliers/index', '/cotacoes' => 'quotations/index', '/ordens' => 'purchases/index', '/recepcoes' => 'receipts/index',
|
||||
'/stock' => 'stock/index', '/stock/catalogo' => 'stock/catalogue', '/stock/operacoes' => 'stock/operations', '/equipamentos' => 'equipment/index',
|
||||
'/planeamento' => 'planning/index', '/pessoas' => 'people/index', '/orcamentos' => 'budgets/index', '/facturas' => 'invoices/index', '/pagamentos' => 'payments/index',
|
||||
'/regras-investimento' => 'investment/index', '/politica-admissao' => 'purchases/admission-policy', '/politica-recomendacao' => 'purchases/recommendation-policy',
|
||||
'/revisao-compras' => 'purchase-controls/index', '/settings/appearance' => 'settings/appearance',
|
||||
];
|
||||
|
||||
/** Public navigation deliberately has no tenant, actor or business data provider. */
|
||||
public function enabled(): bool
|
||||
{
|
||||
return (bool) config('public_navigation.enabled');
|
||||
}
|
||||
|
||||
/** @return array<string,mixed> */
|
||||
public function props(Request $request): array
|
||||
{
|
||||
$filter = $request->validate(['search' => 'nullable|string|max:100', 'status' => 'nullable|string|max:40', 'view' => ['nullable', Rule::in(['all', 'mine', 'approvals', 'decided'])],
|
||||
'page' => 'nullable|integer|min:1|max:10000', 'receipts_page' => 'nullable|integer|min:1|max:10000', 'kind' => 'nullable|in:staff,service', 'from' => 'nullable|date_format:Y-m-d', 'to' => 'nullable|date_format:Y-m-d', 'warehouse_id' => 'nullable|integer|min:1']);
|
||||
$page = fn (string $name = 'page') => (new LengthAwarePaginator([], 0, 15, (int) ($filter[$name] ?? 1), ['path' => $request->url(), 'pageName' => $name]))->appends($filter);
|
||||
$common = ['publicNavigation' => ['paths' => array_keys(self::PAGES), 'dataScope' => 'none'], 'auth' => ['user' => null], 'organizationAccess' => null, 'canRequest' => false, 'payablesAvailable' => false];
|
||||
$props = match ('/'.$request->path()) {
|
||||
'/dashboard' => ['company' => null, 'stats' => ['total' => 0, 'pending' => 0, 'approved' => 0, 'approvedMinor' => 0, 'awaitingMe' => 0], 'recent' => [], 'approvals' => []],
|
||||
'/requisicoes' => ['requisitions' => $page(), 'filters' => $filter],
|
||||
'/requisicoes/criar' => ['costCenters' => [], 'requisition' => null, 'catalogue' => ['ready' => false, 'items' => [], 'search' => '', 'more' => false]],
|
||||
'/fornecedores' => ['suppliers' => $page(), 'filters' => $filter, 'canCreate' => false, 'canReview' => false],
|
||||
'/cotacoes' => ['requisitions' => $page()],
|
||||
'/ordens' => ['orders' => $page(), 'awards' => $page()],
|
||||
'/recepcoes' => ['orders' => $page(), 'receipts' => $page('receipts_page')],
|
||||
'/stock' => ['items' => [], 'warehouses' => [], 'balances' => [], 'movements' => $page(), 'filters' => $filter, 'canManage' => false],
|
||||
'/stock/catalogo' => ['records' => $page(), 'filters' => $filter, 'ready' => true, 'canManage' => false],
|
||||
'/stock/operacoes' => ['items' => [], 'warehouses' => [], 'balances' => [], 'users' => [], 'operations' => $page(), 'counts' => [], 'equipment' => [], 'canOperate' => false],
|
||||
'/equipamentos' => ['equipment' => [], 'parts' => [], 'links' => [], 'levels' => [], 'items' => [], 'departments' => [], 'history' => $page(), 'canManage' => false, 'canConfigure' => false],
|
||||
'/planeamento' => ['items' => $page(), 'milestones' => [], 'history' => [], 'canManage' => false, 'today' => today()->toDateString(), 'departments' => [], 'users' => [], 'requisitions' => []],
|
||||
'/pessoas' => ['ready' => true, 'kinds' => ['staff', 'service'], 'availableKinds' => ['staff', 'service'], 'filters' => $filter, 'workdesk' => null, 'insights' => null, 'company' => null, 'canPrepare' => [], 'formFields' => ['staff' => PeopleForms::STAFF, 'service' => PeopleForms::SERVICE], 'administration' => [], 'processes' => $page()],
|
||||
'/orcamentos' => ['centers' => [], 'budgets' => [], 'reinforcements' => $page(), 'canManage' => false],
|
||||
'/facturas' => ['invoices' => $page(), 'orders' => [], 'canManage' => false],
|
||||
'/pagamentos' => ['invoices' => [], 'payments' => $page(), 'canManage' => false],
|
||||
'/regras-investimento' => ['policies' => [], 'reviews' => [], 'currentPolicyId' => null, 'canManage' => false],
|
||||
'/politica-admissao', '/politica-recomendacao' => ['policies' => $page(), 'latest' => null],
|
||||
'/revisao-compras' => ['role' => '', 'permissions' => [], 'actorId' => null, 'policies' => [], 'reviews' => [], 'awards' => [], 'cancellations' => [], 'sources' => []],
|
||||
default => [],
|
||||
};
|
||||
|
||||
return [...$common, ...$props];
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
use App\Http\Middleware\EnterLocalEvaluation;
|
||||
use App\Http\Middleware\HandleAppearance;
|
||||
use App\Http\Middleware\HandleInertiaRequests;
|
||||
use App\Http\Middleware\PublicDevelopmentNavigation;
|
||||
use App\Services\EvaluationNavigation;
|
||||
use Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests;
|
||||
use Illuminate\Foundation\Application;
|
||||
@@ -21,8 +22,11 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
$middleware->redirectGuestsTo(fn (Request $request) => EvaluationNavigation::local($request) ? '/avaliar' : route('login'));
|
||||
$middleware->encryptCookies(except: ['appearance', 'sidebar_state']);
|
||||
$middleware->prependToPriorityList(AuthenticatesRequests::class, EnterLocalEvaluation::class);
|
||||
$middleware->prependToPriorityList(EnterLocalEvaluation::class, PublicDevelopmentNavigation::class);
|
||||
$middleware->prependToPriorityList(PublicDevelopmentNavigation::class, HandleInertiaRequests::class);
|
||||
|
||||
$middleware->web(append: [
|
||||
PublicDevelopmentNavigation::class,
|
||||
EnterLocalEvaluation::class,
|
||||
HandleAppearance::class,
|
||||
HandleInertiaRequests::class,
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
return ['enabled' => (bool) env('SGC_PUBLIC_NAVIGATION', false)];
|
||||
+16
-16
@@ -8,22 +8,22 @@
|
||||
|
||||
Estado actualizado em 28 de Outubro de 2026. Projecto activo: C:\laragon\www\compras, Laravel + React + PostgreSQL, http://127.0.0.1:8015. Preservar conta, dados, configuração, cookie e porta. SGF Tempo e Investmoz são independentes. A implementação Python separada fica apenas como referência; não importar contas, sessões ou bases experimentais.
|
||||
|
||||
| Área | Implementado no Laravel | Pendente |
|
||||
| ------------------------ | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
|
||||
| Acesso e organização | Fortify, conta, empresa, equipa, departamentos e centros de custo | MFA obrigatório e ciclo completo de acessos |
|
||||
| Requisições e aprovações | Rascunhos, cálculos exactos, etapas, segregação e snapshots | Validação empresarial das regras provisórias |
|
||||
| Fornecedores | Cadastro, anexos privados e versões bancárias com duas validações | Múltiplas contas e verificação bancária externa |
|
||||
| Cotações | Propostas, avaliações e matriz comparativa | Multimoeda e revisão das propostas |
|
||||
| RF-AF-01 | Declarações pessoais, histórico e ConflictGuard no servidor | Política de periodicidade, validade e levantamento |
|
||||
| Adjudicação | Escolha, aprovação final e cancelamento sem ordem sob política publicada; rascunho revisto ligado | Cancelamento de ordens emitidas, compromissos, estornos e devoluções fora deste circuito |
|
||||
| Ordens | Emissão única, snapshots, compromisso orçamental, consulta e impressão | Cancelamento de ordens emitidas e validação operacional integral |
|
||||
| Recepção | Parcial/total, qualidade, divergências, pendentes, segregação do emitente e auditoria | Substituições e anexos; rejeições não autorizam receber acima do total |
|
||||
| Stock | Artigos, armazéns, entradas, saídas fundamentadas, transferências atómicas, contagens e ajustes auditados por empresa | Revisão visual, reposição e conversões |
|
||||
| Equipamentos e peças | Cadastro, compatibilidade, mínimos, validação técnica e investimento condicionado | Configuração empresarial, agregação por projecto e fraccionamento |
|
||||
| Orçamento | Dotação, reforços aprovados, reservas e compromissos por empresa/centro/exercício | Revisão visual, reduções/transferências e validação operacional integral |
|
||||
| Facturas e pagamentos | Facturas, consumo e registo/conferência de pagamentos externos | Estornos, notas de crédito e anexos |
|
||||
| Início e navegação | Página inicial própria, menus por área/perfil e consulta pública por módulo | Revisão visual |
|
||||
| Auditoria | Histórico das operações implementadas | Exportações, retenção e protecção contra SQL administrativo |
|
||||
| Área | Implementado no Laravel | Pendente |
|
||||
| ------------------------ | --------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
||||
| Acesso e organização | Fortify, conta, empresa, equipa, departamentos e centros de custo | MFA obrigatório e ciclo completo de acessos |
|
||||
| Requisições e aprovações | Rascunhos, cálculos exactos, etapas, segregação e snapshots | Validação empresarial das regras provisórias |
|
||||
| Fornecedores | Cadastro, anexos privados e versões bancárias com duas validações | Múltiplas contas e verificação bancária externa |
|
||||
| Cotações | Propostas, avaliações e matriz comparativa | Multimoeda e revisão das propostas |
|
||||
| RF-AF-01 | Declarações pessoais, histórico e ConflictGuard no servidor | Política de periodicidade, validade e levantamento |
|
||||
| Adjudicação | Escolha, aprovação final e cancelamento sem ordem sob política publicada; rascunho revisto ligado | Cancelamento de ordens emitidas, compromissos, estornos e devoluções fora deste circuito |
|
||||
| Ordens | Emissão única, snapshots, compromisso orçamental, consulta e impressão | Cancelamento de ordens emitidas e validação operacional integral |
|
||||
| Recepção | Parcial/total, qualidade, divergências, pendentes, segregação do emitente e auditoria | Substituições e anexos; rejeições não autorizam receber acima do total |
|
||||
| Stock | Artigos, armazéns, entradas, saídas fundamentadas, transferências atómicas, contagens e ajustes auditados por empresa | Revisão visual, reposição e conversões |
|
||||
| Equipamentos e peças | Cadastro, compatibilidade, mínimos, validação técnica e investimento condicionado | Configuração empresarial, agregação por projecto e fraccionamento |
|
||||
| Orçamento | Dotação, reforços aprovados, reservas e compromissos por empresa/centro/exercício | Revisão visual, reduções/transferências e validação operacional integral |
|
||||
| Facturas e pagamentos | Facturas, consumo e registo/conferência de pagamentos externos | Estornos, notas de crédito e anexos |
|
||||
| Início e navegação | Página inicial própria, menus por área/perfil e consulta pública por módulo | Revisão visual |
|
||||
| Auditoria | Histórico das operações implementadas | Exportações, retenção e protecção contra SQL administrativo |
|
||||
|
||||
| Revisão de compras | Política por empresa com duas aprovações administrativas; decisão independente de Finanças; reserva não comprometida libertada uma vez | Sem activação automática; máximo de um pedido por adjudicação, mesmo rejeitado |
|
||||
| Visão do processo | Etapas, responsáveis, documentos, decisões e ligações às revisões, filtrados por perfil | Consulta não certifica execução integral |
|
||||
|
||||
+39
-40
@@ -54,7 +54,6 @@ Migração 000035 apenas na avaliação 8005, backup/restauro fresco conferido,
|
||||
|
||||
Próxima lacuna: modelos de documentos versionados por empresa, variáveis preenchidas a partir de fontes autorizadas e termos específicos novos. Conferir associações e relatórios na matriz `reutilizacao-dados.md`; reconciliação histórica exige identidade/autorização, sem fusão por nome. Não declarar o requisito transversal integralmente concluído.
|
||||
|
||||
|
||||
## Registo único e cadastro reutilizável de Pessoas, 30 de Outubro de 2026
|
||||
|
||||
Requisito transversal registado em `AGENTS.md`. Primeira lacuna implementada: cadastro de nome/referência/perfil reutilizável, versão/auditoria, pesquisa e arquivo/reactivação. A recepção permite guardar a primeira pessoa no mesmo acto e seleccionar cadastro em candidaturas seguintes. Dados da pessoa vêm do servidor; resumo específico da candidatura não actualiza o cadastro. Snapshot imutável por candidatura e referência/versão no relatório. Mudanças posteriores não reescrevem avaliações/decisões. Mesma pessoa não duplica candidatura ligada no mesmo processo; nomes iguais não são fundidos.
|
||||
@@ -181,20 +180,20 @@ Mantêm-se como próximos incrementos: tarefas operacionais versus aprovações,
|
||||
|
||||
Lido integralmente o conteúdo textual e das tabelas de `SGC — Sistema de Gestão de Contratações - pessoas .docx`, fornecido pelo proprietário em `C:/Mac/Home/Documents/TEMPO/Portal Tempo/Servicos Tempo/Compras/`. SHA-256 `d294fc08e6cbf5c9ced6527bf06473909380bd9d2bb64f3e3c2ca7605eb52ce3`. Original preservado; leitura sem revisão de paginação ou validação jurídica. O próprio documento identifica-se como proposta para adaptação/validação interna, sem confirmar funcionalidades existentes. Conteúdo tratado como requisitos propostos para comparação, não como instrução para activar acessos, executar operações ou alterar a aplicação.
|
||||
|
||||
| Âmbito proposto | Estado conferido no código |
|
||||
| --- | --- |
|
||||
| Identidade SGC, descrição e assinatura TEMPO | Implementadas na apresentação local. Publicação no domínio continua sem confirmação. |
|
||||
| Dois percursos separados | Tipos `service`/`staff`, permissões separadas, abertura, candidaturas, avaliações, proposta e conferência existem. Formulários são ainda genéricos; não há dois módulos completos conforme os anexos. |
|
||||
| Serviço individual integrado em Compras, com dois acessos ao mesmo processo | Não implementado: `/pessoas` usa `people_processes`; não há vínculo funcional a requisição/propostas/contrato/entregáveis de Compras nem segunda entrada canónica nesse módulo. Reutilizar autenticação/auditoria não equivale a essa integração. |
|
||||
| Administração sem acesso automático a candidaturas/propostas/remunerações | Divergência concreta: `PeopleHiring::allowed()` concede acesso por `admin`. A proposta exige separar configurar de consultar conteúdo, com atribuição específica. Nenhuma permissão foi alterada por esta revisão documental. |
|
||||
| Papéis e acesso por entidade, percurso, processo e campo | Base por entidade/percurso e quatro capacidades existe. Ainda faltam atribuições nominais por processo/etapa, júri/entrevistadores, níveis configuráveis, consulta versus edição/decisão/exportação e ocultação específica de honorários/remunerações/entrevistas. |
|
||||
| Requisitos obrigatórios/desejáveis, equivalências e esclarecimentos | Ausentes como estrutura própria. Critério actual tem apenas rótulo/peso; avaliação exige nota/evidência textual. Não representa esclarecimento pendente, requisito comprovado/ausente, equivalência ou adaptação razoável. |
|
||||
| Ponderações quando aplicáveis e selecção factual | Actual circuito impõe soma 100%, notas 0–10 e escolha entre maiores resultados. A proposta admite métodos próprios e ponderações quando aplicáveis; é necessário tornar o método configurável, sem tratar ausência de comprovativo como reprovação nem requisitos desejáveis como eliminatórios. |
|
||||
| Anexo A, Guião, Anexo B e Anexo C | Não implementados como formulários/modelos versionados. Faltam campos próprios de vaga e necessidade de serviço, perguntas/exercícios, evidência relatada/observada/comprovada e seguimento dos esclarecimentos. Texto livre actual não equivale aos anexos. |
|
||||
| Regras anteriores à abertura e alterações versionadas | Critérios/âmbito actuais preservados, com abertura independente, versão de operação e auditoria. Ainda não há circuito de revisão/substituição de regras/documentos, correcção/reabertura e aprovação dessas alterações. |
|
||||
| Tarefas, Aprovações e ecrã inicial | Ausentes como filas atribuídas e contagens separadas. A página actual lista processos autorizados e criação, sem painel de trabalho, pesquisa/filtros por percurso/estado/período, alertas ou selector autorizado de entidade. |
|
||||
| Contrato, acompanhamento, relatório final e encerramento | Apenas minuta interna e consulta imprimível. Faltam formalização contratual, entregáveis/marcos/aceitação, integração/formação factual, pendências, relatório final e encerramento. Salários não implementados; classificação individual de desempenho pós-admissão não integra esta proposta. |
|
||||
| Contas externas e dados pessoais | Não há criação automática de contas de candidatos/prestadores. Faltam recepção externa configurável, biblioteca/anexos, retenção, acesso por documento/campo, comunicações e exportação própria autorizada. Não afirmar conformidade jurídica com base nesta leitura. |
|
||||
| Âmbito proposto | Estado conferido no código |
|
||||
| --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Identidade SGC, descrição e assinatura TEMPO | Implementadas na apresentação local. Publicação no domínio continua sem confirmação. |
|
||||
| Dois percursos separados | Tipos `service`/`staff`, permissões separadas, abertura, candidaturas, avaliações, proposta e conferência existem. Formulários são ainda genéricos; não há dois módulos completos conforme os anexos. |
|
||||
| Serviço individual integrado em Compras, com dois acessos ao mesmo processo | Não implementado: `/pessoas` usa `people_processes`; não há vínculo funcional a requisição/propostas/contrato/entregáveis de Compras nem segunda entrada canónica nesse módulo. Reutilizar autenticação/auditoria não equivale a essa integração. |
|
||||
| Administração sem acesso automático a candidaturas/propostas/remunerações | Divergência concreta: `PeopleHiring::allowed()` concede acesso por `admin`. A proposta exige separar configurar de consultar conteúdo, com atribuição específica. Nenhuma permissão foi alterada por esta revisão documental. |
|
||||
| Papéis e acesso por entidade, percurso, processo e campo | Base por entidade/percurso e quatro capacidades existe. Ainda faltam atribuições nominais por processo/etapa, júri/entrevistadores, níveis configuráveis, consulta versus edição/decisão/exportação e ocultação específica de honorários/remunerações/entrevistas. |
|
||||
| Requisitos obrigatórios/desejáveis, equivalências e esclarecimentos | Ausentes como estrutura própria. Critério actual tem apenas rótulo/peso; avaliação exige nota/evidência textual. Não representa esclarecimento pendente, requisito comprovado/ausente, equivalência ou adaptação razoável. |
|
||||
| Ponderações quando aplicáveis e selecção factual | Actual circuito impõe soma 100%, notas 0–10 e escolha entre maiores resultados. A proposta admite métodos próprios e ponderações quando aplicáveis; é necessário tornar o método configurável, sem tratar ausência de comprovativo como reprovação nem requisitos desejáveis como eliminatórios. |
|
||||
| Anexo A, Guião, Anexo B e Anexo C | Não implementados como formulários/modelos versionados. Faltam campos próprios de vaga e necessidade de serviço, perguntas/exercícios, evidência relatada/observada/comprovada e seguimento dos esclarecimentos. Texto livre actual não equivale aos anexos. |
|
||||
| Regras anteriores à abertura e alterações versionadas | Critérios/âmbito actuais preservados, com abertura independente, versão de operação e auditoria. Ainda não há circuito de revisão/substituição de regras/documentos, correcção/reabertura e aprovação dessas alterações. |
|
||||
| Tarefas, Aprovações e ecrã inicial | Ausentes como filas atribuídas e contagens separadas. A página actual lista processos autorizados e criação, sem painel de trabalho, pesquisa/filtros por percurso/estado/período, alertas ou selector autorizado de entidade. |
|
||||
| Contrato, acompanhamento, relatório final e encerramento | Apenas minuta interna e consulta imprimível. Faltam formalização contratual, entregáveis/marcos/aceitação, integração/formação factual, pendências, relatório final e encerramento. Salários não implementados; classificação individual de desempenho pós-admissão não integra esta proposta. |
|
||||
| Contas externas e dados pessoais | Não há criação automática de contas de candidatos/prestadores. Faltam recepção externa configurável, biblioteca/anexos, retenção, acesso por documento/campo, comunicações e exportação própria autorizada. Não afirmar conformidade jurídica com base nesta leitura. |
|
||||
|
||||
Observação editorial: a introdução afirma que os critérios do dashboard ainda não foram disponibilizados, mas o documento contém uma proposta detalhada de ecrã inicial e critérios de aceitação. Convém distinguir os requisitos operacionais já descritos dos indicadores de negócio/metas ainda por definir.
|
||||
|
||||
@@ -296,19 +295,19 @@ Lido o ficheiro do proprietário `recepcao de propostas.docx`, SHA-256 `77e8df9c
|
||||
|
||||
O documento é coerente ao separar recepção, abertura, admissibilidade, avaliação e adjudicação. O registo de entrega não deve decidir admissibilidade; o comprovativo identifica a mesma entrega e preserva essa ressalva; substituições têm referências relacionadas sem reutilizar a identidade da entrega anterior. A descrição externa de volumes/ficheiros, a confidencialidade e a preservação de ocorrências também são adequadas como requisitos funcionais. Para o contexto privado, a constituição de júri/comissão/equipa e a sua competência devem resultar da política, do risco e das delegações da empresa, com nomes, composição, impedimentos, substituições e permissões parametrizáveis. Não adoptar automaticamente o júri como requisito fixo de qualquer compra.
|
||||
|
||||
| Elemento do documento | Cobertura no código local |
|
||||
| --- | --- |
|
||||
| Necessidades de compras | Requisições justificadas, linhas, estimativas, planeamento e orçamento existentes; não equivalem automaticamente a um relatório de necessidades consolidado/aprovado com todas as evidências do modelo |
|
||||
| Condições de entrega e divulgação | Parcial: a acta pode registar prazo/fuso, mas falta a configuração publicada e versionada de canais, formatos, prazos e esclarecimentos antes da recepção |
|
||||
| Registo protocolar por entrega | Não encontrado: falta entidade própria com referência única, recepção exacta, canal/local, entregante, identificação externa dos volumes/ficheiros, ocorrências e guarda |
|
||||
| Comprovativo de entrega | Não encontrado: falta emissão ligada à entrega, número próprio quando aplicável, disponibilização ao fornecedor e preservação da cópia original/metadados |
|
||||
| Recepção electrónica confidencial | Não encontrada como envelope protegido: as cotações já contêm valores/linhas consultáveis antes da acta; não existe mecanismo de recepção selada e abertura autorizada sobre os originais |
|
||||
| Fora de prazo, substituição, retirada e falha técnica | Falta ciclo próprio de ocorrências e decisões, ligado às entregas anteriores e à versão das condições de recepção |
|
||||
| Nomeação e poderes do júri/equipa | Parcial: a acta regista nome/capacidade e grupo `jury`/`participant`; esses campos não são uma nomeação, associação a contas com vigência/quórum ou autorização efectiva de acesso às propostas |
|
||||
| Abertura | Acta versionada, data/fuso, participantes, referências de anexos, selecção de propostas, fingerprint e impressão existentes; não substitui o protocolo/comprovativo nem implementa por si a guarda selada anterior |
|
||||
| Admissibilidade e avaliação | Qualificação, decisões de admissão fundamentadas/revistas, comparação e recomendação existentes nos seus âmbitos; faltam as ligações à entrega protocolada e às ocorrências, e a prova de divulgação prévia/comum de critérios/esclarecimentos |
|
||||
| Classificação e adjudicação | Comparação/classificação, recomendação, aprovações e ordem existentes; comunicação aos concorrentes e tratamento completo de reclamações do procedimento não encontrados. Contraditório da avaliação de desempenho é outro âmbito |
|
||||
| Arquivo | Histórico, referências e versões preservadas em vários módulos; não foi encontrado ciclo próprio de custódia, auditoria de todos os acessos ao conteúdo de propostas, retenção, bloqueio de eliminação e eliminação autorizada dos respectivos originais |
|
||||
| Elemento do documento | Cobertura no código local |
|
||||
| ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Necessidades de compras | Requisições justificadas, linhas, estimativas, planeamento e orçamento existentes; não equivalem automaticamente a um relatório de necessidades consolidado/aprovado com todas as evidências do modelo |
|
||||
| Condições de entrega e divulgação | Parcial: a acta pode registar prazo/fuso, mas falta a configuração publicada e versionada de canais, formatos, prazos e esclarecimentos antes da recepção |
|
||||
| Registo protocolar por entrega | Não encontrado: falta entidade própria com referência única, recepção exacta, canal/local, entregante, identificação externa dos volumes/ficheiros, ocorrências e guarda |
|
||||
| Comprovativo de entrega | Não encontrado: falta emissão ligada à entrega, número próprio quando aplicável, disponibilização ao fornecedor e preservação da cópia original/metadados |
|
||||
| Recepção electrónica confidencial | Não encontrada como envelope protegido: as cotações já contêm valores/linhas consultáveis antes da acta; não existe mecanismo de recepção selada e abertura autorizada sobre os originais |
|
||||
| Fora de prazo, substituição, retirada e falha técnica | Falta ciclo próprio de ocorrências e decisões, ligado às entregas anteriores e à versão das condições de recepção |
|
||||
| Nomeação e poderes do júri/equipa | Parcial: a acta regista nome/capacidade e grupo `jury`/`participant`; esses campos não são uma nomeação, associação a contas com vigência/quórum ou autorização efectiva de acesso às propostas |
|
||||
| Abertura | Acta versionada, data/fuso, participantes, referências de anexos, selecção de propostas, fingerprint e impressão existentes; não substitui o protocolo/comprovativo nem implementa por si a guarda selada anterior |
|
||||
| Admissibilidade e avaliação | Qualificação, decisões de admissão fundamentadas/revistas, comparação e recomendação existentes nos seus âmbitos; faltam as ligações à entrega protocolada e às ocorrências, e a prova de divulgação prévia/comum de critérios/esclarecimentos |
|
||||
| Classificação e adjudicação | Comparação/classificação, recomendação, aprovações e ordem existentes; comunicação aos concorrentes e tratamento completo de reclamações do procedimento não encontrados. Contraditório da avaliação de desempenho é outro âmbito |
|
||||
| Arquivo | Histórico, referências e versões preservadas em vários módulos; não foi encontrado ciclo próprio de custódia, auditoria de todos os acessos ao conteúdo de propostas, retenção, bloqueio de eliminação e eliminação autorizada dos respectivos originais |
|
||||
|
||||
Ponto técnico crítico: `QuotationController::show` disponibiliza a comparação/conteúdo a utilizadores autorizados pelo âmbito/função, sem condição de abertura prévia. `ProposalOpeningWorkflow::proposals` também obtém os preços/linhas para preparar a acta. O `basis_hash` detecta mudanças na representação dos dados, mas não é um selo de ficheiros recebidos nem uma barreira de confidencialidade antes da abertura. Assim, não se deve apresentar o mecanismo actual como cumprimento do requisito de propostas seladas. O módulo de recepções de bens/serviços (`GoodsReceipt`) não é recepção de propostas.
|
||||
|
||||
@@ -322,18 +321,18 @@ Pedido do proprietário: verificar se o fluxo do ficheiro `fluxo sector público
|
||||
|
||||
Conclusão: o percurso-base da compra já existe, mas a proposta privada não está integralmente implementada. A disponibilidade abaixo refere-se ao código local e aos âmbitos documentados, sem nova aceitação visual, activação da configuração da empresa ou confirmação de instalação no domínio.
|
||||
|
||||
| Etapa da proposta privada | Implementação existente | Lacuna para cobertura integral |
|
||||
| --- | --- | --- |
|
||||
| 1 Requisição e justificação | Rascunhos, linhas/quantidades, prazo, justificação, departamento/centro, planeamento e consulta de stock | Verificação prévia obrigatória de existências e alternativas, com evidência associada ao pedido |
|
||||
| 2 Orçamento e autorização | Reserva/compromisso/consumo orçamental, aprovações e limites monetários configuráveis | Cobertura obrigatória em todos os processos: o núcleo ainda retorna `null` quando não existe dotação; prevenção de fraccionamento e matriz de poderes por risco não implementadas |
|
||||
| 3 Estratégia de compra | Consulta e registo de cotações, comparação e adjudicação | Política configurável para consulta, concurso privado, negociação e fornecedor único, número de propostas variável, excepções/urgências aprovadas e revisão posterior |
|
||||
| 4 Verificação do fornecedor | Cadastro, beneficiários em texto, diligência, documentos, revisão/impedimento, conflitos e versões bancárias com revisão independente | Diligência estruturada e proporcional ao risco, partes relacionadas e beneficiários efectivos estruturados, sanções/restrições e antecedentes com verificações documentadas; confirmação por canal independente não tem fluxo específico |
|
||||
| 5 Solicitação e recepção de propostas | Propostas por linhas comparáveis, ponderações, qualificação/admissão e abertura opcionais | Ciclo completo de convites aos fornecedores, esclarecimentos e negociação, com controlo próprio de informação concorrencial |
|
||||
| 6 Avaliação e selecção | Comparação de preço, qualidade, prazo, histórico e condições de pagamento; análises técnicas e recomendação com revisão | Custo total estruturado, risco transversal, assistência e revisão independente accionada pela classificação de risco, com pareceres configuráveis |
|
||||
| 7 Aprovação e formalização | Aprovação da adjudicação e emissão de ordem de compra, preservando regras e evidências | Gestão de contratos/acordos, clausulado, subcontratação, versões/alterações e excepções de execução antecipada aprovadas |
|
||||
| 8 Execução e recepção | Recepções parciais, quantidade/qualidade, reservas/rejeições, aceitação de serviços e stock quando físico | Gestão da execução contratual, marcos de serviços, alterações contratuais e respectivos aprovadores |
|
||||
| 9 Validação e pagamento | Conferência ordem–recepção aceite–factura; quantidades/preços/imposto, duplicados, conferência independente e registo de pagamentos externos | Adiantamentos e marcos contratuais, excepções/tolerâncias aprovadas e conclusão do percurso comercial comum de pagamentos, actualmente restrito ao ensaio |
|
||||
| 10 Encerramento e monitorização | Histórico/auditoria, relatórios, avaliações qualitativas, medidas de melhoria e contraditório do fornecedor | Encerramento formal do processo/contrato e indicadores consolidados de concentração, excepções, compras sem ordem, alterações contratuais e diligências pendentes |
|
||||
| Etapa da proposta privada | Implementação existente | Lacuna para cobertura integral |
|
||||
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 1 Requisição e justificação | Rascunhos, linhas/quantidades, prazo, justificação, departamento/centro, planeamento e consulta de stock | Verificação prévia obrigatória de existências e alternativas, com evidência associada ao pedido |
|
||||
| 2 Orçamento e autorização | Reserva/compromisso/consumo orçamental, aprovações e limites monetários configuráveis | Cobertura obrigatória em todos os processos: o núcleo ainda retorna `null` quando não existe dotação; prevenção de fraccionamento e matriz de poderes por risco não implementadas |
|
||||
| 3 Estratégia de compra | Consulta e registo de cotações, comparação e adjudicação | Política configurável para consulta, concurso privado, negociação e fornecedor único, número de propostas variável, excepções/urgências aprovadas e revisão posterior |
|
||||
| 4 Verificação do fornecedor | Cadastro, beneficiários em texto, diligência, documentos, revisão/impedimento, conflitos e versões bancárias com revisão independente | Diligência estruturada e proporcional ao risco, partes relacionadas e beneficiários efectivos estruturados, sanções/restrições e antecedentes com verificações documentadas; confirmação por canal independente não tem fluxo específico |
|
||||
| 5 Solicitação e recepção de propostas | Propostas por linhas comparáveis, ponderações, qualificação/admissão e abertura opcionais | Ciclo completo de convites aos fornecedores, esclarecimentos e negociação, com controlo próprio de informação concorrencial |
|
||||
| 6 Avaliação e selecção | Comparação de preço, qualidade, prazo, histórico e condições de pagamento; análises técnicas e recomendação com revisão | Custo total estruturado, risco transversal, assistência e revisão independente accionada pela classificação de risco, com pareceres configuráveis |
|
||||
| 7 Aprovação e formalização | Aprovação da adjudicação e emissão de ordem de compra, preservando regras e evidências | Gestão de contratos/acordos, clausulado, subcontratação, versões/alterações e excepções de execução antecipada aprovadas |
|
||||
| 8 Execução e recepção | Recepções parciais, quantidade/qualidade, reservas/rejeições, aceitação de serviços e stock quando físico | Gestão da execução contratual, marcos de serviços, alterações contratuais e respectivos aprovadores |
|
||||
| 9 Validação e pagamento | Conferência ordem–recepção aceite–factura; quantidades/preços/imposto, duplicados, conferência independente e registo de pagamentos externos | Adiantamentos e marcos contratuais, excepções/tolerâncias aprovadas e conclusão do percurso comercial comum de pagamentos, actualmente restrito ao ensaio |
|
||||
| 10 Encerramento e monitorização | Histórico/auditoria, relatórios, avaliações qualitativas, medidas de melhoria e contraditório do fornecedor | Encerramento formal do processo/contrato e indicadores consolidados de concentração, excepções, compras sem ordem, alterações contratuais e diligências pendentes |
|
||||
|
||||
Divergências concretas: `ProposalComparison::inspect` exige pelo menos três propostas elegíveis de fornecedores distintos e duas avaliações elegíveis por proposta; `PurchaseWorkflow::currentBasis` bloqueia excepções sem política. Esses números permanecem fixos e não correspondem à quantidade definida por cada empresa/valor/risco na proposta. `RequisitionWorkflow::submit` determina as etapas por limites monetários e equipamento; ainda não resolve uma matriz geral de risco inicial/residual. `BudgetControl::record` permite continuar sem dotação configurada, retornando `null`. A existência do módulo orçamental, portanto, não prova cobertura obrigatória universal. `SettlementHttp::guard` restringe o percurso comum de pagamentos ao ambiente/base de ensaio; ordens/recepções e pagamentos externos conservam marcadores de desenvolvimento nas snapshots. A remoção de tais marcadores não substitui a conclusão funcional da entrega comercial.
|
||||
|
||||
|
||||
+18
-18
@@ -6,15 +6,15 @@ Versão de definição 1, 30/10/2026. Indicadores factuais, sem modelo de machin
|
||||
|
||||
Método `procurement_facts_v1`, mesmo snapshot de leitura do relatório. O período filtra a criação da requisição, não a data de submissão ou a execução. Contagens incluem todas as páginas. Origem: requisições autorizadas por empresa/departamento, propostas registadas e ordem visível. Actualização ao consultar/aplicar filtros; não há actualização automática em segundo plano.
|
||||
|
||||
| Indicador | Definição e base | Limite de interpretação |
|
||||
| --- | --- | --- |
|
||||
| Requisições no âmbito | Todos os pedidos visíveis que satisfazem os filtros | Não representa necessariamente toda a empresa |
|
||||
| Em aprovação | Estado actual `pending` | Não é uma fila pessoal nem autorização para decidir |
|
||||
| Antiguidade de submissão | Dias de calendário entre submissão e instante de leitura, no fuso configurado da aplicação; até cinco pendentes mais antigos, desempate por ID | Não mede a duração da etapa actual; não estima prazo de conclusão |
|
||||
| Antiguidade desconhecida | Pendentes sem submissão registada ou com data de submissão num dia futuro | Não substitui a data de submissão pela criação nem inventa duração |
|
||||
| Data de necessidade ultrapassada | `draft`/`pending` com `needed_by` anterior ao dia da leitura | Data de necessidade do requisitante não é prazo contratual de entrega; hoje não conta como ultrapassado |
|
||||
| Aprovadas sem propostas registadas | Aprovadas com contagem de propostas igual a zero, denominador: aprovadas do filtro | Uma proposta registada não é necessariamente válida ou comparável |
|
||||
| Aprovadas sem ordem visível | Aprovadas sem ordem encontrada pela mesma projecção autorizada do relatório | Não prova incumprimento ou inexistência absoluta; pode sobrepor-se à ausência de propostas |
|
||||
| Indicador | Definição e base | Limite de interpretação |
|
||||
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| Requisições no âmbito | Todos os pedidos visíveis que satisfazem os filtros | Não representa necessariamente toda a empresa |
|
||||
| Em aprovação | Estado actual `pending` | Não é uma fila pessoal nem autorização para decidir |
|
||||
| Antiguidade de submissão | Dias de calendário entre submissão e instante de leitura, no fuso configurado da aplicação; até cinco pendentes mais antigos, desempate por ID | Não mede a duração da etapa actual; não estima prazo de conclusão |
|
||||
| Antiguidade desconhecida | Pendentes sem submissão registada ou com data de submissão num dia futuro | Não substitui a data de submissão pela criação nem inventa duração |
|
||||
| Data de necessidade ultrapassada | `draft`/`pending` com `needed_by` anterior ao dia da leitura | Data de necessidade do requisitante não é prazo contratual de entrega; hoje não conta como ultrapassado |
|
||||
| Aprovadas sem propostas registadas | Aprovadas com contagem de propostas igual a zero, denominador: aprovadas do filtro | Uma proposta registada não é necessariamente válida ou comparável |
|
||||
| Aprovadas sem ordem visível | Aprovadas sem ordem encontrada pela mesma projecção autorizada do relatório | Não prova incumprimento ou inexistência absoluta; pode sobrepor-se à ausência de propostas |
|
||||
|
||||
As ligações das submissões antigas abrem o processo original e reaplicam a sua autorização. Relatório e exportação CSV anteriores conservam a sua estrutura; os novos indicadores são apresentados no painel, não acrescentados silenciosamente ao contrato do CSV.
|
||||
|
||||
@@ -22,15 +22,15 @@ As ligações das submissões antigas abrem o processo original e reaplicam a su
|
||||
|
||||
Método `operational_facts_v1`, mesmo instante e snapshot da fila pessoal autorizada. Filtros por percurso, estado, título/referência e criação. Origem: processos visíveis, tarefas possíveis para o interveniente, aprovações independentes e esclarecimentos pendentes. Actualizar ou voltar a consultar para reflectir intervenções posteriores.
|
||||
|
||||
| Indicador | Definição e base | Limite de interpretação |
|
||||
| --- | --- | --- |
|
||||
| Processos no âmbito | Processos visíveis nos filtros, todas as páginas | Não equivale ao universo da organização |
|
||||
| Prazos vencidos | Ocorrências de recepção ou esclarecimento ainda pendentes com prazo anterior à data de leitura | Várias ocorrências podem pertencer ao mesmo processo |
|
||||
| Processos com vencimentos | IDs distintos nas ocorrências vencidas | Distingue carga de ocorrências de quantidade de processos |
|
||||
| Maior atraso | Maior diferença em dias de calendário nas ocorrências; nulo quando não existem | Não determina resultado de candidatura; dias corridos, sem calendário laboral |
|
||||
| Tarefas por acção | Contagem das acções disponíveis à pessoa na sua fila | Não classifica desempenho de trabalhadores ou candidatos |
|
||||
| Tarefas sem prazo próprio | Tarefas cuja data da fila é nula | Ausência de prazo não indica incumprimento |
|
||||
| Aprovações | Intervenções formais pendentes para as quais o actor é elegível e independente | Separadas de tarefas operacionais e de conferência de esclarecimentos |
|
||||
| Indicador | Definição e base | Limite de interpretação |
|
||||
| ------------------------- | ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
|
||||
| Processos no âmbito | Processos visíveis nos filtros, todas as páginas | Não equivale ao universo da organização |
|
||||
| Prazos vencidos | Ocorrências de recepção ou esclarecimento ainda pendentes com prazo anterior à data de leitura | Várias ocorrências podem pertencer ao mesmo processo |
|
||||
| Processos com vencimentos | IDs distintos nas ocorrências vencidas | Distingue carga de ocorrências de quantidade de processos |
|
||||
| Maior atraso | Maior diferença em dias de calendário nas ocorrências; nulo quando não existem | Não determina resultado de candidatura; dias corridos, sem calendário laboral |
|
||||
| Tarefas por acção | Contagem das acções disponíveis à pessoa na sua fila | Não classifica desempenho de trabalhadores ou candidatos |
|
||||
| Tarefas sem prazo próprio | Tarefas cuja data da fila é nula | Ausência de prazo não indica incumprimento |
|
||||
| Aprovações | Intervenções formais pendentes para as quais o actor é elegível e independente | Separadas de tarefas operacionais e de conferência de esclarecimentos |
|
||||
|
||||
## Evolução e qualidade
|
||||
|
||||
|
||||
@@ -14,16 +14,16 @@
|
||||
|
||||
Inventário do código actual, preservando as entregas anteriores. `scripts/menu-code-check.mjs` confere a definição real do menu contra as rotas Laravel GET e o destaque activo: **102 entradas somadas nos seis perfis**, com obrigações ligadas/desligadas. Prova em `proofs/menu-code-contract.json`. É prova de ligação/capacidade do menu, não declaração de funcionamento integral de cada módulo.
|
||||
|
||||
| Grupo do menu | Código e percurso existentes | Ligação ao relatório / saldo identificado |
|
||||
| --- | --- | --- |
|
||||
| Requisições | RequisitionController: criar, editar, submeter, decidir, vistas pessoais/rascunhos; PurchasePlanningController: itens, responsáveis e marcos | O processo reúne as etapas; a central nova localiza pedidos por número, data, estado e departamento. |
|
||||
| Aprovações | Elegibilidade e decisões em RequisitionController/RequisitionWorkflow | Conservadas as decisões efectivas; relatórios não executam nem substituem aprovações. |
|
||||
| Consultas e adjudicações | QuotationController, abertura, qualificação, admissão, recomendação e PurchaseController; revisão/cancelamento existente | Relatório de conferência recupera bases, versões e decisões; não inventa validade de documentos antigos. |
|
||||
| Fornecedores | Cadastro/manutenção/documentos em SupplierController e SupplierDocumentController | **Avaliação de fornecedores continua como proposta de interface**: não existem ainda gravação/revisão/histórico operacional desse módulo. Desenvolver esse fluxo até ao seu relatório é a próxima lacuna funcional concreta. |
|
||||
| Recepções e armazém | GoodsReceiptController, StockController, StockOperationController e EquipmentController | O relatório individual passa a incluir recepções associadas e a ligação às respectivas fichas; não presume entrega integral nem adiciona movimentos. |
|
||||
| Finanças | Facturas, pagamentos externos, orçamento/reforços e políticas existentes; núcleo comum sob configuração própria | Relatório individual inclui facturas/registos de pagamentos apenas para Finanças/Administração. Montantes preservados, sem cálculo de saldo ou inferência de liquidação. Pagamentos/afectações sintéticos e relatórios agregados financeiros ainda requerem os incrementos próprios. |
|
||||
| Administração e controlo | Empresa/equipa/centros, políticas de admissão/recomendação e conflitos | Mantidas regras e autorizações existentes; não há criação de contas ou novos direitos por abrir relatórios. |
|
||||
| Relatórios | **Nova central `/relatorios` e exportação `/relatorios/compras.csv`**, ligadas ao backend | Menu → filtros → requisição/processo → relatório de conferência → impressão existente. CSV usa todos os resultados autorizados dos filtros, não só a página visível. |
|
||||
| Grupo do menu | Código e percurso existentes | Ligação ao relatório / saldo identificado |
|
||||
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Requisições | RequisitionController: criar, editar, submeter, decidir, vistas pessoais/rascunhos; PurchasePlanningController: itens, responsáveis e marcos | O processo reúne as etapas; a central nova localiza pedidos por número, data, estado e departamento. |
|
||||
| Aprovações | Elegibilidade e decisões em RequisitionController/RequisitionWorkflow | Conservadas as decisões efectivas; relatórios não executam nem substituem aprovações. |
|
||||
| Consultas e adjudicações | QuotationController, abertura, qualificação, admissão, recomendação e PurchaseController; revisão/cancelamento existente | Relatório de conferência recupera bases, versões e decisões; não inventa validade de documentos antigos. |
|
||||
| Fornecedores | Cadastro/manutenção/documentos em SupplierController e SupplierDocumentController | **Avaliação de fornecedores continua como proposta de interface**: não existem ainda gravação/revisão/histórico operacional desse módulo. Desenvolver esse fluxo até ao seu relatório é a próxima lacuna funcional concreta. |
|
||||
| Recepções e armazém | GoodsReceiptController, StockController, StockOperationController e EquipmentController | O relatório individual passa a incluir recepções associadas e a ligação às respectivas fichas; não presume entrega integral nem adiciona movimentos. |
|
||||
| Finanças | Facturas, pagamentos externos, orçamento/reforços e políticas existentes; núcleo comum sob configuração própria | Relatório individual inclui facturas/registos de pagamentos apenas para Finanças/Administração. Montantes preservados, sem cálculo de saldo ou inferência de liquidação. Pagamentos/afectações sintéticos e relatórios agregados financeiros ainda requerem os incrementos próprios. |
|
||||
| Administração e controlo | Empresa/equipa/centros, políticas de admissão/recomendação e conflitos | Mantidas regras e autorizações existentes; não há criação de contas ou novos direitos por abrir relatórios. |
|
||||
| Relatórios | **Nova central `/relatorios` e exportação `/relatorios/compras.csv`**, ligadas ao backend | Menu → filtros → requisição/processo → relatório de conferência → impressão existente. CSV usa todos os resultados autorizados dos filtros, não só a página visível. |
|
||||
|
||||
O novo recorte não encerra contratos, multimoeda, relatórios financeiros agregados, avaliação de fornecedores ou o percurso comercial dos pagamentos ainda sintéticos. A prioridade seguinte é substituir a proposta de avaliação por um ciclo persistente, com critérios/configuração e evidências explícitos, revisão, histórico e relatório; não activar pesos/políticas provisórias silenciosamente.
|
||||
|
||||
@@ -37,16 +37,16 @@ Verificações deste recorte: 14 testes Feature/223 asserções, incluindo 207 r
|
||||
|
||||
A aplicação autenticada usa agora um menu lateral por tarefas e uma barra superior com pesquisa do menu, atalho de nova requisição e conta. As secções podem ser expandidas, a área actual abre automaticamente e a ligação corrente fica destacada. Pesquisa sem distinção de acentos/maiúsculas, limpeza e mensagem de ausência de resultados. Não é uma pesquisa global de documentos.
|
||||
|
||||
| Secção | Percursos existentes reunidos |
|
||||
| --- | --- |
|
||||
| Painel inicial | Visão geral e indicadores existentes |
|
||||
| Requisições | Nova, minhas, meus rascunhos, todas as autorizadas e planeamento |
|
||||
| Aprovações | Próxima decisão elegível para o utilizador e histórico das suas decisões de requisições |
|
||||
| Consultas e adjudicações | Cotações/comparação, adjudicações/ordens e revisão/cancelamento existente |
|
||||
| Fornecedores | Cadastro e documentos |
|
||||
| Recepções e armazém | Recepções/divergências, artigos/stock, saídas/inventários, equipamentos/peças |
|
||||
| Finanças | Facturas, obrigações autorizadas, pagamentos externos, orçamentos/reforços e investimento |
|
||||
| Administração e controlo | Empresa/equipa/centros, políticas de admissão/recomendação e conflitos |
|
||||
| Secção | Percursos existentes reunidos |
|
||||
| ------------------------ | ----------------------------------------------------------------------------------------- |
|
||||
| Painel inicial | Visão geral e indicadores existentes |
|
||||
| Requisições | Nova, minhas, meus rascunhos, todas as autorizadas e planeamento |
|
||||
| Aprovações | Próxima decisão elegível para o utilizador e histórico das suas decisões de requisições |
|
||||
| Consultas e adjudicações | Cotações/comparação, adjudicações/ordens e revisão/cancelamento existente |
|
||||
| Fornecedores | Cadastro e documentos |
|
||||
| Recepções e armazém | Recepções/divergências, artigos/stock, saídas/inventários, equipamentos/peças |
|
||||
| Finanças | Facturas, obrigações autorizadas, pagamentos externos, orçamentos/reforços e investimento |
|
||||
| Administração e controlo | Empresa/equipa/centros, políticas de admissão/recomendação e conflitos |
|
||||
|
||||
O layout real é `SgcShell`, usado por `AppLayout`; o componente antigo `AppSidebar` não é a navegação activa. A renovação foi feita no layout efectivamente utilizado. As consultas públicas mantêm o seu layout próprio e as ligações anteriores. O núcleo comum de obrigações continua com o seu ecrã próprio e regresso às facturas; a sua ligação usa carregamento completo para não receber uma página HTML como modal Inertia.
|
||||
|
||||
|
||||
@@ -3,11 +3,7 @@
|
||||
"database": "compras_dev",
|
||||
"user_id": 1,
|
||||
"tenant_id": 1,
|
||||
"actions": [
|
||||
"view",
|
||||
"prepare",
|
||||
"validate"
|
||||
],
|
||||
"actions": ["view", "prepare", "validate"],
|
||||
"enabled": true,
|
||||
"source_instance": "compras-local-aa0dbeec75b98ca65246756541f75363",
|
||||
"backup_restore_verified": true,
|
||||
|
||||
@@ -16,7 +16,6 @@ Migração 000035 apenas na avaliação 8005, backup/restauro fresco conferido,
|
||||
|
||||
Próxima lacuna: modelos de documentos versionados por empresa, variáveis preenchidas a partir de fontes autorizadas e termos específicos novos. Conferir associações e relatórios na matriz `reutilizacao-dados.md`; reconciliação histórica exige identidade/autorização, sem fusão por nome. Não declarar o requisito transversal integralmente concluído.
|
||||
|
||||
|
||||
**Registar uma vez/reutilizar — Pessoas, 30/10/2026:** implementado cadastro próprio/partilhado por permissões, nome/referência/perfil, versão/auditoria/arquivo. Primeira candidatura pode guardar perfil no mesmo acto; seguintes seleccionam fonte autorizada. Nome e snapshot vêm do servidor; dados específicos da candidatura não reescrevem cadastro. Snapshot e referência/versão preservados por processo, actualizações/arquivo não alteram histórico; sem duplicar pessoa ligada no mesmo processo ou fundir nomes. Preparador consulta próprios, capacidades `people_registry_read/manage` dão âmbito partilhado; administrador não recebe leitura alheia automática, cadastro não dá acesso aos processos. Migração 000034 apenas em 8005: backup/restauro e 93 tabelas antigas preservados, nova tabela vazia, duas colunas nulas em candidaturas antigas, sem pessoas/grants/importação. Provas `people-registry.json`, `people-registry-install.json`, `people-registry-pg.json`. PHP integral 698/696 aprovados/dois ignorados, 6 739 asserções; módulo/PG 43/1124; React 44; menu seis grupos/111 acessos. Visual/domínio pendentes. Matriz `reutilizacao-dados.md`: requisito global ainda não concluído; próximos catálogo genérico de artigos, modelos documentais e reconciliação assistida de históricos. Não depende de IA; nenhuma chamada paga repetida.
|
||||
|
||||
**Garantia comercial sem IA, 30/10/2026:** proprietário confirmou que as funcionalidades prometidas têm de permanecer disponíveis. Requisito permanente em `AGENTS.md`: SGC base independente do adicional de IA; sem chave/crédito/subscrição/fornecedor não bloqueiam os percursos normais ou relatórios. Indicadores/alertas/sínteses locais fazem parte do núcleo. Preservar documentos, decisões e resultados incorporados após cancelar IA; não mover funções aprovadas do núcleo para o adicional. Aplicar nas futuras integrações, reservas, limites e cobrança, mantendo permissões e auditoria. Conferidos controlos/testes existentes e reutilizada prova `ai-plan.json`; alteração documental, sem novas chamadas/dados/código. Pendências funcionais continuam pendentes, sem alegar conclusão integral.
|
||||
@@ -97,7 +96,6 @@ Próxima lacuna: modelos de documentos versionados por empresa, variáveis preen
|
||||
|
||||
**Pagamentos e afectações sintéticos, 29/10/2026:** percurso comum disponível apenas nas demonstrações isoladas 8005/8006, com preparação, confirmação independente e afectação explícita. Prova nas duas composições e preservação por hashes aprovadas; revisão visual/manual pendente. Sem migração habitual, utilização real ou novos pagamentos sobre importações históricas. Ver [relatório do núcleo comum](../../sgf-tempo/docs/SUPPLIER_SETTLEMENT_DELIVERY.md).
|
||||
|
||||
|
||||
**Conferência posterior dos pendentes implementada, 29/10/2026:** confirmação/rejeição ligada ao núcleo comum com decisão imutável, saldo único e escrita original atómica. Provas SQLite e PostgreSQL descartável, incluindo dois pedidos simultâneos e reversão de falha, aprovadas. Ver [entrega](conferencia-pendentes-importados.md). Migração 000023 e opção de conferência preparadas, ainda não aplicadas/activadas em `compras_dev` ou 8005; importação local anterior preservada. Substitui a pendência de desenvolver esta conferência, sem novos pagamentos/transferências.
|
||||
|
||||
**Aplicação/activação histórica local concluída, 29/10/2026:** após «continue», 000022 aplicada em `compras_dev`, lote 23, e flag histórica activada, após backup/restauro das 72 tabelas. Registos/restrições preservados, duas tabelas novas vazias e dois triggers verificados. Activação auditada sem novas contas/concessões ou alteração de identidade; zero facturas/pagamentos/obrigações e nenhuma incorporação automática. Ver `historico-migracao-000022.json`, `historico-activacao-local.json` e [relatório](passagem-historico-pagamentos.md). Demo 8005 permanece sem esta migração/activação, confirmada em leitura; restantes demos e público preservados. Substitui apenas as indicações históricas de aplicação/activação pendentes abaixo. Novos pagamentos e decisão de pendentes depois da passagem continuam incremento próprio.
|
||||
|
||||
+10
-10
@@ -14,16 +14,16 @@ O plano usa o JSON de política já existente na empresa; não há nova migraç
|
||||
|
||||
Nos relatórios individuais de Compras e Pessoas, um plano com fornecedor escolhido torna disponível «Pré-visualização de IA — plano da empresa». A entrada deriva apenas da síntese factual autorizada/redigida do servidor, respeitando o percurso e as categorias escolhidas. Não consulta anexos, CV, notas de entrevista ou dados bancários. Desactivar a apresentação da síntese local não apaga o plano: o relatório continua disponível e a preparação segue as suas próprias escolhas de âmbito.
|
||||
|
||||
| Categoria | Factos de Compras | Factos de Pessoas |
|
||||
| --- | --- | --- |
|
||||
| Estado e regras | Estado da requisição | Número de critérios e mínimo de avaliações |
|
||||
| Comparação | Contagem de propostas e estado de preparação da matriz | — |
|
||||
| Actualidade documental | Versão/actualidade da qualificação e acta | — |
|
||||
| Ordem/minuta/recepção | Presença de ordem e contagem de recepções | Presença de minuta interna, apenas se autorizada |
|
||||
| Selecção | — | Proposta pendente/devolvida/selecção aprovada |
|
||||
| Candidaturas | — | Contagem na matriz actual ou base preservada |
|
||||
| Esclarecimentos | — | Contagem em acompanhamento |
|
||||
| Finanças | Contagens de facturas/pagamentos, apenas quando o actor já tem acesso | — |
|
||||
| Categoria | Factos de Compras | Factos de Pessoas |
|
||||
| ---------------------- | --------------------------------------------------------------------- | ------------------------------------------------ |
|
||||
| Estado e regras | Estado da requisição | Número de critérios e mínimo de avaliações |
|
||||
| Comparação | Contagem de propostas e estado de preparação da matriz | — |
|
||||
| Actualidade documental | Versão/actualidade da qualificação e acta | — |
|
||||
| Ordem/minuta/recepção | Presença de ordem e contagem de recepções | Presença de minuta interna, apenas se autorizada |
|
||||
| Selecção | — | Proposta pendente/devolvida/selecção aprovada |
|
||||
| Candidaturas | — | Contagem na matriz actual ou base preservada |
|
||||
| Esclarecimentos | — | Contagem em acompanhamento |
|
||||
| Finanças | Contagens de facturas/pagamentos, apenas quando o actor já tem acesso | — |
|
||||
|
||||
As categorias nunca concedem permissões de documento ou acesso retroactivo. O administrador que configura o plano não ganha acesso automático às candidaturas. Nomes, justificações livres, referências de pessoas, pontuações individuais, respostas de entrevista e hashes de fontes não integram os factos pré-visualizados. Os relatórios originais conservam a sua projecção autorizada.
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T15:45:53+00:00",
|
||||
"database": "disposable PostgreSQL",
|
||||
"application_role_reused_without_privilege_change": true,
|
||||
"result": "passed",
|
||||
"scenario": "draft tenant AI plan configuration and permission-scoped previews for procurement and people, no external requests",
|
||||
"original_evaluation_untouched": true,
|
||||
"database_removed": true
|
||||
"checked_at": "2026-10-30T15:45:53+00:00",
|
||||
"database": "disposable PostgreSQL",
|
||||
"application_role_reused_without_privilege_change": true,
|
||||
"result": "passed",
|
||||
"scenario": "draft tenant AI plan configuration and permission-scoped previews for procurement and people, no external requests",
|
||||
"original_evaluation_untouched": true,
|
||||
"database_removed": true
|
||||
}
|
||||
|
||||
+69
-69
@@ -1,71 +1,71 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T15:48:55.811028+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"tenant scoped draft provider model domain and fact-category plan",
|
||||
"planned monthly budget request count and per-request payload/output limits",
|
||||
"optimistic version reason and atomic audit",
|
||||
"preserve local brief settings and AI plan during reciprocal changes",
|
||||
"previews use authorised redacted facts with no people identities free notes bank data or source hashes",
|
||||
"excluded domain empty scope financial privacy and oversized input handling"
|
||||
],
|
||||
"module_tests": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 90,
|
||||
"passed": 90,
|
||||
"assertions": 1469,
|
||||
"duration_ms": 10623
|
||||
},
|
||||
"react_inertia_tests": 40,
|
||||
"postgresql_integration": {
|
||||
"tool": "phpunit",
|
||||
"result": "passed",
|
||||
"tests": 1,
|
||||
"passed": 1,
|
||||
"assertions": 147,
|
||||
"duration_ms": 3367
|
||||
},
|
||||
"postgresql_proof": "ai-plan-pg.json",
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"http_proof": "menu-links.json",
|
||||
"runtime_sha256_files": 18,
|
||||
"accepted_full_suite_reused": "people-clarifications.json",
|
||||
"migrations_applied": false,
|
||||
"schema_changed": false,
|
||||
"business_records_changed": false,
|
||||
"tenant_plan_choices_automatically_created": false,
|
||||
"accounts_or_grants_changed": false,
|
||||
"external_AI_calls_this_increment": 0,
|
||||
"live_execution_enabled": false,
|
||||
"consumption_ledger_or_budget_reservation_implemented": false,
|
||||
"model_compatibility_verified_by_saving_plan": false,
|
||||
"prior_authorised_synthetic_generation_repeated": false,
|
||||
"visual_review": false,
|
||||
"domain_installed": false,
|
||||
"source_sha256": {
|
||||
"app/Services/AiPreparationPlan.php": "3c6345376b3444f561207f1c8c781da214e87609f97d20255cdd79c419a406a9",
|
||||
"app/Services/IntelligencePolicy.php": "07956aa32f585d00a1c9c4414c2baec9e037e243ff23e305aa4b70cf1ace7d95",
|
||||
"app/Services/OrganizationConfiguration.php": "bbeae0c4bafad412830cd902842b4bf56158d9d004f8203825436954185cd793",
|
||||
"app/Models/Tenant.php": "c503893033342a32f02dd89175b785af08a1061da2d5b9cb2d7bb45a1ff30f66",
|
||||
"app/Http/Controllers/OrganizationConfigurationController.php": "3ce1931f6b793626a2faaa84a1f9be5f589c8cdd5e6dc2fa5730e72c095b3103",
|
||||
"app/Http/Controllers/ProcurementReviewReportController.php": "a78ed469ed5e914d47a4afa2fcbd64afce9177aff80d59418b90c7bfc41f89ad",
|
||||
"app/Http/Controllers/PeopleHiringController.php": "ef449c2f1e67454615ff8454d188590b6acac7966f61b6b697887b8d44b112f4",
|
||||
"resources/js/components/ai-plan-settings.tsx": "d06682971acb274376e0986bf00bfd63fde2a7a72f75e90aca4fd2fedbee7621",
|
||||
"resources/js/components/ai-preparation-preview.tsx": "0ba01e2f641a52baf8fbb9b52a1342c86289e6eb9946a3b7b4a71c970ff256be",
|
||||
"resources/js/pages/company/parameters.tsx": "aa9f1f475961dc176f378f5851efbdaf43a0007f0cd233adaffdfb8d40906fdb",
|
||||
"resources/js/pages/purchases/review-report.tsx": "2b1066bb2be52c945f58c143f48417754d4ad8772f04579a23c9fdef150a28ad",
|
||||
"resources/js/pages/people/show.tsx": "10c59a01033094543458b00771faff70249994dc539fdeacd1eb786e561526f7",
|
||||
"tests/Feature/OrganizationConfigurationTest.php": "4bfe5bfe83042c3285ebf5c0e2aa731e9cd02eaf73b1b8774bd2684d80ef71da",
|
||||
"tests/Feature/AiPreparationTest.php": "cf84282b2e879a6d0eb9605d052de070ddf8b45ede07a969bd03d3155d796254",
|
||||
"tests/Feature/PeopleHiringTest.php": "35f444dddaad5e2702c055ef8b02afbc757de2d6a58eb997b06d413f5bf82691",
|
||||
"tests/Support/AiPlanFixture.php": "af62e4b7f46d5ceab5cfbfaadcc85a53d42db1c4e71da7b30427f02fd6deea74",
|
||||
"tests/frontend/ai-plan.test.tsx": "14e6dac9e49ca7b6434f177e5456533275268a8f9f745b2c000b22376a9e1573",
|
||||
"routes/web.php": "38fdd89d976f9b82369aef440cf64ef384a584bdaeb519c0ec24741a296f6868",
|
||||
"docs/plano-ia.md": "aa1ce1186b2b6077b52d7ed7b749250bb7f013a059e4b9f652b0d989c67ee7f3"
|
||||
}
|
||||
"checked_at": "2026-10-30T15:48:55.811028+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"tenant scoped draft provider model domain and fact-category plan",
|
||||
"planned monthly budget request count and per-request payload/output limits",
|
||||
"optimistic version reason and atomic audit",
|
||||
"preserve local brief settings and AI plan during reciprocal changes",
|
||||
"previews use authorised redacted facts with no people identities free notes bank data or source hashes",
|
||||
"excluded domain empty scope financial privacy and oversized input handling"
|
||||
],
|
||||
"module_tests": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 90,
|
||||
"passed": 90,
|
||||
"assertions": 1469,
|
||||
"duration_ms": 10623
|
||||
},
|
||||
"react_inertia_tests": 40,
|
||||
"postgresql_integration": {
|
||||
"tool": "phpunit",
|
||||
"result": "passed",
|
||||
"tests": 1,
|
||||
"passed": 1,
|
||||
"assertions": 147,
|
||||
"duration_ms": 3367
|
||||
},
|
||||
"postgresql_proof": "ai-plan-pg.json",
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"http_proof": "menu-links.json",
|
||||
"runtime_sha256_files": 18,
|
||||
"accepted_full_suite_reused": "people-clarifications.json",
|
||||
"migrations_applied": false,
|
||||
"schema_changed": false,
|
||||
"business_records_changed": false,
|
||||
"tenant_plan_choices_automatically_created": false,
|
||||
"accounts_or_grants_changed": false,
|
||||
"external_AI_calls_this_increment": 0,
|
||||
"live_execution_enabled": false,
|
||||
"consumption_ledger_or_budget_reservation_implemented": false,
|
||||
"model_compatibility_verified_by_saving_plan": false,
|
||||
"prior_authorised_synthetic_generation_repeated": false,
|
||||
"visual_review": false,
|
||||
"domain_installed": false,
|
||||
"source_sha256": {
|
||||
"app/Services/AiPreparationPlan.php": "3c6345376b3444f561207f1c8c781da214e87609f97d20255cdd79c419a406a9",
|
||||
"app/Services/IntelligencePolicy.php": "07956aa32f585d00a1c9c4414c2baec9e037e243ff23e305aa4b70cf1ace7d95",
|
||||
"app/Services/OrganizationConfiguration.php": "bbeae0c4bafad412830cd902842b4bf56158d9d004f8203825436954185cd793",
|
||||
"app/Models/Tenant.php": "c503893033342a32f02dd89175b785af08a1061da2d5b9cb2d7bb45a1ff30f66",
|
||||
"app/Http/Controllers/OrganizationConfigurationController.php": "3ce1931f6b793626a2faaa84a1f9be5f589c8cdd5e6dc2fa5730e72c095b3103",
|
||||
"app/Http/Controllers/ProcurementReviewReportController.php": "a78ed469ed5e914d47a4afa2fcbd64afce9177aff80d59418b90c7bfc41f89ad",
|
||||
"app/Http/Controllers/PeopleHiringController.php": "ef449c2f1e67454615ff8454d188590b6acac7966f61b6b697887b8d44b112f4",
|
||||
"resources/js/components/ai-plan-settings.tsx": "d06682971acb274376e0986bf00bfd63fde2a7a72f75e90aca4fd2fedbee7621",
|
||||
"resources/js/components/ai-preparation-preview.tsx": "0ba01e2f641a52baf8fbb9b52a1342c86289e6eb9946a3b7b4a71c970ff256be",
|
||||
"resources/js/pages/company/parameters.tsx": "aa9f1f475961dc176f378f5851efbdaf43a0007f0cd233adaffdfb8d40906fdb",
|
||||
"resources/js/pages/purchases/review-report.tsx": "2b1066bb2be52c945f58c143f48417754d4ad8772f04579a23c9fdef150a28ad",
|
||||
"resources/js/pages/people/show.tsx": "10c59a01033094543458b00771faff70249994dc539fdeacd1eb786e561526f7",
|
||||
"tests/Feature/OrganizationConfigurationTest.php": "4bfe5bfe83042c3285ebf5c0e2aa731e9cd02eaf73b1b8774bd2684d80ef71da",
|
||||
"tests/Feature/AiPreparationTest.php": "cf84282b2e879a6d0eb9605d052de070ddf8b45ede07a969bd03d3155d796254",
|
||||
"tests/Feature/PeopleHiringTest.php": "35f444dddaad5e2702c055ef8b02afbc757de2d6a58eb997b06d413f5bf82691",
|
||||
"tests/Support/AiPlanFixture.php": "af62e4b7f46d5ceab5cfbfaadcc85a53d42db1c4e71da7b30427f02fd6deea74",
|
||||
"tests/frontend/ai-plan.test.tsx": "14e6dac9e49ca7b6434f177e5456533275268a8f9f745b2c000b22376a9e1573",
|
||||
"routes/web.php": "38fdd89d976f9b82369aef440cf64ef384a584bdaeb519c0ec24741a296f6868",
|
||||
"docs/plano-ia.md": "aa1ce1186b2b6077b52d7ed7b749250bb7f013a059e4b9f652b0d989c67ee7f3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T15:08:33.102627+00:00",
|
||||
"authorisation": "granted by owner after cost estimate",
|
||||
"authorised_live_calls": 1,
|
||||
"live_calls_executed": 1,
|
||||
"destination": "https://api.openai.com/v1/responses",
|
||||
"model": "gpt-5.6-terra",
|
||||
"data_scope": "three built-in synthetic facts only",
|
||||
"max_output_tokens": 1000,
|
||||
"credential_presence_checked_without_value_output": true,
|
||||
"dedicated_credential_configured": false,
|
||||
"standard_openai_credential_available": false,
|
||||
"rehearsal_enabled": false,
|
||||
"execution_status": "completed: one successful synthetic generation, see ai-rehearsal-live.json",
|
||||
"new_authorisation_required_for_same_call": false,
|
||||
"successful_generations": 1,
|
||||
"live_call_count_definition": "successful generation; transport and authentication failures separately recorded in ai-rehearsal-live.json"
|
||||
"checked_at": "2026-10-30T15:08:33.102627+00:00",
|
||||
"authorisation": "granted by owner after cost estimate",
|
||||
"authorised_live_calls": 1,
|
||||
"live_calls_executed": 1,
|
||||
"destination": "https://api.openai.com/v1/responses",
|
||||
"model": "gpt-5.6-terra",
|
||||
"data_scope": "three built-in synthetic facts only",
|
||||
"max_output_tokens": 1000,
|
||||
"credential_presence_checked_without_value_output": true,
|
||||
"dedicated_credential_configured": false,
|
||||
"standard_openai_credential_available": false,
|
||||
"rehearsal_enabled": false,
|
||||
"execution_status": "completed: one successful synthetic generation, see ai-rehearsal-live.json",
|
||||
"new_authorisation_required_for_same_call": false,
|
||||
"successful_generations": 1,
|
||||
"live_call_count_definition": "successful generation; transport and authentication failures separately recorded in ai-rehearsal-live.json"
|
||||
}
|
||||
|
||||
@@ -1,64 +1,64 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T15:37:00+00:00",
|
||||
"authorisation": "owner authorised one synthetic paid call",
|
||||
"attempts": 3,
|
||||
"destination": "https://api.openai.com/v1/responses",
|
||||
"model": "gpt-5.6-terra",
|
||||
"max_output_tokens": 1000,
|
||||
"real_business_data_sent": false,
|
||||
"status": "completed",
|
||||
"automatic_retry": false,
|
||||
"prior_transport_failure": {
|
||||
"php_curl_errno": 60,
|
||||
"diagnosis": "local issuer certificate unavailable before HTTPS establishment"
|
||||
},
|
||||
"tls_verification_disabled": false,
|
||||
"trust_source": "Windows trusted root certificate stores; process-only CA configuration",
|
||||
"prior_rejected_request": {
|
||||
"http_status": 401,
|
||||
"code": "invalid_api_key",
|
||||
"local_cause": "duplicated setting and embedded assignment normalised without exposing key"
|
||||
},
|
||||
"http_status": 200,
|
||||
"usage": {
|
||||
"input_tokens": 204,
|
||||
"output_tokens": 81,
|
||||
"total_tokens": 285
|
||||
},
|
||||
"result_sha256": "d3412a0c4f7e34bfef51be924903c21e8724813e8cb7500a923a55fb0a96ebce",
|
||||
"assessment": {
|
||||
"method": "synthetic_ai_assessment_v1",
|
||||
"facts": 3,
|
||||
"claims": 3,
|
||||
"cited_facts": 3,
|
||||
"uncited_fact_ids": [],
|
||||
"citation_coverage_complete": true,
|
||||
"exact_source_text_matches": 1,
|
||||
"requires_human_review": true,
|
||||
"semantic_accuracy": "not_automatically_verified"
|
||||
},
|
||||
"reviewed_at": "2026-10-30T15:37:34.123863+00:00",
|
||||
"human_content_review": {
|
||||
"three_source_facts_preserved": true,
|
||||
"invented_approval": false,
|
||||
"invented_order": false,
|
||||
"invented_delivery_or_payment": false,
|
||||
"synthetic_scenario_accepted": true,
|
||||
"general_model_quality_certified": false
|
||||
},
|
||||
"attempt_outcomes": [
|
||||
"TLS trust failure before HTTPS establishment",
|
||||
"HTTP 401 for malformed local credential setting",
|
||||
"HTTP 200 completed after local corrections"
|
||||
],
|
||||
"successful_generations": 1,
|
||||
"estimated_text_cost_usd": "0.001380",
|
||||
"cost_basis": {
|
||||
"input_tokens": 204,
|
||||
"output_tokens": 81,
|
||||
"input_usd_per_million": "2.00",
|
||||
"output_usd_per_million": "12.00",
|
||||
"billing_balance_or_credit_deduction_verified": false
|
||||
},
|
||||
"real_document_processing_enabled": false
|
||||
"checked_at": "2026-10-30T15:37:00+00:00",
|
||||
"authorisation": "owner authorised one synthetic paid call",
|
||||
"attempts": 3,
|
||||
"destination": "https://api.openai.com/v1/responses",
|
||||
"model": "gpt-5.6-terra",
|
||||
"max_output_tokens": 1000,
|
||||
"real_business_data_sent": false,
|
||||
"status": "completed",
|
||||
"automatic_retry": false,
|
||||
"prior_transport_failure": {
|
||||
"php_curl_errno": 60,
|
||||
"diagnosis": "local issuer certificate unavailable before HTTPS establishment"
|
||||
},
|
||||
"tls_verification_disabled": false,
|
||||
"trust_source": "Windows trusted root certificate stores; process-only CA configuration",
|
||||
"prior_rejected_request": {
|
||||
"http_status": 401,
|
||||
"code": "invalid_api_key",
|
||||
"local_cause": "duplicated setting and embedded assignment normalised without exposing key"
|
||||
},
|
||||
"http_status": 200,
|
||||
"usage": {
|
||||
"input_tokens": 204,
|
||||
"output_tokens": 81,
|
||||
"total_tokens": 285
|
||||
},
|
||||
"result_sha256": "d3412a0c4f7e34bfef51be924903c21e8724813e8cb7500a923a55fb0a96ebce",
|
||||
"assessment": {
|
||||
"method": "synthetic_ai_assessment_v1",
|
||||
"facts": 3,
|
||||
"claims": 3,
|
||||
"cited_facts": 3,
|
||||
"uncited_fact_ids": [],
|
||||
"citation_coverage_complete": true,
|
||||
"exact_source_text_matches": 1,
|
||||
"requires_human_review": true,
|
||||
"semantic_accuracy": "not_automatically_verified"
|
||||
},
|
||||
"reviewed_at": "2026-10-30T15:37:34.123863+00:00",
|
||||
"human_content_review": {
|
||||
"three_source_facts_preserved": true,
|
||||
"invented_approval": false,
|
||||
"invented_order": false,
|
||||
"invented_delivery_or_payment": false,
|
||||
"synthetic_scenario_accepted": true,
|
||||
"general_model_quality_certified": false
|
||||
},
|
||||
"attempt_outcomes": [
|
||||
"TLS trust failure before HTTPS establishment",
|
||||
"HTTP 401 for malformed local credential setting",
|
||||
"HTTP 200 completed after local corrections"
|
||||
],
|
||||
"successful_generations": 1,
|
||||
"estimated_text_cost_usd": "0.001380",
|
||||
"cost_basis": {
|
||||
"input_tokens": 204,
|
||||
"output_tokens": 81,
|
||||
"input_usd_per_million": "2.00",
|
||||
"output_usd_per_million": "12.00",
|
||||
"billing_balance_or_credit_deduction_verified": false
|
||||
},
|
||||
"real_document_processing_enabled": false
|
||||
}
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T14:55:29.383515+00:00",
|
||||
"scope": "synthetic-only OpenAI Responses rehearsal, no web or business integration",
|
||||
"tests": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 15,
|
||||
"passed": 15,
|
||||
"assertions": 41,
|
||||
"duration_ms": 1041
|
||||
},
|
||||
"phpstan_errors": 0,
|
||||
"pint": "passed",
|
||||
"preview_executed": true,
|
||||
"live_external_calls": 0,
|
||||
"model": "gpt-5.6-terra",
|
||||
"maximum_request_bytes": 4500,
|
||||
"maximum_output_tokens": 1000,
|
||||
"automatic_retries": 0,
|
||||
"HTTP_redirects": false,
|
||||
"server_credentials_changed": false,
|
||||
"real_data_allowed": false,
|
||||
"factual_accuracy_automatically_verified": false,
|
||||
"live_authorisation": "pending",
|
||||
"review_document": "../ensaio-ia.md",
|
||||
"official_docs": [
|
||||
"https://developers.openai.com/api/docs/guides/structured-outputs",
|
||||
"https://developers.openai.com/api/docs/models/gpt-5.6-terra"
|
||||
],
|
||||
"source_sha256": {
|
||||
"app/Services/SyntheticAiRehearsal.php": "96dee8e663b7b211f8b3be0b9c98ded1745a1019457dced413de62d265c35d41",
|
||||
"app/Console/Commands/SgcAiRehearsal.php": "3003a8bcdd1cea5bc53dc4071ea22c970f59e802fe0deeb42abed7d963f5d1e7",
|
||||
"config/sgc_intelligence.php": "ac385a06fd4c5a7421fa7ebc45185e81db790b17a5ef45cce469e769787587da",
|
||||
"tests/Feature/SyntheticAiRehearsalTest.php": "ae03beda519ae06b175b0ad0a63848af831689153387aef5f1adc9d5105874f2",
|
||||
"docs/ensaio-ia.md": "a1bef675b436f63b2f8ff434c73daeea53efc5a01b1374a53b8f0512e0a97b50"
|
||||
}
|
||||
"checked_at": "2026-10-30T14:55:29.383515+00:00",
|
||||
"scope": "synthetic-only OpenAI Responses rehearsal, no web or business integration",
|
||||
"tests": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 15,
|
||||
"passed": 15,
|
||||
"assertions": 41,
|
||||
"duration_ms": 1041
|
||||
},
|
||||
"phpstan_errors": 0,
|
||||
"pint": "passed",
|
||||
"preview_executed": true,
|
||||
"live_external_calls": 0,
|
||||
"model": "gpt-5.6-terra",
|
||||
"maximum_request_bytes": 4500,
|
||||
"maximum_output_tokens": 1000,
|
||||
"automatic_retries": 0,
|
||||
"HTTP_redirects": false,
|
||||
"server_credentials_changed": false,
|
||||
"real_data_allowed": false,
|
||||
"factual_accuracy_automatically_verified": false,
|
||||
"live_authorisation": "pending",
|
||||
"review_document": "../ensaio-ia.md",
|
||||
"official_docs": [
|
||||
"https://developers.openai.com/api/docs/guides/structured-outputs",
|
||||
"https://developers.openai.com/api/docs/models/gpt-5.6-terra"
|
||||
],
|
||||
"source_sha256": {
|
||||
"app/Services/SyntheticAiRehearsal.php": "96dee8e663b7b211f8b3be0b9c98ded1745a1019457dced413de62d265c35d41",
|
||||
"app/Console/Commands/SgcAiRehearsal.php": "3003a8bcdd1cea5bc53dc4071ea22c970f59e802fe0deeb42abed7d963f5d1e7",
|
||||
"config/sgc_intelligence.php": "ac385a06fd4c5a7421fa7ebc45185e81db790b17a5ef45cce469e769787587da",
|
||||
"tests/Feature/SyntheticAiRehearsalTest.php": "ae03beda519ae06b175b0ad0a63848af831689153387aef5f1adc9d5105874f2",
|
||||
"docs/ensaio-ia.md": "a1bef675b436f63b2f8ff434c73daeea53efc5a01b1374a53b8f0512e0a97b50"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T17:04:03.991Z",
|
||||
"administration": 200,
|
||||
"unconfigured_public_portal": 404,
|
||||
"staff_session_does_not_authenticate_candidate": true,
|
||||
"registration_open": false,
|
||||
"accounts_created": 0
|
||||
"checked_at": "2026-10-30T17:04:03.991Z",
|
||||
"administration": 200,
|
||||
"unconfigured_public_portal": 404,
|
||||
"staff_session_does_not_authenticate_candidate": true,
|
||||
"registration_open": false,
|
||||
"accounts_created": 0
|
||||
}
|
||||
|
||||
@@ -1,63 +1,63 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T17:04:58.049470+00:00",
|
||||
"scope": [
|
||||
"separate candidate authentication scoped by company",
|
||||
"explicit portal registration policy and versioned notice acknowledgment",
|
||||
"immutable own profile revisions including province district locality",
|
||||
"no staff or cross-account access",
|
||||
"atomic profile and audit rollback",
|
||||
"login and logout history clearing"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 715,
|
||||
"passed": 713,
|
||||
"assertions": 6985,
|
||||
"duration_ms": 149158,
|
||||
"skipped": 2
|
||||
},
|
||||
"candidate_tests": 5,
|
||||
"candidate_assertions": 81,
|
||||
"disposable_postgresql_tests": 47,
|
||||
"disposable_postgresql_assertions": 1163,
|
||||
"react_inertia_tests": 52,
|
||||
"phpstan_errors": 0,
|
||||
"typescript": "passed",
|
||||
"pint": "passed",
|
||||
"targeted_lint": "passed",
|
||||
"build_exit": 0,
|
||||
"menu_entries_across_six_roles": 111,
|
||||
"runtime_sha256_files": 18,
|
||||
"postgresql_proof": "candidate-portal-pg.json",
|
||||
"installation_proof": "candidate-portal-install.json",
|
||||
"http_proof": "candidate-portal-http.json",
|
||||
"visual_review": false,
|
||||
"domain_installed": false,
|
||||
"registration_open_in_evaluation": false,
|
||||
"candidate_accounts_created_in_evaluation": 0,
|
||||
"external_AI_calls": 0,
|
||||
"emails_sent": 0,
|
||||
"outstanding": [
|
||||
"email/identity verification",
|
||||
"password recovery",
|
||||
"submission to opportunities and frozen profile documents",
|
||||
"attachment security verification",
|
||||
"triage complements revisions",
|
||||
"retention deletion and privacy policy configuration before public use"
|
||||
],
|
||||
"source_sha256": {
|
||||
"database/migrations/2026_10_30_000037_create_candidate_profiles.php": "159648252836af068226978666d8eb11148ed6890cba2082b0d5c8b415365e97",
|
||||
"app/Models/CandidateAccount.php": "ab30a14616554bbe7d5ec0dec2347582754f846c0a4ade5455cfa095d73e4bec",
|
||||
"app/Services/CandidateProfiles.php": "54e1e95e10c9004fef69fbdd14b54f85d617f3296cc27a7aba2f1f51552f2cad",
|
||||
"app/Http/Controllers/CandidatePortalController.php": "9ff56978b43c4a2401c02ca3f7f2a95c7f3a550009b3385c682dafcecd6f2615",
|
||||
"config/auth.php": "e3fd9d4244bc9dd92b6e8d76dc2e5163834ef21b4b9df7dd04f1015d82d32ba6",
|
||||
"app/Http/Middleware/HandleInertiaRequests.php": "8db70b1500d683f1df3b7a26ce019c72b5c917ab51b020fdec59700100d588ed",
|
||||
"resources/js/app.tsx": "5669117aa4b607b1d6c3b1f51af8b18650c80c75a5eb697ff7f89a3146c851a9",
|
||||
"resources/js/pages/candidate/entry.tsx": "90b178c84d4a20ff4a1a8624f317d36eeb02ef017647fe524da03aee28a86e79",
|
||||
"resources/js/pages/candidate/profile.tsx": "6a51689bc0bbe63255467c5c454593707c08bfd0e3867e59b50259eb20d1a306",
|
||||
"resources/js/pages/company/candidate-portal.tsx": "07882a3695897d7cc184c1b09c7c616b76c362556e75f8e82d292925a5da9c09",
|
||||
"tests/Feature/CandidatePortalTest.php": "b603113232db600105462577022da5018b76b8e852a50128e0bceb65e3e3e5b1",
|
||||
"tests/frontend/candidate-portal.test.tsx": "81f3199e33bb46f16c8f73f2b641c1d9778579c12cd1f4f2d33e727f11d241af"
|
||||
}
|
||||
"checked_at": "2026-10-30T17:04:58.049470+00:00",
|
||||
"scope": [
|
||||
"separate candidate authentication scoped by company",
|
||||
"explicit portal registration policy and versioned notice acknowledgment",
|
||||
"immutable own profile revisions including province district locality",
|
||||
"no staff or cross-account access",
|
||||
"atomic profile and audit rollback",
|
||||
"login and logout history clearing"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 715,
|
||||
"passed": 713,
|
||||
"assertions": 6985,
|
||||
"duration_ms": 149158,
|
||||
"skipped": 2
|
||||
},
|
||||
"candidate_tests": 5,
|
||||
"candidate_assertions": 81,
|
||||
"disposable_postgresql_tests": 47,
|
||||
"disposable_postgresql_assertions": 1163,
|
||||
"react_inertia_tests": 52,
|
||||
"phpstan_errors": 0,
|
||||
"typescript": "passed",
|
||||
"pint": "passed",
|
||||
"targeted_lint": "passed",
|
||||
"build_exit": 0,
|
||||
"menu_entries_across_six_roles": 111,
|
||||
"runtime_sha256_files": 18,
|
||||
"postgresql_proof": "candidate-portal-pg.json",
|
||||
"installation_proof": "candidate-portal-install.json",
|
||||
"http_proof": "candidate-portal-http.json",
|
||||
"visual_review": false,
|
||||
"domain_installed": false,
|
||||
"registration_open_in_evaluation": false,
|
||||
"candidate_accounts_created_in_evaluation": 0,
|
||||
"external_AI_calls": 0,
|
||||
"emails_sent": 0,
|
||||
"outstanding": [
|
||||
"email/identity verification",
|
||||
"password recovery",
|
||||
"submission to opportunities and frozen profile documents",
|
||||
"attachment security verification",
|
||||
"triage complements revisions",
|
||||
"retention deletion and privacy policy configuration before public use"
|
||||
],
|
||||
"source_sha256": {
|
||||
"database/migrations/2026_10_30_000037_create_candidate_profiles.php": "159648252836af068226978666d8eb11148ed6890cba2082b0d5c8b415365e97",
|
||||
"app/Models/CandidateAccount.php": "ab30a14616554bbe7d5ec0dec2347582754f846c0a4ade5455cfa095d73e4bec",
|
||||
"app/Services/CandidateProfiles.php": "54e1e95e10c9004fef69fbdd14b54f85d617f3296cc27a7aba2f1f51552f2cad",
|
||||
"app/Http/Controllers/CandidatePortalController.php": "9ff56978b43c4a2401c02ca3f7f2a95c7f3a550009b3385c682dafcecd6f2615",
|
||||
"config/auth.php": "e3fd9d4244bc9dd92b6e8d76dc2e5163834ef21b4b9df7dd04f1015d82d32ba6",
|
||||
"app/Http/Middleware/HandleInertiaRequests.php": "8db70b1500d683f1df3b7a26ce019c72b5c917ab51b020fdec59700100d588ed",
|
||||
"resources/js/app.tsx": "5669117aa4b607b1d6c3b1f51af8b18650c80c75a5eb697ff7f89a3146c851a9",
|
||||
"resources/js/pages/candidate/entry.tsx": "90b178c84d4a20ff4a1a8624f317d36eeb02ef017647fe524da03aee28a86e79",
|
||||
"resources/js/pages/candidate/profile.tsx": "6a51689bc0bbe63255467c5c454593707c08bfd0e3867e59b50259eb20d1a306",
|
||||
"resources/js/pages/company/candidate-portal.tsx": "07882a3695897d7cc184c1b09c7c616b76c362556e75f8e82d292925a5da9c09",
|
||||
"tests/Feature/CandidatePortalTest.php": "b603113232db600105462577022da5018b76b8e852a50128e0bceb65e3e3e5b1",
|
||||
"tests/frontend/candidate-portal.test.tsx": "81f3199e33bb46f16c8f73f2b641c1d9778579c12cd1f4f2d33e727f11d241af"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T17:15:17.008Z",
|
||||
"administration": 200,
|
||||
"unconfigured_recovery": 404,
|
||||
"unauthenticated_opportunity": 401,
|
||||
"unauthenticated_receipt": 401,
|
||||
"registration_open": false,
|
||||
"candidates_created": 0,
|
||||
"email_transport_available": false
|
||||
"checked_at": "2026-10-30T17:15:17.008Z",
|
||||
"administration": 200,
|
||||
"unconfigured_recovery": 404,
|
||||
"unauthenticated_opportunity": 401,
|
||||
"unauthenticated_receipt": 401,
|
||||
"registration_open": false,
|
||||
"candidates_created": 0,
|
||||
"email_transport_available": false
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T17:19:13+00:00",
|
||||
"database": "disposable PostgreSQL",
|
||||
"application_role_reused_without_privilege_change": true,
|
||||
"result": "passed",
|
||||
"test_files": [
|
||||
"CandidateAccessTest",
|
||||
"CandidatePortalTest",
|
||||
"CandidateSubmissionsTest"
|
||||
],
|
||||
"original_evaluation_untouched": true,
|
||||
"database_removed": true,
|
||||
"tests": 14,
|
||||
"assertions": 161
|
||||
"checked_at": "2026-10-30T17:19:13+00:00",
|
||||
"database": "disposable PostgreSQL",
|
||||
"application_role_reused_without_privilege_change": true,
|
||||
"result": "passed",
|
||||
"test_files": [
|
||||
"CandidateAccessTest",
|
||||
"CandidatePortalTest",
|
||||
"CandidateSubmissionsTest"
|
||||
],
|
||||
"original_evaluation_untouched": true,
|
||||
"database_removed": true,
|
||||
"tests": 14,
|
||||
"assertions": 161
|
||||
}
|
||||
|
||||
@@ -1,74 +1,74 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T17:19:44.183589+00:00",
|
||||
"scope": [
|
||||
"single-use hashed email verification and recovery tokens",
|
||||
"no secret links in mail log transports or request query strings",
|
||||
"candidate session revocation on password reset",
|
||||
"explicit publication by assigned process preparer",
|
||||
"verified own-account data-only submission with frozen profile form and answers",
|
||||
"idempotent receipt and own account access",
|
||||
"internal received inbox separated from assessment",
|
||||
"mandatory-file opportunities rejected until attachment intake exists"
|
||||
],
|
||||
"module_tests": 14,
|
||||
"module_assertions": 161,
|
||||
"postgresql_tests": 14,
|
||||
"postgresql_assertions": 161,
|
||||
"react_inertia_tests": 56,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"pint": "passed",
|
||||
"targeted_lint": "passed",
|
||||
"build_exit": 0,
|
||||
"mail_template_render_without_delivery": "passed",
|
||||
"menu_entries_across_six_roles": 111,
|
||||
"runtime_sha256_files": 18,
|
||||
"installation_proof": "candidate-submissions-install.json",
|
||||
"postgresql_proof": "candidate-submissions-pg.json",
|
||||
"http_proof": "candidate-submissions-http.json",
|
||||
"visual_review": false,
|
||||
"domain_installed": false,
|
||||
"emails_sent": 0,
|
||||
"external_AI_calls": 0,
|
||||
"candidate_accounts_created_in_evaluation": 0,
|
||||
"portals_or_opportunities_activated": 0,
|
||||
"outstanding": [
|
||||
"secure attachment intake storage and verification",
|
||||
"triage and controlled reuse into internal assessment",
|
||||
"complements corrections and revisions",
|
||||
"spontaneous submissions and supplier intake",
|
||||
"configured mail transport and real delivery rehearsal",
|
||||
"privacy retention deletion policies and public visual acceptance"
|
||||
],
|
||||
"source_sha256": {
|
||||
"database/migrations/2026_10_30_000038_create_candidate_access_tokens.php": "a461cd157d23c00f0c54dccd8b739e0efe6803d3d3495253a86477972c25b0cb",
|
||||
"database/migrations/2026_10_30_000039_create_candidate_submissions.php": "7901cf6b7dd31e4753ff92ca791f436ee13732eec7faa197b1ef829ba5cc4b7a",
|
||||
"app/Services/CandidateAccess.php": "d63452a1148911fa4bf8e5cb0e2d5c0d68e4d6d4b7c2f9371a4a4ecf85d59a86",
|
||||
"app/Services/CandidateSubmissions.php": "33114852f9c4444675206876662ad28b3846a72b8ded1d03c6f0e074118b8fa2",
|
||||
"app/Services/CandidateProfiles.php": "0bfaa3602261e897b940a63e302bf4c2655c6dd2d8aa8bdd02bd350e3d39081f",
|
||||
"app/Http/Controllers/CandidatePortalController.php": "13477331e1f0a270300143f1465e6493a9e52e20944fb542a0241d66e81ae588",
|
||||
"app/Models/CandidateAccount.php": "8fc2fc485ad1bb2ccc27a971dbd9417bec7263bf5c51d170f45629bfec8e5d86",
|
||||
"app/Mail/CandidateAccessLink.php": "67a9c556646114af96857537e2a60836d4eaa3b5243dbb8493071562fc1cfcbd",
|
||||
"resources/views/mail/candidate-access.blade.php": "81eefd72fb05d2272a05fe9c9479a0652140c06d91dbc6e1694770109046abff",
|
||||
"bootstrap/app.php": "a57fa8ea590c68d1007e1457611c95cf8b52576bfd8e5cdd7af2389ed18384f6",
|
||||
"routes/web.php": "3d9b9755e1387eae89285b3ba3f4ad78730b69df9756853f063aa4e98fe20c7f",
|
||||
"resources/js/pages/candidate/access.tsx": "4a0b92b6903d659a429db7dafaf25acccfee3c48a151a588e352d2b34eb18142",
|
||||
"resources/js/pages/candidate/opportunity.tsx": "4da4db3e1f02138a73e4399aaf50eca969e53b02a004818165891cb24457239b",
|
||||
"resources/js/pages/candidate/receipt.tsx": "59dd13beed2df74481740afabc4e78637ab8f6c244d3873eaafdd9b661c57392",
|
||||
"resources/js/pages/candidate/profile.tsx": "39ae2da3182bf9fac69bdf88b69a376de3aa0750e659d9a05721070e2e207798",
|
||||
"resources/js/pages/people/submissions.tsx": "9f79cbcda18154b685429f10c9bc31b49765ef0c1c93b1a19e4b001a7074eb43",
|
||||
"tests/Feature/CandidateAccessTest.php": "7c953d2a633e3cc71d5d8ad0a97432a4056a01c2985ead0cc5743f368166b490",
|
||||
"tests/Feature/CandidateSubmissionsTest.php": "7862130e921864e74469a364a19aa631635595598324133491fbcbffa419e0e7",
|
||||
"tests/frontend/candidate-submissions.test.tsx": "4857543a847754fed542cb7ec32a961e94cb7fd287a5c67627d0cb17b7520ace"
|
||||
},
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 724,
|
||||
"passed": 722,
|
||||
"assertions": 7063,
|
||||
"duration_ms": 146678,
|
||||
"skipped": 2
|
||||
},
|
||||
"full_suite_reused_with_latest_permission_regression_retested_in_module": true
|
||||
"checked_at": "2026-10-30T17:19:44.183589+00:00",
|
||||
"scope": [
|
||||
"single-use hashed email verification and recovery tokens",
|
||||
"no secret links in mail log transports or request query strings",
|
||||
"candidate session revocation on password reset",
|
||||
"explicit publication by assigned process preparer",
|
||||
"verified own-account data-only submission with frozen profile form and answers",
|
||||
"idempotent receipt and own account access",
|
||||
"internal received inbox separated from assessment",
|
||||
"mandatory-file opportunities rejected until attachment intake exists"
|
||||
],
|
||||
"module_tests": 14,
|
||||
"module_assertions": 161,
|
||||
"postgresql_tests": 14,
|
||||
"postgresql_assertions": 161,
|
||||
"react_inertia_tests": 56,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"pint": "passed",
|
||||
"targeted_lint": "passed",
|
||||
"build_exit": 0,
|
||||
"mail_template_render_without_delivery": "passed",
|
||||
"menu_entries_across_six_roles": 111,
|
||||
"runtime_sha256_files": 18,
|
||||
"installation_proof": "candidate-submissions-install.json",
|
||||
"postgresql_proof": "candidate-submissions-pg.json",
|
||||
"http_proof": "candidate-submissions-http.json",
|
||||
"visual_review": false,
|
||||
"domain_installed": false,
|
||||
"emails_sent": 0,
|
||||
"external_AI_calls": 0,
|
||||
"candidate_accounts_created_in_evaluation": 0,
|
||||
"portals_or_opportunities_activated": 0,
|
||||
"outstanding": [
|
||||
"secure attachment intake storage and verification",
|
||||
"triage and controlled reuse into internal assessment",
|
||||
"complements corrections and revisions",
|
||||
"spontaneous submissions and supplier intake",
|
||||
"configured mail transport and real delivery rehearsal",
|
||||
"privacy retention deletion policies and public visual acceptance"
|
||||
],
|
||||
"source_sha256": {
|
||||
"database/migrations/2026_10_30_000038_create_candidate_access_tokens.php": "a461cd157d23c00f0c54dccd8b739e0efe6803d3d3495253a86477972c25b0cb",
|
||||
"database/migrations/2026_10_30_000039_create_candidate_submissions.php": "7901cf6b7dd31e4753ff92ca791f436ee13732eec7faa197b1ef829ba5cc4b7a",
|
||||
"app/Services/CandidateAccess.php": "d63452a1148911fa4bf8e5cb0e2d5c0d68e4d6d4b7c2f9371a4a4ecf85d59a86",
|
||||
"app/Services/CandidateSubmissions.php": "33114852f9c4444675206876662ad28b3846a72b8ded1d03c6f0e074118b8fa2",
|
||||
"app/Services/CandidateProfiles.php": "0bfaa3602261e897b940a63e302bf4c2655c6dd2d8aa8bdd02bd350e3d39081f",
|
||||
"app/Http/Controllers/CandidatePortalController.php": "13477331e1f0a270300143f1465e6493a9e52e20944fb542a0241d66e81ae588",
|
||||
"app/Models/CandidateAccount.php": "8fc2fc485ad1bb2ccc27a971dbd9417bec7263bf5c51d170f45629bfec8e5d86",
|
||||
"app/Mail/CandidateAccessLink.php": "67a9c556646114af96857537e2a60836d4eaa3b5243dbb8493071562fc1cfcbd",
|
||||
"resources/views/mail/candidate-access.blade.php": "81eefd72fb05d2272a05fe9c9479a0652140c06d91dbc6e1694770109046abff",
|
||||
"bootstrap/app.php": "a57fa8ea590c68d1007e1457611c95cf8b52576bfd8e5cdd7af2389ed18384f6",
|
||||
"routes/web.php": "3d9b9755e1387eae89285b3ba3f4ad78730b69df9756853f063aa4e98fe20c7f",
|
||||
"resources/js/pages/candidate/access.tsx": "4a0b92b6903d659a429db7dafaf25acccfee3c48a151a588e352d2b34eb18142",
|
||||
"resources/js/pages/candidate/opportunity.tsx": "4da4db3e1f02138a73e4399aaf50eca969e53b02a004818165891cb24457239b",
|
||||
"resources/js/pages/candidate/receipt.tsx": "59dd13beed2df74481740afabc4e78637ab8f6c244d3873eaafdd9b661c57392",
|
||||
"resources/js/pages/candidate/profile.tsx": "39ae2da3182bf9fac69bdf88b69a376de3aa0750e659d9a05721070e2e207798",
|
||||
"resources/js/pages/people/submissions.tsx": "9f79cbcda18154b685429f10c9bc31b49765ef0c1c93b1a19e4b001a7074eb43",
|
||||
"tests/Feature/CandidateAccessTest.php": "7c953d2a633e3cc71d5d8ad0a97432a4056a01c2985ead0cc5743f368166b490",
|
||||
"tests/Feature/CandidateSubmissionsTest.php": "7862130e921864e74469a364a19aa631635595598324133491fbcbffa419e0e7",
|
||||
"tests/frontend/candidate-submissions.test.tsx": "4857543a847754fed542cb7ec32a961e94cb7fd287a5c67627d0cb17b7520ace"
|
||||
},
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 724,
|
||||
"passed": 722,
|
||||
"assertions": 7063,
|
||||
"duration_ms": 146678,
|
||||
"skipped": 2
|
||||
},
|
||||
"full_suite_reused_with_latest_permission_regression_retested_in_module": true
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T17:32:58.543Z",
|
||||
"people_workspace": 200,
|
||||
"portal_settings": 200,
|
||||
"unauthenticated_receipt": 401,
|
||||
"portal_opened": false,
|
||||
"candidates_created": 0
|
||||
"checked_at": "2026-10-30T17:32:58.543Z",
|
||||
"people_workspace": 200,
|
||||
"portal_settings": 200,
|
||||
"unauthenticated_receipt": 401,
|
||||
"portal_opened": false,
|
||||
"candidates_created": 0
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T17:30:39+00:00",
|
||||
"database": "disposable PostgreSQL",
|
||||
"application_role_reused_without_privilege_change": true,
|
||||
"result": "passed",
|
||||
"test_files": [
|
||||
"CandidateSubmissionsTest",
|
||||
"PeopleHiringTest",
|
||||
"CandidatePortalTest"
|
||||
],
|
||||
"original_evaluation_untouched": true,
|
||||
"database_removed": true,
|
||||
"tests": 53,
|
||||
"assertions": 1234
|
||||
"checked_at": "2026-10-30T17:30:39+00:00",
|
||||
"database": "disposable PostgreSQL",
|
||||
"application_role_reused_without_privilege_change": true,
|
||||
"result": "passed",
|
||||
"test_files": [
|
||||
"CandidateSubmissionsTest",
|
||||
"PeopleHiringTest",
|
||||
"CandidatePortalTest"
|
||||
],
|
||||
"original_evaluation_untouched": true,
|
||||
"database_removed": true,
|
||||
"tests": 53,
|
||||
"assertions": 1234
|
||||
}
|
||||
|
||||
@@ -1,67 +1,67 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T17:35:03.695737+00:00",
|
||||
"scope": [
|
||||
"explicit accepted-for-analysis or not-accepted triage",
|
||||
"separate confidential reason and public communication",
|
||||
"immutable idempotent decision and atomic application linkage",
|
||||
"frozen submitted profile reused without re-entry or name-based merging",
|
||||
"triager registered as application author preserving independent assessment",
|
||||
"source reading restricted to preparers or reviewers with evidence scope",
|
||||
"pending triage routed in workdesk and checked before close evaluation selection",
|
||||
"admission blocked after reviews start",
|
||||
"cross-company/process composite foreign keys"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 730,
|
||||
"passed": 728,
|
||||
"assertions": 7141,
|
||||
"duration_ms": 150333,
|
||||
"skipped": 2
|
||||
},
|
||||
"module_sqlite_tests": 48,
|
||||
"module_sqlite_assertions": 1153,
|
||||
"postgresql_tests": 53,
|
||||
"postgresql_assertions": 1234,
|
||||
"react_inertia_tests": 59,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"pint": "passed",
|
||||
"targeted_lint": "passed",
|
||||
"build_exit": 0,
|
||||
"menu_entries_across_six_roles": 111,
|
||||
"runtime_sha256_files": 18,
|
||||
"installation_proof": "candidate-triage-install.json",
|
||||
"postgresql_proof": "candidate-triage-pg.json",
|
||||
"http_proof": "candidate-triage-http.json",
|
||||
"visual_review": false,
|
||||
"domain_installed": false,
|
||||
"emails_sent": 0,
|
||||
"external_AI_calls": 0,
|
||||
"candidate_accounts_created_in_evaluation": 0,
|
||||
"automatic_transfers_in_evaluation": 0,
|
||||
"outstanding": [
|
||||
"secure attachment intake",
|
||||
"triage revision and reopening",
|
||||
"candidate complements and corrected submissions",
|
||||
"spontaneous and supplier electronic intake",
|
||||
"real mail transport delivery rehearsal",
|
||||
"visual validation and deployment"
|
||||
],
|
||||
"source_sha256": {
|
||||
"database/migrations/2026_10_30_000040_create_candidate_triage.php": "4a054bceb7abaa7d3ff1796f0529bb777061156927d91fab1f5ab324b2af8195",
|
||||
"app/Services/CandidateTriage.php": "3566f527c99f8fea076d9babe68431c7d9e8f539be685e69d9c43a1cde8d5527",
|
||||
"app/Services/PeopleHiring.php": "6fff082fede593e5a29d02d291d82d1d07492fdd941483a4301c4e99365cacbd",
|
||||
"app/Services/PeopleWorkdesk.php": "f3b8096209897f0b9199804bd5f230a42627b6210f66a881ee755bc9a5c77767",
|
||||
"app/Http/Controllers/CandidatePortalController.php": "e776651fa41ae071f7bfe8da301cc9c9e0b19df0f34c63dd58175de3b231b217",
|
||||
"app/Http/Controllers/PeopleHiringController.php": "365e6cd116ed0d4e253e45b7e1768e586aaf6c6987a5c753809c52204c70469d",
|
||||
"routes/web.php": "5d61be0bfaa196d8b81f1a124f863e475c0e428f282bfa3926d64395d2651a24",
|
||||
"resources/js/pages/people/submissions.tsx": "57a4b57962b22797c560de928b0155d166a43212442641ca860016ce0554390e",
|
||||
"resources/js/pages/people/show.tsx": "de1b290772a7e3f0b2083511dab0fcee9cf5fc7ca71e5014f87a892cdd32c06d",
|
||||
"resources/js/pages/candidate/receipt.tsx": "5667ac51c6fd654fc96bff7bece924617561bac16b552dfce371aafdcb2e8830",
|
||||
"resources/js/components/people-operational-insights.tsx": "55ce0f42e7d574e7c8832ec869aceaecd41d70a2ba8117b9397ce30528aab82f",
|
||||
"tests/Feature/CandidateSubmissionsTest.php": "73808825233b55169f185c1fad555c53aa13bf6933c39fe2d84777638a9bf39d",
|
||||
"tests/frontend/candidate-triage.test.tsx": "9d0455f569112f4869627bd22f1aaa6862cd34fc6f8912ab61b621c67ea1d880"
|
||||
}
|
||||
"checked_at": "2026-10-30T17:35:03.695737+00:00",
|
||||
"scope": [
|
||||
"explicit accepted-for-analysis or not-accepted triage",
|
||||
"separate confidential reason and public communication",
|
||||
"immutable idempotent decision and atomic application linkage",
|
||||
"frozen submitted profile reused without re-entry or name-based merging",
|
||||
"triager registered as application author preserving independent assessment",
|
||||
"source reading restricted to preparers or reviewers with evidence scope",
|
||||
"pending triage routed in workdesk and checked before close evaluation selection",
|
||||
"admission blocked after reviews start",
|
||||
"cross-company/process composite foreign keys"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 730,
|
||||
"passed": 728,
|
||||
"assertions": 7141,
|
||||
"duration_ms": 150333,
|
||||
"skipped": 2
|
||||
},
|
||||
"module_sqlite_tests": 48,
|
||||
"module_sqlite_assertions": 1153,
|
||||
"postgresql_tests": 53,
|
||||
"postgresql_assertions": 1234,
|
||||
"react_inertia_tests": 59,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"pint": "passed",
|
||||
"targeted_lint": "passed",
|
||||
"build_exit": 0,
|
||||
"menu_entries_across_six_roles": 111,
|
||||
"runtime_sha256_files": 18,
|
||||
"installation_proof": "candidate-triage-install.json",
|
||||
"postgresql_proof": "candidate-triage-pg.json",
|
||||
"http_proof": "candidate-triage-http.json",
|
||||
"visual_review": false,
|
||||
"domain_installed": false,
|
||||
"emails_sent": 0,
|
||||
"external_AI_calls": 0,
|
||||
"candidate_accounts_created_in_evaluation": 0,
|
||||
"automatic_transfers_in_evaluation": 0,
|
||||
"outstanding": [
|
||||
"secure attachment intake",
|
||||
"triage revision and reopening",
|
||||
"candidate complements and corrected submissions",
|
||||
"spontaneous and supplier electronic intake",
|
||||
"real mail transport delivery rehearsal",
|
||||
"visual validation and deployment"
|
||||
],
|
||||
"source_sha256": {
|
||||
"database/migrations/2026_10_30_000040_create_candidate_triage.php": "4a054bceb7abaa7d3ff1796f0529bb777061156927d91fab1f5ab324b2af8195",
|
||||
"app/Services/CandidateTriage.php": "3566f527c99f8fea076d9babe68431c7d9e8f539be685e69d9c43a1cde8d5527",
|
||||
"app/Services/PeopleHiring.php": "6fff082fede593e5a29d02d291d82d1d07492fdd941483a4301c4e99365cacbd",
|
||||
"app/Services/PeopleWorkdesk.php": "f3b8096209897f0b9199804bd5f230a42627b6210f66a881ee755bc9a5c77767",
|
||||
"app/Http/Controllers/CandidatePortalController.php": "e776651fa41ae071f7bfe8da301cc9c9e0b19df0f34c63dd58175de3b231b217",
|
||||
"app/Http/Controllers/PeopleHiringController.php": "365e6cd116ed0d4e253e45b7e1768e586aaf6c6987a5c753809c52204c70469d",
|
||||
"routes/web.php": "5d61be0bfaa196d8b81f1a124f863e475c0e428f282bfa3926d64395d2651a24",
|
||||
"resources/js/pages/people/submissions.tsx": "57a4b57962b22797c560de928b0155d166a43212442641ca860016ce0554390e",
|
||||
"resources/js/pages/people/show.tsx": "de1b290772a7e3f0b2083511dab0fcee9cf5fc7ca71e5014f87a892cdd32c06d",
|
||||
"resources/js/pages/candidate/receipt.tsx": "5667ac51c6fd654fc96bff7bece924617561bac16b552dfce371aafdcb2e8830",
|
||||
"resources/js/components/people-operational-insights.tsx": "55ce0f42e7d574e7c8832ec869aceaecd41d70a2ba8117b9397ce30528aab82f",
|
||||
"tests/Feature/CandidateSubmissionsTest.php": "73808825233b55169f185c1fad555c53aa13bf6933c39fe2d84777638a9bf39d",
|
||||
"tests/frontend/candidate-triage.test.tsx": "9d0455f569112f4869627bd22f1aaa6862cd34fc6f8912ab61b621c67ea1d880"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T11:11:26.981246+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"company-configured minimum eligible suppliers and independent evaluators",
|
||||
"company-configured criterion weights totalling 100 percent",
|
||||
"policy frozen when a request is created and retained on draft edits",
|
||||
"old processes retain compatibility rules",
|
||||
"quotation weights validated against process policy",
|
||||
"policy included in new comparison hashes and order snapshots",
|
||||
"policy visible in request comparison award reports and order print"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 622,
|
||||
"passed": 620,
|
||||
"assertions": 5306,
|
||||
"duration_ms": 113835,
|
||||
"skipped": 2
|
||||
},
|
||||
"postgresql": {
|
||||
"tests": 45,
|
||||
"assertions": 367,
|
||||
"disposable_database_removed": true
|
||||
},
|
||||
"phpstan_errors": 0,
|
||||
"typescript": "passed",
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"installation_proof": "comparison-policy-install.json",
|
||||
"company_policy_automatically_activated": false,
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"source_sha256": {
|
||||
"app/Services/ComparisonPolicy.php": "bdcd5739b1d52fc57759ff59a2257a059ce7772b21552a09ef6aa83966f0145e",
|
||||
"app/Services/ProposalComparison.php": "d091f73065064dd132196cb79033835f38f7bd55ec6a98a54a76bbcdfd4e6df0",
|
||||
"app/Services/RequisitionWorkflow.php": "e06c11e0208f0cf2cff80e44b5de3c55de05e8cce079d4f1a77f1d9f37f08323",
|
||||
"app/Services/PurchaseWorkflow.php": "30c622685cb80766addaaba9987281ea40b5890b51c907f1a50743e3ba7540c5",
|
||||
"app/Services/OrganizationConfiguration.php": "478c5605ec6933e868d38f904c9e87727f884b2d1663ba6c0f58cc5872d90e95",
|
||||
"app/Http/Controllers/QuotationController.php": "0ca2ec13459d2daefacc62ac30fa437217cdb648ab35c90083a958261ccd9f84",
|
||||
"app/Http/Controllers/OrganizationConfigurationController.php": "c1d017a550619705ea3521f5ea794e4b5585be3fbbe3bc9557c1dc625502b9bf",
|
||||
"resources/js/components/comparison-policy-summary.tsx": "c086bafc0e08e900938bd49a6b802880f4be7ba11ba29c9272ed85b42143bfe9",
|
||||
"resources/js/pages/company/parameters.tsx": "e7ca03fc6600ec6db7395d0db289f5f277b2ba6a05053b1b136202600e195b3d",
|
||||
"database/migrations/2026_10_30_000029_add_comparison_policy_snapshots.php": "92eacb2835908fe26938111098163e167d1afa6a42ad682c9ef63711c92274cc"
|
||||
}
|
||||
"checked_at": "2026-10-30T11:11:26.981246+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"company-configured minimum eligible suppliers and independent evaluators",
|
||||
"company-configured criterion weights totalling 100 percent",
|
||||
"policy frozen when a request is created and retained on draft edits",
|
||||
"old processes retain compatibility rules",
|
||||
"quotation weights validated against process policy",
|
||||
"policy included in new comparison hashes and order snapshots",
|
||||
"policy visible in request comparison award reports and order print"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 622,
|
||||
"passed": 620,
|
||||
"assertions": 5306,
|
||||
"duration_ms": 113835,
|
||||
"skipped": 2
|
||||
},
|
||||
"postgresql": {
|
||||
"tests": 45,
|
||||
"assertions": 367,
|
||||
"disposable_database_removed": true
|
||||
},
|
||||
"phpstan_errors": 0,
|
||||
"typescript": "passed",
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"installation_proof": "comparison-policy-install.json",
|
||||
"company_policy_automatically_activated": false,
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"source_sha256": {
|
||||
"app/Services/ComparisonPolicy.php": "bdcd5739b1d52fc57759ff59a2257a059ce7772b21552a09ef6aa83966f0145e",
|
||||
"app/Services/ProposalComparison.php": "d091f73065064dd132196cb79033835f38f7bd55ec6a98a54a76bbcdfd4e6df0",
|
||||
"app/Services/RequisitionWorkflow.php": "e06c11e0208f0cf2cff80e44b5de3c55de05e8cce079d4f1a77f1d9f37f08323",
|
||||
"app/Services/PurchaseWorkflow.php": "30c622685cb80766addaaba9987281ea40b5890b51c907f1a50743e3ba7540c5",
|
||||
"app/Services/OrganizationConfiguration.php": "478c5605ec6933e868d38f904c9e87727f884b2d1663ba6c0f58cc5872d90e95",
|
||||
"app/Http/Controllers/QuotationController.php": "0ca2ec13459d2daefacc62ac30fa437217cdb648ab35c90083a958261ccd9f84",
|
||||
"app/Http/Controllers/OrganizationConfigurationController.php": "c1d017a550619705ea3521f5ea794e4b5585be3fbbe3bc9557c1dc625502b9bf",
|
||||
"resources/js/components/comparison-policy-summary.tsx": "c086bafc0e08e900938bd49a6b802880f4be7ba11ba29c9272ed85b42143bfe9",
|
||||
"resources/js/pages/company/parameters.tsx": "e7ca03fc6600ec6db7395d0db289f5f277b2ba6a05053b1b136202600e195b3d",
|
||||
"database/migrations/2026_10_30_000029_add_comparison_policy_snapshots.php": "92eacb2835908fe26938111098163e167d1afa6a42ad682c9ef63711c92274cc"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,52 +1,52 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T10:54:53.071479+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"company default VAT for new lines only",
|
||||
"explicit unit discounts before VAT",
|
||||
"tie warning without automatic winner",
|
||||
"invalid and missing prices excluded",
|
||||
"discount evidence in comparison award order and reports",
|
||||
"six functional navigation groups with unchanged permissions"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 618,
|
||||
"passed": 616,
|
||||
"assertions": 5245,
|
||||
"duration_ms": 181394,
|
||||
"skipped": 2
|
||||
},
|
||||
"postgresql": {
|
||||
"tests": 42,
|
||||
"assertions": 327,
|
||||
"disposable_database_removed": true
|
||||
},
|
||||
"order_workflow_tests": 71,
|
||||
"phpstan_errors": 0,
|
||||
"typescript": "passed",
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"runtime_sha256_files": 18,
|
||||
"menu_contract_entries_by_role": 107,
|
||||
"menu_role_count": 6,
|
||||
"fresh_backup_and_restore": "economic-install.json",
|
||||
"http_proof": "menu-links.json",
|
||||
"spreadsheet_original_modified": false,
|
||||
"spreadsheet_example_values_imported": false,
|
||||
"visual_review": false,
|
||||
"domain_installed": false,
|
||||
"source_sha256": {
|
||||
"app/Services/QuotationPricing.php": "4b4ef22c0be338742b4ebf17a8018bf35dd0996d4b9d0fc460d4c02c2c0b8fa6",
|
||||
"app/Services/ProposalComparison.php": "a106e659a855ddaf8a1279e3918ed7f631ff0ed6c374488d06d4dfe95b9a8872",
|
||||
"app/Services/ProposalOpeningWorkflow.php": "ef7bc41fd696cd51bfa4113c9aa1ab42c91a86a6b36f1926b51a8b1fcd01284c",
|
||||
"app/Services/OrganizationConfiguration.php": "89e452c699ccab4a388e0643e777bf77305e7ea6cde2357801d3d3b16406912a",
|
||||
"app/Http/Controllers/QuotationController.php": "cf2a2064004d73c49d61e1aa21d8fa100300dfb5baa8fc737e1da72b4b57242d",
|
||||
"resources/js/lib/procurement-navigation.ts": "488efff63a6ff1505abaab41f4e248ff740a8da456d9a627cc9143dfd6b26bbf",
|
||||
"resources/js/components/sgc-workspace-shell.tsx": "892de779373aaf7689c95c4b3112734e0683fd60197916464a965ba1eabd1466",
|
||||
"resources/js/pages/quotations/show.tsx": "e756b347e0da3b06e5e5f2d67ccb8d3370360db219b134227b611e91d1af7958",
|
||||
"database/migrations/2026_10_30_000028_add_economic_comparison_terms.php": "dba2a5f8bfd3e76fee5fed032e11e71b2800dead3dfc9d66d040502e53c7e2c5"
|
||||
}
|
||||
"checked_at": "2026-10-30T10:54:53.071479+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"company default VAT for new lines only",
|
||||
"explicit unit discounts before VAT",
|
||||
"tie warning without automatic winner",
|
||||
"invalid and missing prices excluded",
|
||||
"discount evidence in comparison award order and reports",
|
||||
"six functional navigation groups with unchanged permissions"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 618,
|
||||
"passed": 616,
|
||||
"assertions": 5245,
|
||||
"duration_ms": 181394,
|
||||
"skipped": 2
|
||||
},
|
||||
"postgresql": {
|
||||
"tests": 42,
|
||||
"assertions": 327,
|
||||
"disposable_database_removed": true
|
||||
},
|
||||
"order_workflow_tests": 71,
|
||||
"phpstan_errors": 0,
|
||||
"typescript": "passed",
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"runtime_sha256_files": 18,
|
||||
"menu_contract_entries_by_role": 107,
|
||||
"menu_role_count": 6,
|
||||
"fresh_backup_and_restore": "economic-install.json",
|
||||
"http_proof": "menu-links.json",
|
||||
"spreadsheet_original_modified": false,
|
||||
"spreadsheet_example_values_imported": false,
|
||||
"visual_review": false,
|
||||
"domain_installed": false,
|
||||
"source_sha256": {
|
||||
"app/Services/QuotationPricing.php": "4b4ef22c0be338742b4ebf17a8018bf35dd0996d4b9d0fc460d4c02c2c0b8fa6",
|
||||
"app/Services/ProposalComparison.php": "a106e659a855ddaf8a1279e3918ed7f631ff0ed6c374488d06d4dfe95b9a8872",
|
||||
"app/Services/ProposalOpeningWorkflow.php": "ef7bc41fd696cd51bfa4113c9aa1ab42c91a86a6b36f1926b51a8b1fcd01284c",
|
||||
"app/Services/OrganizationConfiguration.php": "89e452c699ccab4a388e0643e777bf77305e7ea6cde2357801d3d3b16406912a",
|
||||
"app/Http/Controllers/QuotationController.php": "cf2a2064004d73c49d61e1aa21d8fa100300dfb5baa8fc737e1da72b4b57242d",
|
||||
"resources/js/lib/procurement-navigation.ts": "488efff63a6ff1505abaab41f4e248ff740a8da456d9a627cc9143dfd6b26bbf",
|
||||
"resources/js/components/sgc-workspace-shell.tsx": "892de779373aaf7689c95c4b3112734e0683fd60197916464a965ba1eabd1466",
|
||||
"resources/js/pages/quotations/show.tsx": "e756b347e0da3b06e5e5f2d67ccb8d3370360db219b134227b611e91d1af7958",
|
||||
"database/migrations/2026_10_30_000028_add_economic_comparison_terms.php": "dba2a5f8bfd3e76fee5fed032e11e71b2800dead3dfc9d66d040502e53c7e2c5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,49 +1,49 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T10:29:54.976Z",
|
||||
"base": "http://127.0.0.1:8005",
|
||||
"owner_modules": [
|
||||
"/dashboard",
|
||||
"/empresa",
|
||||
"/requisicoes",
|
||||
"/planeamento",
|
||||
"/fornecedores",
|
||||
"/cotacoes",
|
||||
"/ordens",
|
||||
"/recepcoes",
|
||||
"/stock",
|
||||
"/stock/operacoes",
|
||||
"/equipamentos",
|
||||
"/orcamentos",
|
||||
"/regras-investimento",
|
||||
"/facturas",
|
||||
"/pagamentos",
|
||||
"/obrigacoes-fornecedores",
|
||||
"/conflitos",
|
||||
"/revisao-compras",
|
||||
"/politica-admissao",
|
||||
"/politica-recomendacao",
|
||||
"/relatorios",
|
||||
"/relatorios/medidas-fornecedores",
|
||||
"/administracao/parametrizacao"
|
||||
],
|
||||
"roles": [
|
||||
"requester",
|
||||
"department_head",
|
||||
"procurement",
|
||||
"finance",
|
||||
"warehouse",
|
||||
"admin"
|
||||
],
|
||||
"csrf_enforced": true,
|
||||
"arbitrary_role_refused": true,
|
||||
"arbitrary_user_refused": true,
|
||||
"logout": true,
|
||||
"business_commands": 0,
|
||||
"real_accounts_changed": false,
|
||||
"operational_entry_status": "not running",
|
||||
"visual_review": false,
|
||||
"dashboard_auto_entry": true,
|
||||
"other_routes_require_session": true,
|
||||
"guest_report_redirect": true,
|
||||
"intended_report_and_filters_preserved": true
|
||||
"checked_at": "2026-10-30T10:29:54.976Z",
|
||||
"base": "http://127.0.0.1:8005",
|
||||
"owner_modules": [
|
||||
"/dashboard",
|
||||
"/empresa",
|
||||
"/requisicoes",
|
||||
"/planeamento",
|
||||
"/fornecedores",
|
||||
"/cotacoes",
|
||||
"/ordens",
|
||||
"/recepcoes",
|
||||
"/stock",
|
||||
"/stock/operacoes",
|
||||
"/equipamentos",
|
||||
"/orcamentos",
|
||||
"/regras-investimento",
|
||||
"/facturas",
|
||||
"/pagamentos",
|
||||
"/obrigacoes-fornecedores",
|
||||
"/conflitos",
|
||||
"/revisao-compras",
|
||||
"/politica-admissao",
|
||||
"/politica-recomendacao",
|
||||
"/relatorios",
|
||||
"/relatorios/medidas-fornecedores",
|
||||
"/administracao/parametrizacao"
|
||||
],
|
||||
"roles": [
|
||||
"requester",
|
||||
"department_head",
|
||||
"procurement",
|
||||
"finance",
|
||||
"warehouse",
|
||||
"admin"
|
||||
],
|
||||
"csrf_enforced": true,
|
||||
"arbitrary_role_refused": true,
|
||||
"arbitrary_user_refused": true,
|
||||
"logout": true,
|
||||
"business_commands": 0,
|
||||
"real_accounts_changed": false,
|
||||
"operational_entry_status": "not running",
|
||||
"visual_review": false,
|
||||
"dashboard_auto_entry": true,
|
||||
"other_routes_require_session": true,
|
||||
"guest_report_redirect": true,
|
||||
"intended_report_and_filters_preserved": true
|
||||
}
|
||||
|
||||
@@ -1,68 +1,68 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T14:55:29.383515+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"separate procurement and people evidence briefs",
|
||||
"source paths and section anchors",
|
||||
"redacted authorised report projections only",
|
||||
"SHA256 basis without private financial content",
|
||||
"current frozen returned approved and unsigned draft distinctions",
|
||||
"PostgreSQL read snapshot on people detail"
|
||||
],
|
||||
"procurement_tests": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 18,
|
||||
"passed": 18,
|
||||
"assertions": 317,
|
||||
"duration_ms": 4718
|
||||
},
|
||||
"people_tests": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 35,
|
||||
"passed": 35,
|
||||
"assertions": 972,
|
||||
"duration_ms": 11099
|
||||
},
|
||||
"postgresql_integration": {
|
||||
"tool": "phpunit",
|
||||
"result": "passed",
|
||||
"tests": 1,
|
||||
"passed": 1,
|
||||
"assertions": 64,
|
||||
"duration_ms": 2543
|
||||
},
|
||||
"react_inertia_tests": 31,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"http_proof": "menu-links.json",
|
||||
"postgresql_proof": "evidence-brief-pg.json",
|
||||
"prior_full_suite_reused": "people-clarifications.json",
|
||||
"migrations_applied": false,
|
||||
"business_records_changed": false,
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"visual_attempt_errors": [
|
||||
"Computer Use native pipe is unavailable (os error 2)",
|
||||
"The browser access host is unavailable"
|
||||
],
|
||||
"AI_provider_used_for_reports": false,
|
||||
"source_sha256": {
|
||||
"app/Services/ProcurementEvidenceBrief.php": "9a610e1d80f1040fa7a46117a5e251bebc4a6ca6d02383aa2c9d62f0ba3aef08",
|
||||
"app/Services/PeopleEvidenceBrief.php": "76f5a6bdd36fb8878eac5aa80cbb34db5784979ad654166778775c5c28f5f80a",
|
||||
"app/Http/Controllers/ProcurementReviewReportController.php": "ba96649bb5585ace55249f706dfb740cc12918676bc2ff3b4dac620ad602fb57",
|
||||
"app/Http/Controllers/PeopleHiringController.php": "bc6656d73a033bffa540d2b555d15d9c52a38c84446d892710d7d8ded35200ec",
|
||||
"resources/js/components/evidence-brief.tsx": "3b4630e44fdc02191d1e8ecebcd451c20b9e5a35b49ded9f5d78044bd1e24932",
|
||||
"resources/js/components/procurement.tsx": "846599386826eece0c9d5f48f4a30d3d52815fce3fde4d781073ff9347bf97c6",
|
||||
"resources/js/pages/purchases/review-report.tsx": "e29457affd4d2005b4393f4d21ee8ae1fbd9e4ccdc4ffbc253122d4628c1ba89",
|
||||
"resources/js/pages/people/show.tsx": "247e4e80d4ffca649b46fe00ee665de7e4c5476f2ce3cc1e5590e7cced049e47",
|
||||
"tests/Feature/ProcurementReviewReportTest.php": "46b95f3fc21b557021dd013357016a312078b99d20911a29e4e5ca3d09a5ed20",
|
||||
"tests/Feature/PeopleHiringTest.php": "fbd93d8134298d6b666634659223487646dbcb8074bd05889d660e378c40be5a",
|
||||
"tests/frontend/evidence-brief.test.tsx": "385b617074ac8c718f97e52981acce2a7b3dda9b965432e7e2be04c1ca7ef279",
|
||||
"tests/frontend/people-hiring.test.tsx": "24c51db9886358dfb1bd43d6b82c2a45ac731abdab76bc4e9dea3b85cbcb8bb4"
|
||||
}
|
||||
"checked_at": "2026-10-30T14:55:29.383515+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"separate procurement and people evidence briefs",
|
||||
"source paths and section anchors",
|
||||
"redacted authorised report projections only",
|
||||
"SHA256 basis without private financial content",
|
||||
"current frozen returned approved and unsigned draft distinctions",
|
||||
"PostgreSQL read snapshot on people detail"
|
||||
],
|
||||
"procurement_tests": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 18,
|
||||
"passed": 18,
|
||||
"assertions": 317,
|
||||
"duration_ms": 4718
|
||||
},
|
||||
"people_tests": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 35,
|
||||
"passed": 35,
|
||||
"assertions": 972,
|
||||
"duration_ms": 11099
|
||||
},
|
||||
"postgresql_integration": {
|
||||
"tool": "phpunit",
|
||||
"result": "passed",
|
||||
"tests": 1,
|
||||
"passed": 1,
|
||||
"assertions": 64,
|
||||
"duration_ms": 2543
|
||||
},
|
||||
"react_inertia_tests": 31,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"http_proof": "menu-links.json",
|
||||
"postgresql_proof": "evidence-brief-pg.json",
|
||||
"prior_full_suite_reused": "people-clarifications.json",
|
||||
"migrations_applied": false,
|
||||
"business_records_changed": false,
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"visual_attempt_errors": [
|
||||
"Computer Use native pipe is unavailable (os error 2)",
|
||||
"The browser access host is unavailable"
|
||||
],
|
||||
"AI_provider_used_for_reports": false,
|
||||
"source_sha256": {
|
||||
"app/Services/ProcurementEvidenceBrief.php": "9a610e1d80f1040fa7a46117a5e251bebc4a6ca6d02383aa2c9d62f0ba3aef08",
|
||||
"app/Services/PeopleEvidenceBrief.php": "76f5a6bdd36fb8878eac5aa80cbb34db5784979ad654166778775c5c28f5f80a",
|
||||
"app/Http/Controllers/ProcurementReviewReportController.php": "ba96649bb5585ace55249f706dfb740cc12918676bc2ff3b4dac620ad602fb57",
|
||||
"app/Http/Controllers/PeopleHiringController.php": "bc6656d73a033bffa540d2b555d15d9c52a38c84446d892710d7d8ded35200ec",
|
||||
"resources/js/components/evidence-brief.tsx": "3b4630e44fdc02191d1e8ecebcd451c20b9e5a35b49ded9f5d78044bd1e24932",
|
||||
"resources/js/components/procurement.tsx": "846599386826eece0c9d5f48f4a30d3d52815fce3fde4d781073ff9347bf97c6",
|
||||
"resources/js/pages/purchases/review-report.tsx": "e29457affd4d2005b4393f4d21ee8ae1fbd9e4ccdc4ffbc253122d4628c1ba89",
|
||||
"resources/js/pages/people/show.tsx": "247e4e80d4ffca649b46fe00ee665de7e4c5476f2ce3cc1e5590e7cced049e47",
|
||||
"tests/Feature/ProcurementReviewReportTest.php": "46b95f3fc21b557021dd013357016a312078b99d20911a29e4e5ca3d09a5ed20",
|
||||
"tests/Feature/PeopleHiringTest.php": "fbd93d8134298d6b666634659223487646dbcb8074bd05889d660e378c40be5a",
|
||||
"tests/frontend/evidence-brief.test.tsx": "385b617074ac8c718f97e52981acce2a7b3dda9b965432e7e2be04c1ca7ef279",
|
||||
"tests/frontend/people-hiring.test.tsx": "24c51db9886358dfb1bd43d6b82c2a45ac731abdab76bc4e9dea3b85cbcb8bb4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T15:13:30+00:00",
|
||||
"database": "disposable PostgreSQL",
|
||||
"application_role_reused_without_privilege_change": true,
|
||||
"result": "passed",
|
||||
"scenario": "real report snapshots, tenant policy toggles, stale version rejection and authorisation",
|
||||
"original_evaluation_untouched": true,
|
||||
"database_removed": true
|
||||
"checked_at": "2026-10-30T15:13:30+00:00",
|
||||
"database": "disposable PostgreSQL",
|
||||
"application_role_reused_without_privilege_change": true,
|
||||
"result": "passed",
|
||||
"scenario": "real report snapshots, tenant policy toggles, stale version rejection and authorisation",
|
||||
"original_evaluation_untouched": true,
|
||||
"database_removed": true
|
||||
}
|
||||
|
||||
@@ -1,78 +1,78 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T15:16:21.466309+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"independent tenant settings for procurement and people local briefs",
|
||||
"optimistic version reason and transactional audit",
|
||||
"preserve full reports process decisions and access rules",
|
||||
"offline synthetic AI assessment of citation coverage and literal matches",
|
||||
"no automatic semantic certification"
|
||||
],
|
||||
"module_tests": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 82,
|
||||
"passed": 82,
|
||||
"assertions": 1337,
|
||||
"duration_ms": 12897
|
||||
},
|
||||
"react_inertia_tests": 33,
|
||||
"postgresql_integration": {
|
||||
"tool": "phpunit",
|
||||
"result": "passed",
|
||||
"tests": 1,
|
||||
"passed": 1,
|
||||
"assertions": 110,
|
||||
"duration_ms": 2935
|
||||
},
|
||||
"postgresql_proof": "intelligence-policy-pg.json",
|
||||
"installation_proof": "intelligence-policy-install.json",
|
||||
"manual_fixture_assessment": {
|
||||
"method": "synthetic_ai_assessment_v1",
|
||||
"facts": 3,
|
||||
"claims": 3,
|
||||
"cited_facts": 3,
|
||||
"uncited_fact_ids": [],
|
||||
"citation_coverage_complete": true,
|
||||
"exact_source_text_matches": 3,
|
||||
"requires_human_review": true,
|
||||
"semantic_accuracy": "not_automatically_verified"
|
||||
},
|
||||
"fixture_is_model_output": false,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"http_proof": "menu-links.json",
|
||||
"prior_full_suite_reused": "people-clarifications.json",
|
||||
"accounts_or_grants_changed": false,
|
||||
"tenant_policy_choices_automatically_created": false,
|
||||
"live_external_calls": 0,
|
||||
"live_authorisation": "previously granted for one synthetic call; pending credential setup by owner",
|
||||
"real_document_AI_enabled": false,
|
||||
"visual_review": false,
|
||||
"domain_installed": false,
|
||||
"source_sha256": {
|
||||
"app/Services/IntelligencePolicy.php": "c25d9c97e3902327486dc128274662d77b3da041b18d675510b0d29a76c7465b",
|
||||
"app/Services/OrganizationConfiguration.php": "6a3a95f03491f47e0d80f12568d7e04a9a202a463d980d82daa794bbba17328e",
|
||||
"app/Services/SyntheticAiAssessment.php": "a664903a3b0e4889e931d085697891588df7aced7e868e8e80c438d798365963",
|
||||
"app/Services/SyntheticAiRehearsal.php": "76d0f2fa2ef07e670dc74b67e098d0d49df3755354342dc8592fbcb8f96796c6",
|
||||
"app/Console/Commands/SgcAiAssess.php": "e47320fa2aeb0787e1d43b3c6fe2acb73c5732b63aa57d2f1ec6ae7109b1213d",
|
||||
"app/Models/Tenant.php": "e7446af40d11ef77747caac54e623efb7c560d60bf05161557dac3086ea72d96",
|
||||
"app/Http/Controllers/OrganizationConfigurationController.php": "88916867ddc3bcaccae9145bf9ca0c8598b3f6c8206c9222cbaed91c1490427b",
|
||||
"app/Http/Controllers/PeopleHiringController.php": "6da8c0728838050b32db5bbde09420e164265cd78b185d500b040f03cb46e35f",
|
||||
"app/Http/Controllers/ProcurementReviewReportController.php": "9ff40d3ee01cc0e5e97b900d2d0d6be8909c93c42df949deb14a33e21de7e828",
|
||||
"database/migrations/2026_10_30_000033_add_intelligence_policy.php": "37b109a2bcad6572498e6350363e75e9f981da5f0ba921c50e861805a34a0ada",
|
||||
"resources/js/components/intelligence-settings.tsx": "f63b9f7795bdfd9a263a9339d752618d13b87d05d3985272062aa5a5cfacfb79",
|
||||
"resources/js/pages/company/parameters.tsx": "380ccc3933131004950d281108100b568c8b90a69a2286788fb8798500fc6ff2",
|
||||
"resources/js/pages/people/show.tsx": "ad2f1ec8aeb350ade2da2ebd242ef448e068ca2974912aae3afdedf0530ecdb0",
|
||||
"resources/js/pages/purchases/review-report.tsx": "7f572d0f27b2e1b35cddb4518846534940dbf99c13e76e3270eb572c0fef83ee",
|
||||
"tests/Feature/OrganizationConfigurationTest.php": "1072451618d1104e61cec0b879597de3e3f3a7060f53c91f3196a181ad912bb3",
|
||||
"tests/Feature/PeopleHiringTest.php": "9db07af59529ea6e9bc559531498813c5983bfc7d64c5f5729ad23d30aedfa1f",
|
||||
"tests/Feature/SyntheticAiAssessmentTest.php": "6913b68906d41806312b244f9909385892ed8ce88129fad6e711c0d9fe6ead36",
|
||||
"tests/Fixtures/ai-synthetic-result.json": "d488c086b2199a70b98f2238634d595d89fd086e3c52dc3aa85c80da1ebd959f",
|
||||
"tests/frontend/intelligence-settings.test.tsx": "80feabb237675ecda8a4a516a58a4cc74cfd633e5900964625d1b0431f3c8670",
|
||||
"routes/web.php": "279f3d92fabd359e8da154a288f6ff45a46c287897b424081d4a19d54636eeb5"
|
||||
}
|
||||
"checked_at": "2026-10-30T15:16:21.466309+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"independent tenant settings for procurement and people local briefs",
|
||||
"optimistic version reason and transactional audit",
|
||||
"preserve full reports process decisions and access rules",
|
||||
"offline synthetic AI assessment of citation coverage and literal matches",
|
||||
"no automatic semantic certification"
|
||||
],
|
||||
"module_tests": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 82,
|
||||
"passed": 82,
|
||||
"assertions": 1337,
|
||||
"duration_ms": 12897
|
||||
},
|
||||
"react_inertia_tests": 33,
|
||||
"postgresql_integration": {
|
||||
"tool": "phpunit",
|
||||
"result": "passed",
|
||||
"tests": 1,
|
||||
"passed": 1,
|
||||
"assertions": 110,
|
||||
"duration_ms": 2935
|
||||
},
|
||||
"postgresql_proof": "intelligence-policy-pg.json",
|
||||
"installation_proof": "intelligence-policy-install.json",
|
||||
"manual_fixture_assessment": {
|
||||
"method": "synthetic_ai_assessment_v1",
|
||||
"facts": 3,
|
||||
"claims": 3,
|
||||
"cited_facts": 3,
|
||||
"uncited_fact_ids": [],
|
||||
"citation_coverage_complete": true,
|
||||
"exact_source_text_matches": 3,
|
||||
"requires_human_review": true,
|
||||
"semantic_accuracy": "not_automatically_verified"
|
||||
},
|
||||
"fixture_is_model_output": false,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"http_proof": "menu-links.json",
|
||||
"prior_full_suite_reused": "people-clarifications.json",
|
||||
"accounts_or_grants_changed": false,
|
||||
"tenant_policy_choices_automatically_created": false,
|
||||
"live_external_calls": 0,
|
||||
"live_authorisation": "previously granted for one synthetic call; pending credential setup by owner",
|
||||
"real_document_AI_enabled": false,
|
||||
"visual_review": false,
|
||||
"domain_installed": false,
|
||||
"source_sha256": {
|
||||
"app/Services/IntelligencePolicy.php": "c25d9c97e3902327486dc128274662d77b3da041b18d675510b0d29a76c7465b",
|
||||
"app/Services/OrganizationConfiguration.php": "6a3a95f03491f47e0d80f12568d7e04a9a202a463d980d82daa794bbba17328e",
|
||||
"app/Services/SyntheticAiAssessment.php": "a664903a3b0e4889e931d085697891588df7aced7e868e8e80c438d798365963",
|
||||
"app/Services/SyntheticAiRehearsal.php": "76d0f2fa2ef07e670dc74b67e098d0d49df3755354342dc8592fbcb8f96796c6",
|
||||
"app/Console/Commands/SgcAiAssess.php": "e47320fa2aeb0787e1d43b3c6fe2acb73c5732b63aa57d2f1ec6ae7109b1213d",
|
||||
"app/Models/Tenant.php": "e7446af40d11ef77747caac54e623efb7c560d60bf05161557dac3086ea72d96",
|
||||
"app/Http/Controllers/OrganizationConfigurationController.php": "88916867ddc3bcaccae9145bf9ca0c8598b3f6c8206c9222cbaed91c1490427b",
|
||||
"app/Http/Controllers/PeopleHiringController.php": "6da8c0728838050b32db5bbde09420e164265cd78b185d500b040f03cb46e35f",
|
||||
"app/Http/Controllers/ProcurementReviewReportController.php": "9ff40d3ee01cc0e5e97b900d2d0d6be8909c93c42df949deb14a33e21de7e828",
|
||||
"database/migrations/2026_10_30_000033_add_intelligence_policy.php": "37b109a2bcad6572498e6350363e75e9f981da5f0ba921c50e861805a34a0ada",
|
||||
"resources/js/components/intelligence-settings.tsx": "f63b9f7795bdfd9a263a9339d752618d13b87d05d3985272062aa5a5cfacfb79",
|
||||
"resources/js/pages/company/parameters.tsx": "380ccc3933131004950d281108100b568c8b90a69a2286788fb8798500fc6ff2",
|
||||
"resources/js/pages/people/show.tsx": "ad2f1ec8aeb350ade2da2ebd242ef448e068ca2974912aae3afdedf0530ecdb0",
|
||||
"resources/js/pages/purchases/review-report.tsx": "7f572d0f27b2e1b35cddb4518846534940dbf99c13e76e3270eb572c0fef83ee",
|
||||
"tests/Feature/OrganizationConfigurationTest.php": "1072451618d1104e61cec0b879597de3e3f3a7060f53c91f3196a181ad912bb3",
|
||||
"tests/Feature/PeopleHiringTest.php": "9db07af59529ea6e9bc559531498813c5983bfc7d64c5f5729ad23d30aedfa1f",
|
||||
"tests/Feature/SyntheticAiAssessmentTest.php": "6913b68906d41806312b244f9909385892ed8ce88129fad6e711c0d9fe6ead36",
|
||||
"tests/Fixtures/ai-synthetic-result.json": "d488c086b2199a70b98f2238634d595d89fd086e3c52dc3aa85c80da1ebd959f",
|
||||
"tests/frontend/intelligence-settings.test.tsx": "80feabb237675ecda8a4a516a58a4cc74cfd633e5900964625d1b0431f3c8670",
|
||||
"routes/web.php": "279f3d92fabd359e8da154a288f6ff45a46c287897b424081d4a19d54636eeb5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"checked_at": "2026-10-29T18:41:52.592Z",
|
||||
"documents_checked": 1,
|
||||
"per_invoice_entry": true,
|
||||
"return_to_invoices": true,
|
||||
"invalid_identifiers_refused": true,
|
||||
"unknown_invoice_refused": true,
|
||||
"full_listing_unchanged": true,
|
||||
"business_commands": 0,
|
||||
"visual_review": false
|
||||
"checked_at": "2026-10-29T18:41:52.592Z",
|
||||
"documents_checked": 1,
|
||||
"per_invoice_entry": true,
|
||||
"return_to_invoices": true,
|
||||
"invalid_identifiers_refused": true,
|
||||
"unknown_invoice_refused": true,
|
||||
"full_listing_unchanged": true,
|
||||
"business_commands": 0,
|
||||
"visual_review": false
|
||||
}
|
||||
|
||||
+1009
-1009
File diff suppressed because it is too large
Load Diff
+169
-169
@@ -1,174 +1,174 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T17:33:21.203Z",
|
||||
"results": [
|
||||
{
|
||||
"role": "admin",
|
||||
"checked_entries": 32,
|
||||
"paths": [
|
||||
"/requisicoes/criar",
|
||||
"/requisicoes?view=mine",
|
||||
"/requisicoes?view=mine&status=draft",
|
||||
"/requisicoes?view=all",
|
||||
"/planeamento",
|
||||
"/requisicoes?view=approvals",
|
||||
"/requisicoes?view=decided",
|
||||
"/cotacoes",
|
||||
"/ordens",
|
||||
"/revisao-compras",
|
||||
"/fornecedores",
|
||||
"/fornecedores/avaliacoes",
|
||||
"/pessoas/cadastro",
|
||||
"/pessoas",
|
||||
"/pessoas?kind=service",
|
||||
"/pessoas?kind=staff",
|
||||
"/recepcoes",
|
||||
"/stock",
|
||||
"/stock/operacoes",
|
||||
"/equipamentos",
|
||||
"/facturas",
|
||||
"/obrigacoes-fornecedores",
|
||||
"/pagamentos",
|
||||
"checked_at": "2026-10-30T17:33:21.203Z",
|
||||
"results": [
|
||||
{
|
||||
"role": "admin",
|
||||
"checked_entries": 32,
|
||||
"paths": [
|
||||
"/requisicoes/criar",
|
||||
"/requisicoes?view=mine",
|
||||
"/requisicoes?view=mine&status=draft",
|
||||
"/requisicoes?view=all",
|
||||
"/planeamento",
|
||||
"/requisicoes?view=approvals",
|
||||
"/requisicoes?view=decided",
|
||||
"/cotacoes",
|
||||
"/ordens",
|
||||
"/revisao-compras",
|
||||
"/fornecedores",
|
||||
"/fornecedores/avaliacoes",
|
||||
"/pessoas/cadastro",
|
||||
"/pessoas",
|
||||
"/pessoas?kind=service",
|
||||
"/pessoas?kind=staff",
|
||||
"/recepcoes",
|
||||
"/stock",
|
||||
"/stock/operacoes",
|
||||
"/equipamentos",
|
||||
"/facturas",
|
||||
"/obrigacoes-fornecedores",
|
||||
"/pagamentos",
|
||||
"/orcamentos",
|
||||
"/regras-investimento",
|
||||
"/empresa",
|
||||
"/administracao/parametrizacao",
|
||||
"/politica-admissao",
|
||||
"/politica-recomendacao",
|
||||
"/conflitos",
|
||||
"/relatorios",
|
||||
"/relatorios/medidas-fornecedores"
|
||||
]
|
||||
},
|
||||
{
|
||||
"role": "procurement",
|
||||
"checked_entries": 20,
|
||||
"paths": [
|
||||
"/requisicoes/criar",
|
||||
"/requisicoes?view=mine",
|
||||
"/requisicoes?view=mine&status=draft",
|
||||
"/requisicoes?view=all",
|
||||
"/planeamento",
|
||||
"/requisicoes?view=approvals",
|
||||
"/requisicoes?view=decided",
|
||||
"/cotacoes",
|
||||
"/ordens",
|
||||
"/revisao-compras",
|
||||
"/fornecedores",
|
||||
"/fornecedores/avaliacoes",
|
||||
"/recepcoes",
|
||||
"/stock",
|
||||
"/stock/operacoes",
|
||||
"/equipamentos",
|
||||
"/regras-investimento",
|
||||
"/conflitos",
|
||||
"/relatorios",
|
||||
"/relatorios/medidas-fornecedores"
|
||||
]
|
||||
},
|
||||
{
|
||||
"role": "finance",
|
||||
"checked_entries": 24,
|
||||
"paths": [
|
||||
"/requisicoes/criar",
|
||||
"/requisicoes?view=mine",
|
||||
"/requisicoes?view=mine&status=draft",
|
||||
"/requisicoes?view=all",
|
||||
"/planeamento",
|
||||
"/requisicoes?view=approvals",
|
||||
"/requisicoes?view=decided",
|
||||
"/cotacoes",
|
||||
"/ordens",
|
||||
"/revisao-compras",
|
||||
"/fornecedores",
|
||||
"/fornecedores/avaliacoes",
|
||||
"/recepcoes",
|
||||
"/stock",
|
||||
"/stock/operacoes",
|
||||
"/equipamentos",
|
||||
"/facturas",
|
||||
"/obrigacoes-fornecedores",
|
||||
"/pagamentos",
|
||||
"/orcamentos",
|
||||
"/regras-investimento",
|
||||
"/conflitos",
|
||||
"/relatorios",
|
||||
"/relatorios/medidas-fornecedores"
|
||||
]
|
||||
},
|
||||
{
|
||||
"role": "department_head",
|
||||
"checked_entries": 17,
|
||||
"paths": [
|
||||
"/requisicoes/criar",
|
||||
"/requisicoes?view=mine",
|
||||
"/requisicoes?view=mine&status=draft",
|
||||
"/requisicoes?view=all",
|
||||
"/planeamento",
|
||||
"/requisicoes?view=approvals",
|
||||
"/requisicoes?view=decided",
|
||||
"/cotacoes",
|
||||
"/ordens",
|
||||
"/revisao-compras",
|
||||
"/fornecedores",
|
||||
"/fornecedores/avaliacoes",
|
||||
"/recepcoes",
|
||||
"/equipamentos",
|
||||
"/conflitos",
|
||||
"/relatorios",
|
||||
"/relatorios/medidas-fornecedores"
|
||||
]
|
||||
},
|
||||
{
|
||||
"role": "warehouse",
|
||||
"checked_entries": 11,
|
||||
"paths": [
|
||||
"/requisicoes/criar",
|
||||
"/requisicoes?view=mine",
|
||||
"/requisicoes?view=mine&status=draft",
|
||||
"/requisicoes?view=all",
|
||||
"/planeamento",
|
||||
"/revisao-compras",
|
||||
"/recepcoes",
|
||||
"/stock",
|
||||
"/stock/operacoes",
|
||||
"/equipamentos",
|
||||
"/conflitos"
|
||||
]
|
||||
},
|
||||
{
|
||||
"role": "requester",
|
||||
"checked_entries": 7,
|
||||
"paths": [
|
||||
"/requisicoes/criar",
|
||||
"/requisicoes?view=mine",
|
||||
"/requisicoes?view=mine&status=draft",
|
||||
"/requisicoes?view=all",
|
||||
"/planeamento",
|
||||
"/revisao-compras",
|
||||
"/conflitos"
|
||||
]
|
||||
}
|
||||
],
|
||||
"request_detail_checked": true,
|
||||
"invoice_count": 1,
|
||||
"process_links_checked": [
|
||||
"/requisicoes/1",
|
||||
"/orcamentos",
|
||||
"/regras-investimento",
|
||||
"/empresa",
|
||||
"/administracao/parametrizacao",
|
||||
"/politica-admissao",
|
||||
"/politica-recomendacao",
|
||||
"/conflitos",
|
||||
"/relatorios",
|
||||
"/relatorios/medidas-fornecedores"
|
||||
]
|
||||
},
|
||||
{
|
||||
"role": "procurement",
|
||||
"checked_entries": 20,
|
||||
"paths": [
|
||||
"/requisicoes/criar",
|
||||
"/requisicoes?view=mine",
|
||||
"/requisicoes?view=mine&status=draft",
|
||||
"/requisicoes?view=all",
|
||||
"/planeamento",
|
||||
"/requisicoes?view=approvals",
|
||||
"/requisicoes?view=decided",
|
||||
"/cotacoes",
|
||||
"/ordens",
|
||||
"/revisao-compras",
|
||||
"/fornecedores",
|
||||
"/fornecedores/avaliacoes",
|
||||
"/recepcoes",
|
||||
"/stock",
|
||||
"/stock/operacoes",
|
||||
"/equipamentos",
|
||||
"/regras-investimento",
|
||||
"/conflitos",
|
||||
"/relatorios",
|
||||
"/relatorios/medidas-fornecedores"
|
||||
]
|
||||
},
|
||||
{
|
||||
"role": "finance",
|
||||
"checked_entries": 24,
|
||||
"paths": [
|
||||
"/requisicoes/criar",
|
||||
"/requisicoes?view=mine",
|
||||
"/requisicoes?view=mine&status=draft",
|
||||
"/requisicoes?view=all",
|
||||
"/planeamento",
|
||||
"/requisicoes?view=approvals",
|
||||
"/requisicoes?view=decided",
|
||||
"/cotacoes",
|
||||
"/ordens",
|
||||
"/revisao-compras",
|
||||
"/fornecedores",
|
||||
"/fornecedores/avaliacoes",
|
||||
"/recepcoes",
|
||||
"/stock",
|
||||
"/stock/operacoes",
|
||||
"/equipamentos",
|
||||
"/requisicoes/1/cotacoes",
|
||||
"/requisicoes/1/adjudicacao",
|
||||
"/ordens/1",
|
||||
"/ordens/1/recepcoes",
|
||||
"/facturas",
|
||||
"/obrigacoes-fornecedores",
|
||||
"/pagamentos",
|
||||
"/orcamentos",
|
||||
"/regras-investimento",
|
||||
"/conflitos",
|
||||
"/relatorios",
|
||||
"/relatorios/medidas-fornecedores"
|
||||
]
|
||||
},
|
||||
{
|
||||
"role": "department_head",
|
||||
"checked_entries": 17,
|
||||
"paths": [
|
||||
"/requisicoes/criar",
|
||||
"/requisicoes?view=mine",
|
||||
"/requisicoes?view=mine&status=draft",
|
||||
"/requisicoes?view=all",
|
||||
"/planeamento",
|
||||
"/requisicoes?view=approvals",
|
||||
"/requisicoes?view=decided",
|
||||
"/cotacoes",
|
||||
"/ordens",
|
||||
"/revisao-compras",
|
||||
"/fornecedores",
|
||||
"/fornecedores/avaliacoes",
|
||||
"/recepcoes",
|
||||
"/equipamentos",
|
||||
"/conflitos",
|
||||
"/relatorios",
|
||||
"/relatorios/medidas-fornecedores"
|
||||
]
|
||||
},
|
||||
{
|
||||
"role": "warehouse",
|
||||
"checked_entries": 11,
|
||||
"paths": [
|
||||
"/requisicoes/criar",
|
||||
"/requisicoes?view=mine",
|
||||
"/requisicoes?view=mine&status=draft",
|
||||
"/requisicoes?view=all",
|
||||
"/planeamento",
|
||||
"/revisao-compras",
|
||||
"/recepcoes",
|
||||
"/stock",
|
||||
"/stock/operacoes",
|
||||
"/equipamentos",
|
||||
"/conflitos"
|
||||
]
|
||||
},
|
||||
{
|
||||
"role": "requester",
|
||||
"checked_entries": 7,
|
||||
"paths": [
|
||||
"/requisicoes/criar",
|
||||
"/requisicoes?view=mine",
|
||||
"/requisicoes?view=mine&status=draft",
|
||||
"/requisicoes?view=all",
|
||||
"/planeamento",
|
||||
"/revisao-compras",
|
||||
"/conflitos"
|
||||
]
|
||||
}
|
||||
],
|
||||
"request_detail_checked": true,
|
||||
"invoice_count": 1,
|
||||
"process_links_checked": [
|
||||
"/requisicoes/1",
|
||||
"/orcamentos",
|
||||
"/requisicoes/1/cotacoes",
|
||||
"/requisicoes/1/adjudicacao",
|
||||
"/ordens/1",
|
||||
"/ordens/1/recepcoes",
|
||||
"/facturas",
|
||||
"/pagamentos",
|
||||
"/requisicoes/1/relatorio",
|
||||
"/requisicoes/1/admissao",
|
||||
"/requisicoes/1/recomendacao",
|
||||
"/recepcoes/1"
|
||||
],
|
||||
"canonical_query_order": true,
|
||||
"active_subroutes": true,
|
||||
"vat_setting_and_default_checked": true,
|
||||
"comparison_ties_and_reasons_checked": true,
|
||||
"business_history_unchanged": true,
|
||||
"business_commands": 0
|
||||
"/requisicoes/1/relatorio",
|
||||
"/requisicoes/1/admissao",
|
||||
"/requisicoes/1/recomendacao",
|
||||
"/recepcoes/1"
|
||||
],
|
||||
"canonical_query_order": true,
|
||||
"active_subroutes": true,
|
||||
"vat_setting_and_default_checked": true,
|
||||
"comparison_ties_and_reasons_checked": true,
|
||||
"business_history_unchanged": true,
|
||||
"business_commands": 0
|
||||
}
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T08:46:09.751Z",
|
||||
"base": "http://127.0.0.1:8005",
|
||||
"path": "/administracao/parametrizacao",
|
||||
"roles": [
|
||||
"admin",
|
||||
"requester",
|
||||
"department_head",
|
||||
"procurement",
|
||||
"finance",
|
||||
"warehouse"
|
||||
],
|
||||
"owner_access": true,
|
||||
"other_legacy_profiles_refused": true,
|
||||
"passwords_not_returned": true,
|
||||
"configuration_state": {
|
||||
"active": false,
|
||||
"version": 1,
|
||||
"profiles": 0
|
||||
},
|
||||
"business_commands": 0,
|
||||
"accounts_created": 0,
|
||||
"permissions_changed": false,
|
||||
"visual_review": false
|
||||
"checked_at": "2026-10-30T08:46:09.751Z",
|
||||
"base": "http://127.0.0.1:8005",
|
||||
"path": "/administracao/parametrizacao",
|
||||
"roles": [
|
||||
"admin",
|
||||
"requester",
|
||||
"department_head",
|
||||
"procurement",
|
||||
"finance",
|
||||
"warehouse"
|
||||
],
|
||||
"owner_access": true,
|
||||
"other_legacy_profiles_refused": true,
|
||||
"passwords_not_returned": true,
|
||||
"configuration_state": {
|
||||
"active": false,
|
||||
"version": 1,
|
||||
"profiles": 0
|
||||
},
|
||||
"business_commands": 0,
|
||||
"accounts_created": 0,
|
||||
"permissions_changed": false,
|
||||
"visual_review": false
|
||||
}
|
||||
|
||||
@@ -1,63 +1,63 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T08:47:37.236992+00:00",
|
||||
"full_suite": {
|
||||
"total": 603,
|
||||
"passed": 601,
|
||||
"skipped": 2,
|
||||
"assertions": 5157,
|
||||
"log": "storage/logs/organization-delivery-tests.log"
|
||||
},
|
||||
"postgresql_configuration": {
|
||||
"tests": 13,
|
||||
"passed": 13,
|
||||
"assertions": 96,
|
||||
"disposable_database_removed": true,
|
||||
"log": "storage/logs/organization-pg-tests.log"
|
||||
},
|
||||
"phpstan_errors": 0,
|
||||
"typescript": "passed",
|
||||
"lint_format_targeted": "passed",
|
||||
"pint": "passed",
|
||||
"build": "passed",
|
||||
"build_log": "storage/logs/organization-build.log",
|
||||
"runtime_files_verified": 18,
|
||||
"menu_entries": 107,
|
||||
"evaluation_catalog_pages": 23,
|
||||
"installed_only_in_evaluation": true,
|
||||
"configuration_activated_in_evaluation": false,
|
||||
"accounts_or_permissions_changed_in_evaluation": false,
|
||||
"maintenance": {
|
||||
"named_account": true,
|
||||
"normal_authentication": true,
|
||||
"expiry_required": true,
|
||||
"revocable": true,
|
||||
"audited": true,
|
||||
"read_only": true,
|
||||
"universal_password": false,
|
||||
"hidden_entry": false
|
||||
},
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"remaining": [
|
||||
"permission granularity by operation",
|
||||
"universal workflow stage/condition editor",
|
||||
"specific common financial identity and entitlement associations",
|
||||
"visual and print review"
|
||||
],
|
||||
"sources": {
|
||||
"app/Services/OrganizationAccess.php": "872f06e50d561714ab50d88dc113c0fa054dea9f170a09bb37f8d39d07b78a09",
|
||||
"app/Services/OrganizationConfiguration.php": "e2d6e725a78dc4f10d931017969d0fcdacaed4dd6b37a4a08a96cb44b09fd1b5",
|
||||
"app/Http/Controllers/OrganizationConfigurationController.php": "9765cee7b1770f48fde4affcefc518a36576190bbaeac8bce079655353377c72",
|
||||
"app/Http/Controllers/CompanyController.php": "5a8112e0f1535f90aeb8cf9785259da8d713d1bfcd3ad908aa3eabd161dba8a3",
|
||||
"app/Http/Middleware/EnsureOrganizationAccess.php": "6782164f18f982fedb91dfb4fb2365895c757d776d7bf1728a91682fa95b8b02",
|
||||
"app/Models/User.php": "835c119c097e7927136e8c7775ebb5ed677f964e766a213ec3c04e0b7388e740",
|
||||
"app/Enums/PurchasingRole.php": "91da2d40bf11988fd6324aba8e5068e6be7c92222be31baf83844c134affe1c4",
|
||||
"database/migrations/2026_10_30_000027_create_organization_access.php": "130d92ba8a093a8f77678e1ce4c1a90125bb996e88ed81633359d224fe77a853",
|
||||
"database/seeders/DatabaseSeeder.php": "2aaea71ca2eba604abd5723e95cd1a99521c61b6f00b70dfedba246f3dcfae48",
|
||||
"resources/js/pages/company/parameters.tsx": "c0760c50d100c8b2dfb3d3ad3b2a05fae14eed7900e95461c766a885d07677d1",
|
||||
"resources/js/pages/company/setup.tsx": "614000ad22692ea3e707a00103227953d95f32fd7d9a7fc031bdbba7a1ccdb48",
|
||||
"resources/js/lib/procurement-navigation.ts": "d7a42ec76e0166e4783bb496834325ad583b903ebf8c999c2ba5a2307431d56d",
|
||||
"tests/Feature/OrganizationConfigurationTest.php": "eef7e82a6efc8ebad46cea8752861c01bec53c552874d72120a6e982f0114d73",
|
||||
"scripts/organization-access-check.mjs": "1ca7cd2bdafa5f62afb26fa0b074d879273cda27c8350b968de54c8f53328bb0"
|
||||
}
|
||||
"checked_at": "2026-10-30T08:47:37.236992+00:00",
|
||||
"full_suite": {
|
||||
"total": 603,
|
||||
"passed": 601,
|
||||
"skipped": 2,
|
||||
"assertions": 5157,
|
||||
"log": "storage/logs/organization-delivery-tests.log"
|
||||
},
|
||||
"postgresql_configuration": {
|
||||
"tests": 13,
|
||||
"passed": 13,
|
||||
"assertions": 96,
|
||||
"disposable_database_removed": true,
|
||||
"log": "storage/logs/organization-pg-tests.log"
|
||||
},
|
||||
"phpstan_errors": 0,
|
||||
"typescript": "passed",
|
||||
"lint_format_targeted": "passed",
|
||||
"pint": "passed",
|
||||
"build": "passed",
|
||||
"build_log": "storage/logs/organization-build.log",
|
||||
"runtime_files_verified": 18,
|
||||
"menu_entries": 107,
|
||||
"evaluation_catalog_pages": 23,
|
||||
"installed_only_in_evaluation": true,
|
||||
"configuration_activated_in_evaluation": false,
|
||||
"accounts_or_permissions_changed_in_evaluation": false,
|
||||
"maintenance": {
|
||||
"named_account": true,
|
||||
"normal_authentication": true,
|
||||
"expiry_required": true,
|
||||
"revocable": true,
|
||||
"audited": true,
|
||||
"read_only": true,
|
||||
"universal_password": false,
|
||||
"hidden_entry": false
|
||||
},
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"remaining": [
|
||||
"permission granularity by operation",
|
||||
"universal workflow stage/condition editor",
|
||||
"specific common financial identity and entitlement associations",
|
||||
"visual and print review"
|
||||
],
|
||||
"sources": {
|
||||
"app/Services/OrganizationAccess.php": "872f06e50d561714ab50d88dc113c0fa054dea9f170a09bb37f8d39d07b78a09",
|
||||
"app/Services/OrganizationConfiguration.php": "e2d6e725a78dc4f10d931017969d0fcdacaed4dd6b37a4a08a96cb44b09fd1b5",
|
||||
"app/Http/Controllers/OrganizationConfigurationController.php": "9765cee7b1770f48fde4affcefc518a36576190bbaeac8bce079655353377c72",
|
||||
"app/Http/Controllers/CompanyController.php": "5a8112e0f1535f90aeb8cf9785259da8d713d1bfcd3ad908aa3eabd161dba8a3",
|
||||
"app/Http/Middleware/EnsureOrganizationAccess.php": "6782164f18f982fedb91dfb4fb2365895c757d776d7bf1728a91682fa95b8b02",
|
||||
"app/Models/User.php": "835c119c097e7927136e8c7775ebb5ed677f964e766a213ec3c04e0b7388e740",
|
||||
"app/Enums/PurchasingRole.php": "91da2d40bf11988fd6324aba8e5068e6be7c92222be31baf83844c134affe1c4",
|
||||
"database/migrations/2026_10_30_000027_create_organization_access.php": "130d92ba8a093a8f77678e1ce4c1a90125bb996e88ed81633359d224fe77a853",
|
||||
"database/seeders/DatabaseSeeder.php": "2aaea71ca2eba604abd5723e95cd1a99521c61b6f00b70dfedba246f3dcfae48",
|
||||
"resources/js/pages/company/parameters.tsx": "c0760c50d100c8b2dfb3d3ad3b2a05fae14eed7900e95461c766a885d07677d1",
|
||||
"resources/js/pages/company/setup.tsx": "614000ad22692ea3e707a00103227953d95f32fd7d9a7fc031bdbba7a1ccdb48",
|
||||
"resources/js/lib/procurement-navigation.ts": "d7a42ec76e0166e4783bb496834325ad583b903ebf8c999c2ba5a2307431d56d",
|
||||
"tests/Feature/OrganizationConfigurationTest.php": "eef7e82a6efc8ebad46cea8752861c01bec53c552874d72120a6e982f0114d73",
|
||||
"scripts/organization-access-check.mjs": "1ca7cd2bdafa5f62afb26fa0b074d879273cda27c8350b968de54c8f53328bb0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,25 @@
|
||||
{
|
||||
"checked_at": "2026-10-29T19:41:38.688Z",
|
||||
"passed": true,
|
||||
"fault_recovery": true,
|
||||
"concurrent_recognition": true,
|
||||
"original_business_history_unchanged": true,
|
||||
"new_transfers_executed": false,
|
||||
"scenario": {
|
||||
"principal_minor": 1160000,
|
||||
"balance_minor": 860000,
|
||||
"confirmed_minor": 300000,
|
||||
"pending_minor": 100000,
|
||||
"rejected_minor": 50000,
|
||||
"obligations": 1,
|
||||
"imports": 1,
|
||||
"payment_links": 3,
|
||||
"business_fingerprint": "fb66326abf7968aa818b0c6a214c2a1f59966fa11d3692e748fa0fa1aaa3cf1f",
|
||||
"ui_balance_correct": true,
|
||||
"due_on": null,
|
||||
"days_overdue": null,
|
||||
"append_only_tables": [
|
||||
"pa_history_imports",
|
||||
"pa_imported_payments"
|
||||
],
|
||||
"changed_history_refused": true
|
||||
},
|
||||
"visual_review": false
|
||||
"checked_at": "2026-10-29T19:41:38.688Z",
|
||||
"passed": true,
|
||||
"fault_recovery": true,
|
||||
"concurrent_recognition": true,
|
||||
"original_business_history_unchanged": true,
|
||||
"new_transfers_executed": false,
|
||||
"scenario": {
|
||||
"principal_minor": 1160000,
|
||||
"balance_minor": 860000,
|
||||
"confirmed_minor": 300000,
|
||||
"pending_minor": 100000,
|
||||
"rejected_minor": 50000,
|
||||
"obligations": 1,
|
||||
"imports": 1,
|
||||
"payment_links": 3,
|
||||
"business_fingerprint": "fb66326abf7968aa818b0c6a214c2a1f59966fa11d3692e748fa0fa1aaa3cf1f",
|
||||
"ui_balance_correct": true,
|
||||
"due_on": null,
|
||||
"days_overdue": null,
|
||||
"append_only_tables": ["pa_history_imports", "pa_imported_payments"],
|
||||
"changed_history_refused": true
|
||||
},
|
||||
"visual_review": false
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
{
|
||||
"checked_at": "2026-10-29T19:41:38.669Z",
|
||||
"passed": true,
|
||||
"atomic_rollback": true,
|
||||
"concurrent_decision_once": true,
|
||||
"original_import_preserved": true,
|
||||
"new_transfers_executed": false,
|
||||
"scenario": {
|
||||
"balance_minor": 760000,
|
||||
"confirmed_minor": 400000,
|
||||
"pending_minor": 0,
|
||||
"decisions": 1,
|
||||
"events": 1,
|
||||
"original_status": "confirmed",
|
||||
"append_only": true,
|
||||
"frozen": "4b5aa40f964c0709661449b4ab5bfd7cd8295eac9c4d186822d0a1e10aa63534"
|
||||
},
|
||||
"visual_review": false
|
||||
"checked_at": "2026-10-29T19:41:38.669Z",
|
||||
"passed": true,
|
||||
"atomic_rollback": true,
|
||||
"concurrent_decision_once": true,
|
||||
"original_import_preserved": true,
|
||||
"new_transfers_executed": false,
|
||||
"scenario": {
|
||||
"balance_minor": 760000,
|
||||
"confirmed_minor": 400000,
|
||||
"pending_minor": 0,
|
||||
"decisions": 1,
|
||||
"events": 1,
|
||||
"original_status": "confirmed",
|
||||
"append_only": true,
|
||||
"frozen": "4b5aa40f964c0709661449b4ab5bfd7cd8295eac9c4d186822d0a1e10aa63534"
|
||||
},
|
||||
"visual_review": false
|
||||
}
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
"database": "disposable PostgreSQL",
|
||||
"application_role_reused_without_privilege_change": true,
|
||||
"result": "passed",
|
||||
"test_files": [
|
||||
"PeopleHiringTest"
|
||||
],
|
||||
"test_files": ["PeopleHiringTest"],
|
||||
"original_evaluation_untouched": true,
|
||||
"database_removed": true
|
||||
}
|
||||
|
||||
@@ -1,55 +1,55 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T12:12:17.828306+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"separate purchasing and people navigation and workflows",
|
||||
"per-process named access with expiry revocation version and audit",
|
||||
"administrative team setup without automatic candidate access",
|
||||
"server-side financial interview and contract projections including selection copies",
|
||||
"separate service and staff annex fields and classified requirements",
|
||||
"typed reported observed and verified interview evidence",
|
||||
"old records and rules preserved"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 638,
|
||||
"passed": 636,
|
||||
"assertions": 5556,
|
||||
"duration_ms": 135871,
|
||||
"skipped": 2
|
||||
},
|
||||
"latest_module_tests": {
|
||||
"sqlite": 16,
|
||||
"postgresql": 16,
|
||||
"assertions": 286,
|
||||
"disposable_database_removed": true
|
||||
},
|
||||
"react_inertia_tests": 22,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"runtime_sha256_files": 18,
|
||||
"menu_entries_by_role": 110,
|
||||
"menu_groups_maximum": 6,
|
||||
"http_proof": "menu-links.json",
|
||||
"installation_proof": "people-access-install.json",
|
||||
"business_accounts_or_grants_created_in_evaluation": false,
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"source_sha256": {
|
||||
"app/Services/PeopleHiring.php": "ec0365c2ef2b1035468f16e8d4923b458889fc8438bbc6532de95791dda17548",
|
||||
"app/Services/PeopleAccess.php": "5ed3feac3f48a4516bd611638e9720ae1096ee86ef6bc00ee6c48fbc67ccf4ea",
|
||||
"app/Services/PeopleForms.php": "9ae5c1da667566cf08c4cc250cdc04a8ecba8a9c9af19b07a4138b1501aa4da8",
|
||||
"app/Http/Controllers/PeopleHiringController.php": "7b4dd572ec3d67c112e02243d6db69d71fe5f17a8e64712738b553b114a4e275",
|
||||
"database/migrations/2026_10_30_000031_create_people_process_access.php": "e899d6adc07122ec6d4ec7606a4714ae69ddc6db30479d4ad66f92767d8d2560",
|
||||
"resources/js/pages/people/access.tsx": "399beb8193906491528954680eb8900d4aa5b5a7a8dd3af6557e6e0fd579780d",
|
||||
"resources/js/pages/people/index.tsx": "a822ee13c1dc158e76396959e944d30e01b7385879050cd0d977c8972e98d6a8",
|
||||
"resources/js/pages/people/show.tsx": "84a3910c9840f88c8c42465d2209834b5d82a94df860dfec31705b1a95623663",
|
||||
"resources/js/lib/procurement-navigation.ts": "bcad7f21492355f8f66687d5dfb74fa105a2b575cf210dc1618f90ddea8e9251",
|
||||
"tests/Feature/PeopleHiringTest.php": "611db1c7e744dae085b966c1c01583336a59ef8daa4087f8904ba14c5b9dcd61",
|
||||
"tests/frontend/people-hiring.test.tsx": "67b9fe5af8f9b076b2b8ee8abc6b441b3a36bd8d4dd5bf04978eb5aa0f02a3a3"
|
||||
}
|
||||
"checked_at": "2026-10-30T12:12:17.828306+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"separate purchasing and people navigation and workflows",
|
||||
"per-process named access with expiry revocation version and audit",
|
||||
"administrative team setup without automatic candidate access",
|
||||
"server-side financial interview and contract projections including selection copies",
|
||||
"separate service and staff annex fields and classified requirements",
|
||||
"typed reported observed and verified interview evidence",
|
||||
"old records and rules preserved"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 638,
|
||||
"passed": 636,
|
||||
"assertions": 5556,
|
||||
"duration_ms": 135871,
|
||||
"skipped": 2
|
||||
},
|
||||
"latest_module_tests": {
|
||||
"sqlite": 16,
|
||||
"postgresql": 16,
|
||||
"assertions": 286,
|
||||
"disposable_database_removed": true
|
||||
},
|
||||
"react_inertia_tests": 22,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"runtime_sha256_files": 18,
|
||||
"menu_entries_by_role": 110,
|
||||
"menu_groups_maximum": 6,
|
||||
"http_proof": "menu-links.json",
|
||||
"installation_proof": "people-access-install.json",
|
||||
"business_accounts_or_grants_created_in_evaluation": false,
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"source_sha256": {
|
||||
"app/Services/PeopleHiring.php": "ec0365c2ef2b1035468f16e8d4923b458889fc8438bbc6532de95791dda17548",
|
||||
"app/Services/PeopleAccess.php": "5ed3feac3f48a4516bd611638e9720ae1096ee86ef6bc00ee6c48fbc67ccf4ea",
|
||||
"app/Services/PeopleForms.php": "9ae5c1da667566cf08c4cc250cdc04a8ecba8a9c9af19b07a4138b1501aa4da8",
|
||||
"app/Http/Controllers/PeopleHiringController.php": "7b4dd572ec3d67c112e02243d6db69d71fe5f17a8e64712738b553b114a4e275",
|
||||
"database/migrations/2026_10_30_000031_create_people_process_access.php": "e899d6adc07122ec6d4ec7606a4714ae69ddc6db30479d4ad66f92767d8d2560",
|
||||
"resources/js/pages/people/access.tsx": "399beb8193906491528954680eb8900d4aa5b5a7a8dd3af6557e6e0fd579780d",
|
||||
"resources/js/pages/people/index.tsx": "a822ee13c1dc158e76396959e944d30e01b7385879050cd0d977c8972e98d6a8",
|
||||
"resources/js/pages/people/show.tsx": "84a3910c9840f88c8c42465d2209834b5d82a94df860dfec31705b1a95623663",
|
||||
"resources/js/lib/procurement-navigation.ts": "bcad7f21492355f8f66687d5dfb74fa105a2b575cf210dc1618f90ddea8e9251",
|
||||
"tests/Feature/PeopleHiringTest.php": "611db1c7e744dae085b966c1c01583336a59ef8daa4087f8904ba14c5b9dcd61",
|
||||
"tests/frontend/people-hiring.test.tsx": "67b9fe5af8f9b076b2b8ee8abc6b441b3a36bd8d4dd5bf04978eb5aa0f02a3a3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
"database": "disposable PostgreSQL",
|
||||
"application_role_reused_without_privilege_change": true,
|
||||
"result": "passed",
|
||||
"test_files": [
|
||||
"PeopleHiringTest"
|
||||
],
|
||||
"test_files": ["PeopleHiringTest"],
|
||||
"original_evaluation_untouched": true,
|
||||
"database_removed": true
|
||||
}
|
||||
|
||||
@@ -1,60 +1,60 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T12:56:09.626883+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"assigned clarification follow-up from immutable review",
|
||||
"late responses and reassignment",
|
||||
"independent review return or justified waiver",
|
||||
"privacy limited to assigned question",
|
||||
"personal operational tasks overdue alerts and printable report",
|
||||
"deadline-only change preserves pending selection basis",
|
||||
"substantive changes checked before pending approval",
|
||||
"approved selection and original scores preserved"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 652,
|
||||
"passed": 650,
|
||||
"assertions": 6142,
|
||||
"duration_ms": 103765,
|
||||
"skipped": 2
|
||||
},
|
||||
"disposable_postgresql": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 30,
|
||||
"passed": 30,
|
||||
"assertions": 836,
|
||||
"duration_ms": 21163
|
||||
},
|
||||
"react_inertia_tests": 25,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build": "passed",
|
||||
"runtime_sha256_files": 18,
|
||||
"http_proof": "menu-links.json",
|
||||
"installation_proof": "people-clarifications-install.json",
|
||||
"postgresql_proof": "people-clarifications-pg.json",
|
||||
"new_accounts": 0,
|
||||
"new_local_assignments": 0,
|
||||
"outbound_messages": 0,
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"production_consulta_hotfix_published": false,
|
||||
"source_sha256": {
|
||||
"app/Services/PeopleClarifications.php": "f52dfd2a29eb7a8180a0449d7c4d9a4ffd15da1569d492f22c58c2c704d245b3",
|
||||
"app/Services/PeopleHiring.php": "ba43edecf4eb09ec0fd34b0639faa3053e985ba65fd10d737a4ee7cc5cc105f2",
|
||||
"app/Services/PeopleWorkdesk.php": "db901201362434d89743641306cfb1cee0b7bb70059ab87593fd6e00fef9bc21",
|
||||
"app/Http/Controllers/PeopleHiringController.php": "11b01291e8378e7e330bcba9e28a68d0fbb707bd0200881a8b126c866c3793ff",
|
||||
"database/migrations/2026_10_30_000032_create_people_clarifications.php": "a8cd29710192f1cb1f9046d4643a10e1aa4df1a463a2939e880082fe4b76da6d",
|
||||
"resources/js/components/people-clarifications.tsx": "a378159cf383d8cfa78c2ffaaf151d311a3e2c6f58ae3d72f21ebe9f9852e95b",
|
||||
"resources/js/pages/people/show.tsx": "e51622810a0c70436cc00eefadf51a9f37cf350098fa2d049f652988916d0d3c",
|
||||
"resources/js/pages/people/index.tsx": "84a956018fd12a4cb8de9df875742a4d119d8975c70f1a88ca2b0b6275f034c5",
|
||||
"tests/Feature/PeopleHiringTest.php": "47e15747b5fbf5a618289ae4d25efc9749166e889078c404f166ab366e615b5b",
|
||||
"tests/frontend/people-hiring.test.tsx": "7709432faf232497ff3c057afd1d5b6516b7cb5a6d8d52d83a1a4cc0afb70561",
|
||||
"routes/web.php": "c7255ae11660d009f9b25d263681500dedb0f1a700d19c2994312612d599286f"
|
||||
}
|
||||
"checked_at": "2026-10-30T12:56:09.626883+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"assigned clarification follow-up from immutable review",
|
||||
"late responses and reassignment",
|
||||
"independent review return or justified waiver",
|
||||
"privacy limited to assigned question",
|
||||
"personal operational tasks overdue alerts and printable report",
|
||||
"deadline-only change preserves pending selection basis",
|
||||
"substantive changes checked before pending approval",
|
||||
"approved selection and original scores preserved"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 652,
|
||||
"passed": 650,
|
||||
"assertions": 6142,
|
||||
"duration_ms": 103765,
|
||||
"skipped": 2
|
||||
},
|
||||
"disposable_postgresql": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 30,
|
||||
"passed": 30,
|
||||
"assertions": 836,
|
||||
"duration_ms": 21163
|
||||
},
|
||||
"react_inertia_tests": 25,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build": "passed",
|
||||
"runtime_sha256_files": 18,
|
||||
"http_proof": "menu-links.json",
|
||||
"installation_proof": "people-clarifications-install.json",
|
||||
"postgresql_proof": "people-clarifications-pg.json",
|
||||
"new_accounts": 0,
|
||||
"new_local_assignments": 0,
|
||||
"outbound_messages": 0,
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"production_consulta_hotfix_published": false,
|
||||
"source_sha256": {
|
||||
"app/Services/PeopleClarifications.php": "f52dfd2a29eb7a8180a0449d7c4d9a4ffd15da1569d492f22c58c2c704d245b3",
|
||||
"app/Services/PeopleHiring.php": "ba43edecf4eb09ec0fd34b0639faa3053e985ba65fd10d737a4ee7cc5cc105f2",
|
||||
"app/Services/PeopleWorkdesk.php": "db901201362434d89743641306cfb1cee0b7bb70059ab87593fd6e00fef9bc21",
|
||||
"app/Http/Controllers/PeopleHiringController.php": "11b01291e8378e7e330bcba9e28a68d0fbb707bd0200881a8b126c866c3793ff",
|
||||
"database/migrations/2026_10_30_000032_create_people_clarifications.php": "a8cd29710192f1cb1f9046d4643a10e1aa4df1a463a2939e880082fe4b76da6d",
|
||||
"resources/js/components/people-clarifications.tsx": "a378159cf383d8cfa78c2ffaaf151d311a3e2c6f58ae3d72f21ebe9f9852e95b",
|
||||
"resources/js/pages/people/show.tsx": "e51622810a0c70436cc00eefadf51a9f37cf350098fa2d049f652988916d0d3c",
|
||||
"resources/js/pages/people/index.tsx": "84a956018fd12a4cb8de9df875742a4d119d8975c70f1a88ca2b0b6275f034c5",
|
||||
"tests/Feature/PeopleHiringTest.php": "47e15747b5fbf5a618289ae4d25efc9749166e889078c404f166ab366e615b5b",
|
||||
"tests/frontend/people-hiring.test.tsx": "7709432faf232497ff3c057afd1d5b6516b7cb5a6d8d52d83a1a4cc0afb70561",
|
||||
"routes/web.php": "c7255ae11660d009f9b25d263681500dedb0f1a700d19c2994312612d599286f"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
"database": "disposable PostgreSQL",
|
||||
"application_role_reused_without_privilege_change": true,
|
||||
"result": "passed",
|
||||
"test_files": [
|
||||
"PeopleHiringTest"
|
||||
],
|
||||
"test_files": ["PeopleHiringTest"],
|
||||
"original_evaluation_untouched": true,
|
||||
"database_removed": true
|
||||
}
|
||||
|
||||
@@ -1,59 +1,59 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T11:46:07.311681+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"individual service provider hiring",
|
||||
"staff recruitment",
|
||||
"process-specific criteria weights and minimum independent reviews",
|
||||
"independent opening evaluation and final decision",
|
||||
"candidate records and evidence",
|
||||
"reasoned selection including ties and returns",
|
||||
"unsigned internal contract draft and printable process report",
|
||||
"separate configurable preparation and review permissions per kind"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 633,
|
||||
"passed": 631,
|
||||
"assertions": 5438,
|
||||
"duration_ms": 178754,
|
||||
"skipped": 2
|
||||
},
|
||||
"latest_module_tests": {
|
||||
"tests": 11,
|
||||
"assertions": 133,
|
||||
"sqlite": "passed",
|
||||
"disposable_postgresql": "passed",
|
||||
"database_removed": true
|
||||
},
|
||||
"react_inertia_tests": 21,
|
||||
"phpstan_errors": 0,
|
||||
"typescript": "passed",
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"runtime_sha256_files": 18,
|
||||
"installation_proof": "people-hiring-install.json",
|
||||
"menu_proof": "menu-links.json",
|
||||
"menu_contract_entries_by_role": 108,
|
||||
"company_accounts_or_grants_created": false,
|
||||
"evaluation_tables_initially_empty": true,
|
||||
"external_messages_sent": false,
|
||||
"signed_contracts_created": false,
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"source_sha256": {
|
||||
"app/Services/PeopleHiring.php": "a7e50e55034ce4085d35184f04d8549741c7fb29e4585dc334dd3bf9525a3d09",
|
||||
"app/Http/Controllers/PeopleHiringController.php": "4e07d58c9f474eec85f5854e9e442978f198888994c27be376f100a0d4e2263c",
|
||||
"app/Models/PeopleProcess.php": "10b1ed29426ab6988d01f1ed43b77f49b689520197c7a9023b41338fe60e7e4b",
|
||||
"app/Services/OrganizationAccess.php": "84f3cddc43b1446f12390d9d3ffcf504f4a1f8909d88612ee30a9fdcb1bd7137",
|
||||
"database/migrations/2026_10_30_000030_create_people_hiring.php": "c1f69354ec82ec5b3aabd0eff85c88c4a8a9eb3cb77c16e9a942d47eb1149c1b",
|
||||
"resources/js/pages/people/index.tsx": "8d65c71d2c5862d640f14a37e7aea997bdfa6aacd7d128568532cbf5821295fd",
|
||||
"resources/js/pages/people/show.tsx": "ffa77f0bb5e72877f7aab6fb30ae129cd7dd7c72911c0ef156a958f821451dc0",
|
||||
"resources/js/lib/people-hiring.ts": "65efd515d39541c7e875127c91731bfc1a379f8db58fcd34e0f39e1a8d75f807",
|
||||
"resources/js/lib/procurement-navigation.ts": "9578b8ac69b8b8a8e6cb0f7134e3cac55e19c1e973ecb21015b592fc8b804318",
|
||||
"tests/Feature/PeopleHiringTest.php": "f1cceadb0ddf7faaf901dd2283953812dad5fb9ebc4f1f3c4f57e546f4591abc",
|
||||
"tests/frontend/people-hiring.test.tsx": "5548b79b353bc36451b42888f7f7727f2524d46df677e106a60968c8e6039dc9"
|
||||
}
|
||||
"checked_at": "2026-10-30T11:46:07.311681+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"individual service provider hiring",
|
||||
"staff recruitment",
|
||||
"process-specific criteria weights and minimum independent reviews",
|
||||
"independent opening evaluation and final decision",
|
||||
"candidate records and evidence",
|
||||
"reasoned selection including ties and returns",
|
||||
"unsigned internal contract draft and printable process report",
|
||||
"separate configurable preparation and review permissions per kind"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 633,
|
||||
"passed": 631,
|
||||
"assertions": 5438,
|
||||
"duration_ms": 178754,
|
||||
"skipped": 2
|
||||
},
|
||||
"latest_module_tests": {
|
||||
"tests": 11,
|
||||
"assertions": 133,
|
||||
"sqlite": "passed",
|
||||
"disposable_postgresql": "passed",
|
||||
"database_removed": true
|
||||
},
|
||||
"react_inertia_tests": 21,
|
||||
"phpstan_errors": 0,
|
||||
"typescript": "passed",
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"runtime_sha256_files": 18,
|
||||
"installation_proof": "people-hiring-install.json",
|
||||
"menu_proof": "menu-links.json",
|
||||
"menu_contract_entries_by_role": 108,
|
||||
"company_accounts_or_grants_created": false,
|
||||
"evaluation_tables_initially_empty": true,
|
||||
"external_messages_sent": false,
|
||||
"signed_contracts_created": false,
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"source_sha256": {
|
||||
"app/Services/PeopleHiring.php": "a7e50e55034ce4085d35184f04d8549741c7fb29e4585dc334dd3bf9525a3d09",
|
||||
"app/Http/Controllers/PeopleHiringController.php": "4e07d58c9f474eec85f5854e9e442978f198888994c27be376f100a0d4e2263c",
|
||||
"app/Models/PeopleProcess.php": "10b1ed29426ab6988d01f1ed43b77f49b689520197c7a9023b41338fe60e7e4b",
|
||||
"app/Services/OrganizationAccess.php": "84f3cddc43b1446f12390d9d3ffcf504f4a1f8909d88612ee30a9fdcb1bd7137",
|
||||
"database/migrations/2026_10_30_000030_create_people_hiring.php": "c1f69354ec82ec5b3aabd0eff85c88c4a8a9eb3cb77c16e9a942d47eb1149c1b",
|
||||
"resources/js/pages/people/index.tsx": "8d65c71d2c5862d640f14a37e7aea997bdfa6aacd7d128568532cbf5821295fd",
|
||||
"resources/js/pages/people/show.tsx": "ffa77f0bb5e72877f7aab6fb30ae129cd7dd7c72911c0ef156a958f821451dc0",
|
||||
"resources/js/lib/people-hiring.ts": "65efd515d39541c7e875127c91731bfc1a379f8db58fcd34e0f39e1a8d75f807",
|
||||
"resources/js/lib/procurement-navigation.ts": "9578b8ac69b8b8a8e6cb0f7134e3cac55e19c1e973ecb21015b592fc8b804318",
|
||||
"tests/Feature/PeopleHiringTest.php": "f1cceadb0ddf7faaf901dd2283953812dad5fb9ebc4f1f3c4f57e546f4591abc",
|
||||
"tests/frontend/people-hiring.test.tsx": "5548b79b353bc36451b42888f7f7727f2524d46df677e106a60968c8e6039dc9"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T13:01:28.499029+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"authorised filtered operational reading",
|
||||
"calendar delays and oldest five source links",
|
||||
"separate overdue occurrence and process counts",
|
||||
"personal task distribution and independent approval counts",
|
||||
"explicit missing deadlines and deterministic method",
|
||||
"documented AI ML product roadmap"
|
||||
],
|
||||
"method": "operational_facts_v1",
|
||||
"module_tests": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 32,
|
||||
"passed": 32,
|
||||
"assertions": 856,
|
||||
"duration_ms": 10518
|
||||
},
|
||||
"react_inertia_tests": 26,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"accepted_full_suite_reused": "people-clarifications.json",
|
||||
"http_proof": "menu-links.json",
|
||||
"new_migrations": 0,
|
||||
"new_data_writes": 0,
|
||||
"external_AI_calls": 0,
|
||||
"trained_ml_model": false,
|
||||
"AI_provider_configured": false,
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"source_sha256": {
|
||||
"app/Services/PeopleOperationalInsights.php": "fab92b0b0c58fe608a7e030cbfaa2e36842cf391a609fd05bb6f4b443be1ede6",
|
||||
"app/Http/Controllers/PeopleHiringController.php": "5cf48435c3ba22505544dbaecd368d7abf27bb97edf55158308507db4c36f767",
|
||||
"resources/js/components/people-operational-insights.tsx": "fce6a71ecc15979749437945639aed6b4ed3bcbfedcee82d50d95a9469ca4300",
|
||||
"resources/js/pages/people/index.tsx": "bf0753393b9d5cc851be7dccadc27fb3421c0bc1a5215b6af779e1a114034bb9",
|
||||
"tests/Feature/PeopleHiringTest.php": "01dacd9895e10391732f65372a7a8872576527e4d13b8a4ae8f612c2485fc958",
|
||||
"tests/frontend/people-hiring.test.tsx": "b1fce7ede76258ee62a686e487e2bcce666b43b9595619d4da1a73d6c9d42d71",
|
||||
"docs/inteligencia-sgc.md": "51e5ecf9fa8d54a54272fa263b4202f69873145572a9ea7892a5603c37c334cd"
|
||||
}
|
||||
"checked_at": "2026-10-30T13:01:28.499029+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"authorised filtered operational reading",
|
||||
"calendar delays and oldest five source links",
|
||||
"separate overdue occurrence and process counts",
|
||||
"personal task distribution and independent approval counts",
|
||||
"explicit missing deadlines and deterministic method",
|
||||
"documented AI ML product roadmap"
|
||||
],
|
||||
"method": "operational_facts_v1",
|
||||
"module_tests": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 32,
|
||||
"passed": 32,
|
||||
"assertions": 856,
|
||||
"duration_ms": 10518
|
||||
},
|
||||
"react_inertia_tests": 26,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"accepted_full_suite_reused": "people-clarifications.json",
|
||||
"http_proof": "menu-links.json",
|
||||
"new_migrations": 0,
|
||||
"new_data_writes": 0,
|
||||
"external_AI_calls": 0,
|
||||
"trained_ml_model": false,
|
||||
"AI_provider_configured": false,
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"source_sha256": {
|
||||
"app/Services/PeopleOperationalInsights.php": "fab92b0b0c58fe608a7e030cbfaa2e36842cf391a609fd05bb6f4b443be1ede6",
|
||||
"app/Http/Controllers/PeopleHiringController.php": "5cf48435c3ba22505544dbaecd368d7abf27bb97edf55158308507db4c36f767",
|
||||
"resources/js/components/people-operational-insights.tsx": "fce6a71ecc15979749437945639aed6b4ed3bcbfedcee82d50d95a9469ca4300",
|
||||
"resources/js/pages/people/index.tsx": "bf0753393b9d5cc851be7dccadc27fb3421c0bc1a5215b6af779e1a114034bb9",
|
||||
"tests/Feature/PeopleHiringTest.php": "01dacd9895e10391732f65372a7a8872576527e4d13b8a4ae8f612c2485fc958",
|
||||
"tests/frontend/people-hiring.test.tsx": "b1fce7ede76258ee62a686e487e2bcce666b43b9595619d4da1a73d6c9d42d71",
|
||||
"docs/inteligencia-sgc.md": "51e5ecf9fa8d54a54272fa263b4202f69873145572a9ea7892a5603c37c334cd"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,7 @@
|
||||
"database": "disposable PostgreSQL",
|
||||
"application_role_reused_without_privilege_change": true,
|
||||
"result": "passed",
|
||||
"test_files": [
|
||||
"PeopleHiringTest",
|
||||
"PeopleRegistryTest"
|
||||
],
|
||||
"test_files": ["PeopleHiringTest", "PeopleRegistryTest"],
|
||||
"original_evaluation_untouched": true,
|
||||
"database_removed": true
|
||||
}
|
||||
|
||||
@@ -1,77 +1,77 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T16:08:01.496023+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"reusable person identity and profile registry",
|
||||
"private creator scope and explicit shared read/manage permissions",
|
||||
"save first profile during reception or select authorised existing record",
|
||||
"immutable source snapshot per candidature and historical source version display",
|
||||
"stale or archived registry selection rejected",
|
||||
"same person unique within process and names never merged automatically",
|
||||
"atomic registry candidature process and audit rollback",
|
||||
"cross-company application/registry composite foreign key",
|
||||
"legacy applications preserved unlinked"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 698,
|
||||
"passed": 696,
|
||||
"assertions": 6739,
|
||||
"duration_ms": 103510,
|
||||
"skipped": 2
|
||||
},
|
||||
"module_tests": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 43,
|
||||
"passed": 43,
|
||||
"assertions": 1124,
|
||||
"duration_ms": 7925
|
||||
},
|
||||
"disposable_postgresql": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 43,
|
||||
"passed": 43,
|
||||
"assertions": 1124,
|
||||
"duration_ms": 24156
|
||||
},
|
||||
"postgresql_proof": "people-registry-pg.json",
|
||||
"installation_proof": "people-registry-install.json",
|
||||
"react_inertia_tests": 44,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"menu_code_proof": "menu-code-contract.json",
|
||||
"menu_entries_across_six_roles": 111,
|
||||
"http_proof": "menu-links.json",
|
||||
"runtime_sha256_files": 18,
|
||||
"legacy_records_auto_linked": false,
|
||||
"people_created_in_evaluation": 0,
|
||||
"accounts_or_grants_changed": false,
|
||||
"external_AI_calls": 0,
|
||||
"global_enter_once_requirement_complete": false,
|
||||
"remaining_coverage_matrix": "../reutilizacao-dados.md",
|
||||
"visual_review": false,
|
||||
"domain_installed": false,
|
||||
"source_sha256": {
|
||||
"database/migrations/2026_10_30_000034_create_people_registry.php": "f5869f09cad06b2ded831bce66da4b1f923c001acd56ac8dbfda53d3d79bcc8c",
|
||||
"app/Services/PeopleRegistry.php": "cbef69cbc127b61731bf44f246201a77f1c78b2447e1d11601635260e066d89c",
|
||||
"app/Services/PeopleHiring.php": "6b6cf58a022fe1fe9da2f40177557735d0e4112c946fde7e0c9b5eae32b78514",
|
||||
"app/Services/OrganizationAccess.php": "76809e2f24003a2a1343cae317f47b972544adb231882ed56c6c8484dbaa4ea1",
|
||||
"app/Http/Controllers/PeopleRegistryController.php": "e702275161464a9835c02f817ebf5215a1c6b3f4c875a77d717fda7c490b0db0",
|
||||
"app/Http/Controllers/PeopleHiringController.php": "f0d82d3d8bff9fe46abc93bfb4faacb6e6a8d49079c6ca8499fbe479db68aa18",
|
||||
"resources/js/pages/people/registry.tsx": "3b1249c3425ac1173f50031db99dc979ea875d5ea33b886dc84d003ba1011e92",
|
||||
"resources/js/components/people-registry-choice.tsx": "9a2a61518b54cbbc30e45126f8fbae76663f0ac5d4c1b7fc010c3c141df00102",
|
||||
"resources/js/pages/people/show.tsx": "9aaf142675ae73b2a9857a56d91121073bb1180bf7b01fec8fe657f0fbc205e1",
|
||||
"resources/js/lib/procurement-navigation.ts": "242ebdea74d4d26eca32b716f88f7c9608bca590abefee56ab282010820afe2e",
|
||||
"routes/web.php": "7097d8299c17ad3912e3fe3d35370d63f38365410325319eb4c571ebf5cc6fb3",
|
||||
"tests/Feature/PeopleRegistryTest.php": "7b3cefce0fcf25aa910d6ffd138af00ee506d693dbb673e334a70298054a480b",
|
||||
"tests/frontend/people-registry.test.tsx": "02e6915167cfdd66121ef3768fd55dd95b46bf169fda791e6e68eaf7f974ef21",
|
||||
"scripts/menu-code-check.mjs": "40063716dee602fd57edd82fed90cc4c69cd315174065f067cfb377877669d9f",
|
||||
"docs/reutilizacao-dados.md": "937620871fa9504b889e81b727468156a5f2cdeb17408e76389478bd10db9405"
|
||||
}
|
||||
"checked_at": "2026-10-30T16:08:01.496023+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"reusable person identity and profile registry",
|
||||
"private creator scope and explicit shared read/manage permissions",
|
||||
"save first profile during reception or select authorised existing record",
|
||||
"immutable source snapshot per candidature and historical source version display",
|
||||
"stale or archived registry selection rejected",
|
||||
"same person unique within process and names never merged automatically",
|
||||
"atomic registry candidature process and audit rollback",
|
||||
"cross-company application/registry composite foreign key",
|
||||
"legacy applications preserved unlinked"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 698,
|
||||
"passed": 696,
|
||||
"assertions": 6739,
|
||||
"duration_ms": 103510,
|
||||
"skipped": 2
|
||||
},
|
||||
"module_tests": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 43,
|
||||
"passed": 43,
|
||||
"assertions": 1124,
|
||||
"duration_ms": 7925
|
||||
},
|
||||
"disposable_postgresql": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 43,
|
||||
"passed": 43,
|
||||
"assertions": 1124,
|
||||
"duration_ms": 24156
|
||||
},
|
||||
"postgresql_proof": "people-registry-pg.json",
|
||||
"installation_proof": "people-registry-install.json",
|
||||
"react_inertia_tests": 44,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"menu_code_proof": "menu-code-contract.json",
|
||||
"menu_entries_across_six_roles": 111,
|
||||
"http_proof": "menu-links.json",
|
||||
"runtime_sha256_files": 18,
|
||||
"legacy_records_auto_linked": false,
|
||||
"people_created_in_evaluation": 0,
|
||||
"accounts_or_grants_changed": false,
|
||||
"external_AI_calls": 0,
|
||||
"global_enter_once_requirement_complete": false,
|
||||
"remaining_coverage_matrix": "../reutilizacao-dados.md",
|
||||
"visual_review": false,
|
||||
"domain_installed": false,
|
||||
"source_sha256": {
|
||||
"database/migrations/2026_10_30_000034_create_people_registry.php": "f5869f09cad06b2ded831bce66da4b1f923c001acd56ac8dbfda53d3d79bcc8c",
|
||||
"app/Services/PeopleRegistry.php": "cbef69cbc127b61731bf44f246201a77f1c78b2447e1d11601635260e066d89c",
|
||||
"app/Services/PeopleHiring.php": "6b6cf58a022fe1fe9da2f40177557735d0e4112c946fde7e0c9b5eae32b78514",
|
||||
"app/Services/OrganizationAccess.php": "76809e2f24003a2a1343cae317f47b972544adb231882ed56c6c8484dbaa4ea1",
|
||||
"app/Http/Controllers/PeopleRegistryController.php": "e702275161464a9835c02f817ebf5215a1c6b3f4c875a77d717fda7c490b0db0",
|
||||
"app/Http/Controllers/PeopleHiringController.php": "f0d82d3d8bff9fe46abc93bfb4faacb6e6a8d49079c6ca8499fbe479db68aa18",
|
||||
"resources/js/pages/people/registry.tsx": "3b1249c3425ac1173f50031db99dc979ea875d5ea33b886dc84d003ba1011e92",
|
||||
"resources/js/components/people-registry-choice.tsx": "9a2a61518b54cbbc30e45126f8fbae76663f0ac5d4c1b7fc010c3c141df00102",
|
||||
"resources/js/pages/people/show.tsx": "9aaf142675ae73b2a9857a56d91121073bb1180bf7b01fec8fe657f0fbc205e1",
|
||||
"resources/js/lib/procurement-navigation.ts": "242ebdea74d4d26eca32b716f88f7c9608bca590abefee56ab282010820afe2e",
|
||||
"routes/web.php": "7097d8299c17ad3912e3fe3d35370d63f38365410325319eb4c571ebf5cc6fb3",
|
||||
"tests/Feature/PeopleRegistryTest.php": "7b3cefce0fcf25aa910d6ffd138af00ee506d693dbb673e334a70298054a480b",
|
||||
"tests/frontend/people-registry.test.tsx": "02e6915167cfdd66121ef3768fd55dd95b46bf169fda791e6e68eaf7f974ef21",
|
||||
"scripts/menu-code-check.mjs": "40063716dee602fd57edd82fed90cc4c69cd315174065f067cfb377877669d9f",
|
||||
"docs/reutilizacao-dados.md": "937620871fa9504b889e81b727468156a5f2cdeb17408e76389478bd10db9405"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
"database": "disposable PostgreSQL",
|
||||
"application_role_reused_without_privilege_change": true,
|
||||
"result": "passed",
|
||||
"test_files": [
|
||||
"PeopleHiringTest"
|
||||
],
|
||||
"test_files": ["PeopleHiringTest"],
|
||||
"original_evaluation_untouched": true,
|
||||
"database_removed": true
|
||||
}
|
||||
|
||||
@@ -1,52 +1,52 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T12:28:22.580872+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"personal operational task queue",
|
||||
"separate independent formal approval queue",
|
||||
"original process links and candidate selection hint",
|
||||
"permission scoped counts filters and reception deadline alerts",
|
||||
"complete filtered totals beyond pagination",
|
||||
"minimal list projection excluding rules financial data interviews selection and contract",
|
||||
"PostgreSQL repeatable read read-only normal index request"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 643,
|
||||
"passed": 641,
|
||||
"assertions": 5859,
|
||||
"duration_ms": 135280,
|
||||
"skipped": 2
|
||||
},
|
||||
"latest_module_tests": {
|
||||
"tests": 21,
|
||||
"assertions": 562,
|
||||
"sqlite": "passed",
|
||||
"disposable_postgresql": "passed",
|
||||
"database_removed": true
|
||||
},
|
||||
"react_inertia_tests": 23,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"runtime_sha256_files": 18,
|
||||
"http_proof": "menu-links.json",
|
||||
"new_tables": 0,
|
||||
"migrations_applied": false,
|
||||
"new_assignments_created": false,
|
||||
"background_decisions_created": false,
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"source_sha256": {
|
||||
"app/Services/PeopleWorkdesk.php": "b840eb70a94cbfc3c02869602097a15cfd9fed0333d4d871dd5240ed446b5a8c",
|
||||
"app/Services/PeopleHiring.php": "c13a0c029496f9e0ab988265501bacfbf5562d70a7176afac2836ad28d4aecd8",
|
||||
"app/Http/Controllers/PeopleHiringController.php": "a207f268ed65c6da6c75d325142f0df81316b2e1242c3e9745c0ab4b116cf43e",
|
||||
"resources/js/pages/people/index.tsx": "ed95ed6a64cf8532ed101cc597423c240e699b243083acc22892c65a2875cac2",
|
||||
"resources/js/pages/people/show.tsx": "67e8d2f3bcd9e85ba52a6f29e285f5572764e65c591a7167f1d4f7f29629a5ab",
|
||||
"tests/Feature/PeopleHiringTest.php": "82a3a2b2351b233a4a40c5f8fb2babb247d80fa6454974fe61bc6749ad444567",
|
||||
"tests/frontend/people-hiring.test.tsx": "5f7bfbfa873a66bc6681c746e196bc89a769e929d67293ab8f5465ee21f6dde4"
|
||||
}
|
||||
"checked_at": "2026-10-30T12:28:22.580872+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"scope": [
|
||||
"personal operational task queue",
|
||||
"separate independent formal approval queue",
|
||||
"original process links and candidate selection hint",
|
||||
"permission scoped counts filters and reception deadline alerts",
|
||||
"complete filtered totals beyond pagination",
|
||||
"minimal list projection excluding rules financial data interviews selection and contract",
|
||||
"PostgreSQL repeatable read read-only normal index request"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 643,
|
||||
"passed": 641,
|
||||
"assertions": 5859,
|
||||
"duration_ms": 135280,
|
||||
"skipped": 2
|
||||
},
|
||||
"latest_module_tests": {
|
||||
"tests": 21,
|
||||
"assertions": 562,
|
||||
"sqlite": "passed",
|
||||
"disposable_postgresql": "passed",
|
||||
"database_removed": true
|
||||
},
|
||||
"react_inertia_tests": 23,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"runtime_sha256_files": 18,
|
||||
"http_proof": "menu-links.json",
|
||||
"new_tables": 0,
|
||||
"migrations_applied": false,
|
||||
"new_assignments_created": false,
|
||||
"background_decisions_created": false,
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"source_sha256": {
|
||||
"app/Services/PeopleWorkdesk.php": "b840eb70a94cbfc3c02869602097a15cfd9fed0333d4d871dd5240ed446b5a8c",
|
||||
"app/Services/PeopleHiring.php": "c13a0c029496f9e0ab988265501bacfbf5562d70a7176afac2836ad28d4aecd8",
|
||||
"app/Http/Controllers/PeopleHiringController.php": "a207f268ed65c6da6c75d325142f0df81316b2e1242c3e9745c0ab4b116cf43e",
|
||||
"resources/js/pages/people/index.tsx": "ed95ed6a64cf8532ed101cc597423c240e699b243083acc22892c65a2875cac2",
|
||||
"resources/js/pages/people/show.tsx": "67e8d2f3bcd9e85ba52a6f29e285f5572764e65c591a7167f1d4f7f29629a5ab",
|
||||
"tests/Feature/PeopleHiringTest.php": "82a3a2b2351b233a4a40c5f8fb2babb247d80fa6454974fe61bc6749ad444567",
|
||||
"tests/frontend/people-hiring.test.tsx": "5f7bfbfa873a66bc6681c746e196bc89a769e929d67293ab8f5465ee21f6dde4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +1,56 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T14:40:19.164501+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"method": "procurement_facts_v1",
|
||||
"scope": [
|
||||
"authorised filtered report across all pages",
|
||||
"pending submission calendar age and unknown age",
|
||||
"need date distinguished from supplier delivery deadline",
|
||||
"approved requests without registered quotations or visible orders",
|
||||
"source links and shared indicator definitions"
|
||||
],
|
||||
"module_tests": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 9,
|
||||
"passed": 9,
|
||||
"assertions": 197,
|
||||
"duration_ms": 5247
|
||||
},
|
||||
"postgresql_integration": {
|
||||
"tool": "phpunit",
|
||||
"result": "passed",
|
||||
"tests": 1,
|
||||
"passed": 1,
|
||||
"assertions": 36,
|
||||
"duration_ms": 2364
|
||||
},
|
||||
"postgresql_note": "Standalone PHPUnit scenario without RefreshDatabase outer transaction; report transaction guard preserved. Disposable database removed.",
|
||||
"postgresql_proof": "procurement-intelligence-pg.json",
|
||||
"react_inertia_tests": 28,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"http_proof": "menu-links.json",
|
||||
"prior_full_suite_reused": "people-clarifications.json",
|
||||
"migrations_installed": false,
|
||||
"business_data_changed": false,
|
||||
"csv_contract_changed": false,
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"ml_model_active": false,
|
||||
"external_AI_calls": 0,
|
||||
"source_sha256": {
|
||||
"app/Services/ProcurementOperationalInsights.php": "ddd1eab9214fd8bd029b6a3de2a6999af703d32eeed639419b93527cd9a92930",
|
||||
"app/Http/Controllers/ProcurementReportController.php": "96c33da41d00de5a6263ce1cb474e8ded64a28a1bdc9688659dbd82f6f37f992",
|
||||
"app/Models/Requisition.php": "e915153723afa5dc3e7c0642502800cd3b7d9df3e3109809adcd43c122e803d6",
|
||||
"resources/js/components/procurement-operational-insights.tsx": "beed70921e0287830aa3ae9126a5ef6b7f4a634c986a5e4a5f5be8a09b3e96fa",
|
||||
"resources/js/pages/reports/procurement.tsx": "11af82a74fc516bf5e1c47741b7c081d0b63c2196c6ac148e45df2da29f03f7a",
|
||||
"tests/Feature/ProcurementReportsTest.php": "e51ad3f19533c0b0e974ccd2e8138755eeadf0d65f81ba7c2a3eb434c1f9fb9d",
|
||||
"tests/frontend/procurement-insights.test.tsx": "182639a220ea4b8bb09195a9623b03f17cc32a588a4c8619648595bc592a89f4",
|
||||
"docs/indicadores-sgc.md": "83a3c486e77622da635574065d5f4147f60a6558b009fb1ab1ad43944c433c11",
|
||||
"docs/inteligencia-sgc.md": "360e21b342c26d31e543ca8ce5dbbe0d7e155f57e0be8d4b214599d4c9f4ddac"
|
||||
}
|
||||
"checked_at": "2026-10-30T14:40:19.164501+00:00",
|
||||
"environment": "local evaluation 8005",
|
||||
"method": "procurement_facts_v1",
|
||||
"scope": [
|
||||
"authorised filtered report across all pages",
|
||||
"pending submission calendar age and unknown age",
|
||||
"need date distinguished from supplier delivery deadline",
|
||||
"approved requests without registered quotations or visible orders",
|
||||
"source links and shared indicator definitions"
|
||||
],
|
||||
"module_tests": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 9,
|
||||
"passed": 9,
|
||||
"assertions": 197,
|
||||
"duration_ms": 5247
|
||||
},
|
||||
"postgresql_integration": {
|
||||
"tool": "phpunit",
|
||||
"result": "passed",
|
||||
"tests": 1,
|
||||
"passed": 1,
|
||||
"assertions": 36,
|
||||
"duration_ms": 2364
|
||||
},
|
||||
"postgresql_note": "Standalone PHPUnit scenario without RefreshDatabase outer transaction; report transaction guard preserved. Disposable database removed.",
|
||||
"postgresql_proof": "procurement-intelligence-pg.json",
|
||||
"react_inertia_tests": 28,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"http_proof": "menu-links.json",
|
||||
"prior_full_suite_reused": "people-clarifications.json",
|
||||
"migrations_installed": false,
|
||||
"business_data_changed": false,
|
||||
"csv_contract_changed": false,
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"ml_model_active": false,
|
||||
"external_AI_calls": 0,
|
||||
"source_sha256": {
|
||||
"app/Services/ProcurementOperationalInsights.php": "ddd1eab9214fd8bd029b6a3de2a6999af703d32eeed639419b93527cd9a92930",
|
||||
"app/Http/Controllers/ProcurementReportController.php": "96c33da41d00de5a6263ce1cb474e8ded64a28a1bdc9688659dbd82f6f37f992",
|
||||
"app/Models/Requisition.php": "e915153723afa5dc3e7c0642502800cd3b7d9df3e3109809adcd43c122e803d6",
|
||||
"resources/js/components/procurement-operational-insights.tsx": "beed70921e0287830aa3ae9126a5ef6b7f4a634c986a5e4a5f5be8a09b3e96fa",
|
||||
"resources/js/pages/reports/procurement.tsx": "11af82a74fc516bf5e1c47741b7c081d0b63c2196c6ac148e45df2da29f03f7a",
|
||||
"tests/Feature/ProcurementReportsTest.php": "e51ad3f19533c0b0e974ccd2e8138755eeadf0d65f81ba7c2a3eb434c1f9fb9d",
|
||||
"tests/frontend/procurement-insights.test.tsx": "182639a220ea4b8bb09195a9623b03f17cc32a588a4c8619648595bc592a89f4",
|
||||
"docs/indicadores-sgc.md": "83a3c486e77622da635574065d5f4147f60a6558b009fb1ab1ad43944c433c11",
|
||||
"docs/inteligencia-sgc.md": "360e21b342c26d31e543ca8ce5dbbe0d7e155f57e0be8d4b214599d4c9f4ddac"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,50 @@
|
||||
{
|
||||
"date": "2026-10-30",
|
||||
"base_commit": "0235c7609ed52345e91ae1a6b268e45e1eb991af",
|
||||
"scope": "Menu routing inventory, central filtered procurement reports/CSV and existing individual report extended to receipt/invoice/external-payment records.",
|
||||
"checks": {
|
||||
"feature_passed": 14,
|
||||
"assertions": 223,
|
||||
"new_cases": 8,
|
||||
"existing_report_regressions": 6,
|
||||
"export_fixture_records": 207,
|
||||
"menu_entries_across_roles": 102,
|
||||
"roles": 6,
|
||||
"types": "passed",
|
||||
"lint": "passed",
|
||||
"format": "passed",
|
||||
"pint": "passed",
|
||||
"phpstan": "passed after correcting the documented date cast type",
|
||||
"build": "passed",
|
||||
"new_module_http": 200,
|
||||
"evaluation_entry_http": 200,
|
||||
"runtime_manifest_files": 18
|
||||
},
|
||||
"source_sha256": {
|
||||
"app/Http/Controllers/ProcurementReportController.php": "5161063f8f9684595c83300f22e6516e03700917e3b578651dee7254ee5f6a55",
|
||||
"app/Services/ProcurementReviewReport.php": "1d5a41b59c471ad1db5e7f39eb15ac4b080239968341ccdf9a78ce17a301144c",
|
||||
"app/Models/GoodsReceipt.php": "ec9b86ca625a8d4d8972ba1d91636bf37475f680f2212faa1e31c44709660a5a",
|
||||
"routes/web.php": "1014ea42f0544428df49304c16cc55fb0632188ac0ad1940ba6a637b9f228be0",
|
||||
"resources/js/lib/procurement-navigation.ts": "f250199564cd56e73ff5d5851a19dcbbe41df6da7b5c2cb9024c0965b8e087dc",
|
||||
"resources/js/pages/reports/procurement.tsx": "a4ec4c51a8b7b8012852f1ad1d36ac931fec25c41a383c10a9449fddc3bd23d5",
|
||||
"resources/js/pages/purchases/review-report.tsx": "8a5a02d99b5dd5d6f50a41e6e76711b15e578fd09dc039e2b6defc64d20abe04",
|
||||
"tests/Feature/ProcurementReportsTest.php": "fc88ceef66d419ef72e9231e8273da20f2e1f400ad9610ad5ff85cadeb2259b2",
|
||||
"scripts/menu-code-check.mjs": "8480ec5b1f3fd26aa7ff7cbc52c304a632b11e0eaa5a4cacadab0e119ceac236"
|
||||
},
|
||||
"menu_proof": "docs/proofs/menu-code-contract.json",
|
||||
"build_log_local": "storage/logs/development-reports-build.log",
|
||||
"build_log_sha256": "2709a9c383c3110469d6aa08a1efc8b821e24c3b7b8f9a5d3552e403ea9f5ad6",
|
||||
"preservation": {
|
||||
"migrations": 0,
|
||||
"active_business_writes": 0,
|
||||
"new_accounts_or_grants": 0,
|
||||
"balance_or_tax_recalculation": false,
|
||||
"remote_deployment": false
|
||||
},
|
||||
"limits": [
|
||||
"Visual and manual printing not executed.",
|
||||
"PostgreSQL snapshot clause implemented using the existing report pattern; new tests used isolated SQLite, not a PostgreSQL concurrency proof.",
|
||||
"Supplier evaluations remain an interface proposal pending its operational implementation.",
|
||||
"Not a consolidated debt/bank/accounting report; common settlements keep their own history."
|
||||
]
|
||||
"date": "2026-10-30",
|
||||
"base_commit": "0235c7609ed52345e91ae1a6b268e45e1eb991af",
|
||||
"scope": "Menu routing inventory, central filtered procurement reports/CSV and existing individual report extended to receipt/invoice/external-payment records.",
|
||||
"checks": {
|
||||
"feature_passed": 14,
|
||||
"assertions": 223,
|
||||
"new_cases": 8,
|
||||
"existing_report_regressions": 6,
|
||||
"export_fixture_records": 207,
|
||||
"menu_entries_across_roles": 102,
|
||||
"roles": 6,
|
||||
"types": "passed",
|
||||
"lint": "passed",
|
||||
"format": "passed",
|
||||
"pint": "passed",
|
||||
"phpstan": "passed after correcting the documented date cast type",
|
||||
"build": "passed",
|
||||
"new_module_http": 200,
|
||||
"evaluation_entry_http": 200,
|
||||
"runtime_manifest_files": 18
|
||||
},
|
||||
"source_sha256": {
|
||||
"app/Http/Controllers/ProcurementReportController.php": "5161063f8f9684595c83300f22e6516e03700917e3b578651dee7254ee5f6a55",
|
||||
"app/Services/ProcurementReviewReport.php": "1d5a41b59c471ad1db5e7f39eb15ac4b080239968341ccdf9a78ce17a301144c",
|
||||
"app/Models/GoodsReceipt.php": "ec9b86ca625a8d4d8972ba1d91636bf37475f680f2212faa1e31c44709660a5a",
|
||||
"routes/web.php": "1014ea42f0544428df49304c16cc55fb0632188ac0ad1940ba6a637b9f228be0",
|
||||
"resources/js/lib/procurement-navigation.ts": "f250199564cd56e73ff5d5851a19dcbbe41df6da7b5c2cb9024c0965b8e087dc",
|
||||
"resources/js/pages/reports/procurement.tsx": "a4ec4c51a8b7b8012852f1ad1d36ac931fec25c41a383c10a9449fddc3bd23d5",
|
||||
"resources/js/pages/purchases/review-report.tsx": "8a5a02d99b5dd5d6f50a41e6e76711b15e578fd09dc039e2b6defc64d20abe04",
|
||||
"tests/Feature/ProcurementReportsTest.php": "fc88ceef66d419ef72e9231e8273da20f2e1f400ad9610ad5ff85cadeb2259b2",
|
||||
"scripts/menu-code-check.mjs": "8480ec5b1f3fd26aa7ff7cbc52c304a632b11e0eaa5a4cacadab0e119ceac236"
|
||||
},
|
||||
"menu_proof": "docs/proofs/menu-code-contract.json",
|
||||
"build_log_local": "storage/logs/development-reports-build.log",
|
||||
"build_log_sha256": "2709a9c383c3110469d6aa08a1efc8b821e24c3b7b8f9a5d3552e403ea9f5ad6",
|
||||
"preservation": {
|
||||
"migrations": 0,
|
||||
"active_business_writes": 0,
|
||||
"new_accounts_or_grants": 0,
|
||||
"balance_or_tax_recalculation": false,
|
||||
"remote_deployment": false
|
||||
},
|
||||
"limits": [
|
||||
"Visual and manual printing not executed.",
|
||||
"PostgreSQL snapshot clause implemented using the existing report pattern; new tests used isolated SQLite, not a PostgreSQL concurrency proof.",
|
||||
"Supplier evaluations remain an interface proposal pending its operational implementation.",
|
||||
"Not a consolidated debt/bank/accounting report; common settlements keep their own history."
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,10 +3,7 @@
|
||||
"database": "disposable PostgreSQL",
|
||||
"application_role_reused_without_privilege_change": true,
|
||||
"result": "passed",
|
||||
"test_files": [
|
||||
"RequisitionCatalogueTest",
|
||||
"ReceiptStockTest"
|
||||
],
|
||||
"test_files": ["RequisitionCatalogueTest", "ReceiptStockTest"],
|
||||
"original_evaluation_untouched": true,
|
||||
"database_removed": true
|
||||
}
|
||||
|
||||
@@ -1,52 +1,52 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T16:26:29.650769+00:00",
|
||||
"scope": [
|
||||
"published company article selection in requisition",
|
||||
"server-authoritative descriptors",
|
||||
"frozen source and version through order",
|
||||
"receipt stock identification and rejection of other stock",
|
||||
"legacy rows preserved without automatic linking"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 703,
|
||||
"passed": 701,
|
||||
"assertions": 6828,
|
||||
"duration_ms": 163777,
|
||||
"skipped": 2
|
||||
},
|
||||
"postgresql_tests": 35,
|
||||
"postgresql_assertions": 252,
|
||||
"react_inertia_tests": 47,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"menu_entries_across_six_roles": 111,
|
||||
"http_proof": "menu-links.json",
|
||||
"runtime_sha256_files": 18,
|
||||
"installation_proof": "requisition-catalogue-install.json",
|
||||
"postgresql_proof": "requisition-catalogue-pg.json",
|
||||
"visual_review": false,
|
||||
"domain_installed": false,
|
||||
"external_AI_calls": 0,
|
||||
"global_enter_once_requirement_complete": false,
|
||||
"remaining_coverage_matrix": "../reutilizacao-dados.md",
|
||||
"source_sha256": {
|
||||
"database/migrations/2026_10_30_000035_create_requisition_catalogue.php": "8899a8f118a3fed3ef2302f82cb75008445ebfdd5bf08f7ef3b3f167bdd9fdb6",
|
||||
"app/Services/RequisitionCatalogue.php": "ddf8079125a0c6ce2a898780ac82c8eb8f0f335e46f499c10f8c04b6a74b879e",
|
||||
"app/Services/RequisitionWorkflow.php": "8d35c229c22bee4ccb2bbde1b204c35b2e61231ae05ce850f1dec3f6c874dd56",
|
||||
"app/Services/PurchaseWorkflow.php": "09cfdb70966b5067b3cfab0a7b8e87d83307ef89e566bf700a38048647b0ac11",
|
||||
"app/Services/ReceiptWorkflow.php": "736c13dce0812e5c4b0b949cbdd38a6988de6a0651566876935379f8f4b673d6",
|
||||
"app/Http/Controllers/RequisitionCatalogueController.php": "86540cba2c4f5219e56a254fa5278ff4421126f8b6c78cc3e92b69800603e422",
|
||||
"app/Http/Requests/StoreRequisitionRequest.php": "83685bf281027d36cc78734078bb567a3808b7c313efe775df205a78e521851e",
|
||||
"resources/js/pages/requisitions/form.tsx": "a8df407caa3aa859f34dfd18820a25b9c7e36ddf15342ff3e13ff409bfd4b070",
|
||||
"resources/js/pages/stock/catalogue.tsx": "560f529f1c61dd1dd731c00f05e0d5a0f4f5147abc63916cafdacd406b4b7776",
|
||||
"resources/js/pages/receipts/order.tsx": "635c8759254bd559b7a0f84bf26a9eeade9062aeb0c847488bd1b9949617ad72",
|
||||
"tests/Feature/RequisitionCatalogueTest.php": "8bc598ff89e4c15bedb763f67891ee493e7a248ce387ff96b9355e9f6385fbf2",
|
||||
"tests/Feature/PurchaseAwardOrderTest.php": "914a8459cb98a049fb38c6e6e745953b3068499829162568f7f7accce71bb068",
|
||||
"tests/frontend/requisition-catalogue.test.tsx": "2dc95238e5b50a93ad4879f0ac5df161c9ced3227afe15a15e501e32e0314c13"
|
||||
}
|
||||
"checked_at": "2026-10-30T16:26:29.650769+00:00",
|
||||
"scope": [
|
||||
"published company article selection in requisition",
|
||||
"server-authoritative descriptors",
|
||||
"frozen source and version through order",
|
||||
"receipt stock identification and rejection of other stock",
|
||||
"legacy rows preserved without automatic linking"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 703,
|
||||
"passed": 701,
|
||||
"assertions": 6828,
|
||||
"duration_ms": 163777,
|
||||
"skipped": 2
|
||||
},
|
||||
"postgresql_tests": 35,
|
||||
"postgresql_assertions": 252,
|
||||
"react_inertia_tests": 47,
|
||||
"typescript": "passed",
|
||||
"phpstan_errors": 0,
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"menu_entries_across_six_roles": 111,
|
||||
"http_proof": "menu-links.json",
|
||||
"runtime_sha256_files": 18,
|
||||
"installation_proof": "requisition-catalogue-install.json",
|
||||
"postgresql_proof": "requisition-catalogue-pg.json",
|
||||
"visual_review": false,
|
||||
"domain_installed": false,
|
||||
"external_AI_calls": 0,
|
||||
"global_enter_once_requirement_complete": false,
|
||||
"remaining_coverage_matrix": "../reutilizacao-dados.md",
|
||||
"source_sha256": {
|
||||
"database/migrations/2026_10_30_000035_create_requisition_catalogue.php": "8899a8f118a3fed3ef2302f82cb75008445ebfdd5bf08f7ef3b3f167bdd9fdb6",
|
||||
"app/Services/RequisitionCatalogue.php": "ddf8079125a0c6ce2a898780ac82c8eb8f0f335e46f499c10f8c04b6a74b879e",
|
||||
"app/Services/RequisitionWorkflow.php": "8d35c229c22bee4ccb2bbde1b204c35b2e61231ae05ce850f1dec3f6c874dd56",
|
||||
"app/Services/PurchaseWorkflow.php": "09cfdb70966b5067b3cfab0a7b8e87d83307ef89e566bf700a38048647b0ac11",
|
||||
"app/Services/ReceiptWorkflow.php": "736c13dce0812e5c4b0b949cbdd38a6988de6a0651566876935379f8f4b673d6",
|
||||
"app/Http/Controllers/RequisitionCatalogueController.php": "86540cba2c4f5219e56a254fa5278ff4421126f8b6c78cc3e92b69800603e422",
|
||||
"app/Http/Requests/StoreRequisitionRequest.php": "83685bf281027d36cc78734078bb567a3808b7c313efe775df205a78e521851e",
|
||||
"resources/js/pages/requisitions/form.tsx": "a8df407caa3aa859f34dfd18820a25b9c7e36ddf15342ff3e13ff409bfd4b070",
|
||||
"resources/js/pages/stock/catalogue.tsx": "560f529f1c61dd1dd731c00f05e0d5a0f4f5147abc63916cafdacd406b4b7776",
|
||||
"resources/js/pages/receipts/order.tsx": "635c8759254bd559b7a0f84bf26a9eeade9062aeb0c847488bd1b9949617ad72",
|
||||
"tests/Feature/RequisitionCatalogueTest.php": "8bc598ff89e4c15bedb763f67891ee493e7a248ce387ff96b9355e9f6385fbf2",
|
||||
"tests/Feature/PurchaseAwardOrderTest.php": "914a8459cb98a049fb38c6e6e745953b3068499829162568f7f7accce71bb068",
|
||||
"tests/frontend/requisition-catalogue.test.tsx": "2dc95238e5b50a93ad4879f0ac5df161c9ced3227afe15a15e501e32e0314c13"
|
||||
}
|
||||
}
|
||||
|
||||
+27
-27
@@ -1,29 +1,29 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T11:53:42.906027+00:00",
|
||||
"name": "SGC - Sistema de Gestão de Contratações",
|
||||
"tagline": "Aquisições, serviços e pessoas",
|
||||
"signature": "Uma solução TEMPO",
|
||||
"acronym": "SGC",
|
||||
"php_tests": 35,
|
||||
"assertions": 228,
|
||||
"react_inertia_tests": 21,
|
||||
"typescript": "passed",
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"local_entry_http": "passed",
|
||||
"migrations_applied": false,
|
||||
"business_data_changed": false,
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"source_sha256": {
|
||||
"resources/js/components/sgc-workspace-shell.tsx": "4c6857d4e66bd5732af61c7ff52659fd38db7ea7805173f58398acb7a4a0f287",
|
||||
"resources/js/components/sgc-shell.tsx": "76e8059c5b5a9ae1e4351ee8efaa0e376394dc7133bc7524b5ee653009836687",
|
||||
"resources/js/pages/home.tsx": "719c1feb0ed0070545a98b00e012b09ef2a8dbd2877c94a0fbb0f0ba25548d87",
|
||||
"resources/js/pages/auth/login.tsx": "13256348dc44f9f19105c320782ba7f54ddea9e92bd95f2dabc38dc86958ffa6",
|
||||
"resources/js/layouts/auth/auth-simple-layout.tsx": "a08bee2476691273897ff1218f93c9da2e26a7d0e9b98558c3aa78a49f76edc4",
|
||||
"resources/views/app.blade.php": "d16385dff24a46c10efe39ee201ad5de9441eaa7dd88407540330c00894fac13",
|
||||
"resources/views/purchases/opening.blade.php": "7a630cf84e8189c65d3e24b477b5685a5411d9f1ca06da238c91c46a1a2d9592",
|
||||
"resources/views/purchases/recommendation-print.blade.php": "6854c841bf6132875e9994dd76598c47074ed8e6640e4983bd9fa9b8bb26edc8"
|
||||
}
|
||||
"checked_at": "2026-10-30T11:53:42.906027+00:00",
|
||||
"name": "SGC - Sistema de Gestão de Contratações",
|
||||
"tagline": "Aquisições, serviços e pessoas",
|
||||
"signature": "Uma solução TEMPO",
|
||||
"acronym": "SGC",
|
||||
"php_tests": 35,
|
||||
"assertions": 228,
|
||||
"react_inertia_tests": 21,
|
||||
"typescript": "passed",
|
||||
"targeted_lint": "passed",
|
||||
"pint": "passed",
|
||||
"build_exit": 0,
|
||||
"local_entry_http": "passed",
|
||||
"migrations_applied": false,
|
||||
"business_data_changed": false,
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"source_sha256": {
|
||||
"resources/js/components/sgc-workspace-shell.tsx": "4c6857d4e66bd5732af61c7ff52659fd38db7ea7805173f58398acb7a4a0f287",
|
||||
"resources/js/components/sgc-shell.tsx": "76e8059c5b5a9ae1e4351ee8efaa0e376394dc7133bc7524b5ee653009836687",
|
||||
"resources/js/pages/home.tsx": "719c1feb0ed0070545a98b00e012b09ef2a8dbd2877c94a0fbb0f0ba25548d87",
|
||||
"resources/js/pages/auth/login.tsx": "13256348dc44f9f19105c320782ba7f54ddea9e92bd95f2dabc38dc86958ffa6",
|
||||
"resources/js/layouts/auth/auth-simple-layout.tsx": "a08bee2476691273897ff1218f93c9da2e26a7d0e9b98558c3aa78a49f76edc4",
|
||||
"resources/views/app.blade.php": "d16385dff24a46c10efe39ee201ad5de9441eaa7dd88407540330c00894fac13",
|
||||
"resources/views/purchases/opening.blade.php": "7a630cf84e8189c65d3e24b477b5685a5411d9f1ca06da238c91c46a1a2d9592",
|
||||
"resources/views/purchases/recommendation-print.blade.php": "6854c841bf6132875e9994dd76598c47074ed8e6640e4983bd9fa9b8bb26edc8"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T16:47:29.726Z",
|
||||
"admin": 200,
|
||||
"warehouse": 403,
|
||||
"modalities": 6,
|
||||
"initial_saved_versions": 0,
|
||||
"availability_location": true,
|
||||
"external_portal_available": false
|
||||
"checked_at": "2026-10-30T16:47:29.726Z",
|
||||
"admin": 200,
|
||||
"warehouse": 403,
|
||||
"modalities": 6,
|
||||
"initial_saved_versions": 0,
|
||||
"availability_location": true,
|
||||
"external_portal_available": false
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T16:45:32+00:00",
|
||||
"database": "disposable PostgreSQL",
|
||||
"application_role_reused_without_privilege_change": true,
|
||||
"result": "passed",
|
||||
"test_files": [
|
||||
"SubmissionAnswersTest",
|
||||
"SubmissionFormsTest",
|
||||
"PeopleHiringTest"
|
||||
],
|
||||
"original_evaluation_untouched": true,
|
||||
"database_removed": true,
|
||||
"tests": 44,
|
||||
"assertions": 1088
|
||||
"checked_at": "2026-10-30T16:45:32+00:00",
|
||||
"database": "disposable PostgreSQL",
|
||||
"application_role_reused_without_privilege_change": true,
|
||||
"result": "passed",
|
||||
"test_files": [
|
||||
"SubmissionAnswersTest",
|
||||
"SubmissionFormsTest",
|
||||
"PeopleHiringTest"
|
||||
],
|
||||
"original_evaluation_untouched": true,
|
||||
"database_removed": true,
|
||||
"tests": 44,
|
||||
"assertions": 1088
|
||||
}
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T16:48:55.574914+00:00",
|
||||
"scope": [
|
||||
"six independently versioned company intake form configurations",
|
||||
"immutable append-only definitions and history",
|
||||
"stale update rejection and audit rollback",
|
||||
"frozen form definition at preparation of new people opportunities",
|
||||
"structured province district locality availability field and reusable validation"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 710,
|
||||
"passed": 708,
|
||||
"assertions": 6904,
|
||||
"duration_ms": 149093,
|
||||
"skipped": 2
|
||||
},
|
||||
"module_sqlite_tests": 44,
|
||||
"module_assertions": 1088,
|
||||
"disposable_postgresql_tests": 44,
|
||||
"disposable_postgresql_assertions": 1088,
|
||||
"react_inertia_tests": 49,
|
||||
"phpstan_errors": 0,
|
||||
"typescript": "passed",
|
||||
"pint": "passed",
|
||||
"targeted_lint": "passed",
|
||||
"build_exit": 0,
|
||||
"menu_code_proof": "menu-code-contract.json",
|
||||
"menu_links_proof": "menu-links.json",
|
||||
"runtime_sha256_files": 18,
|
||||
"postgresql_proof": "submission-forms-pg.json",
|
||||
"installation_proof": "submission-forms-install.json",
|
||||
"http_proof": "submission-forms-http.json",
|
||||
"visual_review": false,
|
||||
"domain_installed": false,
|
||||
"external_portal_available": false,
|
||||
"external_AI_calls": 0,
|
||||
"original_document_changed": false,
|
||||
"outstanding": [
|
||||
"external identity and reusable candidate profile",
|
||||
"submission uploads security verification receipt and triage",
|
||||
"complements and revisions",
|
||||
"official territory validation and multiple availability locations",
|
||||
"privacy and retention configuration before public operation"
|
||||
],
|
||||
"source_sha256": {
|
||||
"database/migrations/2026_10_30_000036_create_submission_forms.php": "158a2a4dbe7a5b1af281281619e13e9fef573dc4e2afe5a6e79c56dd1ecb9e7a",
|
||||
"app/Services/SubmissionForms.php": "033c65865c3a8bdb354a297585ce0728c954b94a78ccb7fbd95c93b8be9b1236",
|
||||
"app/Services/SubmissionAnswers.php": "49c203f85b3eec68d6a343e9f1c9b693eb56d42411d6d3b798c359e12eddc130",
|
||||
"app/Services/PeopleHiring.php": "18ffaaa08774b1a5a0ec3f9a06898f40cc20ee564b89e252113ff8e7ad81b5a3",
|
||||
"app/Http/Controllers/SubmissionFormsController.php": "c9c937e8b03c24410c156d70fbbc0c6e65ede3e57e741dfe6849c2f2cee0eefa",
|
||||
"resources/js/pages/company/submission-forms.tsx": "ebe9b36d1ae9b42780d3f54cb86770358f1333bfc822b602ba1ca5dbfbbc9271",
|
||||
"resources/js/pages/company/parameters.tsx": "80f8bc5304066fcaceb5727374c2f5b3a9cc9e61194d8c35cf64e093b1aee3d3",
|
||||
"resources/js/pages/people/show.tsx": "d7dcd65a1f70b0e30223ac0a8bb1062f991443dc4ed9700be97a9b02eca2307f",
|
||||
"tests/Feature/SubmissionFormsTest.php": "06e567b030486fdfec459ef69d125bc5b6916a6c54968ef1666471cc52d5bda8",
|
||||
"tests/Feature/SubmissionAnswersTest.php": "a152ba6fd7129490d6fa6294645d01ed824a8c4c73342ce38a1fb3d95a3044c4",
|
||||
"tests/Feature/PeopleHiringTest.php": "52028989c405043e96a33807232250a64e8c7e48b8f49eac282664c894e1cb9c",
|
||||
"tests/frontend/submission-forms.test.tsx": "e0460f9227964d27f331c1f0801339998f4fd8693cc5b27e13bcf1d51c5fc156"
|
||||
}
|
||||
"checked_at": "2026-10-30T16:48:55.574914+00:00",
|
||||
"scope": [
|
||||
"six independently versioned company intake form configurations",
|
||||
"immutable append-only definitions and history",
|
||||
"stale update rejection and audit rollback",
|
||||
"frozen form definition at preparation of new people opportunities",
|
||||
"structured province district locality availability field and reusable validation"
|
||||
],
|
||||
"full_suite": {
|
||||
"tool": "pest",
|
||||
"result": "passed",
|
||||
"tests": 710,
|
||||
"passed": 708,
|
||||
"assertions": 6904,
|
||||
"duration_ms": 149093,
|
||||
"skipped": 2
|
||||
},
|
||||
"module_sqlite_tests": 44,
|
||||
"module_assertions": 1088,
|
||||
"disposable_postgresql_tests": 44,
|
||||
"disposable_postgresql_assertions": 1088,
|
||||
"react_inertia_tests": 49,
|
||||
"phpstan_errors": 0,
|
||||
"typescript": "passed",
|
||||
"pint": "passed",
|
||||
"targeted_lint": "passed",
|
||||
"build_exit": 0,
|
||||
"menu_code_proof": "menu-code-contract.json",
|
||||
"menu_links_proof": "menu-links.json",
|
||||
"runtime_sha256_files": 18,
|
||||
"postgresql_proof": "submission-forms-pg.json",
|
||||
"installation_proof": "submission-forms-install.json",
|
||||
"http_proof": "submission-forms-http.json",
|
||||
"visual_review": false,
|
||||
"domain_installed": false,
|
||||
"external_portal_available": false,
|
||||
"external_AI_calls": 0,
|
||||
"original_document_changed": false,
|
||||
"outstanding": [
|
||||
"external identity and reusable candidate profile",
|
||||
"submission uploads security verification receipt and triage",
|
||||
"complements and revisions",
|
||||
"official territory validation and multiple availability locations",
|
||||
"privacy and retention configuration before public operation"
|
||||
],
|
||||
"source_sha256": {
|
||||
"database/migrations/2026_10_30_000036_create_submission_forms.php": "158a2a4dbe7a5b1af281281619e13e9fef573dc4e2afe5a6e79c56dd1ecb9e7a",
|
||||
"app/Services/SubmissionForms.php": "033c65865c3a8bdb354a297585ce0728c954b94a78ccb7fbd95c93b8be9b1236",
|
||||
"app/Services/SubmissionAnswers.php": "49c203f85b3eec68d6a343e9f1c9b693eb56d42411d6d3b798c359e12eddc130",
|
||||
"app/Services/PeopleHiring.php": "18ffaaa08774b1a5a0ec3f9a06898f40cc20ee564b89e252113ff8e7ad81b5a3",
|
||||
"app/Http/Controllers/SubmissionFormsController.php": "c9c937e8b03c24410c156d70fbbc0c6e65ede3e57e741dfe6849c2f2cee0eefa",
|
||||
"resources/js/pages/company/submission-forms.tsx": "ebe9b36d1ae9b42780d3f54cb86770358f1333bfc822b602ba1ca5dbfbbc9271",
|
||||
"resources/js/pages/company/parameters.tsx": "80f8bc5304066fcaceb5727374c2f5b3a9cc9e61194d8c35cf64e093b1aee3d3",
|
||||
"resources/js/pages/people/show.tsx": "d7dcd65a1f70b0e30223ac0a8bb1062f991443dc4ed9700be97a9b02eca2307f",
|
||||
"tests/Feature/SubmissionFormsTest.php": "06e567b030486fdfec459ef69d125bc5b6916a6c54968ef1666471cc52d5bda8",
|
||||
"tests/Feature/SubmissionAnswersTest.php": "a152ba6fd7129490d6fa6294645d01ed824a8c4c73342ce38a1fb3d95a3044c4",
|
||||
"tests/Feature/PeopleHiringTest.php": "52028989c405043e96a33807232250a64e8c7e48b8f49eac282664c894e1cb9c",
|
||||
"tests/frontend/submission-forms.test.tsx": "e0460f9227964d27f331c1f0801339998f4fd8693cc5b27e13bcf1d51c5fc156"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T07:33:29.638Z",
|
||||
"base": "http://127.0.0.1:8005",
|
||||
"report_path": "/fornecedores/avaliacoes/1/relatorio",
|
||||
"revision_path": "/fornecedores/avaliacoes/2",
|
||||
"create_submit_return_correct_approve": true,
|
||||
"self_approval_refused": true,
|
||||
"stale_write_refused": true,
|
||||
"approved_edit_refused": true,
|
||||
"prior_approved_revision_preserved": true,
|
||||
"history_events": 6,
|
||||
"synthetic_example_only": true,
|
||||
"financial_commands": 0,
|
||||
"visual_review": false
|
||||
"checked_at": "2026-10-30T07:33:29.638Z",
|
||||
"base": "http://127.0.0.1:8005",
|
||||
"report_path": "/fornecedores/avaliacoes/1/relatorio",
|
||||
"revision_path": "/fornecedores/avaliacoes/2",
|
||||
"create_submit_return_correct_approve": true,
|
||||
"self_approval_refused": true,
|
||||
"stale_write_refused": true,
|
||||
"approved_edit_refused": true,
|
||||
"prior_approved_revision_preserved": true,
|
||||
"history_events": 6,
|
||||
"synthetic_example_only": true,
|
||||
"financial_commands": 0,
|
||||
"visual_review": false
|
||||
}
|
||||
|
||||
@@ -1,24 +1,16 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T07:51:21.312Z",
|
||||
"base": "http://127.0.0.1:8005",
|
||||
"path": "/relatorios/medidas-fornecedores",
|
||||
"readers": [
|
||||
"admin",
|
||||
"procurement",
|
||||
"finance",
|
||||
"department_head"
|
||||
],
|
||||
"denied": [
|
||||
"requester",
|
||||
"warehouse"
|
||||
],
|
||||
"total": 1,
|
||||
"filtered_total": 1,
|
||||
"filters_and_csv_match": true,
|
||||
"csv_ignores_page": true,
|
||||
"closed_not_overdue": true,
|
||||
"invalid_range_refused": true,
|
||||
"linked_evaluation_and_report": true,
|
||||
"business_commands": 0,
|
||||
"visual_review": false
|
||||
"checked_at": "2026-10-30T07:51:21.312Z",
|
||||
"base": "http://127.0.0.1:8005",
|
||||
"path": "/relatorios/medidas-fornecedores",
|
||||
"readers": ["admin", "procurement", "finance", "department_head"],
|
||||
"denied": ["requester", "warehouse"],
|
||||
"total": 1,
|
||||
"filtered_total": 1,
|
||||
"filters_and_csv_match": true,
|
||||
"csv_ignores_page": true,
|
||||
"closed_not_overdue": true,
|
||||
"invalid_range_refused": true,
|
||||
"linked_evaluation_and_report": true,
|
||||
"business_commands": 0,
|
||||
"visual_review": false
|
||||
}
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T07:52:09.806123+00:00",
|
||||
"targeted_tests": 36,
|
||||
"assertions": 542,
|
||||
"new_report_test_cases": 6,
|
||||
"csv_batch_test_rows": 207,
|
||||
"phpstan_errors": 0,
|
||||
"typescript": "passed",
|
||||
"lint_and_format_targeted": "passed",
|
||||
"pint_targeted": "passed",
|
||||
"build": "passed",
|
||||
"build_log": "storage/logs/supplier-improvement-report-build.log",
|
||||
"menu_entries": 106,
|
||||
"roles": 6,
|
||||
"evaluation_catalog_pages": 22,
|
||||
"schema_changes": false,
|
||||
"business_commands": 0,
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"sources": {
|
||||
"app/Http/Controllers/SupplierImprovementReportController.php": "fdaf0d5484e91481cdd5d676eb6951f8575bcc328961e993acf3290f5d8343cc",
|
||||
"app/Models/SupplierImprovement.php": "762af10d9c14d70dd03e51ef6d9a80efe36171a9a0121a985ae0c17f06f03d4e",
|
||||
"resources/js/pages/reports/supplier-improvements.tsx": "070d4c5eaf0a75439751504b21262df1c2f0e60d59e93538ff5d9c5959fe6398",
|
||||
"resources/js/components/supplier-improvements.tsx": "7add7f51d907943682d3f132dc135f0e41b726dd67ddf2478852cf30ba5a21dd",
|
||||
"resources/js/lib/procurement-navigation.ts": "a2a26bbd50c288e7cde589c7d8c2aaa3929f5ae20fc8b01c502db031d95085d4",
|
||||
"tests/Feature/SupplierImprovementReportTest.php": "daf90f8da729a489bdc5116b318514c82a91b89ffbfb2c082b11457661544985",
|
||||
"scripts/supplier-improvement-report-check.mjs": "60c57d36ebc83928198734573b5072240291504b57f2b88be3167f8f7c40c906"
|
||||
}
|
||||
"checked_at": "2026-10-30T07:52:09.806123+00:00",
|
||||
"targeted_tests": 36,
|
||||
"assertions": 542,
|
||||
"new_report_test_cases": 6,
|
||||
"csv_batch_test_rows": 207,
|
||||
"phpstan_errors": 0,
|
||||
"typescript": "passed",
|
||||
"lint_and_format_targeted": "passed",
|
||||
"pint_targeted": "passed",
|
||||
"build": "passed",
|
||||
"build_log": "storage/logs/supplier-improvement-report-build.log",
|
||||
"menu_entries": 106,
|
||||
"roles": 6,
|
||||
"evaluation_catalog_pages": 22,
|
||||
"schema_changes": false,
|
||||
"business_commands": 0,
|
||||
"domain_installed": false,
|
||||
"visual_review": false,
|
||||
"sources": {
|
||||
"app/Http/Controllers/SupplierImprovementReportController.php": "fdaf0d5484e91481cdd5d676eb6951f8575bcc328961e993acf3290f5d8343cc",
|
||||
"app/Models/SupplierImprovement.php": "762af10d9c14d70dd03e51ef6d9a80efe36171a9a0121a985ae0c17f06f03d4e",
|
||||
"resources/js/pages/reports/supplier-improvements.tsx": "070d4c5eaf0a75439751504b21262df1c2f0e60d59e93538ff5d9c5959fe6398",
|
||||
"resources/js/components/supplier-improvements.tsx": "7add7f51d907943682d3f132dc135f0e41b726dd67ddf2478852cf30ba5a21dd",
|
||||
"resources/js/lib/procurement-navigation.ts": "a2a26bbd50c288e7cde589c7d8c2aaa3929f5ae20fc8b01c502db031d95085d4",
|
||||
"tests/Feature/SupplierImprovementReportTest.php": "daf90f8da729a489bdc5116b318514c82a91b89ffbfb2c082b11457661544985",
|
||||
"scripts/supplier-improvement-report-check.mjs": "60c57d36ebc83928198734573b5072240291504b57f2b88be3167f8f7c40c906"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T07:41:16.109Z",
|
||||
"base": "http://127.0.0.1:8005",
|
||||
"evaluation_path": "/fornecedores/avaliacoes/1",
|
||||
"measure_id": 1,
|
||||
"create_retry_idempotent": true,
|
||||
"missing_evidence_refused": true,
|
||||
"self_closure_refused": true,
|
||||
"planner_closure_refused": true,
|
||||
"return_replan_correct_submit_close": true,
|
||||
"stale_plan_refused": true,
|
||||
"closed_edit_refused": true,
|
||||
"parent_evaluation_unchanged": true,
|
||||
"history_events": 7,
|
||||
"report_includes_closed_measure": true,
|
||||
"synthetic_example_only": true,
|
||||
"financial_commands": 0,
|
||||
"visual_review": false
|
||||
"checked_at": "2026-10-30T07:41:16.109Z",
|
||||
"base": "http://127.0.0.1:8005",
|
||||
"evaluation_path": "/fornecedores/avaliacoes/1",
|
||||
"measure_id": 1,
|
||||
"create_retry_idempotent": true,
|
||||
"missing_evidence_refused": true,
|
||||
"self_closure_refused": true,
|
||||
"planner_closure_refused": true,
|
||||
"return_replan_correct_submit_close": true,
|
||||
"stale_plan_refused": true,
|
||||
"closed_edit_refused": true,
|
||||
"parent_evaluation_unchanged": true,
|
||||
"history_events": 7,
|
||||
"report_includes_closed_measure": true,
|
||||
"synthetic_example_only": true,
|
||||
"financial_commands": 0,
|
||||
"visual_review": false
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T07:57:43.108Z",
|
||||
"base": "http://127.0.0.1:8005",
|
||||
"evaluation_path": "/fornecedores/avaliacoes/1",
|
||||
"response_id": 1,
|
||||
"idempotent_creation": true,
|
||||
"incomplete_submit_refused": true,
|
||||
"stale_save_refused": true,
|
||||
"self_decision_refused": true,
|
||||
"return_correction_independent_conclusion": true,
|
||||
"accepted_follow_up_required": true,
|
||||
"concluded_edit_refused": true,
|
||||
"parent_evaluation_unchanged": true,
|
||||
"history_events": 7,
|
||||
"individual_report": true,
|
||||
"synthetic_example_only": true,
|
||||
"external_messages_sent": 0,
|
||||
"financial_commands": 0,
|
||||
"visual_review": false
|
||||
"checked_at": "2026-10-30T07:57:43.108Z",
|
||||
"base": "http://127.0.0.1:8005",
|
||||
"evaluation_path": "/fornecedores/avaliacoes/1",
|
||||
"response_id": 1,
|
||||
"idempotent_creation": true,
|
||||
"incomplete_submit_refused": true,
|
||||
"stale_save_refused": true,
|
||||
"self_decision_refused": true,
|
||||
"return_correction_independent_conclusion": true,
|
||||
"accepted_follow_up_required": true,
|
||||
"concluded_edit_refused": true,
|
||||
"parent_evaluation_unchanged": true,
|
||||
"history_events": 7,
|
||||
"individual_report": true,
|
||||
"synthetic_example_only": true,
|
||||
"external_messages_sent": 0,
|
||||
"financial_commands": 0,
|
||||
"visual_review": false
|
||||
}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
{
|
||||
"checked_at": "2026-10-30T07:59:03.931961+00:00",
|
||||
"targeted_tests": 39,
|
||||
"assertions": 505,
|
||||
"new_test_cases": 11,
|
||||
"phpstan_errors": 0,
|
||||
"typescript": "passed",
|
||||
"lint_format_pint_targeted": "passed",
|
||||
"build": "passed",
|
||||
"build_log": "storage/logs/supplier-responses-build.log",
|
||||
"menu_entries": 106,
|
||||
"roles": 6,
|
||||
"installed_in_local_evaluation": true,
|
||||
"domain_installed": false,
|
||||
"external_messages_sent": 0,
|
||||
"visual_review": false,
|
||||
"source_hashes": {
|
||||
"app/Models/SupplierResponse.php": "fc3877f2e787483cb56f31ca877eb2dc4f2d266c2eefa47dab1b1ae3ecf163fc",
|
||||
"app/Services/SupplierResponseWorkflow.php": "772af0878f55cd9818cddec62aca3850ae4868c13acf86fe7dd003bac1fbb0b5",
|
||||
"app/Http/Controllers/SupplierResponseController.php": "6960500484974f6ba2be1c63bb1156abde976c9e42837f6d3101a1f8a73092d3",
|
||||
"app/Http/Controllers/SupplierEvaluationController.php": "2523eb6a858fdb62c494d74a8ce7988e7f29d4e0c51c53d3f0899de02987f17d",
|
||||
"database/migrations/2026_10_30_000026_create_supplier_responses.php": "d414f637587088e589d886f792d678674337707e8391a0ab0fcf86dc9ebfaaf4",
|
||||
"resources/js/components/supplier-responses.tsx": "ec0becd110e879f5929a7179c61352e1f31478674e654a41b1f85c64ecd1adf1",
|
||||
"resources/js/pages/suppliers/evaluation.tsx": "509d27438a2a3b5337572357b9c69a2703201789ea75a487b36dfc301f65c8db",
|
||||
"tests/Feature/SupplierResponseTest.php": "00faa0131f7335e1d2f18d781aa16cb914bf13acb1090ab902269f737b87c3c6",
|
||||
"scripts/supplier-response-check.mjs": "31c7b741bbb0becc0426d0437a1d9853616199a86730831dc09b902e66f469c2"
|
||||
}
|
||||
"checked_at": "2026-10-30T07:59:03.931961+00:00",
|
||||
"targeted_tests": 39,
|
||||
"assertions": 505,
|
||||
"new_test_cases": 11,
|
||||
"phpstan_errors": 0,
|
||||
"typescript": "passed",
|
||||
"lint_format_pint_targeted": "passed",
|
||||
"build": "passed",
|
||||
"build_log": "storage/logs/supplier-responses-build.log",
|
||||
"menu_entries": 106,
|
||||
"roles": 6,
|
||||
"installed_in_local_evaluation": true,
|
||||
"domain_installed": false,
|
||||
"external_messages_sent": 0,
|
||||
"visual_review": false,
|
||||
"source_hashes": {
|
||||
"app/Models/SupplierResponse.php": "fc3877f2e787483cb56f31ca877eb2dc4f2d266c2eefa47dab1b1ae3ecf163fc",
|
||||
"app/Services/SupplierResponseWorkflow.php": "772af0878f55cd9818cddec62aca3850ae4868c13acf86fe7dd003bac1fbb0b5",
|
||||
"app/Http/Controllers/SupplierResponseController.php": "6960500484974f6ba2be1c63bb1156abde976c9e42837f6d3101a1f8a73092d3",
|
||||
"app/Http/Controllers/SupplierEvaluationController.php": "2523eb6a858fdb62c494d74a8ce7988e7f29d4e0c51c53d3f0899de02987f17d",
|
||||
"database/migrations/2026_10_30_000026_create_supplier_responses.php": "d414f637587088e589d886f792d678674337707e8391a0ab0fcf86dc9ebfaaf4",
|
||||
"resources/js/components/supplier-responses.tsx": "ec0becd110e879f5929a7179c61352e1f31478674e654a41b1f85c64ecd1adf1",
|
||||
"resources/js/pages/suppliers/evaluation.tsx": "509d27438a2a3b5337572357b9c69a2703201789ea75a487b36dfc301f65c8db",
|
||||
"tests/Feature/SupplierResponseTest.php": "00faa0131f7335e1d2f18d781aa16cb914bf13acb1090ab902269f737b87c3c6",
|
||||
"scripts/supplier-response-check.mjs": "31c7b741bbb0becc0426d0437a1d9853616199a86730831dc09b902e66f469c2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,16 +16,16 @@ Registos sem vínculo de cadastro continuam em compatibilidade; não foram ligad
|
||||
|
||||
## Matriz transversal de cobertura
|
||||
|
||||
| Percurso | Reutilização observada/implementada | Evolução ainda necessária |
|
||||
| --- | --- | --- |
|
||||
| Empresa, utilizadores, departamentos e centros | Identificadores seleccionados; contexto do actor usado nos pedidos; regras preservadas nos processos | Conferir reutilização de campos em todos os documentos e associações de funções/unidades |
|
||||
| Fornecedor → proposta → adjudicação/ordem | Cadastro por ID e linhas/fonte herdadas; dados preservados na emissão | Inventariar referências/documentos repetidos e uniformizar selecção de evidências |
|
||||
| Requisição → proposta → ordem | Catálogo de artigos publicados pelo armazém/administração para insumos/outros bens; descrição, categoria, unidade e tipo vêm da fonte; versão preservada por linha e na ordem | Equipamento/peças mantêm roteiro técnico; serviços continuam manuais; conferir referências documentais repetidas |
|
||||
| Ordem → recepção/factura | Linhas, fornecedor e valores de origem usados; artigo do catálogo identificado automaticamente na recepção; quantidades e datas são novos eventos | Conferir os restantes formulários e associações sem reintrodução de descrições |
|
||||
| Pessoas → candidaturas → avaliação/selecção | Novo cadastro reutilizável, selecção autorizada e snapshot por candidatura; avaliações usam a mesma candidatura | Reconciliação assistida dos históricos e campos de cadastro adicionais quando forem definidos |
|
||||
| Percurso | Reutilização observada/implementada | Evolução ainda necessária |
|
||||
| ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Empresa, utilizadores, departamentos e centros | Identificadores seleccionados; contexto do actor usado nos pedidos; regras preservadas nos processos | Conferir reutilização de campos em todos os documentos e associações de funções/unidades |
|
||||
| Fornecedor → proposta → adjudicação/ordem | Cadastro por ID e linhas/fonte herdadas; dados preservados na emissão | Inventariar referências/documentos repetidos e uniformizar selecção de evidências |
|
||||
| Requisição → proposta → ordem | Catálogo de artigos publicados pelo armazém/administração para insumos/outros bens; descrição, categoria, unidade e tipo vêm da fonte; versão preservada por linha e na ordem | Equipamento/peças mantêm roteiro técnico; serviços continuam manuais; conferir referências documentais repetidas |
|
||||
| Ordem → recepção/factura | Linhas, fornecedor e valores de origem usados; artigo do catálogo identificado automaticamente na recepção; quantidades e datas são novos eventos | Conferir os restantes formulários e associações sem reintrodução de descrições |
|
||||
| Pessoas → candidaturas → avaliação/selecção | Novo cadastro reutilizável, selecção autorizada e snapshot por candidatura; avaliações usam a mesma candidatura | Reconciliação assistida dos históricos e campos de cadastro adicionais quando forem definidos |
|
||||
| Perfil externo → candidatura electrónica → avaliação | Perfil versionado com disponibilidade província/distrito/localidade; respostas pré-preenchidas e comprovativo preservado; aceitação explícita para análise aproveita nome/resumo/referência/data na candidatura interna e identifica a fonte/versão | Anexos verificados, complementos/revisões, modalidades espontâneas e eventual inclusão em cadastro por decisão própria; não fundir por nome |
|
||||
| Minutas/modelos de documentos | Processos e decisões preservam os seus dados; minuta interna de Pessoas ainda recebe termos específicos | Modelos versionados por empresa e preenchimento de variáveis por fontes autorizadas, mantendo termos novos editáveis |
|
||||
| Relatórios, indicadores e sínteses | Derivam dos registos originais autorizados, sem novo preenchimento | Conferir cobertura funcional e visual de todos os relatórios; não confundir síntese com nova evidência |
|
||||
| Minutas/modelos de documentos | Processos e decisões preservam os seus dados; minuta interna de Pessoas ainda recebe termos específicos | Modelos versionados por empresa e preenchimento de variáveis por fontes autorizadas, mantendo termos novos editáveis |
|
||||
| Relatórios, indicadores e sínteses | Derivam dos registos originais autorizados, sem novo preenchimento | Conferir cobertura funcional e visual de todos os relatórios; não confundir síntese com nova evidência |
|
||||
|
||||
## Critério para fechar cada lacuna
|
||||
|
||||
|
||||
@@ -56,7 +56,11 @@ export function AppSidebar() {
|
||||
...(canReadPurchases
|
||||
? [
|
||||
{ title: 'Fornecedores', href: '/fornecedores', icon: Users },
|
||||
{ title: 'Avaliação de fornecedores', href: '/fornecedores/avaliacoes', icon: Users },
|
||||
{
|
||||
title: 'Avaliação de fornecedores',
|
||||
href: '/fornecedores/avaliacoes',
|
||||
icon: Users,
|
||||
},
|
||||
{ title: 'Cotações', href: '/cotacoes', icon: Scale },
|
||||
{
|
||||
title: 'Ordens de compra',
|
||||
@@ -157,7 +161,14 @@ export function AppSidebar() {
|
||||
icon: FileCheck2,
|
||||
},
|
||||
...(payablesAvailable === true
|
||||
? [{title: 'Obrigações a fornecedores', href: '/obrigacoes-fornecedores', icon: FileCheck2, fullPage: true}]
|
||||
? [
|
||||
{
|
||||
title: 'Obrigações a fornecedores',
|
||||
href: '/obrigacoes-fornecedores',
|
||||
icon: FileCheck2,
|
||||
fullPage: true,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
title: 'Pagamentos externos',
|
||||
|
||||
@@ -34,6 +34,9 @@ import { roleLabels } from '@/types/procurement';
|
||||
export function SgcWorkspaceShell({ children }: { children: ReactNode }) {
|
||||
const page = usePage();
|
||||
const user = page.props.auth?.user;
|
||||
const publicNavigation = page.props.publicNavigation as
|
||||
| { paths: string[] }
|
||||
| undefined;
|
||||
const [mobileOpen, setMobileOpen] = useState(false);
|
||||
const [search, setSearch] = useState('');
|
||||
const [expanded, setExpanded] = useState<Record<string, boolean>>({});
|
||||
@@ -44,6 +47,7 @@ export function SgcWorkspaceShell({ children }: { children: ReactNode }) {
|
||||
const groups = procurementNavigation(
|
||||
access?.active ? access.permissions : (user?.purchasing_role ?? ''),
|
||||
page.props.payablesAvailable === true,
|
||||
publicNavigation?.paths,
|
||||
);
|
||||
const activeId = activeProcurementEntry(groups, page.url);
|
||||
const activeGroup = groups.find((group) =>
|
||||
@@ -274,6 +278,13 @@ export function SgcWorkspaceShell({ children }: { children: ReactNode }) {
|
||||
);
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50 text-foreground dark:bg-background print:block">
|
||||
{publicNavigation && (
|
||||
<p className="border-b bg-background p-3 text-center text-sm">
|
||||
Consulta pública de desenvolvimento · Sem organização
|
||||
seleccionada e sem registos privados. Alterações exigem
|
||||
autenticação.
|
||||
</p>
|
||||
)}
|
||||
<a
|
||||
href="#sgc-main"
|
||||
className="sr-only z-50 rounded bg-white p-3 text-black focus:not-sr-only focus:fixed focus:top-2 focus:left-2"
|
||||
|
||||
@@ -31,6 +31,7 @@ const inventory = [...readers, 'warehouse'];
|
||||
export function procurementNavigation(
|
||||
role: string | string[],
|
||||
payables: boolean,
|
||||
publicPaths?: string[],
|
||||
): ProcurementGroup[] {
|
||||
const groups: ProcurementGroup[] = [
|
||||
{
|
||||
@@ -341,17 +342,20 @@ export function procurementNavigation(
|
||||
return compact
|
||||
.map((group) => ({
|
||||
...group,
|
||||
entries: group.entries.filter(
|
||||
(entry) =>
|
||||
(entry.id !== 'new_request' ||
|
||||
!Array.isArray(role) ||
|
||||
role.includes('requester')) &&
|
||||
(!entry.roles ||
|
||||
entry.roles.some((permission) =>
|
||||
(Array.isArray(role) ? role : [role]).includes(
|
||||
permission,
|
||||
),
|
||||
)),
|
||||
entries: group.entries.filter((entry) =>
|
||||
publicPaths
|
||||
? publicPaths.includes(
|
||||
new URL(entry.path, 'http://local').pathname,
|
||||
)
|
||||
: (entry.id !== 'new_request' ||
|
||||
!Array.isArray(role) ||
|
||||
role.includes('requester')) &&
|
||||
(!entry.roles ||
|
||||
entry.roles.some((permission) =>
|
||||
(Array.isArray(role) ? role : [role]).includes(
|
||||
permission,
|
||||
),
|
||||
)),
|
||||
),
|
||||
}))
|
||||
.filter((group) => group.entries.length);
|
||||
|
||||
@@ -222,7 +222,7 @@ export default function Budgets({
|
||||
Dotação acumulada: {money(b.approved_total_minor)}
|
||||
</p>
|
||||
<p className="my-3">{b.reason}</p>
|
||||
{!b.approved_by && b.created_by !== auth.user.id && (
|
||||
{!b.approved_by && b.created_by !== auth.user?.id && (
|
||||
<Button
|
||||
onClick={() =>
|
||||
router.post(`/orcamentos/${b.id}/aprovar`)
|
||||
@@ -270,7 +270,7 @@ export default function Budgets({
|
||||
</p>
|
||||
)}
|
||||
{r.status === 'pending' &&
|
||||
r.created_by !== auth.user.id && (
|
||||
r.created_by !== auth.user?.id && (
|
||||
<ReinforcementReview id={r.id} />
|
||||
)}
|
||||
</article>
|
||||
|
||||
@@ -444,7 +444,12 @@ export default function SubmissionForms({
|
||||
return (
|
||||
<>
|
||||
<Head title="Formulários de candidaturas" />
|
||||
<Link className="m-5 inline-block underline" href="/administracao/candidaturas/portal">Configurar área externa do candidato</Link>
|
||||
<Link
|
||||
className="m-5 inline-block underline"
|
||||
href="/administracao/candidaturas/portal"
|
||||
>
|
||||
Configurar área externa do candidato
|
||||
</Link>
|
||||
<div className="mx-auto max-w-5xl space-y-6 p-5 lg:p-8">
|
||||
<PageTitle
|
||||
title="Inscrições e candidaturas electrónicas"
|
||||
|
||||
@@ -31,6 +31,7 @@ export default function Dashboard({
|
||||
approvals,
|
||||
}: Props) {
|
||||
const canRequest = usePage().props.canRequest === true;
|
||||
const publicNavigation = !!usePage().props.publicNavigation;
|
||||
const cards = [
|
||||
{
|
||||
label: 'Requisições',
|
||||
@@ -66,6 +67,7 @@ export default function Dashboard({
|
||||
title="Visão geral das compras"
|
||||
description="Acompanhe os pedidos, as aprovações e a actividade da sua empresa."
|
||||
action={
|
||||
!publicNavigation &&
|
||||
(!company || canRequest) && (
|
||||
<Button
|
||||
asChild
|
||||
@@ -87,7 +89,7 @@ export default function Dashboard({
|
||||
)
|
||||
}
|
||||
/>
|
||||
{!company ? (
|
||||
{!company && !publicNavigation ? (
|
||||
<Panel
|
||||
title="Vamos configurar a sua empresa"
|
||||
description="Crie a organização e o primeiro departamento. Depois pode adicionar a equipa e começar a registar pedidos."
|
||||
@@ -120,7 +122,7 @@ export default function Dashboard({
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{company.rules_provisional && (
|
||||
{company?.rules_provisional && (
|
||||
<div className="rounded-xl border border-amber-200 bg-amber-50 px-5 py-4 text-sm text-amber-900 dark:border-amber-900 dark:bg-amber-950 dark:text-amber-100">
|
||||
<strong>
|
||||
Ambiente de desenvolvimento · regras
|
||||
|
||||
@@ -283,11 +283,11 @@ export default function Investment({
|
||||
{canManage &&
|
||||
p.status === 'pending' &&
|
||||
p.id === policies[0]?.id &&
|
||||
p.created_by !== auth.user.id &&
|
||||
p.created_by !== auth.user?.id &&
|
||||
!reviews.some(
|
||||
(r) =>
|
||||
r.investment_policy_id === p.id &&
|
||||
r.reviewed_by === auth.user.id,
|
||||
r.reviewed_by === auth.user?.id,
|
||||
) && <Decision id={p.id} />}
|
||||
</Panel>
|
||||
))}
|
||||
|
||||
@@ -71,7 +71,14 @@ export default function Invoices({
|
||||
return (
|
||||
<>
|
||||
<Head title="Facturas" />
|
||||
{payablesAvailable && <a className="m-6 inline-block underline" href="/obrigacoes-fornecedores">Obrigações a fornecedores — ensaio sintético</a>}
|
||||
{payablesAvailable && (
|
||||
<a
|
||||
className="m-6 inline-block underline"
|
||||
href="/obrigacoes-fornecedores"
|
||||
>
|
||||
Obrigações a fornecedores — ensaio sintético
|
||||
</a>
|
||||
)}
|
||||
<div className="mx-auto grid w-full max-w-6xl gap-6 p-6">
|
||||
<PageTitle
|
||||
eyebrow="SGC · Finanças"
|
||||
@@ -287,8 +294,12 @@ export default function Invoices({
|
||||
</p>
|
||||
{payablesAvailable && (
|
||||
<p className="my-3">
|
||||
<a className="underline" href={`/obrigacoes-fornecedores?invoice=${invoice.id}`}>
|
||||
Consultar ou preparar obrigação desta factura
|
||||
<a
|
||||
className="underline"
|
||||
href={`/obrigacoes-fornecedores?invoice=${invoice.id}`}
|
||||
>
|
||||
Consultar ou preparar obrigação desta
|
||||
factura
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
@@ -303,7 +314,7 @@ export default function Invoices({
|
||||
</ul>
|
||||
{canManage &&
|
||||
invoice.status === 'pending' &&
|
||||
invoice.created_by !== auth.user.id && (
|
||||
invoice.created_by !== auth.user?.id && (
|
||||
<Button
|
||||
disabled={validating !== null}
|
||||
onClick={() => {
|
||||
|
||||
@@ -300,7 +300,7 @@ export default function Payments({
|
||||
)}
|
||||
{canManage &&
|
||||
p.status === 'pending' &&
|
||||
p.created_by !== auth.user.id && (
|
||||
p.created_by !== auth.user?.id && (
|
||||
<Review id={p.id} />
|
||||
)}
|
||||
</Panel>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -3,57 +3,176 @@ import fs from 'node:fs/promises';
|
||||
|
||||
const base = 'http://127.0.0.1:8005';
|
||||
const cookies = new Map();
|
||||
async function request(path, method = 'GET', body, accept = 'application/json') {
|
||||
const response = await fetch(base + path, {method, redirect: 'manual', headers: {
|
||||
Accept: accept, Cookie: [...cookies].map(([k,v]) => `${k}=${v}`).join('; '),
|
||||
...(body ? {'Content-Type': 'application/x-www-form-urlencoded'} : {}),
|
||||
}, ...(body ? {body: new URLSearchParams(body)} : {})});
|
||||
async function request(
|
||||
path,
|
||||
method = 'GET',
|
||||
body,
|
||||
accept = 'application/json',
|
||||
) {
|
||||
const response = await fetch(base + path, {
|
||||
method,
|
||||
redirect: 'manual',
|
||||
headers: {
|
||||
Accept: accept,
|
||||
Cookie: [...cookies].map(([k, v]) => `${k}=${v}`).join('; '),
|
||||
...(body
|
||||
? { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
: {}),
|
||||
},
|
||||
...(body ? { body: new URLSearchParams(body) } : {}),
|
||||
});
|
||||
for (const cookie of response.headers.getSetCookie()) {
|
||||
const pair = cookie.split(';', 1)[0], index = pair.indexOf('=');
|
||||
cookies.set(pair.slice(0,index),pair.slice(index+1));
|
||||
const pair = cookie.split(';', 1)[0],
|
||||
index = pair.indexOf('=');
|
||||
cookies.set(pair.slice(0, index), pair.slice(index + 1));
|
||||
}
|
||||
return {status: response.status, text: await response.text(), location: response.headers.get('location')};
|
||||
return {
|
||||
status: response.status,
|
||||
text: await response.text(),
|
||||
location: response.headers.get('location'),
|
||||
};
|
||||
}
|
||||
const token = text => text.match(/name="_token" value="([^"]+)"/)[1];
|
||||
const token = (text) => text.match(/name="_token" value="([^"]+)"/)[1];
|
||||
async function enter(role, destination = '/dashboard') {
|
||||
const page = await request('/avaliar');
|
||||
assert.equal(page.status, 200);
|
||||
const entered = await request('/avaliar/entrar', 'POST', {role, _token: token(page.text)});
|
||||
const entered = await request('/avaliar/entrar', 'POST', {
|
||||
role,
|
||||
_token: token(page.text),
|
||||
});
|
||||
assert.equal(entered.status, 302, role);
|
||||
assert.equal(new URL(entered.location).pathname, destination);
|
||||
assert.equal((await request('/dashboard')).status, 200);
|
||||
return entered;
|
||||
}
|
||||
let page = await request('/avaliar');
|
||||
const guestReport = await request('/relatorios?status=approved', 'GET', undefined, 'text/html');
|
||||
const guestReport = await request(
|
||||
'/relatorios?status=approved',
|
||||
'GET',
|
||||
undefined,
|
||||
'text/html',
|
||||
);
|
||||
assert.equal(guestReport.status, 302);
|
||||
assert.equal(new URL(guestReport.location).pathname, '/avaliar');
|
||||
assert.equal(new URL((await request('/login', 'GET', undefined, 'text/html')).location).pathname, '/avaliar');
|
||||
assert.equal((await request('/avaliar/entrar','POST',{role:'admin'})).status,419);
|
||||
assert.equal((await request('/avaliar/entrar','POST',{role:'root',_token:token(page.text)})).status,422);
|
||||
assert.equal((await request('/avaliar/entrar','POST',{role:'admin',user_id:'1',_token:token(page.text)})).status,422);
|
||||
assert.equal(
|
||||
new URL((await request('/login', 'GET', undefined, 'text/html')).location)
|
||||
.pathname,
|
||||
'/avaliar',
|
||||
);
|
||||
assert.equal(
|
||||
(await request('/avaliar/entrar', 'POST', { role: 'admin' })).status,
|
||||
419,
|
||||
);
|
||||
assert.equal(
|
||||
(
|
||||
await request('/avaliar/entrar', 'POST', {
|
||||
role: 'root',
|
||||
_token: token(page.text),
|
||||
})
|
||||
).status,
|
||||
422,
|
||||
);
|
||||
assert.equal(
|
||||
(
|
||||
await request('/avaliar/entrar', 'POST', {
|
||||
role: 'admin',
|
||||
user_id: '1',
|
||||
_token: token(page.text),
|
||||
})
|
||||
).status,
|
||||
422,
|
||||
);
|
||||
const reportEntry = await enter('admin', '/relatorios');
|
||||
assert.equal(new URL(reportEntry.location).searchParams.get('status'), 'approved');
|
||||
const modules = ['/dashboard','/empresa','/requisicoes','/planeamento','/fornecedores','/cotacoes','/ordens',
|
||||
'/recepcoes','/stock','/stock/operacoes','/equipamentos','/orcamentos','/regras-investimento','/facturas',
|
||||
'/pagamentos','/obrigacoes-fornecedores','/conflitos','/revisao-compras','/politica-admissao','/politica-recomendacao','/relatorios','/relatorios/medidas-fornecedores','/administracao/parametrizacao'];
|
||||
for (const path of modules) assert.equal((await request(path)).status,200,path);
|
||||
const roles = ['requester','department_head','procurement','finance','warehouse','admin'];
|
||||
assert.equal(
|
||||
new URL(reportEntry.location).searchParams.get('status'),
|
||||
'approved',
|
||||
);
|
||||
const modules = [
|
||||
'/dashboard',
|
||||
'/empresa',
|
||||
'/requisicoes',
|
||||
'/planeamento',
|
||||
'/fornecedores',
|
||||
'/cotacoes',
|
||||
'/ordens',
|
||||
'/recepcoes',
|
||||
'/stock',
|
||||
'/stock/operacoes',
|
||||
'/equipamentos',
|
||||
'/orcamentos',
|
||||
'/regras-investimento',
|
||||
'/facturas',
|
||||
'/pagamentos',
|
||||
'/obrigacoes-fornecedores',
|
||||
'/conflitos',
|
||||
'/revisao-compras',
|
||||
'/politica-admissao',
|
||||
'/politica-recomendacao',
|
||||
'/relatorios',
|
||||
'/relatorios/medidas-fornecedores',
|
||||
'/administracao/parametrizacao',
|
||||
];
|
||||
for (const path of modules)
|
||||
assert.equal((await request(path)).status, 200, path);
|
||||
const roles = [
|
||||
'requester',
|
||||
'department_head',
|
||||
'procurement',
|
||||
'finance',
|
||||
'warehouse',
|
||||
'admin',
|
||||
];
|
||||
for (const role of roles) await enter(role);
|
||||
page = await request('/avaliar');
|
||||
assert.equal((await request('/logout','POST',{_token:token(page.text)})).status,204);
|
||||
assert.equal((await request('/empresa')).status,401);
|
||||
assert.equal((await request('/dashboard')).status,200);
|
||||
assert.equal((await request('/empresa')).status,200);
|
||||
assert.equal((await request('/administracao/parametrizacao')).status,200);
|
||||
assert.equal(
|
||||
(await request('/logout', 'POST', { _token: token(page.text) })).status,
|
||||
204,
|
||||
);
|
||||
assert.equal((await request('/empresa')).status, 401);
|
||||
assert.equal((await request('/dashboard')).status, 200);
|
||||
assert.equal((await request('/empresa')).status, 200);
|
||||
assert.equal((await request('/administracao/parametrizacao')).status, 200);
|
||||
page = await request('/avaliar');
|
||||
assert.equal((await request('/logout','POST',{_token:token(page.text)})).status,204);
|
||||
assert.equal((await request('/empresa')).status,401);
|
||||
const operational = await fetch('http://127.0.0.1:8015/avaliar', {redirect:'manual'}).catch(() => null);
|
||||
if (operational) assert.equal(operational.status,404,'Evaluation entry must not exist on the normal installation');
|
||||
await fs.mkdir('docs/proofs',{recursive:true});
|
||||
await fs.writeFile('docs/proofs/evaluation-access.json',JSON.stringify({checked_at:new Date().toISOString(),base,
|
||||
owner_modules:modules,roles,csrf_enforced:true,arbitrary_role_refused:true,arbitrary_user_refused:true,logout:true,
|
||||
business_commands:0,real_accounts_changed:false,operational_entry_status:operational?.status??'not running',
|
||||
visual_review:false,dashboard_auto_entry:true,other_routes_require_session:true,guest_report_redirect:true,intended_report_and_filters_preserved:true},null,2)+'\n');
|
||||
console.log('Owner entry, 23 module pages, six roles, report return, CSRF, rejected identifiers and logout passed.');
|
||||
assert.equal(
|
||||
(await request('/logout', 'POST', { _token: token(page.text) })).status,
|
||||
204,
|
||||
);
|
||||
assert.equal((await request('/empresa')).status, 401);
|
||||
const operational = await fetch('http://127.0.0.1:8015/avaliar', {
|
||||
redirect: 'manual',
|
||||
}).catch(() => null);
|
||||
if (operational)
|
||||
assert.equal(
|
||||
operational.status,
|
||||
404,
|
||||
'Evaluation entry must not exist on the normal installation',
|
||||
);
|
||||
await fs.mkdir('docs/proofs', { recursive: true });
|
||||
await fs.writeFile(
|
||||
'docs/proofs/evaluation-access.json',
|
||||
JSON.stringify(
|
||||
{
|
||||
checked_at: new Date().toISOString(),
|
||||
base,
|
||||
owner_modules: modules,
|
||||
roles,
|
||||
csrf_enforced: true,
|
||||
arbitrary_role_refused: true,
|
||||
arbitrary_user_refused: true,
|
||||
logout: true,
|
||||
business_commands: 0,
|
||||
real_accounts_changed: false,
|
||||
operational_entry_status: operational?.status ?? 'not running',
|
||||
visual_review: false,
|
||||
dashboard_auto_entry: true,
|
||||
other_routes_require_session: true,
|
||||
guest_report_redirect: true,
|
||||
intended_report_and_filters_preserved: true,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
) + '\n',
|
||||
);
|
||||
console.log(
|
||||
'Owner entry, 23 module pages, six roles, report return, CSRF, rejected identifiers and logout passed.',
|
||||
);
|
||||
|
||||
@@ -1,39 +1,89 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs/promises';
|
||||
const cookies = new Map(), base = 'http://127.0.0.1:8005';
|
||||
const cookies = new Map(),
|
||||
base = 'http://127.0.0.1:8005';
|
||||
async function request(path, method = 'GET', body) {
|
||||
const r = await fetch(base + path, {method, redirect:'manual', headers:{Accept:'application/json',
|
||||
Cookie:[...cookies].map(([k,v])=>`${k}=${v}`).join('; '),
|
||||
...(body?{'Content-Type':'application/x-www-form-urlencoded'}:{})},
|
||||
...(body?{body:new URLSearchParams(body)}:{})});
|
||||
for(const c of r.headers.getSetCookie()) {const p=c.split(';',1)[0],i=p.indexOf('=');cookies.set(p.slice(0,i),p.slice(i+1));}
|
||||
return {status:r.status,text:await r.text()};
|
||||
const r = await fetch(base + path, {
|
||||
method,
|
||||
redirect: 'manual',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Cookie: [...cookies].map(([k, v]) => `${k}=${v}`).join('; '),
|
||||
...(body
|
||||
? { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
: {}),
|
||||
},
|
||||
...(body ? { body: new URLSearchParams(body) } : {}),
|
||||
});
|
||||
for (const c of r.headers.getSetCookie()) {
|
||||
const p = c.split(';', 1)[0],
|
||||
i = p.indexOf('=');
|
||||
cookies.set(p.slice(0, i), p.slice(i + 1));
|
||||
}
|
||||
return { status: r.status, text: await r.text() };
|
||||
}
|
||||
const landing=await request('/avaliar');
|
||||
const csrf=landing.text.match(/name="_token" value="([^"]+)"/)[1];
|
||||
assert.equal((await request('/avaliar/entrar','POST',{role:'admin',_token:csrf})).status,302);
|
||||
const before=JSON.parse((await request('/api/supplier-payables')).text);
|
||||
const landing = await request('/avaliar');
|
||||
const csrf = landing.text.match(/name="_token" value="([^"]+)"/)[1];
|
||||
assert.equal(
|
||||
(await request('/avaliar/entrar', 'POST', { role: 'admin', _token: csrf }))
|
||||
.status,
|
||||
302,
|
||||
);
|
||||
const before = JSON.parse((await request('/api/supplier-payables')).text);
|
||||
assert(before.documents.length);
|
||||
for(const doc of before.documents) {
|
||||
const filtered=await request('/api/supplier-payables?invoice='+doc.local_id);
|
||||
assert.equal(filtered.status,200);
|
||||
const data=JSON.parse(filtered.text);
|
||||
assert.deepEqual(data.documents,[doc]);
|
||||
const page=await request('/obrigacoes-fornecedores?invoice='+doc.local_id);
|
||||
assert.equal(page.status,200);
|
||||
for (const doc of before.documents) {
|
||||
const filtered = await request(
|
||||
'/api/supplier-payables?invoice=' + doc.local_id,
|
||||
);
|
||||
assert.equal(filtered.status, 200);
|
||||
const data = JSON.parse(filtered.text);
|
||||
assert.deepEqual(data.documents, [doc]);
|
||||
const page = await request(
|
||||
'/obrigacoes-fornecedores?invoice=' + doc.local_id,
|
||||
);
|
||||
assert.equal(page.status, 200);
|
||||
assert(page.text.includes('href="/facturas"'));
|
||||
assert(page.text.includes('Voltar às facturas'));
|
||||
assert(page.text.includes(doc.number));
|
||||
}
|
||||
for(const query of ['0','-1','abc','1.5']) {
|
||||
assert.equal((await request('/api/supplier-payables?invoice='+query)).status,422);
|
||||
assert.equal((await request('/obrigacoes-fornecedores?invoice='+query)).status,422);
|
||||
for (const query of ['0', '-1', 'abc', '1.5']) {
|
||||
assert.equal(
|
||||
(await request('/api/supplier-payables?invoice=' + query)).status,
|
||||
422,
|
||||
);
|
||||
assert.equal(
|
||||
(await request('/obrigacoes-fornecedores?invoice=' + query)).status,
|
||||
422,
|
||||
);
|
||||
}
|
||||
assert.equal((await request('/api/supplier-payables?invoice=2147483647')).status,404);
|
||||
const after=JSON.parse((await request('/api/supplier-payables')).text);
|
||||
assert.deepEqual(after,before,'Read-only navigation must preserve all documents and histories');
|
||||
await fs.writeFile('docs/proofs/invoice-payable-link.json',JSON.stringify({checked_at:new Date().toISOString(),
|
||||
documents_checked:before.documents.length,per_invoice_entry:true,return_to_invoices:true,
|
||||
invalid_identifiers_refused:true,unknown_invoice_refused:true,full_listing_unchanged:true,business_commands:0,
|
||||
visual_review:false},null,2)+'\n');
|
||||
console.log('Per-invoice payable navigation, invalid/unknown identifiers and unchanged history passed.');
|
||||
assert.equal(
|
||||
(await request('/api/supplier-payables?invoice=2147483647')).status,
|
||||
404,
|
||||
);
|
||||
const after = JSON.parse((await request('/api/supplier-payables')).text);
|
||||
assert.deepEqual(
|
||||
after,
|
||||
before,
|
||||
'Read-only navigation must preserve all documents and histories',
|
||||
);
|
||||
await fs.writeFile(
|
||||
'docs/proofs/invoice-payable-link.json',
|
||||
JSON.stringify(
|
||||
{
|
||||
checked_at: new Date().toISOString(),
|
||||
documents_checked: before.documents.length,
|
||||
per_invoice_entry: true,
|
||||
return_to_invoices: true,
|
||||
invalid_identifiers_refused: true,
|
||||
unknown_invoice_refused: true,
|
||||
full_listing_unchanged: true,
|
||||
business_commands: 0,
|
||||
visual_review: false,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
) + '\n',
|
||||
);
|
||||
console.log(
|
||||
'Per-invoice payable navigation, invalid/unknown identifiers and unchanged history passed.',
|
||||
);
|
||||
|
||||
+44
-10
@@ -81,17 +81,51 @@ const configuredFinance = procurementNavigation(['finance'], false).flatMap(
|
||||
(g) => g.entries,
|
||||
);
|
||||
const separate = procurementNavigation('admin', true);
|
||||
assert.ok(separate.find((group) => group.id === 'purchasing').entries.every((entry) => !entry.path.startsWith('/pessoas')));
|
||||
assert.ok(separate.find((group) => group.id === 'people').entries.every((entry) => entry.path.startsWith('/pessoas')));
|
||||
for (const permission of ['people_service_prepare','people_service_review','people_staff_prepare','people_staff_review']) {
|
||||
const entries = procurementNavigation([permission], false).flatMap((group) => group.entries);
|
||||
assert.equal(entries.some((entry) => entry.id === 'people_hiring'), true);
|
||||
assert.equal(entries.some((entry) => entry.id === 'company' || entry.id === 'invoices' || entry.id === 'new_request'), false);
|
||||
assert.ok(
|
||||
separate
|
||||
.find((group) => group.id === 'purchasing')
|
||||
.entries.every((entry) => !entry.path.startsWith('/pessoas')),
|
||||
);
|
||||
assert.ok(
|
||||
separate
|
||||
.find((group) => group.id === 'people')
|
||||
.entries.every((entry) => entry.path.startsWith('/pessoas')),
|
||||
);
|
||||
for (const permission of [
|
||||
'people_service_prepare',
|
||||
'people_service_review',
|
||||
'people_staff_prepare',
|
||||
'people_staff_review',
|
||||
]) {
|
||||
const entries = procurementNavigation([permission], false).flatMap(
|
||||
(group) => group.entries,
|
||||
);
|
||||
assert.equal(
|
||||
entries.some((entry) => entry.id === 'people_hiring'),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
entries.some(
|
||||
(entry) =>
|
||||
entry.id === 'company' ||
|
||||
entry.id === 'invoices' ||
|
||||
entry.id === 'new_request',
|
||||
),
|
||||
false,
|
||||
);
|
||||
}
|
||||
for (const permission of ['people_registry_read','people_registry_manage']) {
|
||||
const entries = procurementNavigation([permission],false).flatMap((group)=>group.entries);
|
||||
assert.equal(entries.some((entry)=>entry.id==='people_registry'),true);
|
||||
assert.equal(entries.some((entry)=>entry.id==='people_hiring'),false);
|
||||
for (const permission of ['people_registry_read', 'people_registry_manage']) {
|
||||
const entries = procurementNavigation([permission], false).flatMap(
|
||||
(group) => group.entries,
|
||||
);
|
||||
assert.equal(
|
||||
entries.some((entry) => entry.id === 'people_registry'),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
entries.some((entry) => entry.id === 'people_hiring'),
|
||||
false,
|
||||
);
|
||||
}
|
||||
assert.equal(
|
||||
configuredFinance.some((e) => e.id === 'organization_parameters'),
|
||||
|
||||
+218
-62
@@ -1,110 +1,266 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs/promises';
|
||||
import {procurementNavigation, activeProcurementEntry} from '../resources/js/lib/procurement-navigation.ts';
|
||||
import {
|
||||
procurementNavigation,
|
||||
activeProcurementEntry,
|
||||
} from '../resources/js/lib/procurement-navigation.ts';
|
||||
|
||||
const base = 'http://127.0.0.1:8005';
|
||||
const cookies = new Map();
|
||||
let version = '';
|
||||
async function request(path, {method = 'GET', body, inertia = false} = {}) {
|
||||
const response = await fetch(base + path, {method, redirect: 'manual', headers: {
|
||||
Accept: 'application/json', Cookie: [...cookies].map(([k,v]) => `${k}=${v}`).join('; '),
|
||||
...(body ? {'Content-Type': 'application/x-www-form-urlencoded'} : {}),
|
||||
...(inertia ? {'X-Inertia': 'true', 'X-Inertia-Version': version} : {}),
|
||||
}, ...(body ? {body: new URLSearchParams(body)} : {})});
|
||||
async function request(path, { method = 'GET', body, inertia = false } = {}) {
|
||||
const response = await fetch(base + path, {
|
||||
method,
|
||||
redirect: 'manual',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Cookie: [...cookies].map(([k, v]) => `${k}=${v}`).join('; '),
|
||||
...(body
|
||||
? { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
: {}),
|
||||
...(inertia
|
||||
? { 'X-Inertia': 'true', 'X-Inertia-Version': version }
|
||||
: {}),
|
||||
},
|
||||
...(body ? { body: new URLSearchParams(body) } : {}),
|
||||
});
|
||||
for (const cookie of response.headers.getSetCookie()) {
|
||||
const pair = cookie.split(';', 1)[0], index = pair.indexOf('=');
|
||||
cookies.set(pair.slice(0,index),pair.slice(index+1));
|
||||
const pair = cookie.split(';', 1)[0],
|
||||
index = pair.indexOf('=');
|
||||
cookies.set(pair.slice(0, index), pair.slice(index + 1));
|
||||
}
|
||||
const text = await response.text();
|
||||
let data;
|
||||
try {data = JSON.parse(text);} catch {}
|
||||
try {
|
||||
data = JSON.parse(text);
|
||||
} catch {}
|
||||
if (!data) {
|
||||
const page = text.match(/<script data-page="app" type="application\/json">([\s\S]*?)<\/script>/);
|
||||
const page = text.match(
|
||||
/<script data-page="app" type="application\/json">([\s\S]*?)<\/script>/,
|
||||
);
|
||||
if (page) data = JSON.parse(page[1]);
|
||||
}
|
||||
if (data?.version) version = data.version;
|
||||
return {status:response.status, text, data};
|
||||
return { status: response.status, text, data };
|
||||
}
|
||||
async function enter(role) {
|
||||
const landing = await request('/avaliar');
|
||||
assert.equal(landing.status, 200);
|
||||
const csrf = landing.text.match(/name="_token" value="([^"]+)"/)[1];
|
||||
assert.equal((await request('/avaliar/entrar', {method:'POST',body:{role,_token:csrf}})).status,302);
|
||||
assert.equal(
|
||||
(
|
||||
await request('/avaliar/entrar', {
|
||||
method: 'POST',
|
||||
body: { role, _token: csrf },
|
||||
})
|
||||
).status,
|
||||
302,
|
||||
);
|
||||
}
|
||||
await enter('admin');
|
||||
await request('/dashboard');
|
||||
const baseline = (await request('/api/supplier-payables')).data;
|
||||
const results = [];
|
||||
for (const role of ['admin','procurement','finance','department_head','warehouse','requester']) {
|
||||
for (const role of [
|
||||
'admin',
|
||||
'procurement',
|
||||
'finance',
|
||||
'department_head',
|
||||
'warehouse',
|
||||
'requester',
|
||||
]) {
|
||||
await enter(role);
|
||||
const dashboard = await request('/dashboard', {inertia:true});
|
||||
assert.equal(dashboard.status,200);
|
||||
assert.equal(dashboard.data.props.auth.user.purchasing_role,role);
|
||||
const groups = procurementNavigation(role,dashboard.data.props.payablesAvailable === true);
|
||||
const entries = groups.flatMap(group=>group.entries);
|
||||
const dashboard = await request('/dashboard', { inertia: true });
|
||||
assert.equal(dashboard.status, 200);
|
||||
assert.equal(dashboard.data.props.auth.user.purchasing_role, role);
|
||||
const groups = procurementNavigation(
|
||||
role,
|
||||
dashboard.data.props.payablesAvailable === true,
|
||||
);
|
||||
const entries = groups.flatMap((group) => group.entries);
|
||||
for (const entry of entries) {
|
||||
const response = await request(entry.path,{inertia:!entry.fullPage});
|
||||
assert.equal(response.status,200,role+' '+entry.path);
|
||||
if (!entry.fullPage) assert(response.data?.component,role+' Inertia '+entry.path);
|
||||
if (entry.id === 'new_request') assert.equal(typeof response.data.props.defaultTaxRate,'string','Configured VAT must reach new request lines');
|
||||
if (entry.id === 'organization_parameters') assert.equal(typeof response.data.props.companyRules.default_tax_rate,'string','Administration must expose the VAT setting');
|
||||
if (entry.id === 'organization_parameters') assert.equal(Object.values(response.data.props.comparisonPolicy.weights).reduce((sum, value)=>sum+value,0),100,'Comparison policy weights must reach administration');
|
||||
if (entry.id === 'people_hiring') assert.equal(response.data.props.ready,true,'People hiring must be installed in the evaluation environment');
|
||||
const response = await request(entry.path, {
|
||||
inertia: !entry.fullPage,
|
||||
});
|
||||
assert.equal(response.status, 200, role + ' ' + entry.path);
|
||||
if (!entry.fullPage)
|
||||
assert(response.data?.component, role + ' Inertia ' + entry.path);
|
||||
if (entry.id === 'new_request')
|
||||
assert.equal(
|
||||
typeof response.data.props.defaultTaxRate,
|
||||
'string',
|
||||
'Configured VAT must reach new request lines',
|
||||
);
|
||||
if (entry.id === 'organization_parameters')
|
||||
assert.equal(
|
||||
typeof response.data.props.companyRules.default_tax_rate,
|
||||
'string',
|
||||
'Administration must expose the VAT setting',
|
||||
);
|
||||
if (entry.id === 'organization_parameters')
|
||||
assert.equal(
|
||||
Object.values(
|
||||
response.data.props.comparisonPolicy.weights,
|
||||
).reduce((sum, value) => sum + value, 0),
|
||||
100,
|
||||
'Comparison policy weights must reach administration',
|
||||
);
|
||||
if (entry.id === 'people_hiring')
|
||||
assert.equal(
|
||||
response.data.props.ready,
|
||||
true,
|
||||
'People hiring must be installed in the evaluation environment',
|
||||
);
|
||||
if (entry.id === 'people_hiring') {
|
||||
assert.equal(typeof response.data.props.workdesk.counts.tasks,'number');
|
||||
assert.equal(typeof response.data.props.workdesk.counts.approvals,'number');
|
||||
assert(response.data.props.processes.data.every(p=>!('rules' in p)&&!('selection' in p)&&!('contract' in p)),'People index must not expose private process payloads');
|
||||
assert.equal(
|
||||
typeof response.data.props.workdesk.counts.tasks,
|
||||
'number',
|
||||
);
|
||||
assert.equal(
|
||||
typeof response.data.props.workdesk.counts.approvals,
|
||||
'number',
|
||||
);
|
||||
assert(
|
||||
response.data.props.processes.data.every(
|
||||
(p) =>
|
||||
!('rules' in p) &&
|
||||
!('selection' in p) &&
|
||||
!('contract' in p),
|
||||
),
|
||||
'People index must not expose private process payloads',
|
||||
);
|
||||
}
|
||||
assert.equal(activeProcurementEntry(groups,entry.path),entry.id,role+' active '+entry.path);
|
||||
assert.equal(
|
||||
activeProcurementEntry(groups, entry.path),
|
||||
entry.id,
|
||||
role + ' active ' + entry.path,
|
||||
);
|
||||
if (entry.id === 'my_drafts') {
|
||||
assert.equal(response.data.props.filters.view,'mine');
|
||||
assert.equal(response.data.props.filters.status,'draft');
|
||||
assert.equal(response.data.props.filters.view, 'mine');
|
||||
assert.equal(response.data.props.filters.status, 'draft');
|
||||
}
|
||||
}
|
||||
for (const path of ['/settings/profile','/settings/appearance']) assert.equal((await request(path,{inertia:true})).status,200,role+' '+path);
|
||||
for (const path of ['/settings/profile', '/settings/appearance'])
|
||||
assert.equal(
|
||||
(await request(path, { inertia: true })).status,
|
||||
200,
|
||||
role + ' ' + path,
|
||||
);
|
||||
// Hidden administration and finance pages remain enforced on the server.
|
||||
if (role !== 'admin') assert.equal((await request('/empresa')).status,403,role+' company isolation');
|
||||
if (!['admin','finance'].includes(role)) assert.equal((await request('/facturas')).status,403,role+' finance isolation');
|
||||
results.push({role,checked_entries:entries.length,paths:entries.map(entry=>entry.path)});
|
||||
if (role !== 'admin')
|
||||
assert.equal(
|
||||
(await request('/empresa')).status,
|
||||
403,
|
||||
role + ' company isolation',
|
||||
);
|
||||
if (!['admin', 'finance'].includes(role))
|
||||
assert.equal(
|
||||
(await request('/facturas')).status,
|
||||
403,
|
||||
role + ' finance isolation',
|
||||
);
|
||||
results.push({
|
||||
role,
|
||||
checked_entries: entries.length,
|
||||
paths: entries.map((entry) => entry.path),
|
||||
});
|
||||
}
|
||||
await enter('admin');
|
||||
const all = await request('/requisicoes?view=all',{inertia:true});
|
||||
const all = await request('/requisicoes?view=all', { inertia: true });
|
||||
const requestId = all.data.props.requisitions.data[0]?.id;
|
||||
const processLinks = [];
|
||||
if (requestId) {
|
||||
const comparison = await request('/requisicoes/'+requestId+'/cotacoes',{inertia:true});
|
||||
assert.equal(comparison.status,200);
|
||||
const comparison = await request(
|
||||
'/requisicoes/' + requestId + '/cotacoes',
|
||||
{ inertia: true },
|
||||
);
|
||||
assert.equal(comparison.status, 200);
|
||||
assert(Array.isArray(comparison.data.props.topIds));
|
||||
assert(comparison.data.props.comparisonPolicy.minimum_proposals >= 1);
|
||||
assert(comparison.data.props.comparisonPolicy.minimum_evaluators >= 1);
|
||||
assert(comparison.data.props.quotations.every(q=>Array.isArray(q.ineligibility_reasons)));
|
||||
assert.equal((await request('/requisicoes/'+requestId,{inertia:true})).status,200);
|
||||
const process = await request('/requisicoes/'+requestId+'/processo',{inertia:true});
|
||||
assert.equal(process.status,200);
|
||||
assert(
|
||||
comparison.data.props.quotations.every((q) =>
|
||||
Array.isArray(q.ineligibility_reasons),
|
||||
),
|
||||
);
|
||||
assert.equal(
|
||||
(await request('/requisicoes/' + requestId, { inertia: true })).status,
|
||||
200,
|
||||
);
|
||||
const process = await request('/requisicoes/' + requestId + '/processo', {
|
||||
inertia: true,
|
||||
});
|
||||
assert.equal(process.status, 200);
|
||||
function collectLinks(value) {
|
||||
if (!value || typeof value !== 'object') return;
|
||||
if (typeof value.href === 'string' && value.href.startsWith('/') && !value.href.startsWith('//')) processLinks.push(value.href);
|
||||
for (const child of Object.values(value)) if (typeof child === 'object') collectLinks(child);
|
||||
if (
|
||||
typeof value.href === 'string' &&
|
||||
value.href.startsWith('/') &&
|
||||
!value.href.startsWith('//')
|
||||
)
|
||||
processLinks.push(value.href);
|
||||
for (const child of Object.values(value))
|
||||
if (typeof child === 'object') collectLinks(child);
|
||||
}
|
||||
collectLinks(process.data.props);
|
||||
for (const href of new Set(processLinks)) assert.equal((await request(href)).status,200,'Process link '+href);
|
||||
for (const href of new Set(processLinks))
|
||||
assert.equal((await request(href)).status, 200, 'Process link ' + href);
|
||||
}
|
||||
const invoices = await request('/facturas',{inertia:true});
|
||||
const invoices = await request('/facturas', { inertia: true });
|
||||
for (const invoice of invoices.data.props.invoices.data) {
|
||||
const page = await request('/obrigacoes-fornecedores?invoice='+invoice.id);
|
||||
assert.equal(page.status,200);
|
||||
const page = await request(
|
||||
'/obrigacoes-fornecedores?invoice=' + invoice.id,
|
||||
);
|
||||
assert.equal(page.status, 200);
|
||||
assert(page.text.includes('href="/facturas"'));
|
||||
}
|
||||
assert.deepEqual((await request('/api/supplier-payables')).data,baseline,'Business history must remain unchanged');
|
||||
const groups = procurementNavigation('admin',true);
|
||||
assert.equal(activeProcurementEntry(groups,'/requisicoes?status=draft&view=mine'),'my_drafts');
|
||||
assert.equal(activeProcurementEntry(groups,'/ordens/1/recepcoes'),'receipts');
|
||||
assert.equal(activeProcurementEntry(groups,'/requisicoes/1/cotacoes'),'quotations');
|
||||
assert.equal(activeProcurementEntry(groups,'/requisicoes/1/relatorio'),'quotations');
|
||||
assert.equal(activeProcurementEntry(groups,'/requisicoes/1/adjudicacao'),'orders');
|
||||
assert.equal(activeProcurementEntry(groups,'/stock/operacoes'),'stock_operations');
|
||||
await fs.writeFile('docs/proofs/menu-links.json',JSON.stringify({checked_at:new Date().toISOString(),results,
|
||||
request_detail_checked:!!requestId,invoice_count:invoices.data.props.invoices.data.length,
|
||||
process_links_checked:[...new Set(processLinks)],
|
||||
canonical_query_order:true,active_subroutes:true,vat_setting_and_default_checked:true,comparison_ties_and_reasons_checked:!!requestId,business_history_unchanged:true,business_commands:0},null,2)+'\n');
|
||||
console.log('All visible menu links passed for six roles, with active entries, query filters, detail links and unchanged business history.');
|
||||
assert.deepEqual(
|
||||
(await request('/api/supplier-payables')).data,
|
||||
baseline,
|
||||
'Business history must remain unchanged',
|
||||
);
|
||||
const groups = procurementNavigation('admin', true);
|
||||
assert.equal(
|
||||
activeProcurementEntry(groups, '/requisicoes?status=draft&view=mine'),
|
||||
'my_drafts',
|
||||
);
|
||||
assert.equal(activeProcurementEntry(groups, '/ordens/1/recepcoes'), 'receipts');
|
||||
assert.equal(
|
||||
activeProcurementEntry(groups, '/requisicoes/1/cotacoes'),
|
||||
'quotations',
|
||||
);
|
||||
assert.equal(
|
||||
activeProcurementEntry(groups, '/requisicoes/1/relatorio'),
|
||||
'quotations',
|
||||
);
|
||||
assert.equal(
|
||||
activeProcurementEntry(groups, '/requisicoes/1/adjudicacao'),
|
||||
'orders',
|
||||
);
|
||||
assert.equal(
|
||||
activeProcurementEntry(groups, '/stock/operacoes'),
|
||||
'stock_operations',
|
||||
);
|
||||
await fs.writeFile(
|
||||
'docs/proofs/menu-links.json',
|
||||
JSON.stringify(
|
||||
{
|
||||
checked_at: new Date().toISOString(),
|
||||
results,
|
||||
request_detail_checked: !!requestId,
|
||||
invoice_count: invoices.data.props.invoices.data.length,
|
||||
process_links_checked: [...new Set(processLinks)],
|
||||
canonical_query_order: true,
|
||||
active_subroutes: true,
|
||||
vat_setting_and_default_checked: true,
|
||||
comparison_ties_and_reasons_checked: !!requestId,
|
||||
business_history_unchanged: true,
|
||||
business_commands: 0,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
) + '\n',
|
||||
);
|
||||
console.log(
|
||||
'All visible menu links passed for six roles, with active entries, query filters, detail links and unchanged business history.',
|
||||
);
|
||||
|
||||
@@ -1,49 +1,128 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs/promises';
|
||||
import {execFile} from 'node:child_process';
|
||||
import {promisify} from 'node:util';
|
||||
const run=promisify(execFile);
|
||||
const php='C:/Users/macbook/.config/investmoz/php-8.5.11/php.exe';
|
||||
const sgf='C:/laragon/www/sgf-tempo';
|
||||
for(const host of ['compras','sgf']) {
|
||||
const exists=await fs.access(`C:/laragon/data/sgf-tempo/secrets/payables-proof-${host}.json`).then(()=>true,()=>false);
|
||||
assert(!exists,'An existing proof must be reconciled before creating a new one.');
|
||||
import { execFile } from 'node:child_process';
|
||||
import { promisify } from 'node:util';
|
||||
const run = promisify(execFile);
|
||||
const php = 'C:/Users/macbook/.config/investmoz/php-8.5.11/php.exe';
|
||||
const sgf = 'C:/laragon/www/sgf-tempo';
|
||||
for (const host of ['compras', 'sgf']) {
|
||||
const exists = await fs
|
||||
.access(`C:/laragon/data/sgf-tempo/secrets/payables-proof-${host}.json`)
|
||||
.then(
|
||||
() => true,
|
||||
() => false,
|
||||
);
|
||||
assert(
|
||||
!exists,
|
||||
'An existing proof must be reconciled before creating a new one.',
|
||||
);
|
||||
}
|
||||
async function job(name) {
|
||||
const {stdout}=await run(php,['scripts/payable-history-pg.php',name],{cwd:process.cwd(),windowsHide:true,timeout:90000,maxBuffer:100000});
|
||||
const { stdout } = await run(
|
||||
php,
|
||||
['scripts/payable-history-pg.php', name],
|
||||
{
|
||||
cwd: process.cwd(),
|
||||
windowsHide: true,
|
||||
timeout: 90000,
|
||||
maxBuffer: 100000,
|
||||
},
|
||||
);
|
||||
return JSON.parse(stdout);
|
||||
}
|
||||
try {
|
||||
await run(php,[`${sgf}/scripts/payables-provision.php`,'--proof'],{cwd:sgf,windowsHide:true,timeout:180000,maxBuffer:100000});
|
||||
const before=await job('setup');
|
||||
const fault=await job('fault');
|
||||
assert(fault.fault_rolled_back);assert.equal(fault.business_fingerprint,before.business_fingerprint);
|
||||
const recognised=await Promise.all([job('validate'),job('validate')]);
|
||||
assert(recognised.every(result=>result.state==='recognised'));
|
||||
assert.equal(recognised[0].obligation_id,recognised[1].obligation_id);
|
||||
const after=await job('inspect');
|
||||
assert.equal(after.principal_minor,1160000);assert.equal(after.balance_minor,860000);
|
||||
assert.equal(after.confirmed_minor,300000);assert.equal(after.pending_minor,100000);assert.equal(after.rejected_minor,50000);
|
||||
assert.equal(after.obligations,1);assert.equal(after.imports,1);assert.equal(after.payment_links,3);
|
||||
assert.equal(after.business_fingerprint,before.business_fingerprint);
|
||||
assert(after.ui_balance_correct);assert(after.changed_history_refused);
|
||||
assert.equal(after.due_on,null);assert.equal(after.days_overdue,null);assert.equal(after.append_only_tables.length,2);
|
||||
if(process.argv.includes('--decisions')) {
|
||||
const fault=await job('decision_fault');
|
||||
assert(fault.rolled_back);assert.equal(fault.balance_minor,860000);
|
||||
const concurrent=await Promise.all([job('decision_confirm'),job('decision_confirm')]);
|
||||
assert(concurrent.every(r=>r.balance_minor===760000 && r.frozen===r.original));
|
||||
const final=await job('decision_inspect');
|
||||
assert.equal(final.balance_minor,760000);assert.equal(final.confirmed_minor,400000);assert.equal(final.pending_minor,0);
|
||||
assert.equal(final.decisions,1);assert.equal(final.events,1);assert.equal(final.original_status,'confirmed');assert(final.append_only);
|
||||
assert(concurrent.every(r=>r.frozen===final.frozen));
|
||||
await fs.writeFile('docs/proofs/payable-payment-decisions-pg.json',JSON.stringify({checked_at:new Date().toISOString(),passed:true,atomic_rollback:true,
|
||||
concurrent_decision_once:true,original_import_preserved:true,new_transfers_executed:false,scenario:final,visual_review:false},null,2)+'\n');
|
||||
await run(php, [`${sgf}/scripts/payables-provision.php`, '--proof'], {
|
||||
cwd: sgf,
|
||||
windowsHide: true,
|
||||
timeout: 180000,
|
||||
maxBuffer: 100000,
|
||||
});
|
||||
const before = await job('setup');
|
||||
const fault = await job('fault');
|
||||
assert(fault.fault_rolled_back);
|
||||
assert.equal(fault.business_fingerprint, before.business_fingerprint);
|
||||
const recognised = await Promise.all([job('validate'), job('validate')]);
|
||||
assert(recognised.every((result) => result.state === 'recognised'));
|
||||
assert.equal(recognised[0].obligation_id, recognised[1].obligation_id);
|
||||
const after = await job('inspect');
|
||||
assert.equal(after.principal_minor, 1160000);
|
||||
assert.equal(after.balance_minor, 860000);
|
||||
assert.equal(after.confirmed_minor, 300000);
|
||||
assert.equal(after.pending_minor, 100000);
|
||||
assert.equal(after.rejected_minor, 50000);
|
||||
assert.equal(after.obligations, 1);
|
||||
assert.equal(after.imports, 1);
|
||||
assert.equal(after.payment_links, 3);
|
||||
assert.equal(after.business_fingerprint, before.business_fingerprint);
|
||||
assert(after.ui_balance_correct);
|
||||
assert(after.changed_history_refused);
|
||||
assert.equal(after.due_on, null);
|
||||
assert.equal(after.days_overdue, null);
|
||||
assert.equal(after.append_only_tables.length, 2);
|
||||
if (process.argv.includes('--decisions')) {
|
||||
const fault = await job('decision_fault');
|
||||
assert(fault.rolled_back);
|
||||
assert.equal(fault.balance_minor, 860000);
|
||||
const concurrent = await Promise.all([
|
||||
job('decision_confirm'),
|
||||
job('decision_confirm'),
|
||||
]);
|
||||
assert(
|
||||
concurrent.every(
|
||||
(r) => r.balance_minor === 760000 && r.frozen === r.original,
|
||||
),
|
||||
);
|
||||
const final = await job('decision_inspect');
|
||||
assert.equal(final.balance_minor, 760000);
|
||||
assert.equal(final.confirmed_minor, 400000);
|
||||
assert.equal(final.pending_minor, 0);
|
||||
assert.equal(final.decisions, 1);
|
||||
assert.equal(final.events, 1);
|
||||
assert.equal(final.original_status, 'confirmed');
|
||||
assert(final.append_only);
|
||||
assert(concurrent.every((r) => r.frozen === final.frozen));
|
||||
await fs.writeFile(
|
||||
'docs/proofs/payable-payment-decisions-pg.json',
|
||||
JSON.stringify(
|
||||
{
|
||||
checked_at: new Date().toISOString(),
|
||||
passed: true,
|
||||
atomic_rollback: true,
|
||||
concurrent_decision_once: true,
|
||||
original_import_preserved: true,
|
||||
new_transfers_executed: false,
|
||||
scenario: final,
|
||||
visual_review: false,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
) + '\n',
|
||||
);
|
||||
}
|
||||
await fs.writeFile('docs/proofs/payable-history-pg.json',JSON.stringify({checked_at:new Date().toISOString(),passed:true,
|
||||
fault_recovery:true,concurrent_recognition:true,original_business_history_unchanged:true,new_transfers_executed:false,
|
||||
scenario:after,visual_review:false},null,2)+'\n');
|
||||
console.log('PostgreSQL historical import passed: gross principal, single deduction, pending/rejected preservation, concurrency, rollback and immutable history.');
|
||||
await fs.writeFile(
|
||||
'docs/proofs/payable-history-pg.json',
|
||||
JSON.stringify(
|
||||
{
|
||||
checked_at: new Date().toISOString(),
|
||||
passed: true,
|
||||
fault_recovery: true,
|
||||
concurrent_recognition: true,
|
||||
original_business_history_unchanged: true,
|
||||
new_transfers_executed: false,
|
||||
scenario: after,
|
||||
visual_review: false,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
) + '\n',
|
||||
);
|
||||
console.log(
|
||||
'PostgreSQL historical import passed: gross principal, single deduction, pending/rejected preservation, concurrency, rollback and immutable history.',
|
||||
);
|
||||
} finally {
|
||||
await run(php,[`${sgf}/scripts/payables-provision.php`,'--cleanup-proof'],{cwd:sgf,windowsHide:true,timeout:90000,maxBuffer:100000});
|
||||
await run(
|
||||
php,
|
||||
[`${sgf}/scripts/payables-provision.php`, '--cleanup-proof'],
|
||||
{ cwd: sgf, windowsHide: true, timeout: 90000, maxBuffer: 100000 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\PurchasingRole;
|
||||
use App\Models\Tenant;
|
||||
use App\Models\User;
|
||||
use App\Services\PublicNavigation;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Inertia\Testing\AssertableInertia as Assert;
|
||||
|
||||
beforeEach(function () {
|
||||
config(['public_navigation.enabled' => true]);
|
||||
$this->withoutVite();
|
||||
});
|
||||
|
||||
test('anonymous visitors enter the real dashboard without creating an authenticated identity', function () {
|
||||
$this->get('/')->assertRedirect('/dashboard');
|
||||
$this->get('/dashboard')->assertInertia(fn (Assert $p) => $p->component('dashboard')->where('auth.user', null)->where('company', null)->where('stats.total', 0)->where('publicNavigation.dataScope', 'none'));
|
||||
$this->assertGuest('web');
|
||||
expect(User::query()->count())->toBe(0);
|
||||
});
|
||||
|
||||
test('all allowlisted real screens support direct anonymous navigation without business queries', function () {
|
||||
$queries = [];
|
||||
DB::listen(function ($query) use (&$queries) {
|
||||
$queries[] = $query->sql;
|
||||
});
|
||||
$fixtures = [];
|
||||
foreach (PublicNavigation::PAGES as $path => $component) {
|
||||
$response = $this->get($path)->assertOk()->assertInertia(fn (Assert $p) => $p->component($component)->where('auth.user', null));
|
||||
$fixtures[$component] = $response->original->getData()['page'];
|
||||
$this->assertGuest('web');
|
||||
}
|
||||
expect($queries)->toBe([]);
|
||||
file_put_contents(storage_path('app/private/public-navigation-fixtures.json'), json_encode($fixtures, JSON_THROW_ON_ERROR));
|
||||
});
|
||||
|
||||
test('anonymous searches pagination and filters stay empty and never select either organisation', function () {
|
||||
foreach (['Empresa reservada A', 'Empresa reservada B'] as $name) {
|
||||
$tenant = Tenant::query()->create(['name' => $name]);
|
||||
User::factory()->create(['tenant_id' => $tenant->id, 'name' => 'Nome confidencial']);
|
||||
}
|
||||
$this->get('/requisicoes?search=Empresa&page=2&status=pending')->assertInertia(fn (Assert $p) => $p->where('filters.search', 'Empresa')->where('requisitions.data', [])->where('requisitions.current_page', 2))->assertDontSee('Nome confidencial')->assertDontSee('Empresa reservada');
|
||||
$this->get('/stock?search=Nome&warehouse_id=1')->assertInertia(fn (Assert $p) => $p->where('items', [])->where('filters.search', 'Nome'));
|
||||
});
|
||||
|
||||
test('sensitive records access settings exports and every business write remain authenticated', function () {
|
||||
foreach (['/pessoas/cadastro', '/pessoas/1', '/fornecedores/1/dados-bancarios', '/administracao/parametrizacao', '/relatorios', '/relatorios/compras.csv', '/settings/profile'] as $path) {
|
||||
$this->get($path)->assertRedirect();
|
||||
}
|
||||
$this->get('/api/supplier-payables')->assertUnauthorized();
|
||||
foreach (['/requisicoes', '/pessoas', '/stock/artigos', '/administracao/parametrizacao/tax'] as $path) {
|
||||
$this->postJson($path, [])->assertUnauthorized();
|
||||
}
|
||||
});
|
||||
|
||||
test('authenticated sessions retain their original controllers and permissions and disabled mode remains private', function () {
|
||||
$tenant = Tenant::query()->create(['name' => 'Empresa autenticada']);
|
||||
$user = User::factory()->create(['tenant_id' => $tenant->id, 'purchasing_role' => PurchasingRole::Requester]);
|
||||
$this->actingAs($user)->get('/dashboard')->assertInertia(fn (Assert $p) => $p->where('company.name', 'Empresa autenticada')->missing('publicNavigation'));
|
||||
$this->get('/administracao/parametrizacao')->assertForbidden();
|
||||
auth()->logout();
|
||||
config(['public_navigation.enabled' => false]);
|
||||
$this->get('/dashboard')->assertRedirect();
|
||||
});
|
||||
|
||||
test('every authenticated mutation route remains inaccessible to an anonymous visitor', function () {
|
||||
$checked = 0;
|
||||
foreach (Route::getRoutes() as $route) {
|
||||
if (! collect($route->gatherMiddleware())->contains(fn ($middleware) => $middleware === 'auth' || str_starts_with($middleware, 'auth:'))) {
|
||||
continue;
|
||||
}
|
||||
$uri = '/'.preg_replace('/\{[^}]+\}/', '1', $route->uri());
|
||||
foreach (array_intersect($route->methods(), ['POST', 'PUT', 'PATCH', 'DELETE']) as $method) {
|
||||
$response = $this->json($method, $uri, []);
|
||||
expect(in_array($response->status(), [401, 404], true))->toBeTrue($method.' '.$uri.' must remain protected');
|
||||
$checked++;
|
||||
}
|
||||
}
|
||||
expect($checked)->toBeGreaterThan(50);
|
||||
$this->assertGuest('web');
|
||||
});
|
||||
@@ -1,9 +1,18 @@
|
||||
{
|
||||
"synthetic_only": true,
|
||||
"origin": "manual fixture, not model output",
|
||||
"claims": [
|
||||
{"text": "A requisição fictícia está em aprovação; não foi aprovada.", "source_ids": ["request"]},
|
||||
{"text": "Existe uma proposta fictícia registada; a comparação ainda não está pronta.", "source_ids": ["comparison"]},
|
||||
{"text": "Não consta ordem de compra no cenário fictício.", "source_ids": ["order"]}
|
||||
]
|
||||
"synthetic_only": true,
|
||||
"origin": "manual fixture, not model output",
|
||||
"claims": [
|
||||
{
|
||||
"text": "A requisição fictícia está em aprovação; não foi aprovada.",
|
||||
"source_ids": ["request"]
|
||||
},
|
||||
{
|
||||
"text": "Existe uma proposta fictícia registada; a comparação ainda não está pronta.",
|
||||
"source_ids": ["comparison"]
|
||||
},
|
||||
{
|
||||
"text": "Não consta ordem de compra no cenário fictício.",
|
||||
"source_ids": ["order"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { App } from '@inertiajs/react';
|
||||
import type { Page } from '@inertiajs/core';
|
||||
import type { ComponentType } from 'react';
|
||||
import { renderToString } from 'react-dom/server';
|
||||
import { expect, test } from 'vitest';
|
||||
import AppLayout from '@/layouts/app-layout';
|
||||
import { procurementNavigation } from '@/lib/procurement-navigation';
|
||||
|
||||
const fixtures = JSON.parse(
|
||||
readFileSync(
|
||||
new URL(
|
||||
'../../storage/app/private/public-navigation-fixtures.json',
|
||||
import.meta.url,
|
||||
),
|
||||
'utf8',
|
||||
),
|
||||
) as Record<string, Page>;
|
||||
const modules = import.meta.glob<{
|
||||
default: ComponentType<Record<string, unknown>>;
|
||||
}>('../../resources/js/pages/**/*.tsx', { eager: true });
|
||||
test.each(Object.entries(fixtures))(
|
||||
'real public page %s renders with the actual application shell and no authenticated user',
|
||||
(name, initialPage) => {
|
||||
const Screen = modules[`../../resources/js/pages/${name}.tsx`]?.default;
|
||||
expect(Screen).toBeDefined();
|
||||
const Component = (props: Record<string, unknown>) => (
|
||||
<AppLayout>
|
||||
<Screen {...props} />
|
||||
</AppLayout>
|
||||
);
|
||||
const html = renderToString(
|
||||
<App initialPage={initialPage} initialComponent={Component} />,
|
||||
);
|
||||
expect(html).toContain('Consulta pública de desenvolvimento');
|
||||
expect(html).not.toContain('/consulta?');
|
||||
expect(html).toContain('/dashboard');
|
||||
},
|
||||
);
|
||||
test('public menu links only to the explicit public screens and never grants a staff role', () => {
|
||||
const paths = (
|
||||
fixtures.dashboard.props.publicNavigation as { paths: string[] }
|
||||
).paths;
|
||||
const groups = procurementNavigation('', false, paths);
|
||||
expect(groups.length).toBeGreaterThan(3);
|
||||
for (const entry of groups.flatMap((group) => group.entries))
|
||||
expect(paths).toContain(new URL(entry.path, 'http://local').pathname);
|
||||
expect(
|
||||
groups
|
||||
.flatMap((group) => group.entries)
|
||||
.some((entry) => entry.path.startsWith('/administracao')),
|
||||
).toBe(false);
|
||||
expect(fixtures.dashboard.props.auth).toEqual({ user: null });
|
||||
});
|
||||
@@ -6,19 +6,63 @@ import RequisitionForm from '@/pages/requisitions/form';
|
||||
import Catalogue from '@/pages/stock/catalogue';
|
||||
|
||||
const page = {
|
||||
component: 'requisitions/form', url: '/requisicoes/1/editar', version: 'catalogue',
|
||||
props: { errors: {} }, clearHistory: false, encryptHistory: true,
|
||||
component: 'requisitions/form',
|
||||
url: '/requisicoes/1/editar',
|
||||
version: 'catalogue',
|
||||
props: { errors: {} },
|
||||
clearHistory: false,
|
||||
encryptHistory: true,
|
||||
} satisfies Page;
|
||||
|
||||
test('saved article version remains selected when the published source has changed', () => {
|
||||
const initialPage = { ...page, props: { ...page.props, costCenters: [],
|
||||
requisition: { id: 1, cost_center_id: 1, priority: 'normal', needed_by: '2026-11-10', justification: 'Pedido', items: [{
|
||||
id: 4, catalogue_entry_id: 7, catalogue_snapshot: { version: 1, code: 'ART-7' },
|
||||
description: 'Nome original', category: 'Material', purchase_type: 'consumable', quantity: '2', unit: 'cx', unit_price_minor: 1000, tax_rate: '16',
|
||||
}] },
|
||||
catalogue: { ready: true, search: '', more: false, items: [{ id: 7, version: 2, stock_item_id: 3, code: 'ART-7', description: 'Nome actualizado', unit: 'cx', category: 'Material', purchase_type: 'consumable' }] },
|
||||
} };
|
||||
const html = renderToString(<App initialPage={initialPage} initialComponent={RequisitionForm} />);
|
||||
const initialPage = {
|
||||
...page,
|
||||
props: {
|
||||
...page.props,
|
||||
costCenters: [],
|
||||
requisition: {
|
||||
id: 1,
|
||||
cost_center_id: 1,
|
||||
priority: 'normal',
|
||||
needed_by: '2026-11-10',
|
||||
justification: 'Pedido',
|
||||
items: [
|
||||
{
|
||||
id: 4,
|
||||
catalogue_entry_id: 7,
|
||||
catalogue_snapshot: { version: 1, code: 'ART-7' },
|
||||
description: 'Nome original',
|
||||
category: 'Material',
|
||||
purchase_type: 'consumable',
|
||||
quantity: '2',
|
||||
unit: 'cx',
|
||||
unit_price_minor: 1000,
|
||||
tax_rate: '16',
|
||||
},
|
||||
],
|
||||
},
|
||||
catalogue: {
|
||||
ready: true,
|
||||
search: '',
|
||||
more: false,
|
||||
items: [
|
||||
{
|
||||
id: 7,
|
||||
version: 2,
|
||||
stock_item_id: 3,
|
||||
code: 'ART-7',
|
||||
description: 'Nome actualizado',
|
||||
unit: 'cx',
|
||||
category: 'Material',
|
||||
purchase_type: 'consumable',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
const html = renderToString(
|
||||
<App initialPage={initialPage} initialComponent={RequisitionForm} />,
|
||||
);
|
||||
expect(html).toMatch(/<option value="7:1" selected="">/);
|
||||
expect(html).toContain('<option value="7:2">');
|
||||
expect(html).toContain('value="Nome original"');
|
||||
@@ -26,15 +70,42 @@ test('saved article version remains selected when the published source has chang
|
||||
expect(html).toContain('Preenchimento livre');
|
||||
});
|
||||
|
||||
test.each([true, false])('catalogue editing follows authority (%s)', (canManage) => {
|
||||
const initialPage = { ...page, component: 'stock/catalogue', props: { ...page.props,
|
||||
ready: true, canManage, filters: {}, records: { total: 1, prev_page_url: null, next_page_url: null, data: [{
|
||||
id: 3, code: 'ART-7', name: 'Artigo existente', unit: 'cx', stock_basis: 'a'.repeat(64), version: 1,
|
||||
category: 'Material', purchase_type: 'consumable', published: true, source_current: true,
|
||||
}] },
|
||||
} };
|
||||
const html = renderToString(<App initialPage={initialPage} initialComponent={Catalogue} />);
|
||||
expect(html).toContain('Artigo existente');
|
||||
expect(html.includes('Guardar versão do catálogo')).toBe(canManage);
|
||||
});
|
||||
|
||||
test.each([true, false])(
|
||||
'catalogue editing follows authority (%s)',
|
||||
(canManage) => {
|
||||
const initialPage = {
|
||||
...page,
|
||||
component: 'stock/catalogue',
|
||||
props: {
|
||||
...page.props,
|
||||
ready: true,
|
||||
canManage,
|
||||
filters: {},
|
||||
records: {
|
||||
total: 1,
|
||||
prev_page_url: null,
|
||||
next_page_url: null,
|
||||
data: [
|
||||
{
|
||||
id: 3,
|
||||
code: 'ART-7',
|
||||
name: 'Artigo existente',
|
||||
unit: 'cx',
|
||||
stock_basis: 'a'.repeat(64),
|
||||
version: 1,
|
||||
category: 'Material',
|
||||
purchase_type: 'consumable',
|
||||
published: true,
|
||||
source_current: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
const html = renderToString(
|
||||
<App initialPage={initialPage} initialComponent={Catalogue} />,
|
||||
);
|
||||
expect(html).toContain('Artigo existente');
|
||||
expect(html.includes('Guardar versão do catálogo')).toBe(canManage);
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user