commit6
tests / ci (push) Canceled after 0s

This commit is contained in:
2026-10-30 08:07:27 +02:00
parent 413c201d97
commit 465426847e
8 changed files with 72 additions and 0 deletions
@@ -0,0 +1,13 @@
<?php
namespace App\Http\Controllers;
use App\Services\SupplierPayables;
use Tempo\Finance\Payables\SettlementHttp;
final class SupplierPaymentsController extends SettlementHttp
{
protected const MODE = 'compras';
protected const SERVICE = SupplierPayables::class;
}
+1
View File
@@ -120,6 +120,7 @@ final class SupplierPayables
$engine = $this->engine($u);
$d = $engine->listing(Invoice::query()->where('tenant_id', $u->tenant_id)->pluck('id')->all());
$d['permissions'] = ['prepare' => $this->can($u, 'prepare'), 'validate' => $this->can($u, 'validate')];
$d['payments_available'] = app()->environment('payable-testing') && Schema::hasTable('ps_payments') && $this->can($u, 'payments_view');
$d['history_import_available'] = self::historyEnabled();
$d['history_decisions_available'] = self::historyDecisionsEnabled();
+3
View File
@@ -1,5 +1,8 @@
# Continuidade do desenvolvimento do SGC
**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.
+1
View File
@@ -56,6 +56,7 @@ export function AppSidebar() {
...(canReadPurchases
? [
{ title: 'Fornecedores', href: '/fornecedores', icon: Users },
{ title: 'Avaliação de fornecedores', href: '/fornecedores/avaliacoes', icon: Users },
{ title: 'Cotações', href: '/cotacoes', icon: Scale },
{
title: 'Ordens de compra',
@@ -113,6 +113,12 @@ export function procurementNavigation(
path: '/fornecedores',
roles: readers,
},
{
id: 'supplier_evaluations',
label: 'Avaliação de fornecedores',
path: '/fornecedores/avaliacoes',
roles: readers,
},
],
},
{
File diff suppressed because one or more lines are too long
@@ -0,0 +1,39 @@
import { Head } from '@inertiajs/react';
import { useState } from 'react';
import { PageTitle, Panel } from '@/components/procurement';
import proposal from './evaluation-proposal';
const areas = [
['Avaliações planeadas', 'Definir fornecedor, período, âmbito e responsáveis pela avaliação.'],
['Nova avaliação', 'Identificar contratos e ordens abrangidas, escolher a grelha de bens ou serviços e reunir notas, justificações e evidências.'],
['Rascunhos', 'Preparar a avaliação e completar evidências antes de submeter para revisão.'],
['Avaliações em revisão', 'Conferência independente, com devolução para correcção ou aprovação documentada.'],
['Avaliações aprovadas', 'Consultar versões aprovadas; rectificações devem gerar revisão identificável.'],
['Histórico por fornecedor', 'Consultar a evolução, observações e contestações com decisão documentada. Comparar apenas âmbitos e grelhas equivalentes.'],
['Planos de melhoria', 'Acompanhar acções, responsáveis, prazos, evidências de conclusão e validação de eficácia.'],
['Relatórios de avaliação', 'Ficha por fornecedor e período, resultados por critério, ocorrências críticas e cobertura de evidências.'],
];
export default function SupplierEvaluations() {
const [selected, setSelected] = useState(0);
return <>
<Head title="Avaliação de fornecedores" />
<div className="space-y-6 p-4 md:p-8">
<PageTitle eyebrow="Fornecedores" title="Avaliação de fornecedores" description="Acompanhamento da prestação da entidade fornecedora durante ou após o fornecimento." />
<div className="rounded-xl border border-amber-300 bg-amber-50 p-4 text-sm text-amber-950">
Proposta funcional para validação da organização. As áreas abaixo descrevem o circuito previsto; o registo e a aprovação de avaliações ainda não estão disponíveis. Pesos, escala e limiares são ilustrativos.
</div>
<nav aria-label="Áreas da avaliação de fornecedores" className="flex flex-wrap gap-2">
{areas.map(([label], index) => <button key={label} type="button" aria-pressed={selected === index} onClick={() => setSelected(index)} className={`rounded-lg border px-4 py-2 text-sm ${selected === index ? 'bg-primary text-primary-foreground' : 'bg-card hover:bg-muted'}`}>{label}</button>)}
</nav>
<Panel title={areas[selected][0]} description={areas[selected][1]}>
<p className="text-sm text-muted-foreground">Área prevista na proposta. Não são apresentados resultados de fornecedores reais.</p>
</Panel>
<Panel title="Proposta funcional completa" description="Qualificação verifica elegibilidade; avaliação analisa o cumprimento efectivo. Uma pontuação não suspende nem exclui automaticamente um fornecedor.">
<details><summary className="cursor-pointer font-medium">Consultar campos, grelhas, evidências e regras propostas</summary>
<div className="mt-4 whitespace-pre-wrap text-sm leading-7">{proposal}</div>
</details>
</Panel>
</div>
</>;
}
SupplierEvaluations.layout = { breadcrumbs: [{ title: 'Fornecedores', href: '/fornecedores' }, { title: 'Avaliação de fornecedores', href: '/fornecedores/avaliacoes' }] };
+8
View File
@@ -41,7 +41,15 @@ Route::middleware(['auth', 'verified'])->group(function () {
Route::post('empresa/iniciar', [CompanyController::class, 'store'])->name('company.store');
Route::middleware(EnsureCompanyIsConfigured::class)->group(function () {
Route::get('fornecedores/avaliacoes', function (\Illuminate\Http\Request $request) {
abort_unless(in_array($request->user()->purchasing_role, [\App\Enums\PurchasingRole::Admin, \App\Enums\PurchasingRole::Procurement, \App\Enums\PurchasingRole::Finance, \App\Enums\PurchasingRole::DepartmentHead]), 403);
return \Inertia\Inertia::render('suppliers/evaluations');
})->name('suppliers.evaluations');
Route::get('obrigacoes-fornecedores', [\App\Http\Controllers\SupplierPayablesController::class,'page']);
Route::get('supplier-payments', [\App\Http\Controllers\SupplierPaymentsController::class,'page']);
Route::get('api/supplier-payments', [\App\Http\Controllers\SupplierPaymentsController::class,'index']);
Route::post('api/supplier-payments/{action}', [\App\Http\Controllers\SupplierPaymentsController::class,'command'])->whereIn('action',['payment-prepare','payment-decide','allocation-prepare','allocation-decide']);
Route::get('api/supplier-payables', [\App\Http\Controllers\SupplierPayablesController::class,'index']);
Route::post('api/supplier-payables/{action}', [\App\Http\Controllers\SupplierPayablesController::class,'command'])->whereIn('action',['prepare','validate','prepare_history','validate_history']);
Route::get('politica-recomendacao', [FormalRecommendationController::class, 'policy'])->name('recommendations.policy');