fix: exclusao em cascata de pecas e importacao inteligente sem duplicidade
This commit is contained in:
@@ -18,6 +18,7 @@ interface PecasTableProps {
|
|||||||
pecas: Peca[];
|
pecas: Peca[];
|
||||||
onOpenComponentPopup: (pecaId: string) => void;
|
onOpenComponentPopup: (pecaId: string) => void;
|
||||||
onDeletePeca?: (pecaId: string) => void;
|
onDeletePeca?: (pecaId: string) => void;
|
||||||
|
onDeletePecasBatch?: (pecaIds: string[]) => Promise<any>;
|
||||||
onEditPeca?: (peca: Peca) => void;
|
onEditPeca?: (peca: Peca) => void;
|
||||||
onDeleteLastImport?: () => void;
|
onDeleteLastImport?: () => void;
|
||||||
onBatchUpdatePecas?: (pecaIds: string[], updates: any) => Promise<void>;
|
onBatchUpdatePecas?: (pecaIds: string[], updates: any) => Promise<void>;
|
||||||
@@ -31,6 +32,7 @@ export function PecasTable({
|
|||||||
pecas,
|
pecas,
|
||||||
onOpenComponentPopup,
|
onOpenComponentPopup,
|
||||||
onDeletePeca,
|
onDeletePeca,
|
||||||
|
onDeletePecasBatch,
|
||||||
onEditPeca,
|
onEditPeca,
|
||||||
onDeleteLastImport,
|
onDeleteLastImport,
|
||||||
onBatchUpdatePecas,
|
onBatchUpdatePecas,
|
||||||
@@ -89,19 +91,24 @@ export function PecasTable({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteSelected = async () => {
|
const handleDeleteSelected = async () => {
|
||||||
if (!canDelete || !onDeletePeca) {
|
if (!canDelete || (!onDeletePecasBatch && !onDeletePeca)) {
|
||||||
toast.error('Você não tem permissão para excluir peças');
|
toast.error('Você não tem permissão para excluir peças');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.confirm(`Tem certeza que deseja excluir ${selectedPecas.size} peças?`)) {
|
if (window.confirm(`Tem certeza que deseja excluir ${selectedPecas.size} peças selecionadas e seus vínculos?`)) {
|
||||||
try {
|
try {
|
||||||
for (const pecaId of selectedPecas) {
|
const pecaIdsArray = Array.from(selectedPecas);
|
||||||
await onDeletePeca(pecaId);
|
if (onDeletePecasBatch) {
|
||||||
|
await onDeletePecasBatch(pecaIdsArray);
|
||||||
|
} else if (onDeletePeca) {
|
||||||
|
for (const pecaId of pecaIdsArray) {
|
||||||
|
await onDeletePeca(pecaId);
|
||||||
|
}
|
||||||
|
toast.success('Peças excluídas com sucesso!');
|
||||||
}
|
}
|
||||||
toast.success('Peças excluídas com sucesso!');
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error('Erro ao excluir peças.');
|
console.error('Erro ao excluir peças:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -172,6 +172,13 @@ export function useComponentesPeca(pecaIds: string | string[] = []) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 1. Apagar apontamentos de produção vinculados ao componente
|
||||||
|
await supabase
|
||||||
|
.from('apontamentos_producao')
|
||||||
|
.delete()
|
||||||
|
.eq('componente_id', id);
|
||||||
|
|
||||||
|
// 2. Apagar o componente
|
||||||
const { error } = await supabase
|
const { error } = await supabase
|
||||||
.from('componentes_peca')
|
.from('componentes_peca')
|
||||||
.delete()
|
.delete()
|
||||||
|
|||||||
+194
-107
@@ -241,6 +241,83 @@ export function usePecas() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deletePecasCascade = async (pecaIds: string[]): Promise<boolean> => {
|
||||||
|
if (!pecaIds || pecaIds.length === 0) return true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('🗑️ Iniciando exclusão em cascata para peças:', pecaIds);
|
||||||
|
|
||||||
|
// 1. Obter todos os IDs de componentes vinculados a essas peças
|
||||||
|
const { data: componentes } = await supabase
|
||||||
|
.from('componentes_peca')
|
||||||
|
.select('id')
|
||||||
|
.in('peca_id', pecaIds);
|
||||||
|
|
||||||
|
const componenteIds = componentes?.map(c => c.id) || [];
|
||||||
|
|
||||||
|
// 2. Apagar apontamentos de produção vinculados aos componentes
|
||||||
|
if (componenteIds.length > 0) {
|
||||||
|
const { error: apCompError } = await supabase
|
||||||
|
.from('apontamentos_producao')
|
||||||
|
.delete()
|
||||||
|
.in('componente_id', componenteIds);
|
||||||
|
if (apCompError) console.warn('Aviso ao apagar apontamentos de componentes:', apCompError);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Apagar apontamentos de produção vinculados diretamente às peças
|
||||||
|
const { error: apPecaError } = await supabase
|
||||||
|
.from('apontamentos_producao')
|
||||||
|
.delete()
|
||||||
|
.in('peca_id', pecaIds);
|
||||||
|
if (apPecaError) console.warn('Aviso ao apagar apontamentos de peças:', apPecaError);
|
||||||
|
|
||||||
|
// 4. Apagar itens de prioridades de fabricação
|
||||||
|
const { error: prioridadesError } = await supabase
|
||||||
|
.from('itens_prioridade_fabricacao')
|
||||||
|
.delete()
|
||||||
|
.in('peca_id', pecaIds);
|
||||||
|
if (prioridadesError) console.warn('Aviso ao apagar itens de prioridade:', prioridadesError);
|
||||||
|
|
||||||
|
// 5. Apagar itens de romaneio de peças
|
||||||
|
const { error: romaneioError } = await supabase
|
||||||
|
.from('itens_romaneio_pecas')
|
||||||
|
.delete()
|
||||||
|
.in('peca_id', pecaIds);
|
||||||
|
if (romaneioError) console.warn('Aviso ao apagar itens de romaneio:', romaneioError);
|
||||||
|
|
||||||
|
// 6. Apagar datas de processos
|
||||||
|
const { error: processosError } = await supabase
|
||||||
|
.from('processos_pecas_datas')
|
||||||
|
.delete()
|
||||||
|
.in('peca_id', pecaIds);
|
||||||
|
if (processosError) console.warn('Aviso ao apagar processos datas:', processosError);
|
||||||
|
|
||||||
|
// 7. Apagar componentes das peças
|
||||||
|
const { error: compError } = await supabase
|
||||||
|
.from('componentes_peca')
|
||||||
|
.delete()
|
||||||
|
.in('peca_id', pecaIds);
|
||||||
|
if (compError) console.warn('Aviso ao apagar componentes:', compError);
|
||||||
|
|
||||||
|
// 8. Apagar as peças principais
|
||||||
|
const { error: deleteError } = await supabase
|
||||||
|
.from('pecas')
|
||||||
|
.delete()
|
||||||
|
.in('id', pecaIds);
|
||||||
|
|
||||||
|
if (deleteError) {
|
||||||
|
console.error('❌ Erro ao apagar peças do banco:', deleteError);
|
||||||
|
throw deleteError;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('✅ Peças e vínculos apagados com sucesso');
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Erro na exclusão em cascata das peças:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const deletePeca = async (pecaId: string): Promise<boolean> => {
|
const deletePeca = async (pecaId: string): Promise<boolean> => {
|
||||||
if (!user) {
|
if (!user) {
|
||||||
toast.error('Usuário não autenticado');
|
toast.error('Usuário não autenticado');
|
||||||
@@ -248,17 +325,7 @@ export function usePecas() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { error } = await supabase
|
await deletePecasCascade([pecaId]);
|
||||||
.from('pecas')
|
|
||||||
.delete()
|
|
||||||
.eq('id', pecaId);
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
console.error('Erro ao apagar peça:', error);
|
|
||||||
toast.error('Erro ao apagar peça');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.success('Peça apagada com sucesso!');
|
toast.success('Peça apagada com sucesso!');
|
||||||
await loadPecas();
|
await loadPecas();
|
||||||
return true;
|
return true;
|
||||||
@@ -269,6 +336,24 @@ export function usePecas() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deletePecasBatch = async (pecaIds: string[]): Promise<boolean> => {
|
||||||
|
if (!user) {
|
||||||
|
toast.error('Usuário não autenticado');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await deletePecasCascade(pecaIds);
|
||||||
|
toast.success(`${pecaIds.length} peça(s) apagada(s) com sucesso!`);
|
||||||
|
await loadPecas();
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erro ao apagar peças selecionadas:', error);
|
||||||
|
toast.error('Erro ao apagar peças selecionadas');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const importCSV = async (file: File) => {
|
const importCSV = async (file: File) => {
|
||||||
console.log('📄 Importando CSV...');
|
console.log('📄 Importando CSV...');
|
||||||
if (!user) {
|
if (!user) {
|
||||||
@@ -313,25 +398,7 @@ export function usePecas() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { error } = await supabase
|
await importPecas(pecasData);
|
||||||
.from('pecas')
|
|
||||||
.insert(pecasData);
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
console.error('❌ Erro ao importar CSV:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('✅ CSV importado com sucesso:', pecasData.length, 'peças');
|
|
||||||
toast.success(`${pecasData.length} peça(s) importada(s) com sucesso!`);
|
|
||||||
setHasRecentImport(true);
|
|
||||||
|
|
||||||
// Sincronização manual apenas após importação
|
|
||||||
setTimeout(() => {
|
|
||||||
sincronizarPrioridades(false);
|
|
||||||
}, 3000);
|
|
||||||
|
|
||||||
await loadPecas();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Erro ao importar CSV:', error);
|
console.error('❌ Erro ao importar CSV:', error);
|
||||||
toast.error('Erro ao importar arquivo CSV');
|
toast.error('Erro ao importar arquivo CSV');
|
||||||
@@ -340,7 +407,7 @@ export function usePecas() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const importPecas = async (pecasData: any[]) => {
|
const importPecas = async (pecasData: any[]) => {
|
||||||
console.log('📦 Iniciando importação de peças:', pecasData.length);
|
console.log('📦 Iniciando importação inteligente de peças:', pecasData.length);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
toast.error('Usuário não autenticado');
|
toast.error('Usuário não autenticado');
|
||||||
return;
|
return;
|
||||||
@@ -353,8 +420,6 @@ export function usePecas() {
|
|||||||
console.log('🔍 Separando dados de peças e componentes...');
|
console.log('🔍 Separando dados de peças e componentes...');
|
||||||
|
|
||||||
pecasData.forEach((item, index) => {
|
pecasData.forEach((item, index) => {
|
||||||
console.log(`📋 Processando item ${index + 1}:`, item);
|
|
||||||
|
|
||||||
const pecaKey = `${item.of_number || ''}-${item.etapa_fase || ''}-${item.marca || ''}`;
|
const pecaKey = `${item.of_number || ''}-${item.etapa_fase || ''}-${item.marca || ''}`;
|
||||||
|
|
||||||
const pecaData = {
|
const pecaData = {
|
||||||
@@ -394,75 +459,135 @@ export function usePecas() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const pecasParaInserir = Array.from(pecasMap.values());
|
const pecasParaProcessar = Array.from(pecasMap.values());
|
||||||
console.log('✅ Peças para inserir:', pecasParaInserir.length);
|
console.log('✅ Peças únicas a processar:', pecasParaProcessar.length);
|
||||||
console.log('✅ Peças com componentes:', componentesMap.size);
|
console.log('✅ Peças com componentes:', componentesMap.size);
|
||||||
|
|
||||||
if (pecasParaInserir.length === 0) {
|
if (pecasParaProcessar.length === 0) {
|
||||||
toast.error('Nenhuma peça válida encontrada para importação');
|
toast.error('Nenhuma peça válida encontrada para importação');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('💾 Inserindo peças principais...');
|
// 1. Consultar peças existentes para não duplicar (mesma OF, Fase e Marca)
|
||||||
const { data: pecasInseridas, error: pecasError } = await supabase
|
const ofsParaConsultar = Array.from(new Set(pecasParaProcessar.map(p => p.of_number).filter(Boolean)));
|
||||||
|
const { data: pecasExistentes, error: queryExistentesError } = await supabase
|
||||||
.from('pecas')
|
.from('pecas')
|
||||||
.insert(pecasParaInserir)
|
.select('id, of_number, etapa_fase, marca')
|
||||||
.select('id, of_number, etapa_fase, marca');
|
.eq('user_id', user.id)
|
||||||
|
.in('of_number', ofsParaConsultar);
|
||||||
|
|
||||||
if (pecasError) {
|
if (queryExistentesError) {
|
||||||
console.error('❌ Erro ao inserir peças:', pecasError);
|
console.warn('Aviso ao consultar peças existentes:', queryExistentesError);
|
||||||
toast.error(`Erro ao inserir peças: ${pecasError.message}`);
|
|
||||||
throw pecasError;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('✅ Peças inseridas com sucesso:', pecasInseridas?.length || 0);
|
const existingPecasMap = new Map<string, string>();
|
||||||
|
if (pecasExistentes) {
|
||||||
|
pecasExistentes.forEach(ep => {
|
||||||
|
const key = `${ep.of_number || ''}-${ep.etapa_fase || ''}-${ep.marca || ''}`;
|
||||||
|
existingPecasMap.set(key, ep.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const pecaIdResolvedMap = new Map<string, string>();
|
||||||
|
const pecasNovasParaInserir: any[] = [];
|
||||||
|
let totalAtualizadas = 0;
|
||||||
|
|
||||||
|
// 2. Atualizar existentes mantendo ID (preservando apontamentos e prioridades)
|
||||||
|
for (const peca of pecasParaProcessar) {
|
||||||
|
const key = `${peca.of_number || ''}-${peca.etapa_fase || ''}-${peca.marca || ''}`;
|
||||||
|
const existingId = existingPecasMap.get(key);
|
||||||
|
|
||||||
|
if (existingId) {
|
||||||
|
const { error: updateError } = await supabase
|
||||||
|
.from('pecas')
|
||||||
|
.update({
|
||||||
|
descricao: peca.descricao,
|
||||||
|
quantidade: peca.quantidade,
|
||||||
|
peso_unitario: peca.peso_unitario,
|
||||||
|
peso_total: peca.peso_total,
|
||||||
|
tratamento_superficial: peca.tratamento_superficial,
|
||||||
|
material: peca.material,
|
||||||
|
perfil_principal: peca.perfil_principal,
|
||||||
|
tem_componentes: peca.tem_componentes
|
||||||
|
})
|
||||||
|
.eq('id', existingId);
|
||||||
|
|
||||||
|
if (!updateError) {
|
||||||
|
pecaIdResolvedMap.set(key, existingId);
|
||||||
|
totalAtualizadas++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pecasNovasParaInserir.push(peca);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Inserir novas peças
|
||||||
|
let totalNovas = 0;
|
||||||
|
if (pecasNovasParaInserir.length > 0) {
|
||||||
|
console.log('💾 Inserindo novas peças principais:', pecasNovasParaInserir.length);
|
||||||
|
const { data: pecasInseridas, error: pecasError } = await supabase
|
||||||
|
.from('pecas')
|
||||||
|
.insert(pecasNovasParaInserir)
|
||||||
|
.select('id, of_number, etapa_fase, marca');
|
||||||
|
|
||||||
|
if (pecasError) {
|
||||||
|
console.error('❌ Erro ao inserir novas peças:', pecasError);
|
||||||
|
toast.error(`Erro ao inserir peças: ${pecasError.message}`);
|
||||||
|
throw pecasError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pecasInseridas) {
|
||||||
|
totalNovas = pecasInseridas.length;
|
||||||
|
pecasInseridas.forEach(p => {
|
||||||
|
const key = `${p.of_number || ''}-${p.etapa_fase || ''}-${p.marca || ''}`;
|
||||||
|
pecaIdResolvedMap.set(key, p.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Inserir/Sincronizar Componentes
|
||||||
let totalComponentesInseridos = 0;
|
let totalComponentesInseridos = 0;
|
||||||
|
if (componentesMap.size > 0) {
|
||||||
if (componentesMap.size > 0 && pecasInseridas) {
|
console.log('🔧 Inserindo e sincronizando componentes...');
|
||||||
console.log('🔧 Inserindo componentes...');
|
|
||||||
|
|
||||||
for (const [pecaKey, componentes] of componentesMap.entries()) {
|
for (const [pecaKey, componentes] of componentesMap.entries()) {
|
||||||
const [of_number, etapa_fase, marca] = pecaKey.split('-');
|
const pecaId = pecaIdResolvedMap.get(pecaKey);
|
||||||
|
|
||||||
const pecaInserida = pecasInseridas.find(p =>
|
if (pecaId && componentes.length > 0) {
|
||||||
p.of_number === of_number &&
|
// Limpar componentes antigos desta peça para atualizar de forma limpa
|
||||||
p.etapa_fase === etapa_fase &&
|
await supabase
|
||||||
p.marca === marca
|
.from('componentes_peca')
|
||||||
);
|
.delete()
|
||||||
|
.eq('peca_id', pecaId);
|
||||||
|
|
||||||
if (pecaInserida && componentes.length > 0) {
|
|
||||||
const componentesComPecaId = componentes.map(comp => ({
|
const componentesComPecaId = componentes.map(comp => ({
|
||||||
...comp,
|
...comp,
|
||||||
peca_id: pecaInserida.id
|
peca_id: pecaId
|
||||||
}));
|
}));
|
||||||
|
|
||||||
console.log(`🔧 Inserindo ${componentesComPecaId.length} componentes para peça ${marca}...`);
|
|
||||||
|
|
||||||
const { error: componentesError } = await supabase
|
const { error: componentesError } = await supabase
|
||||||
.from('componentes_peca')
|
.from('componentes_peca')
|
||||||
.insert(componentesComPecaId);
|
.insert(componentesComPecaId);
|
||||||
|
|
||||||
if (componentesError) {
|
if (componentesError) {
|
||||||
console.error('❌ Erro ao inserir componentes:', componentesError);
|
console.error(`❌ Erro ao inserir componentes para ${pecaKey}:`, componentesError);
|
||||||
} else {
|
} else {
|
||||||
totalComponentesInseridos += componentesComPecaId.length;
|
totalComponentesInseridos += componentesComPecaId.length;
|
||||||
console.log(`✅ Componentes inseridos para peça ${marca}: ${componentesComPecaId.length}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let successMessage = `✅ ${pecasInseridas?.length || 0} peça(s) importada(s) com sucesso!`;
|
let successMessage = `✅ Importação concluída!`;
|
||||||
if (totalComponentesInseridos > 0) {
|
if (totalNovas > 0) successMessage += ` ${totalNovas} nova(s) peça(s).`;
|
||||||
successMessage += ` ${totalComponentesInseridos} componente(s) também foram criados.`;
|
if (totalAtualizadas > 0) successMessage += ` ${totalAtualizadas} peça(s) atualizada(s).`;
|
||||||
}
|
if (totalComponentesInseridos > 0) successMessage += ` ${totalComponentesInseridos} componente(s) vinculados.`;
|
||||||
|
|
||||||
console.log('🎉 Importação concluída:', successMessage);
|
console.log('🎉 Importação concluída:', successMessage);
|
||||||
toast.success(successMessage);
|
toast.success(successMessage);
|
||||||
setHasRecentImport(true);
|
setHasRecentImport(true);
|
||||||
|
|
||||||
// Sincronização manual apenas após importação
|
// Sincronização manual de prioridades
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
sincronizarPrioridades(false);
|
sincronizarPrioridades(false);
|
||||||
}, 3000);
|
}, 3000);
|
||||||
@@ -500,27 +625,7 @@ export function usePecas() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pecaIds = pecasParaApagar.map(peca => peca.id);
|
const pecaIds = pecasParaApagar.map(peca => peca.id);
|
||||||
const { error: deleteComponentesError } = await supabase
|
await deletePecasCascade(pecaIds);
|
||||||
.from('componentes_peca')
|
|
||||||
.delete()
|
|
||||||
.in('peca_id', pecaIds);
|
|
||||||
|
|
||||||
if (deleteComponentesError) {
|
|
||||||
console.error('Erro ao apagar componentes das peças:', deleteComponentesError);
|
|
||||||
toast.error('Erro ao apagar componentes das peças');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { error: deleteError } = await supabase
|
|
||||||
.from('pecas')
|
|
||||||
.delete()
|
|
||||||
.in('id', pecaIds);
|
|
||||||
|
|
||||||
if (deleteError) {
|
|
||||||
console.error('Erro ao apagar peças:', deleteError);
|
|
||||||
toast.error('Erro ao apagar peças');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.success('Última importação desfeita com sucesso!');
|
toast.success('Última importação desfeita com sucesso!');
|
||||||
setHasRecentImport(false);
|
setHasRecentImport(false);
|
||||||
@@ -556,27 +661,7 @@ export function usePecas() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pecaIds = pecasParaApagar.map(peca => peca.id);
|
const pecaIds = pecasParaApagar.map(peca => peca.id);
|
||||||
const { error: deleteComponentesError } = await supabase
|
await deletePecasCascade(pecaIds);
|
||||||
.from('componentes_peca')
|
|
||||||
.delete()
|
|
||||||
.in('peca_id', pecaIds);
|
|
||||||
|
|
||||||
if (deleteComponentesError) {
|
|
||||||
console.error('Erro ao apagar componentes das peças:', deleteComponentesError);
|
|
||||||
toast.error('Erro ao apagar componentes das peças');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { error: deleteError } = await supabase
|
|
||||||
.from('pecas')
|
|
||||||
.delete()
|
|
||||||
.in('id', pecaIds);
|
|
||||||
|
|
||||||
if (deleteError) {
|
|
||||||
console.error('Erro ao apagar peças:', deleteError);
|
|
||||||
toast.error('Erro ao apagar peças');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.success('Última importação apagada com sucesso!');
|
toast.success('Última importação apagada com sucesso!');
|
||||||
setHasRecentImport(false);
|
setHasRecentImport(false);
|
||||||
@@ -635,6 +720,7 @@ export function usePecas() {
|
|||||||
savePeca,
|
savePeca,
|
||||||
updatePeca,
|
updatePeca,
|
||||||
deletePeca,
|
deletePeca,
|
||||||
|
deletePecasBatch,
|
||||||
importCSV,
|
importCSV,
|
||||||
importPecas,
|
importPecas,
|
||||||
undoLastImport,
|
undoLastImport,
|
||||||
@@ -645,3 +731,4 @@ export function usePecas() {
|
|||||||
sincronizarPrioridades
|
sincronizarPrioridades
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export default function CadastroPecas() {
|
|||||||
savePeca,
|
savePeca,
|
||||||
updatePeca,
|
updatePeca,
|
||||||
deletePeca,
|
deletePeca,
|
||||||
|
deletePecasBatch,
|
||||||
importCSV,
|
importCSV,
|
||||||
importPecas,
|
importPecas,
|
||||||
undoLastImport,
|
undoLastImport,
|
||||||
@@ -70,11 +71,16 @@ export default function CadastroPecas() {
|
|||||||
|
|
||||||
const handleDelete = async (pecaId: string) => {
|
const handleDelete = async (pecaId: string) => {
|
||||||
if (!canDelete()) return;
|
if (!canDelete()) return;
|
||||||
if (confirm('Tem certeza que deseja apagar esta peça?')) {
|
if (confirm('Tem certeza que deseja apagar esta peça e seus vínculos?')) {
|
||||||
await deletePeca(pecaId);
|
await deletePeca(pecaId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDeleteBatch = async (pecaIds: string[]) => {
|
||||||
|
if (!canDelete()) return;
|
||||||
|
await deletePecasBatch(pecaIds);
|
||||||
|
};
|
||||||
|
|
||||||
const handleEdit = (peca: Peca) => {
|
const handleEdit = (peca: Peca) => {
|
||||||
if (!canEdit()) return;
|
if (!canEdit()) return;
|
||||||
setEditingPeca(peca);
|
setEditingPeca(peca);
|
||||||
@@ -233,6 +239,7 @@ export default function CadastroPecas() {
|
|||||||
pecas={pecas}
|
pecas={pecas}
|
||||||
onOpenComponentPopup={handleOpenComponentPopup}
|
onOpenComponentPopup={handleOpenComponentPopup}
|
||||||
onDeletePeca={canDelete() ? handleDelete : undefined}
|
onDeletePeca={canDelete() ? handleDelete : undefined}
|
||||||
|
onDeletePecasBatch={canDelete() ? handleDeleteBatch : undefined}
|
||||||
onEditPeca={canEdit() ? handleEdit : undefined}
|
onEditPeca={canEdit() ? handleEdit : undefined}
|
||||||
onDeleteLastImport={canDelete() ? handleDeleteLastImport : undefined}
|
onDeleteLastImport={canDelete() ? handleDeleteLastImport : undefined}
|
||||||
hasRecentImport={hasRecentImport}
|
hasRecentImport={hasRecentImport}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export default function CadastroPecasFiltrado() {
|
|||||||
savePeca,
|
savePeca,
|
||||||
updatePeca,
|
updatePeca,
|
||||||
deletePeca,
|
deletePeca,
|
||||||
|
deletePecasBatch,
|
||||||
importCSV,
|
importCSV,
|
||||||
importPecas,
|
importPecas,
|
||||||
undoLastImport,
|
undoLastImport,
|
||||||
@@ -45,12 +46,10 @@ export default function CadastroPecasFiltrado() {
|
|||||||
if (!canCreate()) return false;
|
if (!canCreate()) return false;
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
try {
|
try {
|
||||||
// Garantir que a peça seja salva com a OF selecionada
|
const pecaComOF = { ...formData, of_number: ofSelecionada };
|
||||||
const formDataComOF = { ...formData, of_number: ofSelecionada };
|
const success = await savePeca(pecaComOF);
|
||||||
const success = await savePeca(formDataComOF);
|
|
||||||
if (success) {
|
if (success) {
|
||||||
setShowForm(false);
|
setShowForm(false);
|
||||||
// Reload data after successful save
|
|
||||||
loadPecas();
|
loadPecas();
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
@@ -59,15 +58,15 @@ export default function CadastroPecasFiltrado() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUpdate = async (id: string, formData: any) => {
|
const handleUpdate = async (formData: any) => {
|
||||||
if (!canEdit()) return false;
|
if (!canEdit() || !editingPeca) return false;
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
try {
|
try {
|
||||||
const success = await updatePeca(id, formData);
|
const pecaComOF = { ...formData, of_number: ofSelecionada };
|
||||||
|
const success = await updatePeca(editingPeca.id, pecaComOF);
|
||||||
if (success) {
|
if (success) {
|
||||||
setEditingPeca(null);
|
setEditingPeca(null);
|
||||||
setShowForm(false);
|
setShowForm(false);
|
||||||
// Reload data after successful update
|
|
||||||
loadPecas();
|
loadPecas();
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
@@ -78,15 +77,22 @@ export default function CadastroPecasFiltrado() {
|
|||||||
|
|
||||||
const handleDelete = async (pecaId: string) => {
|
const handleDelete = async (pecaId: string) => {
|
||||||
if (!canDelete()) return;
|
if (!canDelete()) return;
|
||||||
if (confirm('Tem certeza que deseja apagar esta peça?')) {
|
if (confirm('Tem certeza que deseja apagar esta peça e seus vínculos?')) {
|
||||||
const success = await deletePeca(pecaId);
|
const success = await deletePeca(pecaId);
|
||||||
if (success) {
|
if (success) {
|
||||||
// Reload data after successful delete
|
|
||||||
loadPecas();
|
loadPecas();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDeleteBatch = async (pecaIds: string[]) => {
|
||||||
|
if (!canDelete()) return;
|
||||||
|
const success = await deletePecasBatch(pecaIds);
|
||||||
|
if (success) {
|
||||||
|
loadPecas();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleEdit = (peca: Peca) => {
|
const handleEdit = (peca: Peca) => {
|
||||||
if (!canEdit()) return;
|
if (!canEdit()) return;
|
||||||
setEditingPeca(peca);
|
setEditingPeca(peca);
|
||||||
@@ -102,7 +108,6 @@ export default function CadastroPecasFiltrado() {
|
|||||||
if (!canDelete()) return;
|
if (!canDelete()) return;
|
||||||
if (confirm('Tem certeza que deseja apagar todas as peças da última importação?')) {
|
if (confirm('Tem certeza que deseja apagar todas as peças da última importação?')) {
|
||||||
await deleteLastImport();
|
await deleteLastImport();
|
||||||
// Reload data after successful delete
|
|
||||||
loadPecas();
|
loadPecas();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -122,13 +127,11 @@ export default function CadastroPecasFiltrado() {
|
|||||||
throw new Error('Sem permissão');
|
throw new Error('Sem permissão');
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// Adicionar a OF selecionada a todas as peças importadas
|
|
||||||
const pecasComOF = pecasData.map(peca => ({
|
const pecasComOF = pecasData.map(peca => ({
|
||||||
...peca,
|
...peca,
|
||||||
of_number: ofSelecionada
|
of_number: ofSelecionada
|
||||||
}));
|
}));
|
||||||
await importPecas(pecasComOF);
|
await importPecas(pecasComOF);
|
||||||
// Reload data after successful import
|
|
||||||
loadPecas();
|
loadPecas();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
@@ -142,38 +145,25 @@ export default function CadastroPecasFiltrado() {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await importCSV(file);
|
await importCSV(file);
|
||||||
// Reload data after successful import
|
|
||||||
loadPecas();
|
loadPecas();
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erro ao importar CSV:', error);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBatchUpdatePecas = async (pecaIds: string[], updates: any) => {
|
|
||||||
if (!canEdit()) {
|
|
||||||
toast.error('Você não tem permissão para editar peças');
|
|
||||||
throw new Error('Sem permissão');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await batchUpdatePecas(pecaIds, updates);
|
|
||||||
// Reload data after successful batch update
|
|
||||||
loadPecas();
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleUndoLastImport = async () => {
|
const handleUndoLastImport = async () => {
|
||||||
await undoLastImport();
|
if (!canDelete()) return;
|
||||||
// Reload data after successful undo
|
if (confirm('Tem certeza que deseja desfazer a última importação?')) {
|
||||||
loadPecas();
|
await undoLastImport();
|
||||||
|
loadPecas();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleVoltar = () => {
|
const handleBatchUpdatePecas = async (pecaIds: string[], updates: any) => {
|
||||||
navigate('/seletor-of');
|
if (!canEdit()) return;
|
||||||
|
await batchUpdatePecas(pecaIds, updates);
|
||||||
|
loadPecas();
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -197,56 +187,52 @@ export default function CadastroPecasFiltrado() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background p-6">
|
<StandardPageLayout
|
||||||
<div className="max-w-7xl mx-auto space-y-6">
|
title="Cadastro de Peças"
|
||||||
{/* Header */}
|
description="Gerencie as peças da OF selecionada"
|
||||||
<div className="flex items-center justify-between">
|
badges={[
|
||||||
<div>
|
{ label: `OF: ${ofSelecionada}`, variant: "secondary" }
|
||||||
<div className="flex items-center gap-4 mb-2">
|
]}
|
||||||
<h1 className="text-3xl font-bold text-foreground">Cadastro de Peças</h1>
|
actions={
|
||||||
<Badge variant="secondary" className="text-lg px-3 py-1">
|
<div className="flex gap-2">
|
||||||
OF: {ofSelecionada}
|
<Button
|
||||||
</Badge>
|
variant="outline"
|
||||||
</div>
|
onClick={() => navigate('/ordens-fabricacao')}
|
||||||
<p className="text-muted-foreground">Gerencie as peças da OF selecionada</p>
|
className="flex items-center gap-2"
|
||||||
</div>
|
>
|
||||||
<div className="flex items-center gap-4">
|
<ArrowLeft className="h-4 w-4" />
|
||||||
|
Voltar
|
||||||
|
</Button>
|
||||||
|
<Badge variant="outline" className="text-sm px-3 py-1">
|
||||||
|
{pecasFiltradas.length} peças
|
||||||
|
</Badge>
|
||||||
|
{canCreate() && (
|
||||||
<Button
|
<Button
|
||||||
onClick={handleVoltar}
|
onClick={() => {
|
||||||
variant="outline"
|
setEditingPeca(null);
|
||||||
|
setShowForm(!showForm);
|
||||||
|
}}
|
||||||
className="flex items-center gap-2"
|
className="flex items-center gap-2"
|
||||||
>
|
>
|
||||||
<ArrowLeft className="h-4 w-4" />
|
<Plus className="h-4 w-4" />
|
||||||
Voltar
|
{showForm ? 'Fechar' : 'Nova Peça'}
|
||||||
</Button>
|
</Button>
|
||||||
<Badge variant="secondary" className="bg-secondary text-secondary-foreground border-border">
|
)}
|
||||||
{pecasFiltradas.length} {pecasFiltradas.length === 1 ? 'peça' : 'peças'}
|
|
||||||
</Badge>
|
|
||||||
{canCreate() && (
|
|
||||||
<Button
|
|
||||||
onClick={() => setShowForm(true)}
|
|
||||||
className="bg-primary hover:bg-primary/90 text-primary-foreground flex items-center gap-2"
|
|
||||||
>
|
|
||||||
<Plus className="h-4 w-4" />
|
|
||||||
Nova Peça
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="space-y-6">
|
||||||
{/* Form Card */}
|
{/* Form Card */}
|
||||||
{showForm && canCreate() && (
|
{showForm && canCreate() && (
|
||||||
<Card className="bg-card border-border">
|
<Card className="bg-card border-border">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="text-card-foreground flex items-center gap-2">
|
<CardTitle className="text-card-foreground">
|
||||||
<Package className="h-5 w-5" />
|
{editingPeca ? 'Editar Peça' : 'Nova Peça'}
|
||||||
{editingPeca ? 'Editar Peça' : 'Nova Peça'} - OF: {ofSelecionada}
|
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<PecaForm
|
<PecaForm
|
||||||
ofNumbers={[ofSelecionada]} // Apenas a OF selecionada
|
ofNumber={ofSelecionada}
|
||||||
ofDefault={ofSelecionada}
|
|
||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
onUpdate={handleUpdate}
|
onUpdate={handleUpdate}
|
||||||
onImportCSV={handleImportCSV}
|
onImportCSV={handleImportCSV}
|
||||||
@@ -278,6 +264,7 @@ export default function CadastroPecasFiltrado() {
|
|||||||
pecas={pecasFiltradas}
|
pecas={pecasFiltradas}
|
||||||
onOpenComponentPopup={handleOpenComponentPopup}
|
onOpenComponentPopup={handleOpenComponentPopup}
|
||||||
onDeletePeca={canDelete() ? handleDelete : undefined}
|
onDeletePeca={canDelete() ? handleDelete : undefined}
|
||||||
|
onDeletePecasBatch={canDelete() ? handleDeleteBatch : undefined}
|
||||||
onEditPeca={canEdit() ? handleEdit : undefined}
|
onEditPeca={canEdit() ? handleEdit : undefined}
|
||||||
onDeleteLastImport={canDelete() ? handleDeleteLastImport : undefined}
|
onDeleteLastImport={canDelete() ? handleDeleteLastImport : undefined}
|
||||||
onBatchUpdatePecas={canEdit() ? handleBatchUpdatePecas : undefined}
|
onBatchUpdatePecas={canEdit() ? handleBatchUpdatePecas : undefined}
|
||||||
@@ -301,6 +288,6 @@ export default function CadastroPecasFiltrado() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</StandardPageLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user