feat: suporte a pecas e OFs sem componentes cadastrados no corte e solda

This commit is contained in:
2026-10-20 16:42:21 -03:00
parent 8a707894e0
commit d28e131d7a
3 changed files with 34 additions and 20 deletions
+13 -9
View File
@@ -136,21 +136,26 @@ export const ApontamentoForm = () => {
let qtdDisponivelParaEntrar = Number(peca.quantidade) || 0;
// Identificar se a peça de fato possui subcomponentes cadastrados nesta OF
const compsDaPeca = componentesAgrupados?.filter(c => c.peca_ids?.includes(peca.id)) || [];
const temComponentesCadastrados = peca.tem_componentes !== false && compsDaPeca.length > 0;
const ehSemMontagem = peca.tem_componentes === false;
// 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) {
// Se tiver componentes cadastrados, são apontados os subcomponentes.
// Se NÃO tiver componentes cadastrados (opção do usuário não controlar componentes), aponta a peça mestre normalmente!
if (isCorte && temComponentesCadastrados) {
qtdDisponivelParaEntrar = 0;
} else if (!peca.tem_componentes && procNomeLower.includes('solda')) {
// Regra: peças sem componentes não passam por solda
} else if (ehSemMontagem && procNomeLower.includes('solda')) {
// Regra: peças sem montagem (S/M) não passam por solda
qtdDisponivelParaEntrar = 0;
} else if (indexAtual > 0) {
// Encontrar processo anterior válido
let processoAnteriorValido = null;
for (let i = indexAtual - 1; i >= 0; i--) {
const p = processosOrdenados[i];
if (!peca.tem_componentes && p.nome.toLowerCase().includes('solda')) {
continue; // Pula a solda na busca do processo anterior para peças simples
if (ehSemMontagem && p.nome.toLowerCase().includes('solda')) {
continue; // Pula a solda na busca do processo anterior para peças simples (S/M)
}
processoAnteriorValido = p;
break;
@@ -162,8 +167,7 @@ export const ApontamentoForm = () => {
.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 (temComponentesCadastrados && qtdApontadaAnterior === 0 && processoAnteriorValido.nome.toLowerCase().includes('corte')) {
if (compsDaPeca.length > 0) {
const todosCortados = compsDaPeca.every(c => {
const aptsComp = apontamentos
@@ -17,7 +17,8 @@ import {
PackageCheck,
ShieldCheck,
Wrench,
Scissors
Scissors,
Info
} from 'lucide-react';
import { useKitsProducao, PecaKitInfo } from '@/hooks/useKitsProducao';
import { useOFsAtivas, OFAtiva } from '@/hooks/useOFsAtivas';
@@ -445,6 +446,15 @@ export const MapaTermicoKitsView: React.FC<MapaTermicoKitsViewProps> = ({
</div>
{/* ──────────────── Lista de Peças com Mapa Térmico ──────────────── */}
{!loading && listaKits.length > 0 && !listaKits.some((k) => k.componentes && k.componentes.length > 0) && (
<div className="p-3.5 rounded-2xl bg-amber-500/10 border border-amber-500/30 text-amber-900 dark:text-amber-200 flex items-center gap-3 shadow-xs">
<Info className="w-5 h-5 shrink-0 text-amber-600 dark:text-amber-400" />
<div className="text-xs leading-relaxed">
<strong className="font-bold">Modo Peça Direta:</strong> Esta OF não possui subcomponentes cadastrados no sistema. O avanço no <strong>Corte</strong> e a liberação das peças para a <strong>Solda</strong> são gerenciados diretamente pelo apontamento das peças principais.
</div>
</div>
)}
{loading ? (
<div className="py-16 flex flex-col items-center justify-center text-slate-400 gap-3">
<div className="h-10 w-10 animate-spin rounded-full border-4 border-orange-500 border-t-transparent" />
@@ -78,21 +78,21 @@ export const calcularSaldoProcessoPeca = (
let qtdDisponivelParaEntrar = Number(peca.quantidade) || 0;
let motivoBloqueio: string | null = null;
const kitInfo = mapaKits?.get(peca.id);
const temControleComponentes = Boolean(kitInfo && kitInfo.tem_componentes && (kitInfo.componentes?.length || 0) > 0);
// REGRA 1: Peças sem montagem (S/M) não passam por Solda
if (ehSemMontagem && ehSolda) {
qtdDisponivelParaEntrar = 0;
motivoBloqueio = 'Peça S/M (Pula Solda - vai direto para Pintura)';
} else if (ehSolda && !ehSemMontagem && mapaKits?.has(peca.id)) {
} else if (ehSolda && !ehSemMontagem && temControleComponentes && kitInfo) {
// REGRA 1.1: REGRA DO KIT COMPLETO DE CORTE PARA A SOLDA!
// Apenas peças com componentes 100% cortados (em lotes de kits inteiros) são liberadas para Solda!
const kitInfo = mapaKits.get(peca.id)!;
if (kitInfo.tem_componentes) {
qtdDisponivelParaEntrar = kitInfo.kits_completos_cortados;
if (kitInfo.kits_completos_cortados === 0) {
motivoBloqueio = kitInfo.motivo_bloqueio_solda || 'Aguardando corte completo dos kits';
} else if (kitInfo.kits_completos_cortados <= jaProduzidoAtual) {
motivoBloqueio = `Todos os ${kitInfo.kits_completos_cortados} kits cortados já foram soldados (${jaProduzidoAtual}/${peca.quantidade})`;
}
qtdDisponivelParaEntrar = kitInfo.kits_completos_cortados;
if (kitInfo.kits_completos_cortados === 0) {
motivoBloqueio = kitInfo.motivo_bloqueio_solda || 'Aguardando corte completo dos kits';
} else if (kitInfo.kits_completos_cortados <= jaProduzidoAtual) {
motivoBloqueio = `Todos os ${kitInfo.kits_completos_cortados} kits cortados já foram soldados (${jaProduzidoAtual}/${peca.quantidade})`;
}
} else if (indexAtual > 0) {
// REGRA 2: Encontrar processo anterior válido
@@ -1217,7 +1217,7 @@ export const SmartProducaoFlow: React.FC<SmartProducaoFlowProps> = ({
fase={faseSelecionada}
onSelectPecaParaApontar={(pecaKit) => {
const ehProcCorte = processoSelecionado?.id === procCorteId;
if (ehProcCorte && pecaKit.tem_componentes) {
if (ehProcCorte && pecaKit.tem_componentes && pecaKit.componentes.length > 0) {
setPecaParaCorteModal(pecaKit);
} else {
const pItem = pecasComSaldo.find((p) => p.id === pecaKit.peca_id);