Files
compras/resources/js/pages/purchases/show.tsx
T

294 lines
14 KiB
TypeScript

import { Head, Link } from '@inertiajs/react';
import { Printer } from 'lucide-react';
import { PageTitle, Panel } from '@/components/procurement';
import { Button } from '@/components/ui/button';
import { date, money, roleLabels } from '@/types/procurement';
import type { PurchaseOrder } from '@/types/purchase';
type History = {
id: number;
action: string;
user_id: number;
created_at: string;
};
const actions: Record<string, string> = {
'award.created': 'Adjudicação registada',
'award.approved': 'Etapa do valor final aprovada',
'award.rejected': 'Adjudicação rejeitada',
'purchase_order.issued': 'Ordem emitida para revisão',
};
export default function OrderShow({
order,
history,
}: {
order: PurchaseOrder;
history: History[];
}) {
const snapshot = order.snapshot;
return (
<>
<Head title={order.number} />
<Link
href={`/ordens/${order.id}/recepcoes`}
className="mx-auto mt-4 w-full max-w-6xl px-5 text-sm font-medium text-teal-700 dark:text-teal-400"
>
Recepções, qualidade e quantidades pendentes
</Link>
<div className="mx-auto flex w-full max-w-6xl flex-col gap-7 p-5 lg:p-8">
<PageTitle
eyebrow={snapshot.company.name}
title={order.number}
description={`${snapshot.requisition.number} · Emitida em ${date(order.issued_at)} por ${snapshot.issuer.name}`}
action={
<Button asChild>
<a
href={`/ordens/${order.id}/imprimir`}
target="_blank"
rel="noopener noreferrer"
>
<Printer className="size-4" />
Imprimir / guardar PDF
</a>
</Button>
}
/>
<div className="rounded-xl border border-amber-200 bg-amber-50 p-5 text-sm text-amber-900 dark:border-amber-900 dark:bg-amber-950 dark:text-amber-100">
<strong>
Documento de desenvolvimento. Não autoriza compras
reais.
</strong>{' '}
Orçamento não verificado. Os dados apresentados foram
congelados na emissão.
{snapshot.approval_rules.rules_provisional &&
' Regras de aprovação provisórias.'}
</div>
<div className="grid gap-6 md:grid-cols-2">
{snapshot.investment_rules && (
<Panel title="Regra de investimento congelada">
<p>
Versão {snapshot.investment_rules.revision} ·
Tecto{' '}
{money(snapshot.investment_rules.ceiling_minor)}{' '}
·{' '}
{snapshot.investment_rules.includes_tax
? 'com IVA'
: 'sem IVA'}
</p>
<p>
Vigência: {snapshot.investment_rules.valid_from}{' '}
a {snapshot.investment_rules.valid_until}
</p>
{snapshot.equipment_requests?.map((line) => (
<p key={line.id} className="mt-2">
{line.description} · Área:{' '}
{line.production_area}
</p>
))}
</Panel>
)}
<Panel title="Fornecedor">
<dl className="space-y-3 text-sm">
<div>
<dt className="text-muted-foreground">
Nome e identificação fiscal
</dt>
<dd>
{snapshot.supplier.name} · NUIT{' '}
{snapshot.supplier.nuit}
</dd>
</div>
<div>
<dt className="text-muted-foreground">
Contacto
</dt>
<dd>
{snapshot.supplier.contact} ·{' '}
{snapshot.supplier.phone}
<br />
{snapshot.supplier.email}
</dd>
</div>
<div>
<dt className="text-muted-foreground">
Morada
</dt>
<dd className="whitespace-pre-wrap">
{snapshot.supplier.address}
</dd>
</div>
</dl>
</Panel>
<Panel title="Condições acordadas">
<dl className="space-y-3 text-sm">
<div>
<dt className="text-muted-foreground">
Entrega
</dt>
<dd>
{date(snapshot.award.delivery_date)} ·{' '}
{snapshot.award.delivery_location}
</dd>
</div>
<div>
<dt className="text-muted-foreground">
Pagamento
</dt>
<dd>{snapshot.quotation.payment_terms}</dd>
</div>
<div>
<dt className="text-muted-foreground">
Departamento e centro de custo
</dt>
<dd>
{snapshot.department.name} ·{' '}
{snapshot.cost_center.code} ·{' '}
{snapshot.cost_center.name}
</dd>
</div>
<div>
<dt className="text-muted-foreground">
Proposta
</dt>
<dd>
{snapshot.quotation.reference} ·{' '}
{order.currency}
</dd>
</div>
</dl>
</Panel>
</div>
<Panel title="Artigos e valores congelados">
<div className="overflow-x-auto">
<table className="w-full text-left text-sm">
<thead className="border-b text-muted-foreground">
<tr>
<th className="pr-4 pb-3">Descrição</th>
<th className="pr-4">Quantidade</th>
<th className="pr-4">Preço sem IVA</th>
<th className="pr-4">IVA</th>
<th>Total com IVA</th>
</tr>
</thead>
<tbody>
{order.items.map((item) => (
<tr key={item.id} className="border-b">
<td className="py-4 pr-4">
{item.description}
</td>
<td className="pr-4 whitespace-nowrap">
{Number(item.quantity)} {item.unit}
</td>
<td className="pr-4 whitespace-nowrap">
{money(item.unit_price_minor)}
</td>
<td className="pr-4">
{Number(item.tax_rate)}%
</td>
<td className="whitespace-nowrap">
{money(item.total_minor)}
</td>
</tr>
))}
</tbody>
</table>
</div>
<dl className="mt-5 ml-auto max-w-sm space-y-3 text-sm">
<div className="flex justify-between gap-4">
<dt>Subtotal sem IVA</dt>
<dd>{money(order.subtotal_minor)}</dd>
</div>
<div className="flex justify-between gap-4">
<dt>IVA</dt>
<dd>{money(order.tax_minor)}</dd>
</div>
<div className="flex justify-between gap-4 border-t pt-3 font-semibold">
<dt>Total com IVA</dt>
<dd>{money(order.total_minor)}</dd>
</div>
</dl>
</Panel>
<Panel title="Fundamento e matriz congelada">
<p className="text-sm whitespace-pre-wrap">
{snapshot.award.justification}
</p>
<div className="mt-5 space-y-4">
{snapshot.decision_matrix.map((row) => (
<div
key={row.id}
className="rounded-lg border p-4 text-sm"
>
<p className="font-semibold">
{row.supplier.name} · {row.reference} ·{' '}
{row.score === null
? 'Não elegível / incompleta'
: `${row.score.toFixed(2)} / 10`}
</p>
<p className="mt-1 text-muted-foreground">
{money(row.total_minor)} com IVA ·{' '}
{row.evaluation_count} avaliações elegíveis
</p>
{row.evaluations.map((e) => (
<p
key={e.evaluator_id}
className="mt-2 text-xs"
>
Avaliador #{e.evaluator_id}: qualidade{' '}
{e.quality}, histórico {e.history},
pagamento {e.payment}. {e.comment}
</p>
))}
</div>
))}
</div>
</Panel>
<Panel title="Aprovações do valor final">
<ol className="space-y-3">
{snapshot.final_approvals.map((step) => (
<li key={step.id} className="text-sm">
{roleLabels[step.role]} · Utilizador #
{step.approver_id} · Aprovada
{step.comment && (
<p className="mt-1 text-muted-foreground">
{step.comment}
</p>
)}
</li>
))}
</ol>
<p className="mt-4 text-xs text-muted-foreground">
Base:{' '}
{snapshot.approval_rules.approval_includes_tax
? 'com'
: 'sem'}{' '}
IVA. As aprovações da estimativa foram preservadas
separadamente.
</p>
</Panel>
<Panel title="Histórico do processo">
<ol className="space-y-3">
{history.map((event) => (
<li key={event.id} className="text-sm">
{actions[event.action]} ·{' '}
{date(event.created_at)} · Utilizador #
{event.user_id}
</li>
))}
</ol>
</Panel>
<Link
href="/ordens"
className="text-sm text-teal-700 dark:text-teal-400"
>
Voltar às ordens
</Link>
</div>
</>
);
}
OrderShow.layout = {
breadcrumbs: [
{ title: 'Ordens de compra', href: '/ordens' },
{ title: 'Documento emitido', href: '#' },
],
};