🚀 Initial commit: Versão atual do TrackSteel APP
This commit is contained in:
197
src/hooks/useEstoqueMovimentacoes.tsx
Normal file
197
src/hooks/useEstoqueMovimentacoes.tsx
Normal file
@@ -0,0 +1,197 @@
|
||||
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { supabase } from '@/integrations/supabase/client';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
export interface MovimentacaoEstoque {
|
||||
id: string;
|
||||
material_id: string;
|
||||
tipo_movimentacao: 'entrada' | 'saida' | 'transferencia' | 'ajuste' | 'empenho' | 'desempenho';
|
||||
quantidade: number;
|
||||
valor_unitario?: number;
|
||||
valor_total?: number;
|
||||
lote?: string;
|
||||
fornecedor?: string;
|
||||
nota_fiscal?: string;
|
||||
of_vinculada?: string;
|
||||
observacoes?: string;
|
||||
data_movimentacao: string;
|
||||
created_at: string;
|
||||
created_by?: string;
|
||||
user_name?: string;
|
||||
estoque_materiais?: {
|
||||
codigo: string;
|
||||
descricao: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const useMovimentacoesEstoque = () => {
|
||||
return useQuery({
|
||||
queryKey: ['movimentacoes-estoque'],
|
||||
queryFn: async () => {
|
||||
const { data, error } = await supabase
|
||||
.from('movimentacoes_estoque')
|
||||
.select(`
|
||||
*,
|
||||
estoque_materiais (
|
||||
codigo,
|
||||
descricao
|
||||
)
|
||||
`)
|
||||
.order('created_at', { ascending: false });
|
||||
|
||||
if (error) throw error;
|
||||
return data as MovimentacaoEstoque[];
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useCriarMovimentacao = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (movimentacao: {
|
||||
material_id: string;
|
||||
tipo_movimentacao: 'entrada' | 'saida' | 'transferencia' | 'ajuste' | 'empenho' | 'desempenho';
|
||||
quantidade: number;
|
||||
lote?: string;
|
||||
fornecedor?: string;
|
||||
nota_fiscal?: string;
|
||||
of_vinculada?: string;
|
||||
observacoes?: string;
|
||||
data_movimentacao?: string;
|
||||
valor_unitario?: number;
|
||||
user_name?: string;
|
||||
}) => {
|
||||
// Validações básicas
|
||||
if (!movimentacao.material_id) {
|
||||
throw new Error('Material é obrigatório');
|
||||
}
|
||||
|
||||
if (movimentacao.quantidade <= 0) {
|
||||
throw new Error('Quantidade deve ser maior que zero');
|
||||
}
|
||||
|
||||
// Validações específicas para empenho e desempenho
|
||||
if (movimentacao.tipo_movimentacao === 'empenho' && !movimentacao.of_vinculada) {
|
||||
throw new Error('OF vinculada é obrigatória para movimentações de empenho');
|
||||
}
|
||||
|
||||
if (movimentacao.tipo_movimentacao === 'desempenho' && !movimentacao.of_vinculada) {
|
||||
throw new Error('OF vinculada é obrigatória para movimentações de desempenho');
|
||||
}
|
||||
|
||||
// Para empenhos e saídas, verificar disponibilidade
|
||||
if (movimentacao.tipo_movimentacao === 'empenho' || movimentacao.tipo_movimentacao === 'saida') {
|
||||
const { data: materialAtual, error: materialError } = await supabase
|
||||
.from('estoque_materiais')
|
||||
.select('quantidade_disponivel, descricao, codigo')
|
||||
.eq('id', movimentacao.material_id)
|
||||
.single();
|
||||
|
||||
if (materialError) {
|
||||
throw new Error('Material não encontrado');
|
||||
}
|
||||
|
||||
if (!materialAtual) {
|
||||
throw new Error('Material não encontrado');
|
||||
}
|
||||
|
||||
if (materialAtual.quantidade_disponivel < movimentacao.quantidade) {
|
||||
const errorMsg = `Quantidade insuficiente para ${materialAtual.codigo} - ${materialAtual.descricao}. Disponível: ${materialAtual.quantidade_disponivel}, Solicitado: ${movimentacao.quantidade}`;
|
||||
throw new Error(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
// Preparar dados para inserção
|
||||
const { data: currentUser } = await supabase.auth.getUser();
|
||||
const dadosInsercao = {
|
||||
material_id: movimentacao.material_id,
|
||||
tipo_movimentacao: movimentacao.tipo_movimentacao,
|
||||
quantidade: movimentacao.quantidade,
|
||||
lote: movimentacao.lote || null,
|
||||
fornecedor: movimentacao.fornecedor || null,
|
||||
nota_fiscal: movimentacao.nota_fiscal || null,
|
||||
of_vinculada: movimentacao.of_vinculada || null,
|
||||
observacoes: movimentacao.observacoes || null,
|
||||
data_movimentacao: movimentacao.data_movimentacao || new Date().toISOString().split('T')[0],
|
||||
valor_unitario: movimentacao.valor_unitario || null,
|
||||
created_by: currentUser.user?.id
|
||||
};
|
||||
|
||||
// Criar a movimentação - o trigger do banco cuidará dos empenhos automaticamente
|
||||
const { data: movimentacaoData, error: movError } = await supabase
|
||||
.from('movimentacoes_estoque')
|
||||
.insert([dadosInsercao])
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (movError) {
|
||||
console.error('Erro detalhado:', movError);
|
||||
throw new Error(movError.message || 'Erro ao criar movimentação');
|
||||
}
|
||||
|
||||
if (!movimentacaoData) {
|
||||
throw new Error('Nenhum dado foi retornado após a criação');
|
||||
}
|
||||
|
||||
return movimentacaoData;
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['estoque-materiais'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['movimentacoes-estoque'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['empenhos-material'] });
|
||||
toast.success('Movimentação criada com sucesso!');
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
console.error('Erro capturado na mutação:', error);
|
||||
toast.error(error.message || 'Erro ao criar movimentação');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useExcluirMovimentacao = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (movimentacaoId: string) => {
|
||||
// Buscar detalhes da movimentação
|
||||
const { data: movimentacao, error: fetchError } = await supabase
|
||||
.from('movimentacoes_estoque')
|
||||
.select('*, estoque_materiais(codigo, descricao)')
|
||||
.eq('id', movimentacaoId)
|
||||
.single();
|
||||
|
||||
if (fetchError) {
|
||||
throw new Error('Movimentação não encontrada');
|
||||
}
|
||||
|
||||
// Excluir a movimentação - o trigger cuidará da reversão
|
||||
const { error: deleteError } = await supabase
|
||||
.from('movimentacoes_estoque')
|
||||
.delete()
|
||||
.eq('id', movimentacaoId);
|
||||
|
||||
if (deleteError) {
|
||||
throw deleteError;
|
||||
}
|
||||
|
||||
return movimentacao;
|
||||
},
|
||||
onSuccess: (movimentacao) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['movimentacoes-estoque'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['estoque-materiais'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['empenhos-material'] });
|
||||
|
||||
const materialInfo = movimentacao.estoque_materiais;
|
||||
const materialDesc = materialInfo ? `${materialInfo.codigo} - ${materialInfo.descricao}` : 'Material';
|
||||
|
||||
toast.success(`Movimentação de ${movimentacao.tipo_movimentacao} para ${materialDesc} excluída com sucesso!`);
|
||||
},
|
||||
onError: (error: any) => {
|
||||
console.error('Erro ao excluir movimentação:', error);
|
||||
const errorMessage = error?.message || 'Erro ao excluir movimentação';
|
||||
toast.error(errorMessage);
|
||||
}
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user