import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { supabase } from '@/integrations/supabase/client'; import { toast } from 'sonner'; import { validarRomaneioParaEntrega } from '@/utils/validacaoRomaneio'; export interface RomaneioExpedicao { id: string; numero_romaneio: string; of_number: string; data_romaneio: string; data_criacao: string; revisao: number; motivo_revisao?: string; data_prevista_entrega?: string; prioridade: string; status: string; observacoes?: string; peso_total_romaneio: number; previsao_kg?: number; maior_dimensao?: string; comprimento_maximo_veiculo?: number; tipo_transporte?: string; frete_tipo?: string; nome_motorista?: string; created_at: string; updated_at: string; created_by?: string; itens_pecas?: ItemRomaneioPeca[]; itens_insumos?: ItemRomaneioInsumo[]; } export interface ItemRomaneioPeca { id: string; romaneio_id: string; peca_id: string; marca: string; fase?: string; descricao?: string; comprimento?: number; peso_unitario: number; quantidade_expedida: number; peso_total: number; quantidade_faltante: number; created_at: string; } export interface ItemRomaneioInsumo { id: string; romaneio_id: string; tipo_insumo: string; descricao: string; unidade: string; quantidade_expedida: number; peso_unitario?: number; peso_total?: number; observacoes?: string; created_at: string; } export interface PecaPintura { id: string; marca: string; etapa_fase?: string; descricao?: string; comprimento?: number | null; peso_unitario: number; quantidade_produzida_pintura: number; quantidade_ja_expedida: number; quantidade_disponivel: number; of_number: string; } export const useRomaneios = (ofNumberFilter?: string) => { return useQuery({ queryKey: ['romaneios', ofNumberFilter], queryFn: async () => { let query = supabase .from('romaneios_expedicao') .select(` *, itens_pecas:itens_romaneio_pecas(*), itens_insumos:itens_romaneio_insumos(*) `) .order('created_at', { ascending: false }); if (ofNumberFilter && ofNumberFilter !== 'all') { query = query.eq('of_number', ofNumberFilter); } const { data, error } = await query; if (error) throw error; // Calcular peso total correto das peças para cada romaneio const normalizedData = data?.map(romaneio => { const pesoTotalPecas = romaneio.itens_pecas?.reduce((sum: number, item: any) => sum + (item.peso_total || 0), 0) || 0; const pesoTotalInsumos = romaneio.itens_insumos?.reduce((sum: number, item: any) => sum + (item.peso_total || 0), 0) || 0; return { ...romaneio, peso_total_romaneio: pesoTotalPecas + pesoTotalInsumos, prioridade: romaneio.prioridade === 'Normal' || romaneio.prioridade === 'Urgente' ? romaneio.prioridade : 'Normal', status: ['Em planejamento', 'Confirmado', 'Expedido', 'Entregue', 'Conferido em Obra'].includes(romaneio.status) ? romaneio.status : 'Em planejamento', frete_tipo: romaneio.frete_tipo === 'terceiros' || romaneio.frete_tipo === 'proprio' ? romaneio.frete_tipo : undefined }; }); return normalizedData as RomaneioExpedicao[]; }, }); }; export const usePecasPintura = (ofNumber?: string) => { return useQuery({ queryKey: ['pecas-pintura', ofNumber], queryFn: async () => { if (!ofNumber) return []; // Buscar peças que passaram pelo processo de pintura const { data: apontamentosPintura, error: apontamentosError } = await supabase .from('apontamentos_producao') .select(` peca_id, componente_id, tipo_apontamento, quantidade_produzida, pecas ( id, marca, etapa_fase, descricao, quantidade, peso_unitario, comprimento, of_number ), processos_fabricacao ( nome ) `) .eq('of_number', ofNumber) .ilike('processos_fabricacao.nome', '%pintura%'); if (apontamentosError) throw apontamentosError; // Buscar quantidades já expedidas const { data: itensExpedidos, error: expedidosError } = await supabase .from('itens_romaneio_pecas') .select('peca_id, quantidade_expedida') .in('peca_id', apontamentosPintura?.map(a => a.peca_id).filter(Boolean) || []); if (expedidosError) throw expedidosError; // Calcular disponibilidade (apenas peças principais, ignorando componentes) const pecasPintura: PecaPintura[] = []; const pecasMap = new Map(); apontamentosPintura?.forEach(apontamento => { if (apontamento.tipo_apontamento === 'componente' || apontamento.componente_id) return; const pecaId = apontamento.peca_id; const peca = apontamento.pecas; if (!peca) return; if (pecasMap.has(pecaId)) { pecasMap.get(pecaId).quantidade_produzida_pintura += apontamento.quantidade_produzida; } else { pecasMap.set(pecaId, { id: peca.id, marca: peca.marca, etapa_fase: peca.etapa_fase, descricao: peca.descricao, quantidade_total: peca.quantidade, peso_unitario: peca.peso_unitario, comprimento: peca.comprimento, quantidade_produzida_pintura: apontamento.quantidade_produzida, of_number: peca.of_number }); } }); pecasMap.forEach((peca, pecaId) => { const totalExpedido = itensExpedidos ?.filter(item => item.peca_id === pecaId) .reduce((sum, item) => sum + item.quantidade_expedida, 0) || 0; const qtdProdLimitada = peca.quantidade_total ? Math.min(Number(peca.quantidade_total), peca.quantidade_produzida_pintura) : peca.quantidade_produzida_pintura; pecasPintura.push({ ...peca, quantidade_ja_expedida: totalExpedido, quantidade_disponivel: qtdProdLimitada - totalExpedido }); }); return pecasPintura.filter(p => p.quantidade_disponivel > 0); }, enabled: !!ofNumber, }); }; const generateRomaneioNumber = (ofNumber: string, existingRomaneios: RomaneioExpedicao[]) => { // Extrair apenas o número da OF (sem o B) const ofNumOnly = ofNumber.replace('B', ''); // Contar romaneios existentes para esta OF const existingCount = existingRomaneios.filter(r => r.of_number === ofNumber).length; const nextSequence = existingCount + 1; return `RO${ofNumOnly}-${nextSequence.toString().padStart(2, '0')}`; }; export const useCriarRomaneio = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (romaneio: Omit) => { const { data: existingRomaneios } = await supabase .from('romaneios_expedicao') .select('*') .eq('of_number', romaneio.of_number); const numeroRomaneio = generateRomaneioNumber(romaneio.of_number, existingRomaneios || []); const { data, error } = await supabase .from('romaneios_expedicao') .insert([{ numero_romaneio: numeroRomaneio, of_number: romaneio.of_number, data_romaneio: romaneio.data_romaneio, data_criacao: romaneio.data_criacao, revisao: romaneio.revisao || 1, motivo_revisao: romaneio.motivo_revisao, data_prevista_entrega: romaneio.data_prevista_entrega, prioridade: romaneio.prioridade, status: romaneio.status, observacoes: romaneio.observacoes, peso_total_romaneio: romaneio.peso_total_romaneio, previsao_kg: romaneio.previsao_kg, maior_dimensao: romaneio.maior_dimensao, tipo_transporte: romaneio.tipo_transporte, frete_tipo: romaneio.frete_tipo, nome_motorista: romaneio.nome_motorista, created_by: (await supabase.auth.getUser()).data.user?.id }]) .select() .single(); if (error) throw error; return data; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['romaneios'] }); toast.success('Romaneio criado com sucesso!'); }, onError: (error) => { console.error('Erro ao criar romaneio:', error); toast.error('Erro ao criar romaneio'); }, }); }; export const useAtualizarRomaneio = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: async ({ id, ...romaneio }: Partial & { id: string }) => { // Buscar status atual antes da atualização const { data: romaneioAtual, error: errorAtual } = await supabase .from('romaneios_expedicao') .select('status, numero_romaneio, of_number') .eq('id', id) .single(); if (errorAtual) { console.error('❌ Erro ao buscar romaneio atual:', errorAtual); throw errorAtual; } console.log('📋 Status atual do romaneio:', romaneioAtual?.status); console.log('📋 Novo status a ser definido:', romaneio.status); // Se status for Entregue e não passou pelo modal de confirmação, validar amigavelmente if (romaneio.status === 'Entregue' && romaneioAtual?.status !== 'Entregue') { const validacao = await validarRomaneioParaEntrega(id, romaneio.of_number || romaneioAtual?.of_number); if (!validacao.valido) { console.warn('⚠️ Romaneio possui pendências fabris ao passar para Entregue:', validacao.pendencias?.length); } } const { data, error } = await supabase .from('romaneios_expedicao') .update(romaneio) .eq('id', id) .select(` *, itens_pecas:itens_romaneio_pecas(*) `) .single(); if (error) throw error; // VERIFICAÇÃO ESPECÍFICA: Disparar apontamento automático APENAS quando status muda PARA "Entregue" const statusAnterior = romaneioAtual?.status; const novoStatus = romaneio.status; console.log('🔍 Verificando mudança de status:'); console.log('- Status anterior:', statusAnterior); console.log('- Novo status:', novoStatus); if (novoStatus === 'Entregue' && statusAnterior !== 'Entregue') { console.log('🚀 CONDIÇÃO ATENDIDA: Status mudou PARA "Entregue" - iniciando apontamento automático'); console.log('📦 Dados do romaneio atualizado:', data); // Buscar dados completos do romaneio incluindo itens const { data: romaneioCompleto, error: errorCompleto } = await supabase .from('romaneios_expedicao') .select(` *, itens_pecas:itens_romaneio_pecas(*) `) .eq('id', id) .single(); if (errorCompleto) { console.error('❌ Erro ao buscar dados completos do romaneio:', errorCompleto); return data; } console.log('📋 Dados completos do romaneio para apontamento:', romaneioCompleto); console.log('🔢 Número de peças no romaneio:', romaneioCompleto?.itens_pecas?.length || 0); // Verificar se há peças para apontar if (!romaneioCompleto?.itens_pecas || romaneioCompleto.itens_pecas.length === 0) { console.log('⚠️ Romaneio sem peças - não será processado apontamento automático'); return data; } // Disparar evento customizado para apontamento automático const eventDetail = { romaneioId: id, status: novoStatus, romaneioData: romaneioCompleto, statusAnterior: statusAnterior }; console.log('🎯 Disparando evento romaneio-entregue para apontamento automático:', eventDetail); // Usar setTimeout para garantir que o evento seja disparado após a atualização setTimeout(() => { window.dispatchEvent(new CustomEvent('romaneio-entregue', { detail: eventDetail })); }, 100); } else if (novoStatus === 'Entregue' && statusAnterior === 'Entregue') { console.log('ℹ️ Status já era "Entregue" - apontamento automático não será disparado'); } else { console.log(`ℹ️ Status atualizado de "${statusAnterior}" para "${novoStatus}" - apontamento automático não será disparado (só para mudança PARA "Entregue")`); } return data; }, onSuccess: (data) => { queryClient.invalidateQueries({ queryKey: ['romaneios'] }); toast.success('Romaneio atualizado com sucesso!'); }, onError: (error: any) => { console.error('Erro ao atualizar romaneio:', error); toast.error(error?.message || 'Erro ao atualizar romaneio'); }, }); }; export const useAdicionarItemPeca = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (item: Omit) => { const { data, error } = await supabase .from('itens_romaneio_pecas') .insert([item]) .select() .single(); if (error) throw error; return data; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['romaneios'] }); toast.success('Item adicionado com sucesso!'); }, onError: (error) => { console.error('Erro ao adicionar item:', error); toast.error('Erro ao adicionar item'); }, }); }; export const useAdicionarItemInsumo = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (item: Omit) => { const { data, error } = await supabase .from('itens_romaneio_insumos') .insert([item]) .select() .single(); if (error) throw error; return data; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['romaneios'] }); toast.success('Insumo adicionado com sucesso!'); }, onError: (error) => { console.error('Erro ao adicionar insumo:', error); toast.error('Erro ao adicionar insumo'); }, }); };