├── .env ├── .gitignore ├── src ├── routes │ ├── routes.md │ ├── rotinaVerificacaoEstoque.js │ ├── fluxoRoutes.js │ └── produtosRoutes.js ├── models │ ├── models.md │ ├── produtosModel.js │ └── controleFluxoModel.js ├── controllers │ ├── controllers.md │ ├── fluxoController.js │ └── produtosController.js ├── config │ ├── config.md │ ├── config.js │ └── createDB.js ├── services │ └── validate.js ├── app.js └── index │ └── index.html ├── .eslintrc.json ├── server.js ├── criarBaseDadosTeste.js ├── package.json ├── test └── valitade.test.js ├── public └── metodosHTTP.js ├── README.md ├── produtos.js └── Postman Collection └── API-Estoque.postman_collection.json /.env: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /src/routes/routes.md: -------------------------------------------------------------------------------- 1 | Arquivo para configurar as rotas. -------------------------------------------------------------------------------- /src/models/models.md: -------------------------------------------------------------------------------- 1 | Cada modelo de dados fica guardado aqui. -------------------------------------------------------------------------------- /src/controllers/controllers.md: -------------------------------------------------------------------------------- 1 | Nesta pasta ficam todos os arquivos que funcionam como controladores de dados. -------------------------------------------------------------------------------- /src/config/config.md: -------------------------------------------------------------------------------- 1 | Arquivo para configuraçoes iniciais da aplicação 2 | 3 | Arquivo de configuração das blibliotecas 4 | Criar um para cada bliblioteca 5 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": "airbnb-base", 7 | "overrides": [], 8 | "parserOptions": { 9 | "ecmaVersion": "latest", 10 | "sourceType": "module" 11 | }, 12 | "rules": { 13 | "linebreak-style": ["error", "windows"] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | import app from './src/app.js' 2 | 3 | 4 | const porta = process.env.PORT || 3000; 5 | 6 | 7 | 8 | app.listen(porta,() => { 9 | // http://localhost:3000/ 10 | console.log(`Servidor escutando em http://localhost:${porta}/`); 11 | }) 12 | // app.listen(porta, '0.0.0.0', () => { 13 | // console.log(`Servidor escutando em http://0.0.0.0:${porta}/`); 14 | // }); 15 | -------------------------------------------------------------------------------- /src/routes/rotinaVerificacaoEstoque.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import produtosController from "../controllers/produtosController.js"; 3 | 4 | const rotinaEstoque = express.Router(); 5 | 6 | 7 | const nomeRota = "produtosQuantidadeMinima"; 8 | 9 | rotinaEstoque.route(`/${nomeRota}/`) 10 | .get(produtosController.listarProdutosComQuantidadeMinima); 11 | 12 | 13 | export default rotinaEstoque; -------------------------------------------------------------------------------- /src/services/validate.js: -------------------------------------------------------------------------------- 1 | function validate(obj, qtd) { 2 | if (qtd < obj.min_qtd) { 3 | return { 4 | bool: true, 5 | message: 'A quantidade informada está abaixo do limite mínimo permitido.', 6 | min_qtd: obj.min_qtd, 7 | }; 8 | } if (qtd > obj.max_qtd) { 9 | return { 10 | bool: true, 11 | message: 'A quantidade informada está acima do limite máximo permitido.', 12 | max_qtd: obj.max_qtd, 13 | }; 14 | } 15 | return { bool: false }; 16 | } 17 | 18 | export default validate; 19 | -------------------------------------------------------------------------------- /src/routes/fluxoRoutes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import controleFluxo from "../controllers/fluxoController.js"; 3 | const fluxoRoutes = express.Router(); 4 | 5 | 6 | const nomeRota = "fluxo"; 7 | 8 | fluxoRoutes.route(`/${nomeRota}/entrada/:id`) 9 | .post(controleFluxo.registrarEntrada); 10 | 11 | fluxoRoutes.route(`/${nomeRota}/saida/:id`) 12 | .post(controleFluxo.registrarSaida); 13 | 14 | fluxoRoutes.route(`/${nomeRota}/`).get(controleFluxo.read); 15 | 16 | fluxoRoutes.route(`/${nomeRota}/:produto_id`).get(controleFluxo.read_produto_id); 17 | 18 | export default fluxoRoutes; -------------------------------------------------------------------------------- /src/config/config.js: -------------------------------------------------------------------------------- 1 | import Sequelize from "sequelize"; 2 | import createDataBase from "./createDB.js"; 3 | 4 | const DB = "estoquedatabase";// Cuidado ao escolher o nome do banco ! 5 | const usuario = "root"; 6 | const senha = "meusql"; 7 | 8 | const sequelize = new Sequelize(DB, usuario, senha, { 9 | host: "localhost", 10 | dialect: "mysql", 11 | }); 12 | 13 | 14 | let dbStatusCreate = false; 15 | console.log(dbStatusCreate); 16 | 17 | if (dbStatusCreate) { 18 | await createDataBase(DB,usuario,senha); 19 | dbStatusCreate = false; 20 | console.log(dbStatusCreate); 21 | 22 | } 23 | 24 | 25 | export default sequelize; 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/config/createDB.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | // Cria uma bando de dados se o mesmo nao existir 5 | import mysql from "mysql2"; 6 | 7 | 8 | async function createDataBase(dbName,dbUser,dbPassword) { 9 | 10 | 11 | const connection = mysql.createConnection({ 12 | host: "localhost", 13 | user: dbUser, 14 | password: dbPassword, 15 | }); 16 | 17 | 18 | connection.query( 19 | `CREATE DATABASE IF NOT EXISTS ${dbName}`, 20 | function (err, results) { 21 | console.log(results); 22 | console.log(err||"None"); 23 | } 24 | ); 25 | 26 | 27 | connection.end(); 28 | } 29 | 30 | export default createDataBase; -------------------------------------------------------------------------------- /criarBaseDadosTeste.js: -------------------------------------------------------------------------------- 1 | import sequelize from "./src/config/config.js"; 2 | import Produto from "./src/models/produtosModel.js"; 3 | import Movimentacao from "./src/models/controleFluxoModel.js"; 4 | import { listaJSONplus } from "./produtos.js"; 5 | 6 | 7 | sequelize.authenticate().then(() => { 8 | console.log("Conectado com sucesso!"); 9 | }).catch((erro) => { 10 | console.log("Falha ao se conectar:", erro); 11 | }); 12 | 13 | 14 | 15 | await sequelize.sync({ force: true }); 16 | 17 | listaJSONplus.forEach(async input => { 18 | 19 | const produto = await Produto.create(input); 20 | await Movimentacao.create({ 21 | nome:produto.nome, 22 | tipo:"entrada", 23 | produto_id:produto.id, 24 | qtd: produto.qtd, 25 | data: new Date() 26 | }) 27 | 28 | }); 29 | 30 | -------------------------------------------------------------------------------- /src/models/produtosModel.js: -------------------------------------------------------------------------------- 1 | import { Sequelize } from 'sequelize'; 2 | import sequelize from '../config/config.js'; 3 | 4 | // Esquema Produtos 5 | const Produto = sequelize.define('produtos', { 6 | id: { 7 | type: Sequelize.INTEGER, 8 | autoIncrement: true, 9 | primaryKey: true, 10 | }, 11 | nome: { 12 | type: Sequelize.STRING, 13 | allowNull: false, 14 | }, 15 | marca: { 16 | type: Sequelize.STRING, 17 | allowNull: false, 18 | }, 19 | qtd: { 20 | type: Sequelize.INTEGER, 21 | allowNull: false, 22 | defaultValue: 0, 23 | }, 24 | min_qtd: { 25 | type: Sequelize.INTEGER, 26 | allowNull: false, 27 | defaultValue: 30, 28 | }, 29 | max_qtd: { 30 | type: Sequelize.INTEGER, 31 | allowNull: false, 32 | defaultValue: 999, 33 | }, 34 | }); 35 | 36 | export default Produto; 37 | -------------------------------------------------------------------------------- /src/routes/produtosRoutes.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import produtosController from "../controllers/produtosController.js"; 3 | 4 | const produtosRouter = express.Router(); 5 | 6 | const nomeRota = "cadastroProdutos"; 7 | 8 | produtosRouter.route(`/${nomeRota}`) 9 | .post(produtosController.create) 10 | .get(produtosController.read); 11 | 12 | //http://localhost:3000/cadastroProdutos/pagina?itensPorPagina=10&pagina=1 13 | produtosRouter.route(`/${nomeRota}/pagina`) 14 | .get(produtosController.readQuery); 15 | 16 | // http://localhost:3000/cadastroProdutos/busca?nome=&marca=&qtd= 17 | 18 | produtosRouter.route(`/${nomeRota}/busca`) 19 | .get(produtosController.find); 20 | 21 | produtosRouter.route(`/${nomeRota}/:id`) 22 | .put(produtosController.update) 23 | .delete(produtosController.delete) 24 | .get(produtosController.readById); 25 | 26 | export default produtosRouter; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "controle-estoque-api", 3 | "version": "1.0.0", 4 | "description": "API para controle de estoque de produtos relacionados a informática", 5 | "main": "server.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node server.js", 9 | "dev": "nodemon server.js", 10 | "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js" 11 | }, 12 | "keywords": [ 13 | "API", 14 | "NodeJS", 15 | "express", 16 | "sequelize", 17 | "mysql" 18 | ], 19 | "author": "Fabrício Neves", 20 | "license": "MIT", 21 | "dependencies": { 22 | "cors": "^2.8.5", 23 | "express": "^4.18.2", 24 | "mysql2": "^3.2.0", 25 | "sequelize": "^6.29.3" 26 | }, 27 | "devDependencies": { 28 | "eslint": "^8.40.0", 29 | "eslint-config-airbnb-base": "^15.0.0", 30 | "eslint-plugin-import": "^2.27.5", 31 | "jest": "28.1.0", 32 | "nodemon": "^2.0.22" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import fs from 'fs'; 3 | import cors from 'cors'; 4 | // eslint-disable-next-line import/extensions 5 | import produtosRouter from './routes/produtosRoutes.js'; 6 | // eslint-disable-next-line import/extensions 7 | import rotinaEstoque from './routes/rotinaVerificacaoEstoque.js'; 8 | // eslint-disable-next-line import/extensions 9 | import fluxoRoutes from './routes/fluxoRoutes.js'; 10 | 11 | /** Add arquivo de index.js */ 12 | const html = fs.readFileSync('./src/index/index.html', 'utf8'); 13 | 14 | const app = express(); 15 | 16 | /** add o middleware Json */ 17 | app.use(express.json()); 18 | 19 | /** habilita CORS */ 20 | app.use(cors({ 21 | origin: '*', 22 | })); 23 | 24 | app.get('/', (req, res) => { 25 | res.status(200).send(html); 26 | }); 27 | 28 | /** Add Rotas */ 29 | app.use(produtosRouter); 30 | app.use(rotinaEstoque); 31 | app.use(fluxoRoutes); 32 | 33 | export default app; 34 | -------------------------------------------------------------------------------- /src/models/controleFluxoModel.js: -------------------------------------------------------------------------------- 1 | import { Sequelize } from 'sequelize'; 2 | import sequelize from '../config/config.js'; 3 | // import Produto from './produtosModel.js'; 4 | 5 | const Movimentacao = sequelize.define('Movimentacao', { 6 | nome: { 7 | type: Sequelize.STRING, 8 | allowNull: true, 9 | }, 10 | produto_id: { 11 | type: Sequelize.INTEGER, 12 | allowNull: true, 13 | }, 14 | tipo: { 15 | type: Sequelize.ENUM('entrada', 'saida'), 16 | allowNull: false, 17 | }, 18 | qtd: { 19 | type: Sequelize.INTEGER, 20 | allowNull: false, 21 | }, 22 | data: { 23 | type: Sequelize.DATE, 24 | allowNull: false, 25 | }, 26 | }); 27 | 28 | // Produto.hasMany(Movimentacao, { 29 | // onDelete: 'cascade', 30 | // hooks: true, 31 | // foreignKey: { 32 | // name: 'produto_id', 33 | // allowNull: false 34 | // } 35 | // }); 36 | 37 | // Movimentacao.belongsTo(Produto, { 38 | // foreignKey: { 39 | // name: 'produto_id', 40 | // allowNull: false 41 | // } 42 | // }); 43 | 44 | export default Movimentacao; 45 | -------------------------------------------------------------------------------- /test/valitade.test.js: -------------------------------------------------------------------------------- 1 | import validate from "../src/services/validate"; 2 | 3 | describe('validate', () => { 4 | it('deve retornar false se a quantidade estiver dentro dos limites', () => { 5 | const obj = { min_qtd: 5, max_qtd: 10 }; 6 | const qtd = 8; 7 | 8 | const result = validate(obj, qtd); 9 | 10 | expect(result.bool).toBe(false); 11 | }); 12 | 13 | it('deve retornar true e a mensagem correta se a quantidade for menor que o limite mínimo', () => { 14 | const obj = { min_qtd: 5, max_qtd: 10 }; 15 | const qtd = 3; 16 | 17 | const result = validate(obj, qtd); 18 | 19 | expect(result.bool).toBe(true); 20 | expect(result.message).toBe('A quantidade informada está abaixo do limite mínimo permitido.'); 21 | expect(result.min_qtd).toBe(5); 22 | }); 23 | 24 | it('deve retornar true e a mensagem correta se a quantidade for maior que o limite máximo', () => { 25 | const obj = { min_qtd: 5, max_qtd: 10 }; 26 | const qtd = 12; 27 | 28 | const result = validate(obj, qtd); 29 | 30 | expect(result.bool).toBe(true); 31 | expect(result.message).toBe('A quantidade informada está acima do limite máximo permitido.'); 32 | expect(result.max_qtd).toBe(10); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /public/metodosHTTP.js: -------------------------------------------------------------------------------- 1 | // Revisar todos os itens !!! 2 | 3 | class ProdutosAPI { 4 | baseURL = "http://localhost:3000/cadastroProdutos"; 5 | 6 | async getItem() { 7 | try { 8 | const response = await fetch(this.baseURL); 9 | return response.json(); 10 | } catch (error) { 11 | console.error(error); 12 | throw error; 13 | } 14 | } 15 | 16 | async createItem(item) { 17 | try { 18 | const response = await fetch(this.baseURL, { 19 | method: "POST", 20 | headers: { 21 | "Content-Type": "application/json", 22 | }, 23 | body: JSON.stringify(item), 24 | }); 25 | return response.json(); 26 | } catch (error) { 27 | console.error(error); 28 | throw error; 29 | } 30 | } 31 | 32 | async updateItem(id, item) { 33 | try { 34 | const response = await fetch(`${this.baseURL}/${id}`, { 35 | method: "PUT", 36 | headers: { 37 | "Content-Type": "application/json", 38 | }, 39 | body: JSON.stringify(item), 40 | }); 41 | return response.json(); 42 | } catch (error) { 43 | console.error(error); 44 | throw error; 45 | } 46 | } 47 | 48 | async deleteItem(id) { 49 | try { 50 | const response = await fetch(`${this.baseURL}/${id}`, { 51 | method: "DELETE", 52 | }); 53 | return response.json(); 54 | } catch (error) { 55 | console.error(error); 56 | throw error; 57 | } 58 | } 59 | 60 | async searchItems({ nome, marca, qtd }) { 61 | const params = new URLSearchParams({ 62 | nome: nome || "", 63 | marca: marca || "", 64 | qtd: qtd || "", 65 | }); 66 | 67 | const url = `${this.baseURL}/busca?${params.toString()}`; 68 | 69 | try { 70 | const response = await fetch(url); 71 | return response.json(); 72 | } catch (error) { 73 | console.error(error); 74 | throw error; 75 | } 76 | } 77 | 78 | async getItensPagination({ itensPorPagina = 10, pagina = 1 }) { 79 | const params = new URLSearchParams({ 80 | itensPorPagina: itensPorPagina, 81 | pagina: pagina, 82 | }); 83 | 84 | const url = `${this.baseURL}/pagina?${params.toString()}`; 85 | 86 | try { 87 | const response = await fetch(url); 88 | return response.json(); 89 | } catch (error) { 90 | console.error(error); 91 | throw error; 92 | } 93 | } 94 | 95 | async getItensById(id) { 96 | try { 97 | const response = await fetch(`${this.baseURL}/${id}`); 98 | return response.json(); 99 | } catch (error) { 100 | console.error(error); 101 | throw error; 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/controllers/fluxoController.js: -------------------------------------------------------------------------------- 1 | import Movimentacao from '../models/controleFluxoModel.js'; 2 | import Produto from '../models/produtosModel.js'; 3 | class controleFluxo { 4 | 5 | //**Create Entrada */ 6 | static async entrada(produto, qtd, data_in) { 7 | 8 | data_in = data_in ? data_in : new Date(); 9 | 10 | try { 11 | await Movimentacao.create({ 12 | nome: produto.nome, 13 | tipo: "entrada", 14 | produto_id: produto.id, 15 | qtd: qtd, 16 | data: data_in 17 | }) 18 | return true; 19 | } catch (err) { 20 | console.log(err); 21 | return false; 22 | } 23 | 24 | } 25 | //**Create Saída */ 26 | static async saida(produto, qtd, data_out) { 27 | 28 | data_out = data_out ? data_out : new Date(); 29 | 30 | try { 31 | await Movimentacao.create({ 32 | nome: produto.nome, 33 | tipo: "saida", 34 | produto_id: produto.id, 35 | qtd: -(qtd), 36 | data: data_out 37 | }) 38 | return true; 39 | } catch (err) { 40 | console.log(err); 41 | return false; 42 | } 43 | 44 | } 45 | 46 | //** Registro Nova Entrada */ 47 | static async registrarEntrada(req, res) { 48 | const { id } = req.params; 49 | const { qtd_in, data } = req.body; 50 | 51 | try { 52 | const produto = await Produto.findOne({ where: { id } }); 53 | if (!produto) { 54 | return res.status(404).json({ isSuccess: false, message: 'Produto não encontrado.' }); 55 | } 56 | 57 | if (!qtd_in || qtd_in <= 0) { 58 | return res.status(409).json({ isSuccess: false, message: 'Quantidade de saída inválida.' }); 59 | } 60 | const qtd_total = produto.qtd + qtd_in; 61 | 62 | await produto.update({ qtd: qtd_total }); 63 | await controleFluxo.entrada(produto, qtd_in, data); 64 | res.status(200).json({ isSuccess: true, message: 'Entrada registrada com sucesso.', qtd_total: qtd_total }); 65 | } catch (error) { 66 | console.error(error); 67 | res.status(500).json({ isSuccess: false, message: 'Ocorreu um erro ao registrar a entrada.' }); 68 | } 69 | } 70 | //** Registro Nova Saída */ 71 | static async registrarSaida(req, res) { 72 | const { id } = req.params; 73 | const { qtd_out, data } = req.body; 74 | 75 | try { 76 | const produto = await Produto.findOne({ where: { id } }); 77 | if (!produto) { 78 | return res.status(404).json({ isSuccess: false, message: 'Produto não encontrado.' }); 79 | } 80 | const { qtd } = produto; 81 | if (!qtd_out || qtd_out <= 0) { 82 | return res.status(409).json({ isSuccess: false, message: 'Quantidade de saída inválida.' }); 83 | } 84 | if (qtd < qtd_out) { 85 | return res.status(409).json({ isSuccess: false, message: 'Quantidade indisponível para saída.' }); 86 | } 87 | const qtd_total = qtd - qtd_out; 88 | console.log(qtd_total); 89 | const result = await produto.update({ qtd: qtd_total }); 90 | if (result) { 91 | const movimentacao = await controleFluxo.saida(produto, qtd_out, data); 92 | if (!movimentacao) { 93 | return res.status(500).json({ isSuccess: false, message: 'Erro ao registrar saída na movimentação.' }); 94 | } 95 | return res.status(200).json({ isSuccess: true, message: 'Saída registrada com sucesso.', qtd_total: qtd_total }); 96 | } 97 | } catch (error) { 98 | res.status(500).json({ isSuccess: false, message: error.message }); 99 | } 100 | } 101 | 102 | //**READ-ALL-ITEMS */ 103 | static async read(req, res) { 104 | try { 105 | const Mov = await Movimentacao.findAll(); 106 | res.status(200).json({ isSuccess: true, message: Mov }); 107 | } catch (error) { 108 | res.status(500).json({ isSuccess: false, message: error }); 109 | } 110 | } 111 | //**READ produto_id */ 112 | static async read_produto_id(req, res) { 113 | const { produto_id } = req.params; 114 | console.log(produto_id); 115 | try { 116 | const registro = await Movimentacao.findAll({ where: { produto_id } }); 117 | if (registro) { 118 | res.status(200).json({ isSuccess: true, message: registro }); 119 | } else { 120 | res.status(404).send({ isSuccess: false, message: 'Produtos não encontrados.' }); 121 | } 122 | } catch (error) { 123 | res.status(500).send({ isSuccess: false, message: error }); 124 | } 125 | } 126 | 127 | } 128 | 129 | export default controleFluxo; -------------------------------------------------------------------------------- /src/controllers/produtosController.js: -------------------------------------------------------------------------------- 1 | import Sequelize, { Op } from 'sequelize'; 2 | 3 | import Produto from '../models/produtosModel.js'; 4 | import controleFluxo from './fluxoController.js'; 5 | 6 | class produtosController { 7 | // READ 8 | static async read(req, res) { 9 | try { 10 | const produtos = await Produto.findAll(); 11 | res.status(200).json(produtos); 12 | } catch (error) { 13 | res.status(500).send(error); 14 | } 15 | } 16 | 17 | // READ-QUERY 18 | static async readQuery(req, res) { 19 | try { 20 | const itensPorPagina = Number(req.query.itensPorPagina) || 10; // Valor padrão é 10 21 | const pagina = Number(req.query.pagina) || 1; // Valor padrão é 1 22 | const offset = (pagina - 1) * itensPorPagina; 23 | console.log(itensPorPagina, pagina, offset); 24 | const produtos = await Produto.findAndCountAll({ 25 | where: {}, 26 | limit: itensPorPagina, 27 | offset, 28 | }); 29 | const totalItens = produtos.count; 30 | const totalPaginas = Math.ceil(totalItens / itensPorPagina); 31 | res.status(200).json({ 32 | produtos: produtos.rows, 33 | paginaAtual: pagina, 34 | totalPaginas, 35 | }); 36 | } catch (error) { 37 | console.log(error); 38 | res.status(500).send(error); 39 | } 40 | } 41 | 42 | // READ-BY-ID 43 | static async readById(req, res) { 44 | const { id } = req.params; 45 | try { 46 | const produto = await Produto.findOne({ where: { id } }); 47 | if (produto) { 48 | res.status(200).json(produto); 49 | } else { 50 | res.status(404).send('Produto não encontrado.'); 51 | } 52 | } catch (error) { 53 | res.status(500).send(error); 54 | } 55 | } 56 | 57 | // CREATE 58 | static async create(req, res) { 59 | const novoProduto = req.body; 60 | const { qtd, nome } = novoProduto; 61 | 62 | const produto = await Produto.findOne({ where: { nome } }); 63 | 64 | if (produto) { 65 | return res.status(409).send('Produto já existe.'); 66 | } if (Number(qtd) < 0) { 67 | return res.status(409).send('Quantidada precisa ser maior que 0'); 68 | } 69 | 70 | try { 71 | const produtoCriado = await Produto.create(novoProduto); 72 | if (qtd) { 73 | await controleFluxo.entrada(produtoCriado, qtd); 74 | } 75 | res.status(200).json(produtoCriado); 76 | } catch (error) { 77 | res.status(500).send(error); 78 | } 79 | } 80 | 81 | // UPDATE 82 | static async update(req, res) { 83 | const { id } = req.params; 84 | const { 85 | nome, marca, min_qtd, max_qtd, 86 | } = req.body; 87 | 88 | if (!nome && !marca && !min_qtd && !max_qtd) { 89 | return res.status(400).send('Pelo menos um campo de nome, marca, min_qtd ou max_qtd é obrigatório para atualização.'); 90 | } 91 | 92 | try { 93 | const produto = await Produto.findOne({ where: { id } }); 94 | 95 | if (produto) { 96 | await produto.update({ 97 | nome, marca, min_qtd, max_qtd, 98 | }); 99 | res.status(200).json(produto); 100 | } else { 101 | res.status(404).send('Produto não encontrado.'); 102 | } 103 | } catch (error) { 104 | res.status(500).send(error); 105 | } 106 | } 107 | 108 | // DELETE 109 | static async delete(req, res) { 110 | const { id } = req.params; 111 | try { 112 | const produto = await Produto.findOne({ where: { id } }); 113 | const { qtd } = produto; 114 | if (produto) { 115 | if (qtd) { 116 | await controleFluxo.saida(produto, qtd); 117 | } 118 | await produto.destroy(); 119 | res.json({ message: 'Produto_deletado', produto }); 120 | } else { 121 | res.status(404).send('Produto não encontrado.'); 122 | } 123 | } catch (error) { 124 | console.log(error); 125 | res.status(500).send(error); 126 | } 127 | } 128 | 129 | // FIND 130 | static async find(req, res) { 131 | const { query } = req; 132 | const objSQLQuery = {}; 133 | 134 | // Construção do Objeto de Procura. 135 | for (const key in query) { 136 | objSQLQuery[key] = { 137 | 138 | [Op.like]: `${query[key]}%`, 139 | }; 140 | } 141 | try { 142 | const produtos = await Produto.findAll({ 143 | where: objSQLQuery, 144 | 145 | }); 146 | res.status(200).json(produtos); 147 | } catch (error) { 148 | res.status(500).json({ error: error.message }); 149 | } 150 | } 151 | 152 | // Listar Protudos Abaixo da quantidade Mínima 153 | static async listarProdutosComQuantidadeMinima(req, res) { 154 | try { 155 | const produtos = await Produto.findAll({ 156 | where: Sequelize.literal('qtd < min_qtd'), 157 | }); 158 | 159 | if (produtos.length > 0) { 160 | return res.status(200).json(produtos); 161 | } 162 | return res.status(404).json({ message: 'Nenhum produto encontrado com quantidade mínima abaixo do limite.' }); 163 | } catch (error) { 164 | return res.status(500).json({ message: error.message }); 165 | } 166 | } 167 | } 168 | 169 | export default produtosController; 170 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # API de cadastro de produtos 2 | API desenvolvida em Node.js com Express e banco de dados MySQL, utilizando a biblioteca Sequelize para ORM. Permite o registro de entrada e saída de produtos, atualizando a quantidade total em estoque e registrando tambem as movimentações de produtos. 3 | 4 | ## ♻ Atualização 5 | 6 | Foram adicionadas novas funcionalidades à API. Para ter acesso às novidades, utilize o comando "git pull" em seu repositório. Entre as atualizações, destacam-se: 7 | - Registro de entrada e saída de mercadorias. 8 | - Inclusão de método e modelo no banco para definir valores máximos e mínimos de produtos no estoque. 9 | 10 | OBS: Como não foi criado um arquivo de migração, e este projeto esta em constante aprimoramento. Considere recriar as tabelas usando o comando **"node criarBaseDadosTeste.js"** , agora com 46 itens. 11 | 12 | ## 🔝 Metas 13 | - [ ] Forçar um estilo de programação (Airbnb). 14 | 15 | - [ ] Criar testes unitários. 16 | 17 | - [ ] Add ao projeto o Sequelize CLI e adcionar um arquivo de migração. 18 | ## 🏃‍♂️ Motivações e Objetivos 19 | Esta API foi desenvolvida para atender as especificações do cliente [Pedro Marins](https://github.com/pedromarins). Que tem como a demanda de funcionalidades : 20 | 21 | ### Lista de funcionalidades 22 | 23 | #### Básico 24 | - Ter uma lista de produtos com um identificador para cada item. 25 | - Ler a lista de produtos. 26 | - Poder adicionar um item na lista de produtos. 27 | - Poder remover um item da lista de produtos. 28 | - Adicionar quantidade de itens na lista de produtos. 29 | - Poder alterar a quantidade de itens de um produto específico. 30 | - Consultar um item na lista de produtos para saber a quantidade disponível. 31 | 32 | #### Avançado 33 | - Poder adicionar um limite mínimo e máximo para cada item da lista de produtos. 34 | - Rotina para verificar se alguma quantidade de itens está abaixo do limite mínimo. 35 | 36 | #### Extra 37 | - Armazenar cada transação na lista de produtos. 38 | ## 📚 Dependências 39 | ### Blibliotecas & FrameWorks 40 | - express 41 | - mysql2 42 | - sequelize 43 | - nodemon (somente para desenvolvimento) 44 | 45 | ### Banco de dados 46 | - MySQL 47 | 48 | ## 🏗 Instalação 49 | Clone este repositório. 50 | Na pasta raiz do projeto, execute o comando **npm install** para instalar as dependências. 51 | Configure as variáveis de ambiente no arquivo **config.js** com as informações do banco de dados. 52 | Execute o comando **npm start** para rodar a aplicação. 53 | 54 | ## 🎲 Banco de Dados 55 | Para configurar corretamente o banco de dados é necessario alterar as variáveis no arquivo **src/config/config.js**. 56 | ```javascript 57 | const DB = "NomeDoBanco"; // somente letras e numeros 58 | const usuario = "root"; 59 | const senha = "123"; 60 | ``` 61 | Para criar um banco de dados para teste execute o comando **"node criarBaseDadosTeste.js"** , para criar 46 itens. 62 | 63 | ## 🔀 Rotas de produtos 64 | A API possui as seguintes rotas: 65 | 66 | ### POST /cadastroProdutos 67 | Cria um novo produto. 68 | 69 | Exemplo de requisição: 70 | 71 | ```json 72 | { 73 | "nome": "Produto A", 74 | "marca": "Marca A", 75 | "qtd": 10, 76 | "min_qtd" : 30, 77 | "max_qtd" : 999 78 | } 79 | ``` 80 | * Usando esse método automaticamente registra a quantidade como entrada. 81 | 82 | 83 | ### GET /cadastroProdutos 84 | Retorna todos os produtos. 85 | 86 | ### GET /cadastroProdutos/pagina?itensPorPagina=10&pagina=1 87 | Retorna uma lista de produtos paginados. É possível passar os parâmetros **itensPorPagina** e **pagina** para definir a quantidade de itens por página e a página desejada. 88 | 89 | ### GET /cadastroProdutos/busca?nome=&marca= 90 | Retorna uma lista de produtos filtrados por **nome**, **marca**, Passando os parâmetros **nome**, **marca** para realizar a busca. 91 | 92 | ### GET /cadastroProdutos/:id 93 | Retorna um produto específico pelo seu **ID**. 94 | 95 | ### PUT /cadastroProdutos/:id 96 | Atualiza um produto existente. 97 | 98 | Exemplo de requisição: 99 | 100 | ```json 101 | { 102 | "nome": "Produto B", 103 | "marca": "Marca B", 104 | "min_qtd" : 30, 105 | "max_qtd" : 999 106 | } 107 | ``` 108 | * Quantidade não pode ser atualizada por esta rota. 109 | 110 | ### DELETE /cadastroProdutos/:id 111 | Deleta um produto existente. 112 | 113 | * Usando esse método automaticamente registra toda a quantidade desse item como saída. 114 | 115 | ### GET /produtosQuantidadeMinima 116 | Retorna uma lista de produtos com quantidade abaixo do limite mínimo definido no cadastro do produto. 117 | 118 | ## 🛒 Rotas de entrada e saída de mercadorias. 119 | 120 | ### POST /fluxo/entrada/:id 121 | 122 | Registra a entrada de um determinado produto no estoque. O parâmetro **:id** corresponde ao ID do produto que será atualizado. O corpo da requisição deve conter um objeto JSON com as chaves **qtd_in** (quantidade que será adicionada ao estoque) e **data** (data e hora da entrada no formato "YYYY-MM-DD HH:mm:ss"). 123 | 124 | ```json 125 | { 126 | "qtd_in" : 30, 127 | "data" : "YYYY-MM-DD HH:mm:ss" 128 | } 129 | ``` 130 | 131 | ### POST /fluxo/saida/:id 132 | 133 | Registra a saída de um determinado produto no estoque. O parâmetro **:id** corresponde ao ID do produto que será atualizado. O corpo da requisição deve conter um objeto JSON com as chaves **qtd_out** (quantidade que será retirada do estoque) e **data** (data e hora da saída no formato "YYYY-MM-DD HH:mm:ss"). 134 | 135 | ```json 136 | { 137 | "qtd_in" : 30, 138 | "data" : "YYYY-MM-DD HH:mm:ss" 139 | } 140 | ``` 141 | 142 | ### GET /fluxo 143 | 144 | Retorna uma lista de todas as movimentações registradas. 145 | ### GET /fluxo/:produto_id 146 | 147 | Retorna uma lista de movimentações de um produto específico com base no seu id. 148 | 149 | ## 💻 Postman 150 | 151 | Dentro da pasta **Postman Collection** está salvo o arquivo JSON usado para testar essa API usando o programa [Postman](https://www.postman.com/downloads/) -------------------------------------------------------------------------------- /src/index/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | API de Estoque 9 | 96 | 97 | 98 | 99 |
100 |

