feat: implement barcode search configuration in 'Almoxarifado', integrating multiple external APIs for enhanced product information retrieval and improving user experience with new modals for data handling

This commit is contained in:
2025-12-21 20:40:40 -03:00
parent 06ab7369bd
commit 639f7c6467
9 changed files with 2024 additions and 122 deletions

View File

@@ -0,0 +1,166 @@
import { v } from 'convex/values';
import { mutation, query, internalQuery } from './_generated/server';
import { encryptSMTPPassword } from './auth/utils';
import { registrarAtividade } from './logsAtividades';
/**
* Obter configuração de busca de código de barras ativa (credenciais mascaradas)
*/
export const obterConfigBuscaCodigoBarras = query({
args: {},
handler: async (ctx) => {
const config = await ctx.db
.query('configuracaoBuscaCodigoBarras')
.withIndex('by_ativo', (q) => q.eq('ativo', true))
.first();
if (!config) {
return null;
}
// Retornar config com credenciais mascaradas
return {
_id: config._id,
// GS1 Brasil
gs1BrasilClientId: config.gs1BrasilClientId || '',
gs1BrasilClientSecret: '********', // Mascarar
gs1BrasilTokenUrl: config.gs1BrasilTokenUrl || '',
gs1BrasilApiUrl: config.gs1BrasilApiUrl || '',
gs1BrasilAtivo: config.gs1BrasilAtivo,
// Bluesoft Cosmo
bluesoftApiKey: '********', // Mascarar
bluesoftApiUrl: config.bluesoftApiUrl || '',
bluesoftAtivo: config.bluesoftAtivo,
// Product-Search.net
productSearchApiKey: '********', // Mascarar
productSearchApiUrl: config.productSearchApiUrl || '',
productSearchAtivo: config.productSearchAtivo,
ativo: config.ativo,
atualizadoEm: config.atualizadoEm
};
}
});
/**
* Salvar configuração de busca de código de barras
*/
export const salvarConfigBuscaCodigoBarras = mutation({
args: {
// GS1 Brasil
gs1BrasilClientId: v.optional(v.string()),
gs1BrasilClientSecret: v.optional(v.string()),
gs1BrasilTokenUrl: v.optional(v.string()),
gs1BrasilApiUrl: v.optional(v.string()),
gs1BrasilAtivo: v.boolean(),
// Bluesoft Cosmo
bluesoftApiKey: v.optional(v.string()),
bluesoftApiUrl: v.optional(v.string()),
bluesoftAtivo: v.boolean(),
// Product-Search.net
productSearchApiKey: v.optional(v.string()),
productSearchApiUrl: v.optional(v.string()),
productSearchAtivo: v.boolean(),
configuradoPorId: v.id('usuarios')
},
returns: v.union(
v.object({ sucesso: v.literal(true), configId: v.id('configuracaoBuscaCodigoBarras') }),
v.object({ sucesso: v.literal(false), erro: v.string() })
),
handler: async (ctx, args) => {
// Buscar config ativa anterior para manter credenciais se não fornecidas
const configAtiva = await ctx.db
.query('configuracaoBuscaCodigoBarras')
.withIndex('by_ativo', (q) => q.eq('ativo', true))
.first();
// Determinar credenciais: usar novas se fornecidas, senão manter as atuais
let gs1BrasilClientSecret: string | undefined;
if (args.gs1BrasilClientSecret && args.gs1BrasilClientSecret.trim().length > 0) {
// Nova credencial fornecida, criptografar
gs1BrasilClientSecret = await encryptSMTPPassword(args.gs1BrasilClientSecret);
} else if (configAtiva?.gs1BrasilClientSecret) {
// Credencial não fornecida, manter a atual
gs1BrasilClientSecret = configAtiva.gs1BrasilClientSecret;
}
let bluesoftApiKey: string | undefined;
if (args.bluesoftApiKey && args.bluesoftApiKey.trim().length > 0) {
bluesoftApiKey = await encryptSMTPPassword(args.bluesoftApiKey);
} else if (configAtiva?.bluesoftApiKey) {
bluesoftApiKey = configAtiva.bluesoftApiKey;
}
let productSearchApiKey: string | undefined;
if (args.productSearchApiKey && args.productSearchApiKey.trim().length > 0) {
productSearchApiKey = await encryptSMTPPassword(args.productSearchApiKey);
} else if (configAtiva?.productSearchApiKey) {
productSearchApiKey = configAtiva.productSearchApiKey;
}
// Desativar config anterior
const configsAntigas = await ctx.db
.query('configuracaoBuscaCodigoBarras')
.withIndex('by_ativo', (q) => q.eq('ativo', true))
.collect();
for (const config of configsAntigas) {
await ctx.db.patch(config._id, { ativo: false });
}
// Criar nova config
const configId = await ctx.db.insert('configuracaoBuscaCodigoBarras', {
gs1BrasilClientId: args.gs1BrasilClientId || undefined,
gs1BrasilClientSecret,
gs1BrasilTokenUrl: args.gs1BrasilTokenUrl || undefined,
gs1BrasilApiUrl: args.gs1BrasilApiUrl || undefined,
gs1BrasilAtivo: args.gs1BrasilAtivo,
bluesoftApiKey,
bluesoftApiUrl: args.bluesoftApiUrl || undefined,
bluesoftAtivo: args.bluesoftAtivo,
productSearchApiKey,
productSearchApiUrl: args.productSearchApiUrl || undefined,
productSearchAtivo: args.productSearchAtivo,
ativo: true,
configuradoPor: args.configuradoPorId,
atualizadoEm: Date.now()
});
// Log de atividade
await registrarAtividade(
ctx,
args.configuradoPorId,
'configurar',
'buscaCodigoBarras',
JSON.stringify({
gs1BrasilAtivo: args.gs1BrasilAtivo,
bluesoftAtivo: args.bluesoftAtivo,
productSearchAtivo: args.productSearchAtivo
}),
configId
);
return { sucesso: true as const, configId };
}
});
/**
* Obter configuração de busca de código de barras (internal query)
* Usado pela action de busca para obter credenciais descriptografadas
*/
export const obterConfigBuscaCodigoBarrasInternal = internalQuery({
args: {},
handler: async (ctx) => {
const config = await ctx.db
.query('configuracaoBuscaCodigoBarras')
.withIndex('by_ativo', (q) => q.eq('ativo', true))
.first();
if (!config) {
return null;
}
// Retornar config completa (para uso interno)
return config;
}
});