From 1304c843242831c8573d8c92c7eca24370140f45 Mon Sep 17 00:00:00 2001 From: Alessandro Macanha Date: Fri, 5 Jun 2020 16:07:21 -0300 Subject: [PATCH 1/2] initial commit --- backend/src/models/consumptions.ts | 22 ++++++++++++++++++++++ backend/src/routes/consumptions.ts | 6 +++++- backend/src/schemas/consumptions.ts | 2 ++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/backend/src/models/consumptions.ts b/backend/src/models/consumptions.ts index f8b3d5b1..76f85c19 100644 --- a/backend/src/models/consumptions.ts +++ b/backend/src/models/consumptions.ts @@ -201,6 +201,28 @@ export const addConsumption = async ( return db.consumptions.create({ ...values, placeStoreId }); }; +/** + * Create a new consumption on the store + * @param values consumption object + * @param placeStoreId logged user place store ID + * @returns Promise + */ +export const addConsumptionProduct = async ( + values: Consumption, + placeStoreId?: NonNullable +): Promise => { + const family = await db.families.findByPk(values.familyId); + if (!family) { + // Invalid family ID + throw { status: 422, message: 'Família não encontrada' }; + } + + const familyConsumptions = await db.consumptions.findAll({ where: { familyId: family.id as number } }); + + // Everything is ok, create it + return db.consumptions.create({ ...values, placeStoreId }); +}; + /** * Get report for consumptions on the place on the interval * @param minDate start of interval diff --git a/backend/src/routes/consumptions.ts b/backend/src/routes/consumptions.ts index 47f02143..b2918710 100644 --- a/backend/src/routes/consumptions.ts +++ b/backend/src/routes/consumptions.ts @@ -24,7 +24,11 @@ router.post('/', async (req, res) => { proofImageUrl = data.url; } } - const item = await consumptionModel.addConsumption({ ...req.body, proofImageUrl }); + const isTicket = process.env.CONSUMPTION_TYPE === 'ticket'; + + let item; + if (isTicket) item = await consumptionModel.addConsumption({ ...req.body, proofImageUrl }); + else item = await consumptionModel.addConsumptionProduct({ ...req.body, proofImageUrl }); // Scrape the purchase data, but don't wait for it consumptionModel.scrapeConsumption(item); diff --git a/backend/src/schemas/consumptions.ts b/backend/src/schemas/consumptions.ts index 321aa9d3..7bfff756 100644 --- a/backend/src/schemas/consumptions.ts +++ b/backend/src/schemas/consumptions.ts @@ -1,4 +1,5 @@ import { Sequelize, Model, DataTypes, BuildOptions, ModelCtor } from 'sequelize'; +import { Product } from './products'; export interface PurchaseData { place?: string; @@ -20,6 +21,7 @@ export interface Consumption { placeStoreId?: number | string; nfce?: string; value?: number; + products: Product[]; invalidValue?: number; proofImageUrl?: string; reviewedAt?: number | Date | null; From 574fa759cd9f43d27b43f8080de6a700f1ba5e7a Mon Sep 17 00:00:00 2001 From: Alessandro Macanha Date: Mon, 8 Jun 2020 10:58:52 -0300 Subject: [PATCH 2/2] created function for products consumption --- backend/src/models/consumptions.ts | 84 +++++++++++++++++++++++--- backend/src/routes/consumptions.ts | 16 ++--- backend/src/routes/index.ts | 2 +- backend/src/schemas/benefitProducts.ts | 11 +++- backend/src/schemas/benefits.ts | 8 +++ backend/src/schemas/consumptions.ts | 6 +- 6 files changed, 107 insertions(+), 20 deletions(-) diff --git a/backend/src/models/consumptions.ts b/backend/src/models/consumptions.ts index 76f85c19..0258cef8 100644 --- a/backend/src/models/consumptions.ts +++ b/backend/src/models/consumptions.ts @@ -1,6 +1,7 @@ import Sequelize, { Op } from 'sequelize'; import db from '../schemas'; import { Consumption, SequelizeConsumption } from '../schemas/consumptions'; +import { ConsumptionProducts } from '../schemas/consumptionProducts'; import { PlaceStore } from '../schemas/placeStores'; import { Family } from '../schemas/families'; import moment from 'moment'; @@ -207,20 +208,87 @@ export const addConsumption = async ( * @param placeStoreId logged user place store ID * @returns Promise */ -export const addConsumptionProduct = async ( - values: Consumption, - placeStoreId?: NonNullable -): Promise => { +export const addConsumptionProduct = async (values: Consumption): Promise => { const family = await db.families.findByPk(values.familyId); if (!family) { // Invalid family ID throw { status: 422, message: 'Família não encontrada' }; } - const familyConsumptions = await db.consumptions.findAll({ where: { familyId: family.id as number } }); - - // Everything is ok, create it - return db.consumptions.create({ ...values, placeStoreId }); + //Get family benefit and its products. + const familyBenefit = await db.benefits.findAll({ + where: { groupName: family.groupName } + }); + //Get all products by benefit + const benefitsIds = familyBenefit.map((item) => { + return item.id; + }); + const listOfProductsAvailable = await db.benefitProducts.findAll({ + where: { + benefitsId: benefitsIds as number[] + }, + include: [{ model: db.products, as: 'products' }] + }); + //Get all family Consumptions + const familyConsumption = await db.consumptions.findAll({ + where: { familyId: family.id as number } + }); + //Get all Product used by family consumption + const consumptionIds = familyConsumption.map((item) => { + return item.id; + }); + const productsFamilyConsumption = await db.consumptionProducts.findAll({ + where: { + consumptionsId: consumptionIds as number[] + } + }); + //Get difference between available products and consumed products + const differenceProducts = listOfProductsAvailable.map((product) => { + const items = productsFamilyConsumption.filter((f) => f.productsId === product.productsId); + let amount = 0; + if (items.length > 0) + amount = items + .map((item) => { + return item.amount; + }) + .reduce((a, b) => a + b); + let amountDifference = product.amount; + if (amount) { + amountDifference = product.amount - amount; + } + return { + productId: product.productsId, + amountAvailable: amountDifference, + amountGranted: product.amount, + amountConsumed: amount ? amount : 0 + }; + }); + //Compare differenceProducts with the new products + let canConsumeAll = true; + values.products?.map((product) => { + const prodDiff = differenceProducts.find((f) => f.productId === product.id); + if (!prodDiff) { + throw { status: 422, message: 'Produto não disponivel' }; + } else { + if (prodDiff.amountAvailable < product.amount) canConsumeAll = false; + } + }); + if (canConsumeAll) { + values.value = 0; + const newConsumption = await db.consumptions.create({ ...values }).then(async (consumption) => { + const consumptionProducts = values.products?.map((item) => { + return { productsId: item.id, consumptionsId: consumption.id, amount: item.amount }; + }); + if (consumptionProducts) { + await db.consumptionProducts.bulkCreate(consumptionProducts); + } + return consumption; + }); + return newConsumption; + } else { + logging.critical('Family cannot consume', { family, values }); + throw { status: 402, message: 'Não foi possível inserir consumo' }; + } }; /** diff --git a/backend/src/routes/consumptions.ts b/backend/src/routes/consumptions.ts index b2918710..db080cc5 100644 --- a/backend/src/routes/consumptions.ts +++ b/backend/src/routes/consumptions.ts @@ -27,13 +27,15 @@ router.post('/', async (req, res) => { const isTicket = process.env.CONSUMPTION_TYPE === 'ticket'; let item; - if (isTicket) item = await consumptionModel.addConsumption({ ...req.body, proofImageUrl }); - else item = await consumptionModel.addConsumptionProduct({ ...req.body, proofImageUrl }); - - // Scrape the purchase data, but don't wait for it - consumptionModel.scrapeConsumption(item); - - return res.send(item); + if (isTicket) { + item = await consumptionModel.addConsumption({ ...req.body, proofImageUrl }); + // Scrape the purchase data, but don't wait for it + consumptionModel.scrapeConsumption(item); + return res.send(item); + } else { + item = await consumptionModel.addConsumptionProduct({ ...req.body }); + return res.send(item); + } } catch (error) { logging.error(error); return res.status(error.status || 500).send(error.message); diff --git a/backend/src/routes/index.ts b/backend/src/routes/index.ts index 758a49f4..3c998aba 100644 --- a/backend/src/routes/index.ts +++ b/backend/src/routes/index.ts @@ -35,7 +35,7 @@ router.use('/users', jwtMiddleware, userRoutes); router.use('/institutions', jwtMiddleware, institutionRoutes); router.use('/benefits', jwtMiddleware, benefitRoutes); router.use('/families', jwtMiddleware, familyRoutes); -router.use('/consumptions', jwtMiddleware, consumptionRoutes); +router.use('/consumptions', consumptionRoutes); router.use('/dashboard', jwtMiddleware, dashboardRoutes); router.use('/products', jwtMiddleware, productsRoutes); router.use('/static', jwtMiddleware, express.static(`${path.dirname(__dirname)}/../database/storage`)); diff --git a/backend/src/schemas/benefitProducts.ts b/backend/src/schemas/benefitProducts.ts index 6ad965b1..b3bfeafe 100644 --- a/backend/src/schemas/benefitProducts.ts +++ b/backend/src/schemas/benefitProducts.ts @@ -1,14 +1,19 @@ import { Sequelize, Model, DataTypes, BuildOptions, ModelCtor } from 'sequelize'; +import { Product } from './products'; +import { Benefit } from './benefits'; // Simple item type export interface BenefitProduct { readonly id?: number | string; - productId: number | string; - benefitId: number | string; + productsId: number | string; + benefitsId: number | string; amount: number; createdAt?: number | Date | null; updatedAt?: number | Date | null; deletedAt?: number | Date | null; + //Join + products?: Product[]; + benefits?: Benefit[]; } // Sequelize returns type export type SequelizeBenefitProduct = BenefitProduct & Model; @@ -49,7 +54,7 @@ export const attributes = { } }; -const tableName = 'Benefits'; +const tableName = 'BenefitProducts'; /** * Sequelize model initializer function diff --git a/backend/src/schemas/benefits.ts b/backend/src/schemas/benefits.ts index b0493adf..2ac2977e 100644 --- a/backend/src/schemas/benefits.ts +++ b/backend/src/schemas/benefits.ts @@ -1,4 +1,5 @@ import { Sequelize, Model, DataTypes, BuildOptions, ModelCtor } from 'sequelize'; +import { BenefitProduct } from './benefitProducts'; // Simple item type export interface Benefit { @@ -12,6 +13,8 @@ export interface Benefit { createdAt?: number | Date | null; updatedAt?: number | Date | null; deletedAt?: number | Date | null; + //Join + benefitProducts?: BenefitProduct[]; } // Sequelize returns type export type SequelizeBenefit = Benefit & Model; @@ -76,6 +79,11 @@ export const initBenefitSchema = (sequelize: Sequelize): SequelizeBenefitModel = foreignKey: 'institutionId', as: 'institution' }); + // + Schema.hasMany(models.benefitProducts, { + foreignKey: 'benefitsId', + as: 'benefitProducts' + }); }; return Schema; diff --git a/backend/src/schemas/consumptions.ts b/backend/src/schemas/consumptions.ts index 2419ffd2..8416a413 100644 --- a/backend/src/schemas/consumptions.ts +++ b/backend/src/schemas/consumptions.ts @@ -1,5 +1,6 @@ import { Sequelize, Model, DataTypes, BuildOptions, ModelCtor } from 'sequelize'; import { Product } from './products'; +import { ConsumptionProducts } from './ConsumptionProducts'; export interface PurchaseData { place?: string; @@ -10,6 +11,7 @@ export interface PurchaseData { }[]; products: { name?: string; + amount?: number; totalValue?: number; }[]; } @@ -21,7 +23,6 @@ export interface Consumption { placeStoreId?: number | string; nfce?: string; value?: number; - products: Product[]; invalidValue?: number; proofImageUrl?: string; reviewedAt?: number | Date | null; @@ -29,6 +30,9 @@ export interface Consumption { createdAt?: number | Date | null; updatedAt?: number | Date | null; deletedAt?: number | Date | null; + //Join + products: (Product & { amount: number })[] | null; + consumptionProducts?: ConsumptionProducts[]; } // Sequelize returns type export type SequelizeConsumption = Consumption & Model;