import { Head, Link, useForm } from '@inertiajs/react'; import { useState } from 'react'; import { FormErrors, PageTitle, Panel, controlClass, } from '@/components/procurement'; import { Button } from '@/components/ui/button'; type Fields = Record; type Application = { operation_id: number; reference: string; quantity_milli: number; }; type RecordVersion = { id: number; record_key: string; version: number; state: string; data: Fields & { applications?: Application[] }; reason: string; created_at: string; snapshot: { effective_allocated_milli?: number; reversed_operations?: number[]; item: { name: string; code: string; unit: string }; warehouse: { name: string }; substitute?: { name: string; code: string; unit: string }; order?: { number: string }; }; }; type Source = { id: number; label: string; stock_item_id: number | null; quantity_milli: number; reference?: string; needed_on?: string; }; type Props = { kind: 'reservation' | 'delivery' | 'alternative'; ready: boolean; canPrepare: boolean; canReview: boolean; eligible: Record; pending: Record; reversedOperations?: number[]; records: RecordVersion[]; history: Record; items: { id: number; name: string; code: string; unit: string }[]; warehouses: { id: number; name: string; code: string }[]; requestLines: Source[]; orderLines: Source[]; operations: { id: number; reference: string; stock_item_id: number; warehouse_id: number; quantity_milli: number; }[]; }; const titles = { reservation: 'Reservas de necessidades', delivery: 'Entregas confirmadas', alternative: 'Alternativas técnicas', }; const states: Record = { draft: 'Em análise técnica', returned: 'Devolvida para correcção', approved: 'Validada tecnicamente', suspended: 'Suspensa', active: 'Activa', cancelled: 'Cancelada', fulfilled: 'Satisfeita por saídas', }; const compatibility: Record = { equivalent: 'Equivalente técnica', conditional: 'Substituição condicionada', incompatible: 'Não compatível', pending: 'Pendente de análise', }; const quantity = (milli: number) => (milli / 1000).toLocaleString('pt-MZ', { maximumFractionDigits: 3 }); function Prepare({ props, old }: { props: Props; old?: RecordVersion }) { const defaults: Fields = { stock_item_id: '', warehouse_id: '', ...(props.kind === 'alternative' ? { substitute_id: '', application: '', compatibility: 'pending', conditions: '', evidence: '', factor_numerator: 1, factor_denominator: 1, valid_until: '', } : props.kind === 'reservation' ? { requisition_item_id: '', quantity: '', needed_on: '', additional: false, reference: '', } : { purchase_order_item_id: '', quantity: '', expected_on: '', confirmed_on: '', evidence: '', }), }; if (old) { for (const key of Object.keys(defaults)) if (key in old.data) defaults[key] = old.data[key]; if (old.data.quantity_milli !== undefined) defaults.quantity = Number(old.data.quantity_milli) / 1000; } const form = useForm({ operation_key: crypto.randomUUID(), record_key: old?.record_key, version: old?.version ?? 0, action: 'save', reason: '', data: defaults, }); const update = (key: string, value: Fields[string]) => form.setData('data', { ...form.data.data, [key]: value }); const field = ( key: string, label: string, type = 'text', optional = false, ) => ( ); const selector = ( key: string, label: string, entries: { id: number; label: string }[], ) => ( ); const sources = props.kind === 'delivery' ? props.orderLines : props.requestLines; return (
{ e.preventDefault(); form.transform((input) => { const data = { ...input.data }; if (props.kind !== 'alternative') { data.quantity_milli = Math.round( Number(data.quantity) * 1000, ); delete data.quantity; } return { ...input, data }; }); form.post(`/compras/fornecimento/${props.kind}`, { preserveScroll: true, onSuccess: () => form.setData('operation_key', crypto.randomUUID()), }); }} >
{props.kind !== 'alternative' && ( )} {selector( 'stock_item_id', 'Artigo e unidade', props.items.map((i) => ({ id: i.id, label: `${i.code} · ${i.name} (${i.unit})`, })), )} {selector( 'warehouse_id', 'Armazém de destino ou utilização', props.warehouses.map((w) => ({ id: w.id, label: `${w.code} · ${w.name}`, })), )} {props.kind === 'alternative' ? ( <> {selector( 'substitute_id', 'Artigo substituto — neste sentido', props.items .filter( (i) => i.id !== Number(form.data.data.stock_item_id), ) .map((i) => ({ id: i.id, label: `${i.code} · ${i.name} (${i.unit})`, })), )} {field( 'application', 'Aplicação ou equipamento autorizado', )} {form.data.data.compatibility === 'conditional' && field( 'conditions', 'Condições obrigatórias de utilização', )} {field( 'evidence', 'Referência das especificações comparadas e evidência técnica', )} {field('valid_until', 'Validade da análise', 'date')} ) : ( <> {field( 'quantity', props.kind === 'delivery' ? 'Quantidade acumulada confirmada na linha (inclui recepções anteriores)' : 'Quantidade na unidade do artigo', 'number', )} {props.kind === 'reservation' ? ( <> {field( 'needed_on', 'Data da necessidade', 'date', )} {!form.data.data.requisition_item_id && field( 'reference', 'Referência única da necessidade', )} ) : ( <> {field( 'expected_on', 'Disponível para utilização em', 'date', )} {field( 'confirmed_on', 'Data da confirmação do fornecedor', 'date', )} {field( 'evidence', 'Referência da confirmação recebida', )} )} )}
{props.kind === 'alternative' && (
Conversão técnica de quantidades

Quantidade do substituto = quantidade original × numerador ÷ denominador. O factor pertence apenas a esta relação e necessita da validação técnica.

{field('factor_numerator', 'Numerador', 'number')} {field('factor_denominator', 'Denominador', 'number')}
)}