From 97459648375b28064dff4940b2cd7a33f4061092 Mon Sep 17 00:00:00 2001 From: admbrainsteel Date: Fri, 16 Oct 2026 19:13:24 -0300 Subject: [PATCH] feat: blindagem de peso em dashboards e kit completo de corte para solda --- check_b134.mjs | 2 + .../apontamento/ApontamentoForm.tsx | 44 ++++++--- .../apontamento/SeletorItensOtimizado.tsx | 7 +- src/hooks/useApontamentoDiarioChart.tsx | 11 +-- src/hooks/useApontamentosProducao.tsx | 95 +++++++++++++++++++ src/hooks/useDashboardProducaoOtimizado.tsx | 39 +++----- src/hooks/useProcessChartData.tsx | 11 +-- src/hooks/useRelatorioDiario.tsx | 7 +- src/hooks/useValidacaoSequencialProcessos.tsx | 44 +++------ 9 files changed, 173 insertions(+), 87 deletions(-) create mode 100644 check_b134.mjs diff --git a/check_b134.mjs b/check_b134.mjs new file mode 100644 index 0000000..2b719f3 --- /dev/null +++ b/check_b134.mjs @@ -0,0 +1,2 @@ +// Script temporário de auditoria da OF B134 +export {}; diff --git a/src/components/apontamento/ApontamentoForm.tsx b/src/components/apontamento/ApontamentoForm.tsx index 9a46664..58d9b64 100644 --- a/src/components/apontamento/ApontamentoForm.tsx +++ b/src/components/apontamento/ApontamentoForm.tsx @@ -118,6 +118,8 @@ export const ApontamentoForm = () => { const processosOrdenados = [...processos].sort((a, b) => a.ordem - b.ordem); const indexAtual = processosOrdenados.findIndex(p => p.id === processo_id); const procAtual = processosOrdenados[indexAtual]; + const procNomeLower = (procAtual?.nome || '').toLowerCase(); + const isCorte = procNomeLower.includes('corte') || procNomeLower.includes('prepar') || indexAtual === 0; // 1. Peças da OF e Fase selecionadas const pecasDaFase = pecas.filter( @@ -134,8 +136,13 @@ export const ApontamentoForm = () => { let qtdDisponivelParaEntrar = Number(peca.quantidade) || 0; - // Regra: peças sem componentes não passam por solda - if (!peca.tem_componentes && procAtual && procAtual.nome.toLowerCase().includes('solda')) { + // Se for CORTE: + // Peças compostas são produzidas através do corte de seus subcomponentes. + // Ao atingir 100% dos componentes cortados, o kit é concluído e a peça mestre é liberada para Solda. + if (isCorte && peca.tem_componentes) { + qtdDisponivelParaEntrar = 0; + } else if (!peca.tem_componentes && procNomeLower.includes('solda')) { + // Regra: peças sem componentes não passam por solda qtdDisponivelParaEntrar = 0; } else if (indexAtual > 0) { // Encontrar processo anterior válido @@ -150,9 +157,27 @@ export const ApontamentoForm = () => { } if (processoAnteriorValido) { - qtdDisponivelParaEntrar = apontamentos + let qtdApontadaAnterior = apontamentos .filter(a => a.tipo_apontamento === 'peca' && a.peca_id === peca.id && a.processo_id === processoAnteriorValido.id) .reduce((sum, a) => sum + (Number(a.quantidade_produzida) || 0), 0); + + // Fallback de retrocompatibilidade para peças com componentes cortados antes da automação + if (peca.tem_componentes && qtdApontadaAnterior === 0 && processoAnteriorValido.nome.toLowerCase().includes('corte')) { + const compsDaPeca = componentesAgrupados?.filter(c => c.peca_ids?.includes(peca.id)) || []; + if (compsDaPeca.length > 0) { + const todosCortados = compsDaPeca.every(c => { + const aptsComp = apontamentos + .filter(a => a.tipo_apontamento === 'componente' && c.componente_ids.includes(a.componente_id || '') && a.processo_id === processoAnteriorValido.id) + .reduce((sum, a) => sum + (Number(a.quantidade_produzida) || 0), 0); + return aptsComp >= c.quantidade_total; + }); + if (todosCortados) { + qtdApontadaAnterior = Number(peca.quantidade) || 0; + } + } + } + + qtdDisponivelParaEntrar = qtdApontadaAnterior; } } @@ -173,21 +198,14 @@ export const ApontamentoForm = () => { }); // 2. Componentes da OF e Fase selecionadas + // RESTRIÇÃO INDUSTRIAL: Componentes só existem no processo de CORTE / PREPARAÇÃO! + // Em Solda, Pintura, Expedição, Montagem etc., componentes não são exibidos nem apontados. const componentesDisponiveis: ItemDisponivel[] = []; - if (componentesAgrupados && componentesAgrupados.length > 0) { + if (isCorte && componentesAgrupados && componentesAgrupados.length > 0) { componentesAgrupados.forEach(comp => { let qtdDisponivelParaEntrarComp = Number(comp.quantidade_total) || 0; - if (indexAtual > 0) { - const processoAnteriorValido = processosOrdenados[indexAtual - 1]; - if (processoAnteriorValido) { - qtdDisponivelParaEntrarComp = apontamentos - .filter(a => a.tipo_apontamento === 'componente' && comp.componente_ids.includes(a.componente_id || '') && a.processo_id === processoAnteriorValido.id) - .reduce((sum, a) => sum + (Number(a.quantidade_produzida) || 0), 0); - } - } - const totalApontadoComp = apontamentos .filter(a => a.tipo_apontamento === 'componente' && comp.componente_ids.includes(a.componente_id || '') && a.processo_id === processo_id) .reduce((sum, a) => sum + (Number(a.quantidade_produzida) || 0), 0); diff --git a/src/components/apontamento/SeletorItensOtimizado.tsx b/src/components/apontamento/SeletorItensOtimizado.tsx index 8c40b76..fcac3b8 100644 --- a/src/components/apontamento/SeletorItensOtimizado.tsx +++ b/src/components/apontamento/SeletorItensOtimizado.tsx @@ -4,7 +4,7 @@ import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { ScrollArea } from "@/components/ui/scroll-area"; import { toast } from 'sonner'; -import { Search, Package, Shield, AlertTriangle, Settings, Check, Loader2 } from 'lucide-react'; +import { Search, Package, Shield, AlertTriangle, Settings, Check, Loader2, Layers } from 'lucide-react'; import { useDebounce } from '@/hooks/useDebounce'; import { Peca } from '@/hooks/usePecas'; import { useComponentesPeca } from '@/hooks/useComponentesPeca'; @@ -628,7 +628,10 @@ export const SeletorItensOtimizado: React.FC = ({ {componentesDisponiveis.length > 0 && (
-

Componentes Disponíveis

+

+ + Componentes para Corte (Controle de Kit) +