Record cut layers and marker references
This commit is contained in:
@@ -647,7 +647,9 @@ const initDB = async () => {
|
||||
ADD COLUMN IF NOT EXISTS actual_cut_quantity NUMERIC(14, 4),
|
||||
ADD COLUMN IF NOT EXISTS rejected_quantity NUMERIC(14, 4) NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS finished_quantity NUMERIC(14, 4),
|
||||
ADD COLUMN IF NOT EXISTS quality_notes TEXT;
|
||||
ADD COLUMN IF NOT EXISTS quality_notes TEXT,
|
||||
ADD COLUMN IF NOT EXISTS cut_layers INTEGER,
|
||||
ADD COLUMN IF NOT EXISTS marker_reference VARCHAR(160);
|
||||
`).catch(() => {});
|
||||
|
||||
await pool.query(`
|
||||
|
||||
@@ -322,6 +322,8 @@ const mapProductionOrderRow = (row) => {
|
||||
rejectedQuantity: Number(row.rejected_quantity || 0),
|
||||
finishedQuantity: row.finished_quantity === null ? null : Number(row.finished_quantity),
|
||||
qualityNotes: row.quality_notes || '',
|
||||
cutLayers: row.cut_layers === null || row.cut_layers === undefined ? null : Number(row.cut_layers),
|
||||
markerReference: row.marker_reference || '',
|
||||
defects: Array.isArray(row.defects) ? row.defects.map(mapDefect) : [],
|
||||
lotReservations: Array.isArray(row.lot_reservations) ? row.lot_reservations.map(mapLotReservation) : [],
|
||||
markers: Array.isArray(row.markers) ? row.markers.filter(Boolean) : [],
|
||||
@@ -526,6 +528,8 @@ const getOrderById = async (id, client = pool) => {
|
||||
po.rejected_quantity,
|
||||
po.finished_quantity,
|
||||
po.quality_notes,
|
||||
po.cut_layers,
|
||||
po.marker_reference,
|
||||
po.created_at,
|
||||
po.updated_at,
|
||||
COALESCE(
|
||||
@@ -664,6 +668,8 @@ const baseProductionOrderSelect = `
|
||||
po.rejected_quantity,
|
||||
po.finished_quantity,
|
||||
po.quality_notes,
|
||||
po.cut_layers,
|
||||
po.marker_reference,
|
||||
po.created_at,
|
||||
po.updated_at,
|
||||
COALESCE(
|
||||
@@ -1842,15 +1848,19 @@ const updateProductionOrderExecution = async (id, payload = {}) => {
|
||||
const rejectedQuantity = normalizeNonNegativeQuantity(payload.rejectedQuantity);
|
||||
const finishedQuantity = normalizeNonNegativeQuantity(payload.finishedQuantity);
|
||||
const qualityNotes = normalizeText(payload.qualityNotes);
|
||||
const cutLayers = payload.cutLayers === '' || payload.cutLayers === null || payload.cutLayers === undefined
|
||||
? null
|
||||
: normalizeInteger(payload.cutLayers);
|
||||
const markerReference = normalizeText(payload.markerReference);
|
||||
|
||||
if (actualCutQuantity === null && rejectedQuantity === null && finishedQuantity === null && !qualityNotes) {
|
||||
if (actualCutQuantity === null && rejectedQuantity === null && finishedQuantity === null && !qualityNotes && cutLayers === null && !markerReference) {
|
||||
const error = new Error('Informe uma quantidade de execução ou uma observação de qualidade.');
|
||||
error.statusCode = 400;
|
||||
throw error;
|
||||
}
|
||||
|
||||
const currentResult = await pool.query(`
|
||||
SELECT actual_cut_quantity, rejected_quantity, finished_quantity
|
||||
SELECT actual_cut_quantity, rejected_quantity, finished_quantity, cut_layers, marker_reference
|
||||
FROM production_orders
|
||||
WHERE id = $1;
|
||||
`, [id]);
|
||||
@@ -1864,6 +1874,14 @@ const updateProductionOrderExecution = async (id, payload = {}) => {
|
||||
const effectiveActual = actualCutQuantity === null ? (current.actual_cut_quantity === null ? null : Number(current.actual_cut_quantity)) : actualCutQuantity;
|
||||
const effectiveRejected = rejectedQuantity === null ? Number(current.rejected_quantity || 0) : rejectedQuantity;
|
||||
const effectiveFinished = finishedQuantity === null ? (current.finished_quantity === null ? null : Number(current.finished_quantity)) : finishedQuantity;
|
||||
const effectiveLayers = cutLayers === null ? (current.cut_layers === null ? null : Number(current.cut_layers)) : cutLayers;
|
||||
const effectiveMarkerReference = markerReference || current.marker_reference || '';
|
||||
|
||||
if (effectiveLayers !== null && effectiveLayers <= 0) {
|
||||
const error = new Error('O número de camadas deve ser maior que zero.');
|
||||
error.statusCode = 400;
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (effectiveActual !== null && effectiveRejected > effectiveActual) {
|
||||
const error = new Error('As perdas não podem ser maiores que a quantidade cortada.');
|
||||
@@ -1882,9 +1900,11 @@ const updateProductionOrderExecution = async (id, payload = {}) => {
|
||||
rejected_quantity = $2,
|
||||
finished_quantity = $3,
|
||||
quality_notes = NULLIF($4, ''),
|
||||
cut_layers = $5,
|
||||
marker_reference = NULLIF($6, ''),
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $5;
|
||||
`, [effectiveActual, effectiveRejected, effectiveFinished, qualityNotes, id]);
|
||||
WHERE id = $7;
|
||||
`, [effectiveActual, effectiveRejected, effectiveFinished, qualityNotes, effectiveLayers, effectiveMarkerReference, id]);
|
||||
|
||||
return getOrderById(id);
|
||||
};
|
||||
|
||||
@@ -307,6 +307,8 @@ const ProductionOrders = () => {
|
||||
rejectedQuantity: parseOptionalQuantity('rejectedQuantity'),
|
||||
finishedQuantity: parseOptionalQuantity('finishedQuantity'),
|
||||
qualityNotes: String(form.get('qualityNotes') || '').trim(),
|
||||
cutLayers: parseOptionalQuantity('cutLayers'),
|
||||
markerReference: String(form.get('markerReference') || '').trim(),
|
||||
});
|
||||
const nextSummary = await fetchProductionOrders(dateRange, { search: searchTerm }, { force: true });
|
||||
setSummary(nextSummary);
|
||||
@@ -419,6 +421,8 @@ const ProductionOrders = () => {
|
||||
<label className="text-xs font-bold text-dark-muted">Cortado real<input name="actualCutQuantity" inputMode="decimal" defaultValue={order.actualCutQuantity ?? ''} className="mt-1.5 h-11 w-full rounded-lg border border-dark-border bg-dark-input px-3 text-sm font-semibold text-dark-text outline-none placeholder:text-dark-muted focus:border-brand-primary" placeholder={formatQuantity(order.quantity)} /></label>
|
||||
<label className="text-xs font-bold text-dark-muted">Perdas / defeitos<input name="rejectedQuantity" inputMode="decimal" defaultValue={order.rejectedQuantity || ''} className="mt-1.5 h-11 w-full rounded-lg border border-dark-border bg-dark-input px-3 text-sm font-semibold text-dark-text outline-none placeholder:text-dark-muted focus:border-brand-primary" placeholder="0" /></label>
|
||||
<label className="text-xs font-bold text-dark-muted">Aprovado final<input name="finishedQuantity" inputMode="decimal" defaultValue={order.finishedQuantity ?? ''} className="mt-1.5 h-11 w-full rounded-lg border border-dark-border bg-dark-input px-3 text-sm font-semibold text-dark-text outline-none placeholder:text-dark-muted focus:border-brand-primary" placeholder="Após revisão" /></label>
|
||||
<label className="text-xs font-bold text-dark-muted">Camadas cortadas<input name="cutLayers" inputMode="numeric" defaultValue={order.cutLayers ?? ''} className="mt-1.5 h-11 w-full rounded-lg border border-dark-border bg-dark-input px-3 text-sm font-semibold text-dark-text outline-none placeholder:text-dark-muted focus:border-brand-primary" placeholder="Ex.: 12" /></label>
|
||||
<label className="text-xs font-bold text-dark-muted sm:col-span-2">Risco / encaixe<input name="markerReference" defaultValue={order.markerReference} className="mt-1.5 h-11 w-full rounded-lg border border-dark-border bg-dark-input px-3 text-sm font-semibold text-dark-text outline-none placeholder:text-dark-muted focus:border-brand-primary" placeholder="Código do risco, encaixe ou arquivo" /></label>
|
||||
<label className="text-xs font-bold text-dark-muted sm:col-span-3">Motivo de perda / observação<textarea name="qualityNotes" defaultValue={order.qualityNotes} className="mt-1.5 min-h-20 w-full rounded-lg border border-dark-border bg-dark-input px-3 py-2.5 text-sm font-semibold text-dark-text outline-none placeholder:text-dark-muted focus:border-brand-primary" placeholder="Ex.: 4 peças com mancha de tinta; 2 recuperadas na revisão." /></label>
|
||||
</div>
|
||||
<div className="mt-4 flex flex-wrap items-center justify-between gap-3"><p className="text-xs font-semibold text-dark-muted">Planejado: {formatQuantity(order.quantity)} {order.unit}</p><button type="submit" disabled={busyExecutionOrderId === order.id} className="inline-flex h-10 items-center gap-2 rounded-lg border border-brand-primary/35 bg-brand-primary/10 px-4 text-sm font-bold text-brand-primary transition-colors hover:bg-brand-primary/20 disabled:cursor-not-allowed disabled:opacity-50 cursor-pointer"><CheckCircle2 className="h-4 w-4" />{busyExecutionOrderId === order.id ? 'Salvando...' : 'Salvar execução'}</button></div>
|
||||
|
||||
@@ -159,6 +159,8 @@ export interface ProductionOrderItem {
|
||||
rejectedQuantity: number;
|
||||
finishedQuantity: number | null;
|
||||
qualityNotes: string;
|
||||
cutLayers: number | null;
|
||||
markerReference: string;
|
||||
defects: ProductionOrderDefect[];
|
||||
lotReservations: ProductionOrderLotReservation[];
|
||||
markers: ProductionOrderMarker[];
|
||||
@@ -285,6 +287,8 @@ export type ProductionOrderExecutionPayload = {
|
||||
rejectedQuantity?: number | null;
|
||||
finishedQuantity?: number | null;
|
||||
qualityNotes?: string;
|
||||
cutLayers?: number | null;
|
||||
markerReference?: string;
|
||||
};
|
||||
|
||||
export interface CreateProductionOrdersResult {
|
||||
|
||||
Reference in New Issue
Block a user