Bem-vindo à minha API de controle de estoque!

101 |

Esta é uma API desenvolvida em Node.js com Express e banco de dados MySQL, utilizando a biblioteca Sequelize 102 | para ORM. Ela permite o registro de entrada e saída de produtos, atualizando a quantidade total em estoque e 103 | registrando também as movimentações de produtos.

104 |

Se você está interessado em dar uma olhada no código, pode encontrar o repositório no GitHub.

106 |

Sinta-se livre para explorar e contribuir para o desenvolvimento desta API!

107 |
108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 131 | 132 | 133 | 134 | 135 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 155 | 156 | 157 | 158 | 159 | 161 | 162 | 163 | 164 | 165 | 170 | 171 | 172 | 173 | 174 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /produtos.js: -------------------------------------------------------------------------------- 1 | const listaJSON = [{ 2 | "id": "0001", 3 | "nome": "Teclado Gamer", 4 | "marca": "Genérico", 5 | "qtd": "43" 6 | }, 7 | { 8 | "id": "0002", 9 | "nome": "Mouse Gamer", 10 | "marca": "Genérico", 11 | "qtd": "103" 12 | }, 13 | { 14 | "id": "0003", 15 | "nome": "Monitor 4k", 16 | "marca": "LG", 17 | "qtd": "45" 18 | }, 19 | { 20 | "id": "0004", 21 | "nome": "Tablet", 22 | "marca": "Samsung", 23 | "qtd": "23" 24 | }, 25 | { 26 | "id": "0005", 27 | "nome":"Notebook", 28 | "marca": "Genérico", 29 | "qtd": "16" 30 | }, 31 | { 32 | "id": "0006", 33 | "nome":"Impressora", 34 | "marca": "Genérico", 35 | "qtd": "160" 36 | }, 37 | { 38 | "id": "0007", 39 | "nome":"Roteador", 40 | "marca": "Genérico", 41 | "qtd": "8" 42 | }, 43 | { 44 | "id": "0008", 45 | "nome":"Pen Drive", 46 | "marca": "Genérico", 47 | "qtd": "99" 48 | }, 49 | { 50 | "id": "0009", 51 | "nome":"Caixas de som", 52 | "marca": "Genérico", 53 | "qtd": "200" 54 | }] 55 | 56 | export const listaJSONplus = [ 57 | { 58 | "id": "0001", 59 | "nome": "Teclado Gamer", 60 | "marca": "Genérico", 61 | "qtd": "23" 62 | }, 63 | { 64 | "id": "0002", 65 | "nome": "Mouse Gamer", 66 | "marca": "Genérico", 67 | "qtd": "56" 68 | }, 69 | { 70 | "id": "0003", 71 | "nome": "Monitor 4k", 72 | "marca": "LG", 73 | "qtd": "56" 74 | }, 75 | { 76 | "id": "0004", 77 | "nome": "Tablet", 78 | "marca": "Samsung", 79 | "qtd": "12" 80 | }, 81 | { 82 | "id": "0005", 83 | "nome": "Notebook", 84 | "marca": "Genérico", 85 | "qtd": "10" 86 | }, 87 | { 88 | "id": "0006", 89 | "nome": "Impressora", 90 | "marca": "Genérico", 91 | "qtd": "78" 92 | }, 93 | { 94 | "id": "0007", 95 | "nome": "Roteador", 96 | "marca": "Genérico", 97 | "qtd": "23" 98 | }, 99 | { 100 | "id": "0008", 101 | "nome": "Pen Drive", 102 | "marca": "Genérico", 103 | "qtd": "12" 104 | }, 105 | { 106 | "id": "0009", 107 | "nome": "Caixas de som", 108 | "marca": "Genérico", 109 | "qtd": "203" 110 | }, 111 | 112 | { 113 | "id": "0010", 114 | "nome": "Smartphone", 115 | "marca": "Apple", 116 | "qtd": "100" 117 | }, 118 | { 119 | "id": "0011", 120 | "nome": "HD Externo", 121 | "marca": "Western Digital", 122 | "qtd": "87" 123 | }, 124 | { 125 | "id": "0012", 126 | "nome": "Câmera Fotográfica", 127 | "marca": "Canon", 128 | "qtd": "4" 129 | }, 130 | { 131 | "id": "0013", 132 | "nome": "Gabinete para PC", 133 | "marca": "Corsair", 134 | "qtd": "20" 135 | }, 136 | { 137 | "id": "0014", 138 | "nome": "Fone de Ouvido", 139 | "marca": "Sony", 140 | "qtd": "208" 141 | }, 142 | { 143 | "id": "0015", 144 | "nome": "Smart TV", 145 | "marca": "Samsung", 146 | "qtd": "10" 147 | }, 148 | { 149 | "id": "0016", 150 | "nome": "Máquina de Lavar", 151 | "marca": "Electrolux", 152 | "qtd": "3" 153 | }, 154 | { 155 | "id": "0017", 156 | "nome": "Batedeira", 157 | "marca": "KitchenAid", 158 | "qtd": "16" 159 | }, 160 | { 161 | "id": "0018", 162 | "nome": "Aspirador de Pó", 163 | "marca": "Philips", 164 | "qtd": "21" 165 | }, 166 | { 167 | "id": "0019", 168 | "nome": "Cadeira Gamer", 169 | "marca": "ThunderX3", 170 | "qtd": "7" 171 | }, 172 | { 173 | "id": "0020", 174 | "nome": "Headset Gamer", 175 | "marca": "HyperX", 176 | "qtd": "19" 177 | }, 178 | { 179 | "id": "0021", 180 | "nome": "Mochila para Notebook", 181 | "marca": "Targus", 182 | "qtd": "23" 183 | }, 184 | { 185 | "id": "0022", 186 | "nome": "Teclado Bluetooth", 187 | "marca": "Logitech", 188 | "qtd": "56" 189 | }, 190 | { 191 | "id": "0023", 192 | "nome": "Smartwatch", 193 | "marca": "Samsung", 194 | "qtd": "98" 195 | }, 196 | { 197 | "id": "0024", 198 | "nome": "HD SSD", 199 | "marca": "Kingston", 200 | "qtd": "150" 201 | }, 202 | { 203 | "id": "0025", 204 | "nome": "Projetor", 205 | "marca": "Epson", 206 | "qtd": "2" 207 | }, 208 | { 209 | "id": "0026", 210 | "nome": "Câmera de Segurança", 211 | "marca": "Intelbras", 212 | "qtd": "30" 213 | }, 214 | { 215 | "id": "0027", 216 | "nome": "Forno Elétrico", 217 | "marca": "Brastemp", 218 | "qtd": "7" 219 | }, 220 | { 221 | "id": "0028", 222 | "nome": "Máquina de Café", 223 | "marca": "Nespresso", 224 | "qtd": "10" 225 | }, 226 | { 227 | "id": "0029", 228 | "nome": "Cadeira de Escritório", 229 | "marca": "Ergométrica", 230 | "qtd": "12" 231 | },{ 232 | "id": "0030", 233 | "nome": "Caixa térmica", 234 | "marca": "Igloo", 235 | "qtd": "29" 236 | }, 237 | { 238 | "id": "0031", 239 | "nome": "Fone de Ouvido Bluetooth", 240 | "marca": "Sony", 241 | "qtd": "145" 242 | }, 243 | { 244 | "id": "0032", 245 | "nome": "Smart TV", 246 | "marca": "Samsung", 247 | "qtd": "10" 248 | }, 249 | { 250 | "id": "0033", 251 | "nome": "Ventilador de teto", 252 | "marca": "Arno", 253 | "qtd": "10" 254 | }, 255 | { 256 | "id": "0034", 257 | "nome": "Smartphone", 258 | "marca": "Apple", 259 | "qtd": "8" 260 | }, 261 | { 262 | "id": "0035", 263 | "nome": "Máquina de Lavar Roupas", 264 | "marca": "Electrolux", 265 | "qtd": "9" 266 | }, 267 | { 268 | "id": "0036", 269 | "nome": "Aspirador de Pó", 270 | "marca": "Philco", 271 | "qtd": "10" 272 | }, 273 | { 274 | "id": "0037", 275 | "nome": "Webcam", 276 | "marca": "Logitech", 277 | "qtd": "23" 278 | }, 279 | { 280 | "id": "0038", 281 | "nome": "Caixa de Ferramentas", 282 | "marca": "Stanley", 283 | "qtd": "10" 284 | }, 285 | { 286 | "id": "0039", 287 | "nome": "GPS Automotivo", 288 | "marca": "Garmin", 289 | "qtd": "8" 290 | }, 291 | { 292 | "id": "0040", 293 | "nome": "Tablet", 294 | "marca": "Multilaser", 295 | "qtd": "12" 296 | }, 297 | { 298 | "id": "0041", 299 | "nome": "Carregador Portátil", 300 | "marca": "Xiaomi", 301 | "qtd": "16" 302 | }, 303 | { 304 | "id": "0042", 305 | "nome": "Batedeira", 306 | "marca": "Oster", 307 | "qtd": "14" 308 | }, 309 | { 310 | "id": "0043", 311 | "nome": "Ferro de Passar Roupa", 312 | "marca": "Black+Decker", 313 | "qtd": "18" 314 | }, 315 | { 316 | "id": "0044", 317 | "nome": "Cortador de Cabelo", 318 | "marca": "Wahl", 319 | "qtd": "11" 320 | }, 321 | { 322 | "id": "0045", 323 | "nome": "Mochila Esportiva", 324 | "marca": "Nike", 325 | "qtd": "1" 326 | }, 327 | { 328 | "id": "0046", 329 | "nome": "Fone de Ouvido com Microfone", 330 | "marca": "JBL", 331 | "qtd": "20" 332 | } 333 | 334 | ] 335 | 336 | -------------------------------------------------------------------------------- /Postman Collection/API-Estoque.postman_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "8d6ea8e1-1805-4e12-a0bc-713fd3b44df4", 4 | "name": "API-Estoque", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", 6 | "_exporter_id": "25628747" 7 | }, 8 | "item": [ 9 | { 10 | "name": "Rotas de Produtos", 11 | "item": [ 12 | { 13 | "name": "Get_All_Itens", 14 | "request": { 15 | "method": "GET", 16 | "header": [], 17 | "url": { 18 | "raw": "http://localhost:3000/cadastroProdutos/", 19 | "protocol": "http", 20 | "host": [ 21 | "localhost" 22 | ], 23 | "port": "3000", 24 | "path": [ 25 | "cadastroProdutos", 26 | "" 27 | ] 28 | } 29 | }, 30 | "response": [] 31 | }, 32 | { 33 | "name": "Get Itens Page", 34 | "protocolProfileBehavior": { 35 | "disableBodyPruning": true 36 | }, 37 | "request": { 38 | "method": "GET", 39 | "header": [], 40 | "body": { 41 | "mode": "raw", 42 | "raw": "{\r\n \"nome\": \"Produto c\",\r\n \"marca\": \"Marca c\", \r\n \"qtd\": \"200\"\r\n}", 43 | "options": { 44 | "raw": { 45 | "language": "json" 46 | } 47 | } 48 | }, 49 | "url": { 50 | "raw": "http://localhost:3000/cadastroProdutos/pagina?itensPorPagina=10&pagina=1", 51 | "protocol": "http", 52 | "host": [ 53 | "localhost" 54 | ], 55 | "port": "3000", 56 | "path": [ 57 | "cadastroProdutos", 58 | "pagina" 59 | ], 60 | "query": [ 61 | { 62 | "key": "itensPorPagina", 63 | "value": "10" 64 | }, 65 | { 66 | "key": "pagina", 67 | "value": "1" 68 | } 69 | ] 70 | } 71 | }, 72 | "response": [] 73 | }, 74 | { 75 | "name": "Busca", 76 | "protocolProfileBehavior": { 77 | "disableBodyPruning": true 78 | }, 79 | "request": { 80 | "method": "GET", 81 | "header": [], 82 | "body": { 83 | "mode": "raw", 84 | "raw": "{\r\n \"nome\": \"Produto c\",\r\n \"marca\": \"Marca c\", \r\n \"qtd\": \"200\"\r\n}", 85 | "options": { 86 | "raw": { 87 | "language": "json" 88 | } 89 | } 90 | }, 91 | "url": { 92 | "raw": "http://localhost:3000/cadastroProdutos/busca?nome=Teclado Gamer&marca=Genérico", 93 | "protocol": "http", 94 | "host": [ 95 | "localhost" 96 | ], 97 | "port": "3000", 98 | "path": [ 99 | "cadastroProdutos", 100 | "busca" 101 | ], 102 | "query": [ 103 | { 104 | "key": "nome", 105 | "value": "Teclado Gamer" 106 | }, 107 | { 108 | "key": "marca", 109 | "value": "Genérico" 110 | } 111 | ] 112 | } 113 | }, 114 | "response": [] 115 | }, 116 | { 117 | "name": "Update", 118 | "request": { 119 | "method": "PUT", 120 | "header": [], 121 | "body": { 122 | "mode": "raw", 123 | "raw": "{\r\n \"nome\": \"Mouse Ergonômico\",\r\n \"marca\": \"Mouse Tech\", \r\n \"qtd\": \"30\"\r\n}", 124 | "options": { 125 | "raw": { 126 | "language": "json" 127 | } 128 | } 129 | }, 130 | "url": { 131 | "raw": "http://localhost:3000/cadastroProdutos/1", 132 | "protocol": "http", 133 | "host": [ 134 | "localhost" 135 | ], 136 | "port": "3000", 137 | "path": [ 138 | "cadastroProdutos", 139 | "1" 140 | ] 141 | } 142 | }, 143 | "response": [] 144 | }, 145 | { 146 | "name": "Criar Item", 147 | "request": { 148 | "method": "POST", 149 | "header": [], 150 | "body": { 151 | "mode": "raw", 152 | "raw": "{\r\n \"nome\": \"Mouse Duplo\",\r\n \"marca\": \"Double Damage\", \r\n \"qtd\": \"31\"\r\n}", 153 | "options": { 154 | "raw": { 155 | "language": "json" 156 | } 157 | } 158 | }, 159 | "url": { 160 | "raw": "http://localhost:3000/cadastroProdutos/", 161 | "protocol": "http", 162 | "host": [ 163 | "localhost" 164 | ], 165 | "port": "3000", 166 | "path": [ 167 | "cadastroProdutos", 168 | "" 169 | ] 170 | } 171 | }, 172 | "response": [] 173 | }, 174 | { 175 | "name": "Delete Item", 176 | "request": { 177 | "method": "DELETE", 178 | "header": [], 179 | "url": { 180 | "raw": "http://localhost:3000/cadastroProdutos/47", 181 | "protocol": "http", 182 | "host": [ 183 | "localhost" 184 | ], 185 | "port": "3000", 186 | "path": [ 187 | "cadastroProdutos", 188 | "47" 189 | ] 190 | } 191 | }, 192 | "response": [] 193 | }, 194 | { 195 | "name": "Quantidade Mínima", 196 | "request": { 197 | "method": "GET", 198 | "header": [], 199 | "url": { 200 | "raw": "http://localhost:3000/produtosQuantidadeMinima/", 201 | "protocol": "http", 202 | "host": [ 203 | "localhost" 204 | ], 205 | "port": "3000", 206 | "path": [ 207 | "produtosQuantidadeMinima", 208 | "" 209 | ] 210 | } 211 | }, 212 | "response": [] 213 | }, 214 | { 215 | "name": "Get por ID", 216 | "request": { 217 | "method": "GET", 218 | "header": [], 219 | "url": { 220 | "raw": "http://localhost:3000/cadastroProdutos/23", 221 | "protocol": "http", 222 | "host": [ 223 | "localhost" 224 | ], 225 | "port": "3000", 226 | "path": [ 227 | "cadastroProdutos", 228 | "23" 229 | ] 230 | } 231 | }, 232 | "response": [] 233 | } 234 | ] 235 | }, 236 | { 237 | "name": "Rotas de Registro", 238 | "item": [ 239 | { 240 | "name": "Entrada", 241 | "request": { 242 | "method": "POST", 243 | "header": [], 244 | "body": { 245 | "mode": "raw", 246 | "raw": "{ \r\n \"qtd_in\" : 100\r\n}", 247 | "options": { 248 | "raw": { 249 | "language": "json" 250 | } 251 | } 252 | }, 253 | "url": { 254 | "raw": "http://localhost:3000/fluxo/entrada/1", 255 | "protocol": "http", 256 | "host": [ 257 | "localhost" 258 | ], 259 | "port": "3000", 260 | "path": [ 261 | "fluxo", 262 | "entrada", 263 | "1" 264 | ] 265 | } 266 | }, 267 | "response": [] 268 | }, 269 | { 270 | "name": "Saida", 271 | "request": { 272 | "method": "POST", 273 | "header": [], 274 | "body": { 275 | "mode": "raw", 276 | "raw": "{ \r\n \"qtd_out\" : 45\r\n}", 277 | "options": { 278 | "raw": { 279 | "language": "json" 280 | } 281 | } 282 | }, 283 | "url": { 284 | "raw": "http://localhost:3000/fluxo/saida/1", 285 | "protocol": "http", 286 | "host": [ 287 | "localhost" 288 | ], 289 | "port": "3000", 290 | "path": [ 291 | "fluxo", 292 | "saida", 293 | "1" 294 | ] 295 | } 296 | }, 297 | "response": [] 298 | }, 299 | { 300 | "name": "Get registro", 301 | "protocolProfileBehavior": { 302 | "disableBodyPruning": true 303 | }, 304 | "request": { 305 | "method": "GET", 306 | "header": [], 307 | "body": { 308 | "mode": "raw", 309 | "raw": "{ \r\n \"qtd_in\" : 200 \r\n}", 310 | "options": { 311 | "raw": { 312 | "language": "json" 313 | } 314 | } 315 | }, 316 | "url": { 317 | "raw": "http://localhost:3000/fluxo/", 318 | "protocol": "http", 319 | "host": [ 320 | "localhost" 321 | ], 322 | "port": "3000", 323 | "path": [ 324 | "fluxo", 325 | "" 326 | ] 327 | } 328 | }, 329 | "response": [] 330 | }, 331 | { 332 | "name": "Get registro ID", 333 | "protocolProfileBehavior": { 334 | "disableBodyPruning": true 335 | }, 336 | "request": { 337 | "method": "GET", 338 | "header": [], 339 | "body": { 340 | "mode": "raw", 341 | "raw": "{ \r\n \"qtd_in\" : 200 \r\n}", 342 | "options": { 343 | "raw": { 344 | "language": "json" 345 | } 346 | } 347 | }, 348 | "url": { 349 | "raw": "http://localhost:3000/fluxo/", 350 | "protocol": "http", 351 | "host": [ 352 | "localhost" 353 | ], 354 | "port": "3000", 355 | "path": [ 356 | "fluxo", 357 | "" 358 | ] 359 | } 360 | }, 361 | "response": [] 362 | } 363 | ] 364 | } 365 | ] 366 | } --------------------------------------------------------------------------------
Método HTTPRota da APIDescrição
POST/cadastroProdutosCria um novo produto.
GET/cadastroProdutosRetorna todos os produtos.
GET/cadastroProdutos/pagina?itensPorPagina=10&pagina=1Retorna uma lista de produtos paginados. É possível passar os parâmetros itensPorPagina e 128 | pagina 129 | para definir a quantidade de itens por página e a página desejada. 130 |
GET/cadastroProdutos/busca?nome=&marca=Retorna uma lista de produtos filtrados por nome , marca . Passando os 136 | parâmetros nome , 137 | marca para realizar a busca. 138 |
GET/cadastroProdutos/:idRetorna um produto específico pelo seu ID .
PUT/cadastroProdutos/:idAtualiza um produto existente. Quantidade não pode ser atualizada por esta rota.
DELETE/cadastroProdutos/:idDeleta um produto existente. Usando esse método automaticamente registra toda a quantidade desse item 154 | como saída.
GET/produtosQuantidadeMinimaRetorna uma lista de produtos com quantidade abaixo do limite mínimo definido no cadastro do produto. 160 |
POST/fluxo/entrada/:idRegistra a entrada de um determinado produto no estoque. O parâmetro :id corresponde ao ID 166 | do produto que será atualizado. O corpo da requisição deve conter um objeto JSON com as chaves 167 | qtd_in (quantidade que será adicionada ao estoque) e data (data e hora da 168 | entrada no formato "YYYY-MM-DD HH:mm:ss"). 169 |
POST/fluxo/saida/:idRegistra a saída de um determinado produto no estoque. O parâmetro :id corresponde ao ID 175 | do produto 176 | que será atualizado. O corpo da requisição deve conter um objeto JSON com as chaves qtd_out 177 | 178 | (quantidade que será retirada do estoque) e data (data e hora da saída no formato 179 | "YYYY-MM-DD 180 | HH:mm:ss").
GET/fluxoRetorna uma lista de todas as movimentações registradas.
GET/fluxo/:idRetorna uma lista de movimentações de um produto específico com base no seu id.