From a748cacdd478d176b4e41ce2d84f4a4aeb7a2f8b Mon Sep 17 00:00:00 2001 From: femedici Date: Sun, 24 Aug 2025 21:38:06 -0300 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20implementa=20opera=C3=A7=C3=B5es=20?= =?UTF-8?q?para=20gerenciamento=20de=20c=C3=B3digos=20de=20barras?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/tombos-controller.js | 94 ++++++++++++++++++++++++- src/routes/tombos.js | 100 ++++++++++++++++++--------- 2 files changed, 160 insertions(+), 34 deletions(-) diff --git a/src/controllers/tombos-controller.js b/src/controllers/tombos-controller.js index b1e50352..6521f3d4 100644 --- a/src/controllers/tombos-controller.js +++ b/src/controllers/tombos-controller.js @@ -12,6 +12,7 @@ import { converteInteiroParaRomano } from '../helpers/tombo'; import models from '../models'; import codigos from '../resources/codigos-http'; import verifyRecaptcha from '../utils/verify-recaptcha'; +import { ForeignKeyConstraintError } from 'sequelize'; const { Solo, Relevo, Cidade, Estado, Vegetacao, FaseSucessional, Pais, Tipo, LocalColeta, Familia, sequelize, @@ -1577,6 +1578,95 @@ export const getUltimoNumeroTombo = (request, response, next) => { .catch(next); }; +export async function deletarCodigoBarras(request, response, next) { + try { + const count = await TomboFoto.destroy({ + where: { num_barra: request.params.codigo }, + }); + + if (count === 0) { + return response.status(404).json({ error: 'Código de barras não encontrado' }); + } + return response.status(204).end(); + } catch (err) { + return next(err); + } +} + +export const getUltimoCodigoBarra = (request, response, next) => { + Tombo.findAll({ attributes: ['hcf'] }) + .then((tombos) => { + const findByHcf = (hcf) => + TomboFoto.findOne({ + where: { tombo_hcf: hcf }, + attributes: ['id', 'codigo_barra', 'num_barra', 'caminho_foto'], + order: [['num_barra', 'DESC'], ['id', 'DESC']], + }); + + if (!tombos || tombos.length === 0) { + return Promise.resolve(null); + } + + const maximo = Math.max(...tombos.map((e) => e.hcf)); + + return findByHcf(maximo).then((registro) => { + if (registro) return registro; + return findByHcf(maximo - 1); + }); + }) + .then((tomboFoto) => + response.status(codigos.BUSCAR_UM_ITEM).json(tomboFoto || null) + ) + .catch(next); +}; + +export const postCodigoBarraTombo = (request, response, next) => { + const toHCFFormat = (hcf, codigo) => { + const codigoStr = String(codigo || '').padStart(9, '0'); + return `HCF${codigoStr}`; + }; + + const criarTransacaoCodigoBarra = (transaction) => + Promise.resolve().then(() => { + const { hcf, codigo_barra: codigoBarra } = request.body || {}; + + if (hcf == null || codigoBarra == null) { + const err = new Error('Parâmetros obrigatórios: hcf e codigo_barra.'); + err.status = 400; + throw err; + } + + const formattedCodigoBarra = toHCFFormat(hcf, codigoBarra); + if (!formattedCodigoBarra) { + const err = new Error('Erro ao formatar o codigo_barra.'); + err.status = 400; + throw err; + } + + const payload = { + tombo_hcf: hcf, + em_vivo: true, + codigo_barra: formattedCodigoBarra, + num_barra: codigoBarra, + caminho_foto: null, + }; + + return TomboFoto.create(payload, { transaction }); + }); + + return sequelize + .transaction(criarTransacaoCodigoBarra) + .then((foto) => response.status(201).json(foto)) + .catch((err) => { + if (err instanceof ForeignKeyConstraintError) { + return response + .status(400) + .json({ error: 'Violação de chave estrangeira.' }); + } + return next(err); + }); +}; + export const getUltimoNumeroCodigoBarras = (request, response, next) => { const { emVivo } = request.params; Promise.resolve() @@ -1642,7 +1732,7 @@ export const editarCodigoBarra = (request, response, next) => { .catch(next); }; -export const deletaCodigoBarra = codBarra => TomboFoto.destroy({ +/* export const deletaCodigoBarra = codBarra => TomboFoto.destroy({ where: { codigo_barra: codBarra, }, @@ -1659,6 +1749,6 @@ export const deletarCodigoBarra = (request, response, next) => { response.status(codigos.EDITAR_SEM_RETORNO).send(); }) .catch(next); -}; +}; */ export default {}; diff --git a/src/routes/tombos.js b/src/routes/tombos.js index dd1e1f4c..9166afb8 100644 --- a/src/routes/tombos.js +++ b/src/routes/tombos.js @@ -7,7 +7,8 @@ import { getDadosCadTombo, getNumeroTombo, cadastro, listagem, desativar, obterTombo, cadastrarTipo, buscarTipos, cadastrarColetores, buscarColetores, buscarProximoNumeroColetor, alteracao, getNumeroColetor, getUltimoNumeroTombo, getCodigoBarraTombo, - editarCodigoBarra, deletarCodigoBarra, getUltimoNumeroCodigoBarras, + editarCodigoBarra, getUltimoNumeroCodigoBarras, postCodigoBarraTombo, + getUltimoCodigoBarra, deletarCodigoBarras, } from '../controllers/tombos-controller'; import exportarTombosController from '../controllers/tombos-exportacoes-controller'; import criaJsonMiddleware from '../middlewares/json-middleware'; @@ -110,7 +111,7 @@ export default app => { /** * @swagger - * /tombos/codBarras/{idTombo}: + * /tombos/codigo_barras/{idTombo}: * get: * summary: Obtém o código de barras de um tombo * tags: [Tombos] @@ -143,39 +144,10 @@ export default app => { * $ref: '#/components/responses/NotFound' * '500': * $ref: '#/components/responses/InternalServerError' - * delete: - * summary: Remove o código de barras de um tombo - * tags: [Tombos] - * parameters: - * - in: path - * name: idTombo - * required: true - * schema: - * type: integer - * description: ID do tombo - * responses: - * 204: - * description: Código de barras removido com sucesso - * '401': - * $ref: '#/components/responses/Unauthorized' - * '403': - * $ref: '#/components/responses/Forbidden' - * '404': - * $ref: '#/components/responses/NotFound' - * '500': - * $ref: '#/components/responses/InternalServerError' */ - app.route('/tombos/codBarras/:idTombo') + app.route('/tombos/codigo_barras/:idTombo') .get([ getCodigoBarraTombo, - ]) - .delete([ - tokensMiddleware([ - TIPOS_USUARIOS.CURADOR, - TIPOS_USUARIOS.OPERADOR, - TIPOS_USUARIOS.IDENTIFICADOR, - ]), - deletarCodigoBarra, ]); /** @@ -334,6 +306,70 @@ export default app => { getUltimoNumeroTombo, ]); + /** + * @swagger + * /tombos/ultimo_codigo_barra: + * get: + * summary: Obtém o último codigo de barras + * tags: [Tombos] + * responses: + * 200: + * description: Último número retornado com sucesso + * '500': + * $ref: '#/components/responses/InternalServerError' + */ + app.route('/tombos/ultimo_codigo_barra') + .get([ + getUltimoCodigoBarra, + ]); + + /** + * @swagger + * /tombos/codigo_barras: + * post: + * summary: Cadastra um código de barra a um tombo + * tags: [Tombos] + * responses: + * 200: + * description: Último número retornado com sucesso + * '500': + * $ref: '#/components/responses/InternalServerError' + */ + app.route('/tombos/codigo_barras') + .post([ + postCodigoBarraTombo, + ]); + + /** + * @swagger + * /tombos/codigo_barras/{codigo}: + * delete: + * summary: Exclui um código de barras + * tags: [Tombos] + * parameters: + * - in: path + * name: codigo + * required: true + * schema: + * type: integer + * description: codigo de barras + * responses: + * 204: + * description: Código de barras removido com sucesso + * '401': + * $ref: '#/components/responses/Unauthorized' + * '403': + * $ref: '#/components/responses/Forbidden' + * '404': + * $ref: '#/components/responses/NotFound' + * '500': + * $ref: '#/components/responses/InternalServerError' + */ + app.route('/tombos/codigo_barras/:codigo') + .delete([ + deletarCodigoBarras, + ]); + /** * @swagger * /tombos: From 398a97c0abcb86e97fbc40b4022b9042e6ea674c Mon Sep 17 00:00:00 2001 From: femedici Date: Tue, 26 Aug 2025 21:08:59 -0300 Subject: [PATCH 2/3] fix: new requested changes and new validation schema --- src/controllers/tombos-controller.js | 113 ++++++++++------------- src/routes/tombos.js | 2 + src/validators/codigo-barras-cadastro.js | 14 +++ 3 files changed, 63 insertions(+), 66 deletions(-) create mode 100644 src/validators/codigo-barras-cadastro.js diff --git a/src/controllers/tombos-controller.js b/src/controllers/tombos-controller.js index 95288ec5..3b41bfa2 100644 --- a/src/controllers/tombos-controller.js +++ b/src/controllers/tombos-controller.js @@ -1,5 +1,7 @@ /* eslint-disable quotes */ // @ts-nocheck +import { ForeignKeyConstraintError } from 'sequelize'; + import { padronizarNomeDarwincore } from '~/helpers/padroniza-nome-darwincore'; import BadRequestExeption from '../errors/bad-request-exception'; @@ -1499,78 +1501,57 @@ export async function deletarCodigoBarras(request, response, next) { } } -export const getUltimoCodigoBarra = (request, response, next) => { - Tombo.findAll({ attributes: ['hcf'] }) - .then((tombos) => { - const findByHcf = (hcf) => - TomboFoto.findOne({ - where: { tombo_hcf: hcf }, - attributes: ['id', 'codigo_barra', 'num_barra', 'caminho_foto'], - order: [['num_barra', 'DESC'], ['id', 'DESC']], - }); - - if (!tombos || tombos.length === 0) { - return Promise.resolve(null); - } - - const maximo = Math.max(...tombos.map((e) => e.hcf)); +export const getUltimoCodigoBarra = async (req, res, next) => { + try { + const row = await TomboFoto.findOne({ + attributes: ['id', 'codigo_barra', 'num_barra', 'caminho_foto'], + order: [ + ['tombo_hcf', 'DESC'], + ['num_barra', 'DESC'], + ['id', 'DESC'], + ], + }); + return res.status(codigos.BUSCAR_UM_ITEM).json(row || null); + } catch (err) { + return next(err); + } +}; - return findByHcf(maximo).then((registro) => { - if (registro) return registro; - return findByHcf(maximo - 1); - }); - }) - .then((tomboFoto) => - response.status(codigos.BUSCAR_UM_ITEM).json(tomboFoto || null) - ) - .catch(next); -}; +const toHCFFormat = (codigo) => { + const codigoStr = String(codigo || '').padStart(9, '0'); + return `HCF${codigoStr}`; +}; export const postCodigoBarraTombo = (request, response, next) => { - const toHCFFormat = (hcf, codigo) => { - const codigoStr = String(codigo || '').padStart(9, '0'); - return `HCF${codigoStr}`; - }; - const criarTransacaoCodigoBarra = (transaction) => - Promise.resolve().then(() => { - const { hcf, codigo_barra: codigoBarra } = request.body || {}; - - if (hcf == null || codigoBarra == null) { - const err = new Error('Parâmetros obrigatórios: hcf e codigo_barra.'); - err.status = 400; - throw err; - } - - const formattedCodigoBarra = toHCFFormat(hcf, codigoBarra); - if (!formattedCodigoBarra) { - const err = new Error('Erro ao formatar o codigo_barra.'); - err.status = 400; - throw err; - } - - const payload = { - tombo_hcf: hcf, - em_vivo: true, - codigo_barra: formattedCodigoBarra, - num_barra: codigoBarra, - caminho_foto: null, - }; - - return TomboFoto.create(payload, { transaction }); - }); + Promise.resolve().then(() => { + const { hcf, codigo_barra: codigoBarra } = request.body || {}; + + const formattedCodigoBarra = toHCFFormat(codigoBarra); + if (!formattedCodigoBarra) { + throw new BadRequestExeption(417); + } + + const payload = { + tombo_hcf: hcf, + em_vivo: true, + codigo_barra: formattedCodigoBarra, + num_barra: codigoBarra, + caminho_foto: null, + }; + + return TomboFoto.create(payload, { transaction }); + }); return sequelize - .transaction(criarTransacaoCodigoBarra) - .then((foto) => response.status(201).json(foto)) - .catch((err) => { - if (err instanceof ForeignKeyConstraintError) { - return response - .status(400) - .json({ error: 'Violação de chave estrangeira.' }); - } - return next(err); - }); + .transaction(criarTransacaoCodigoBarra) + .then((foto) => response.status(201).json(foto)) + .catch((err) => { + if (err instanceof ForeignKeyConstraintError) { + return response.status(400).json({ error: 'Violação de chave estrangeira.' }); + } + return next(err); + }); }; export const getUltimoNumeroCodigoBarras = (request, response, next) => { diff --git a/src/routes/tombos.js b/src/routes/tombos.js index 9166afb8..d2311b6f 100644 --- a/src/routes/tombos.js +++ b/src/routes/tombos.js @@ -19,6 +19,7 @@ import coletorCadastro from '../validators/coletor-cadastro'; import cadastrarTipoEsquema from '../validators/tipo-cadastro'; import cadastrarTomboEsquema from '../validators/tombo-cadastro'; import listagemTombo from '../validators/tombo-listagem'; +import cadastrarCodigoBarrasEsquema from '../validators/codigo-barras-cadastro'; /** * @swagger @@ -337,6 +338,7 @@ export default app => { */ app.route('/tombos/codigo_barras') .post([ + validacoesMiddleware(cadastrarCodigoBarrasEsquema), postCodigoBarraTombo, ]); diff --git a/src/validators/codigo-barras-cadastro.js b/src/validators/codigo-barras-cadastro.js new file mode 100644 index 00000000..8befc8d5 --- /dev/null +++ b/src/validators/codigo-barras-cadastro.js @@ -0,0 +1,14 @@ +export default { + hcf: { + in: 'body', + isEmpty: false, + isInt: true, + errorMessage: 'HCF é obrigatório e deve ser um número inteiro' + }, + codigo_barra: { + in: 'body', + isEmpty: false, + isInt: true, + errorMessage: 'Código de barras é obrigatório e deve ser um número inteiro' + } +}; From 8816000c981f0307ffb56ec810779740fc3b8980 Mon Sep 17 00:00:00 2001 From: femedici Date: Tue, 26 Aug 2025 21:20:14 -0300 Subject: [PATCH 3/3] feat: removing unused code and adding validation schema --- src/controllers/tombos-controller.js | 50 +++++++----------------- src/routes/tombos.js | 2 +- src/validators/codigo-barras-cadastro.js | 6 +-- 3 files changed, 19 insertions(+), 39 deletions(-) diff --git a/src/controllers/tombos-controller.js b/src/controllers/tombos-controller.js index 3b41bfa2..e537a47c 100644 --- a/src/controllers/tombos-controller.js +++ b/src/controllers/tombos-controller.js @@ -14,7 +14,6 @@ import { converteInteiroParaRomano } from '../helpers/tombo'; import models from '../models'; import codigos from '../resources/codigos-http'; import verifyRecaptcha from '../utils/verify-recaptcha'; -import { ForeignKeyConstraintError } from 'sequelize'; import { aprovarPendencia } from './pendencias-controller'; const { @@ -1503,27 +1502,27 @@ export async function deletarCodigoBarras(request, response, next) { export const getUltimoCodigoBarra = async (req, res, next) => { try { - const row = await TomboFoto.findOne({ - attributes: ['id', 'codigo_barra', 'num_barra', 'caminho_foto'], - order: [ - ['tombo_hcf', 'DESC'], - ['num_barra', 'DESC'], - ['id', 'DESC'], - ], - }); - return res.status(codigos.BUSCAR_UM_ITEM).json(row || null); + const row = await TomboFoto.findOne({ + attributes: ['id', 'codigo_barra', 'num_barra', 'caminho_foto'], + order: [ + ['tombo_hcf', 'DESC'], + ['num_barra', 'DESC'], + ['id', 'DESC'], + ], + }); + return res.status(codigos.BUSCAR_UM_ITEM).json(row || null); } catch (err) { - return next(err); + return next(err); } }; - -const toHCFFormat = (codigo) => { + +const toHCFFormat = codigo => { const codigoStr = String(codigo || '').padStart(9, '0'); return `HCF${codigoStr}`; }; export const postCodigoBarraTombo = (request, response, next) => { - const criarTransacaoCodigoBarra = (transaction) => + const criarTransacaoCodigoBarra = transaction => Promise.resolve().then(() => { const { hcf, codigo_barra: codigoBarra } = request.body || {}; @@ -1545,8 +1544,8 @@ export const postCodigoBarraTombo = (request, response, next) => { return sequelize .transaction(criarTransacaoCodigoBarra) - .then((foto) => response.status(201).json(foto)) - .catch((err) => { + .then(foto => response.status(201).json(foto)) + .catch(err => { if (err instanceof ForeignKeyConstraintError) { return response.status(400).json({ error: 'Violação de chave estrangeira.' }); } @@ -1619,23 +1618,4 @@ export const editarCodigoBarra = (request, response, next) => { .catch(next); }; -/* export const deletaCodigoBarra = codBarra => TomboFoto.destroy({ - where: { - codigo_barra: codBarra, - }, -}); - -export const deletarCodigoBarra = (request, response, next) => { - const { idTombo } = request.params; - Promise.resolve() - .then(() => deletaCodigoBarra(idTombo)) - .then(retorno => { - if (!retorno) { - throw new BadRequestExeption(111); - } - response.status(codigos.EDITAR_SEM_RETORNO).send(); - }) - .catch(next); -}; */ - export default {}; diff --git a/src/routes/tombos.js b/src/routes/tombos.js index d2311b6f..a3bde1e9 100644 --- a/src/routes/tombos.js +++ b/src/routes/tombos.js @@ -15,11 +15,11 @@ import criaJsonMiddleware from '../middlewares/json-middleware'; import listagensMiddleware from '../middlewares/listagens-middleware'; import tokensMiddleware, { TIPOS_USUARIOS } from '../middlewares/tokens-middleware'; import validacoesMiddleware from '../middlewares/validacoes-middleware'; +import cadastrarCodigoBarrasEsquema from '../validators/codigo-barras-cadastro'; import coletorCadastro from '../validators/coletor-cadastro'; import cadastrarTipoEsquema from '../validators/tipo-cadastro'; import cadastrarTomboEsquema from '../validators/tombo-cadastro'; import listagemTombo from '../validators/tombo-listagem'; -import cadastrarCodigoBarrasEsquema from '../validators/codigo-barras-cadastro'; /** * @swagger diff --git a/src/validators/codigo-barras-cadastro.js b/src/validators/codigo-barras-cadastro.js index 8befc8d5..a9dde94f 100644 --- a/src/validators/codigo-barras-cadastro.js +++ b/src/validators/codigo-barras-cadastro.js @@ -3,12 +3,12 @@ export default { in: 'body', isEmpty: false, isInt: true, - errorMessage: 'HCF é obrigatório e deve ser um número inteiro' + errorMessage: 'HCF é obrigatório e deve ser um número inteiro', }, codigo_barra: { in: 'body', isEmpty: false, isInt: true, - errorMessage: 'Código de barras é obrigatório e deve ser um número inteiro' - } + errorMessage: 'Código de barras é obrigatório e deve ser um número inteiro', + }, };