5 Commits

Author SHA1 Message Date
Gitea Action
834c2cb01a chore: update image tag to a63ca5e [skip ci] 2026-05-18 00:04:21 +00:00
Luis Eduardo Estevao
a63ca5e598 feat: implement sales order services, redis-based concurrency control, and build-time path alias resolution for production.
All checks were successful
Build (develop) / Promote (main) / build-and-push-deploy (push) Successful in 3m33s
2026-05-17 21:00:31 -03:00
Gitea Action
ce064fc160 chore: update image tag to 141e6fd [skip ci] 2026-05-14 15:35:41 +00:00
Luis Eduardo Estevao
141e6fd800 Merge branch 'main' of http://172.35.0.216:3000/simplifique/Vendaweb-api
All checks were successful
Build (develop) / Promote (main) / build-and-push-deploy (push) Successful in 1m43s
2026-05-14 12:33:55 -03:00
Luis Eduardo Estevao
936d9d2673 feat: implement optimized product retrieval with Redis caching and locking in SalesService 2026-05-14 12:33:51 -03:00
8 changed files with 308 additions and 153 deletions

View File

@@ -7,4 +7,4 @@ spec:
spec: spec:
containers: containers:
- name: api - name: api
image: 172.35.0.216:3000/simplifique/vendaweb-api:5279ae9 image: 172.35.0.216:3000/simplifique/vendaweb-api:a63ca5e

View File

@@ -7,7 +7,7 @@
"license": "JURUNENSE HOME CENTER", "license": "JURUNENSE HOME CENTER",
"scripts": { "scripts": {
"prebuild": "rimraf dist", "prebuild": "rimraf dist",
"build": "nest build", "build": "nest build && node scripts/rewrite-dist-src-aliases.js",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start", "start": "nest start",
"start:dev": "nest start --watch", "start:dev": "nest start --watch",

View File

@@ -0,0 +1,40 @@
const fs = require('fs');
const path = require('path');
const distDir = path.resolve(__dirname, '..', 'dist');
function walk(dir) {
if (!fs.existsSync(dir)) {
return [];
}
return fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
return walk(fullPath);
}
return fullPath.endsWith('.js') ? [fullPath] : [];
});
}
function toRequirePath(fromFile, srcImport) {
const target = path.join(distDir, srcImport.replace(/^src[\\/]/, ''));
let relative = path.relative(path.dirname(fromFile), target).replace(/\\/g, '/');
if (!relative.startsWith('.')) {
relative = `./${relative}`;
}
return relative;
}
for (const file of walk(distDir)) {
const source = fs.readFileSync(file, 'utf8');
const rewritten = source.replace(/require\((['"])src\/([^'"]+)\1\)/g, (_match, quote, importPath) => {
return `require(${quote}${toRequirePath(file, `src/${importPath}`)}${quote})`;
});
if (rewritten !== source) {
fs.writeFileSync(file, rewritten);
}
}

View File

@@ -1,3 +1,4 @@
import './polyfills/node-util';
import { NestFactory } from '@nestjs/core'; import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
const compression = require('compression'); const compression = require('compression');

View File

@@ -0,0 +1,9 @@
const util = require('util');
if (typeof util.isNullOrUndefined !== 'function') {
util.isNullOrUndefined = (value: any) => value === null || value === undefined;
}
if (typeof util.isObject !== 'function') {
util.isObject = (value: any) => value !== null && typeof value === 'object';
}

View File

@@ -28,11 +28,38 @@ export class OrderService {
) { } ) { }
async create(cart: Cart) { async create(cart: Cart) {
const shopping = await this.findShopping(cart.id); const lockKey = `lock:order:create:${cart.id}`;
if (shopping == null) const lockValue = `${Date.now()}:${Math.random()}`;
throw new HttpException("Carrinho de compras não localizado.", HttpStatus.NOT_FOUND); const lockTimeout = 120;
const order = await this.createOrder(cart); let acquiredLock: string | null = null;
return order;
try {
acquiredLock = await this.redisClient.set(lockKey, lockValue, 'NX', 'EX', lockTimeout);
} catch (err) {
console.error('Erro ao adquirir lock no Redis (createOrder):', err?.message || err);
acquiredLock = 'OK';
}
if (acquiredLock !== 'OK') {
throw new HttpException('Venda em processamento para este carrinho. Aguarde a finalizacao e tente novamente.', HttpStatus.CONFLICT);
}
try {
const shopping = await this.findShopping(cart.id);
if (shopping == null)
throw new HttpException("Carrinho de compras não localizado.", HttpStatus.NOT_FOUND);
const order = await this.createOrder(cart);
return order;
} finally {
try {
const currentLockValue = await this.redisClient.get(lockKey);
if (currentLockValue === lockValue) {
await this.redisClient.del(lockKey);
}
} catch (lockErr) {
console.error('Erro ao liberar lock no Redis (createOrder):', lockErr?.message || lockErr);
}
}
} }
async deleteOrdersDelivery(orderId: number, userId: number) { async deleteOrdersDelivery(orderId: number, userId: number) {
@@ -57,7 +84,7 @@ export class OrderService {
} }
} catch (erro) { } catch (erro) {
if (queryRunner.isTransactionActive) { if (queryRunner.isTransactionActive) {
await queryRunner.commitTransaction(); await queryRunner.rollbackTransaction();
} }
throw erro; throw erro;
} finally { } finally {
@@ -125,7 +152,7 @@ export class OrderService {
} }
if (queryRunner.isTransactionActive) { if (queryRunner.isTransactionActive) {
queryRunner.commitTransaction(); await queryRunner.commitTransaction();
} }
await queryRunner.startTransaction(); await queryRunner.startTransaction();
@@ -320,7 +347,7 @@ export class OrderService {
}; };
} catch (err) { } catch (err) {
console.log(err); console.log(err);
this.deletePreOrder(idPreOrder); await this.deletePreOrder(idPreOrder);
throw err; throw err;
// since we have errors let's rollback changes we made // since we have errors let's rollback changes we made
} finally { } finally {
@@ -743,16 +770,15 @@ export class OrderService {
const queryRunner = connection.createQueryRunner(); const queryRunner = connection.createQueryRunner();
await queryRunner.connect(); await queryRunner.connect();
console.log('Consultando usuario ' + idSeller); console.log('Consultando usuario ' + idSeller);
let sql = 'SELECT NVL(PROXNUMPEDWEB,0) as "proxnumpedweb" FROM PCUSUARI ' +
' WHERE PCUSUARI.CODUSUR = :1 FOR UPDATE';
const seller = await queryRunner.query(sql, [idSeller]);
const idOrder = seller[0].proxnumpedweb;
console.log(idOrder);
await queryRunner.startTransaction(); await queryRunner.startTransaction();
try { try {
let sql = 'SELECT NVL(PROXNUMPEDWEB,0) as "proxnumpedweb" FROM PCUSUARI ' +
' WHERE PCUSUARI.CODUSUR = :1 FOR UPDATE';
const seller = await queryRunner.query(sql, [idSeller]);
const idOrder = seller[0].proxnumpedweb;
console.log(idOrder);
sql = 'UPDATE PCUSUARI SET PROXNUMPEDWEB = NVL(PROXNUMPEDWEB,0) + 1 ' + sql = 'UPDATE PCUSUARI SET PROXNUMPEDWEB = NVL(PROXNUMPEDWEB,0) + 1 ' +
' WHERE PCUSUARI.CODUSUR = :1'; ' WHERE PCUSUARI.CODUSUR = :1';

View File

@@ -69,7 +69,7 @@ export class SalesService {
pageNumber = 1; pageNumber = 1;
const offSet = (pageNumber - 1) * pageSize; const offSet = (pageNumber - 1) * pageSize;
if (filter && filter.text.length > 0) { if (filter && filter.text && filter.text.length > 0) {
const description = filter.text.toUpperCase(); const description = filter.text.toUpperCase();
console.log('consultando por texto: ' + description); console.log('consultando por texto: ' + description);
@@ -116,7 +116,7 @@ export class SalesService {
.addSelect("\"esvlistaprodutos\".QUANTIDADE_ESTOQUE_GERAL", "full_stock") .addSelect("\"esvlistaprodutos\".QUANTIDADE_ESTOQUE_GERAL", "full_stock")
.addSelect("\"esvlistaprodutos\".TEM_PRODUTO_SIMILAR", "similar") .addSelect("\"esvlistaprodutos\".TEM_PRODUTO_SIMILAR", "similar")
.addSelect("\"esvlistaprodutos\".TIPO_CAMPANHA", "type_campaing") .addSelect("\"esvlistaprodutos\".TIPO_CAMPANHA", "type_campaing")
.where("(UPPER(\"esvlistaprodutos\".CODFAB) LIKE '%'||REPLACE(:description, '@', '%')||'%' OR UPPER(\"esvlistaprodutos\".descricao) LIKE '%'||REPLACE(:description, '@', '%')||'%')", { description }) .where("(\"esvlistaprodutos\".CODFAB LIKE REPLACE(:description, '@', '%')||'%' OR \"esvlistaprodutos\".descricao LIKE REPLACE(:description, '@', '%')||'%' OR \"esvlistaprodutos\".NOMEECOMMERCE LIKE REPLACE(:description, '@', '%')||'%')", { description })
.andWhere("(\"esvlistaprodutos\".codfilial = :codfilial OR :codfilial = '99')", { codfilial: store }) .andWhere("(\"esvlistaprodutos\".codfilial = :codfilial OR :codfilial = '99')", { codfilial: store })
.andWhere("(\"esvlistaprodutos\".produto_com_reducao_preco = :produtoComReducaoPreco OR :produtoComReducaoPreco = 'N')", .andWhere("(\"esvlistaprodutos\".produto_com_reducao_preco = :produtoComReducaoPreco OR :produtoComReducaoPreco = 'N')",
{ produtoComReducaoPreco: (filter.markdown) ? 'S' : 'N' }) { produtoComReducaoPreco: (filter.markdown) ? 'S' : 'N' })
@@ -450,8 +450,18 @@ export class SalesService {
} }
async searchProduct(store: string, search: string, pageSize: number, pageNumber: number) { async searchProduct(store: string, search: string, pageSize: number, pageNumber: number) {
const connectionDb = new Connection(connectionOptions); const cacheKey = `searchProduct:${store}:${pageSize}:${pageNumber}:${search}`;
await connectionDb.connect(); try {
const cachedProducts = await this.redisClient.get(cacheKey);
if (cachedProducts) {
console.log('searchProduct - Retornando produtos do cache');
return JSON.parse(cachedProducts);
}
} catch (err) {
console.error('Erro ao acessar o Redis no searchProduct:', err?.message || err);
}
const connectionDb = getConnection();
const queryRunner = connectionDb.createQueryRunner(); const queryRunner = connectionDb.createQueryRunner();
await queryRunner.connect(); await queryRunner.connect();
try { try {
@@ -509,7 +519,7 @@ export class SalesService {
.andWhere("\"esvlistaprodutos\".codfilial = :codfilial OR :codfilial = '99'", { codfilial: store }) .andWhere("\"esvlistaprodutos\".codfilial = :codfilial OR :codfilial = '99'", { codfilial: store })
.limit(pageSize) .limit(pageSize)
.offset(offSet) .offset(offSet)
.orderBy("REPLACE(\"esvlistaprodutos\".DESCRICAO,'#', '')", "ASC") .orderBy("\"esvlistaprodutos\".DESCRICAO", "ASC")
.getRawMany(); .getRawMany();
if (products.length === 0) { if (products.length === 0) {
@@ -558,12 +568,12 @@ export class SalesService {
.andWhere("(\"esvlistaprodutos\".codfilial = :codfilial OR :codfilial = '99')", { codfilial: store }) .andWhere("(\"esvlistaprodutos\".codfilial = :codfilial OR :codfilial = '99')", { codfilial: store })
.limit(pageSize) .limit(pageSize)
.offset(offSet) .offset(offSet)
.orderBy("REPLACE(\"esvlistaprodutos\".DESCRICAO,'#', '')", "ASC") .orderBy("\"esvlistaprodutos\".DESCRICAO", "ASC")
.getRawMany(); .getRawMany();
} }
} else { } else {
const description = '%' + search.toUpperCase() + '%'; const description = search.toUpperCase().replace('@', '%') + '%';
console.log('consultando por codigo de fabrica'); console.log('consultando por codigo de fabrica');
products = await queryRunner.manager products = await queryRunner.manager
.getRepository(SalesProduct) .getRepository(SalesProduct)
@@ -605,11 +615,11 @@ export class SalesService {
.addSelect("\"esvlistaprodutos\".QUANTIDADE_ESTOQUE_GERAL", "full_stock") .addSelect("\"esvlistaprodutos\".QUANTIDADE_ESTOQUE_GERAL", "full_stock")
.addSelect("\"esvlistaprodutos\".TEM_PRODUTO_SIMILAR", "similar") .addSelect("\"esvlistaprodutos\".TEM_PRODUTO_SIMILAR", "similar")
.addSelect("\"esvlistaprodutos\".TIPO_CAMPANHA", "type_campaing") .addSelect("\"esvlistaprodutos\".TIPO_CAMPANHA", "type_campaing")
.where("UPPER(esvlistaprodutos.CODFAB) like REPLACE(:description, '@', '%')", { description }) .where("esvlistaprodutos.CODFAB like :description", { description })
.andWhere("\"esvlistaprodutos\".codfilial = :codfilial OR :codfilial = '99'", { codfilial: store }) .andWhere("\"esvlistaprodutos\".codfilial = :codfilial OR :codfilial = '99'", { codfilial: store })
.limit(pageSize) .limit(pageSize)
.offset(offSet) .offset(offSet)
.orderBy("REPLACE(\"esvlistaprodutos\".DESCRICAO,'#', '')", "ASC") .orderBy("\"esvlistaprodutos\".DESCRICAO", "ASC")
.getRawMany(); .getRawMany();
if (products.length == 0) { if (products.length == 0) {
@@ -653,11 +663,11 @@ export class SalesService {
.addSelect("\"esvlistaprodutos\".QUANTIDADE_ESTOQUE_GERAL", "full_stock") .addSelect("\"esvlistaprodutos\".QUANTIDADE_ESTOQUE_GERAL", "full_stock")
.addSelect("\"esvlistaprodutos\".TEM_PRODUTO_SIMILAR", "similar") .addSelect("\"esvlistaprodutos\".TEM_PRODUTO_SIMILAR", "similar")
.addSelect("\"esvlistaprodutos\".TIPO_CAMPANHA", "type_campaing") .addSelect("\"esvlistaprodutos\".TIPO_CAMPANHA", "type_campaing")
.where("UPPER(esvlistaprodutos.DESCRICAO) like REPLACE(:description, '@', '%')", { description }) .where("(esvlistaprodutos.DESCRICAO like :description OR esvlistaprodutos.NOMEECOMMERCE like :description)", { description })
.andWhere("\"esvlistaprodutos\".codfilial = :codfilial OR :codfilial = '99'", { codfilial: store }) .andWhere("\"esvlistaprodutos\".codfilial = :codfilial OR :codfilial = '99'", { codfilial: store })
.limit(pageSize) .limit(pageSize)
.offset(offSet) .offset(offSet)
.orderBy("REPLACE(\"esvlistaprodutos\".DESCRICAO,'#', '')", "ASC") .orderBy("\"esvlistaprodutos\".DESCRICAO", "ASC")
.getRawMany(); .getRawMany();
} }
@@ -665,6 +675,7 @@ export class SalesService {
products = this.createListImages(products); products = this.createListImages(products);
console.log(products.length); console.log(products.length);
try { await this.redisClient.set(cacheKey, JSON.stringify(products), 'EX', 900); } catch (cacheErr) { console.error('Erro ao salvar cache searchProduct:', cacheErr?.message || cacheErr); }
return products; return products;
} catch (error) { } catch (error) {
@@ -672,7 +683,7 @@ export class SalesService {
throw error; throw error;
} finally { } finally {
await queryRunner.release(); await queryRunner.release();
await connectionDb.close(); // Nao fecha a conexao global; ela e reutilizada pelo pool do TypeORM.
} }
} }
@@ -1237,12 +1248,42 @@ export class SalesService {
async searchProduct2(store: string, pageSize: number, pageNumber: number, filter: FilterProduct) { async searchProduct2(store: string, pageSize: number, pageNumber: number, filter: FilterProduct) {
console.log('searchProduct2'); console.log('searchProduct2');
const cacheKey = `searchProduct2:${store}:${pageSize}:${pageNumber}:${JSON.stringify(filter)}`; filter = filter || new FilterProduct();
if (pageSize == 0)
pageSize = 500;
if (pageNumber == 0)
pageNumber = 1;
const cachedProducts = await this.redisClient.get(cacheKey); const toBoolean = (value: any) => value === true || value === 'true' || value === 'S';
if (cachedProducts) { const escapeSqlText = (value: string) => value.replace(/'/g, "''");
console.log('Retornando produtos do cache'); const orderByColumns = {
return JSON.parse(cachedProducts); DESCRICAO: 'DESCRICAO',
PRECOVENDA: 'PRECOVENDA',
PRECOPROMOCIONAL: 'PRECOPROMOCIONAL',
PERCENTUALDESCONTO: 'PERCENTUALDESCONTO',
NOMEMARCA: 'NOMEMARCA',
};
const normalizedFilter = {
...filter,
text: filter.text ? filter.text.trim() : null,
markdown: toBoolean(filter.markdown),
offers: toBoolean(filter.offers),
oportunity: toBoolean(filter.oportunity),
promotion: toBoolean(filter.promotion),
onlyWithStock: toBoolean(filter.onlyWithStock),
brands: filter.brands || [],
};
const cacheKey = `searchProduct2:${store}:${pageSize}:${pageNumber}:${JSON.stringify(normalizedFilter)}`;
try {
const cachedProducts = await this.redisClient.get(cacheKey);
if (cachedProducts) {
console.log('Retornando produtos do cache');
return JSON.parse(cachedProducts);
}
} catch (err) {
console.error('Erro ao acessar o Redis no searchProduct2:', err?.message || err);
} }
const sql = 'SELECT esvlistaprodutos.CODPROD "idProduct" ' + const sql = 'SELECT esvlistaprodutos.CODPROD "idProduct" ' +
@@ -1290,59 +1331,73 @@ export class SalesService {
' WHERE 1 = 1 '; ' WHERE 1 = 1 ';
let where = ""; let where = "";
if (filter.text != null) { if (normalizedFilter.text != null && normalizedFilter.text.length > 0) {
where += ` AND ( ESF_REMOVE_ACENTUACAO(UPPER(esvlistaprodutos.descricao)) LIKE ` + const textSearch = escapeSqlText(normalizedFilter.text.toUpperCase()).replace('@', '%');
` ESF_REMOVE_ACENTUACAO(REPLACE('${filter.text}', '@', '%'))||'%' ` + const numbers = normalizedFilter.text.replace(/[^0-9]/g, '');
` OR ESF_REMOVE_ACENTUACAO(UPPER(esvlistaprodutos.nomeecommerce)) LIKE ` + if (numbers.length > 0 && numbers === normalizedFilter.text) {
` ESF_REMOVE_ACENTUACAO(REPLACE('${filter.text}', '@', '%'))||'%' ` + where += ` AND ( esvlistaprodutos.codprod = ${numbers} ` +
` OR esvlistaprodutos.codprod = REGEXP_REPLACE('${filter.text}', '[^0-9]', '') ` + ` OR esvlistaprodutos.codauxiliar = '${numbers}' )`;
` OR esvlistaprodutos.codauxiliar = REGEXP_REPLACE('${filter.text}', '[^0-9]', '') ` + } else {
` OR esvlistaprodutos.nomemarca = REPLACE('${filter.text}', '@', '%') ` + where += ` AND ( esvlistaprodutos.descricao LIKE '${textSearch}%' ` +
` OR esvlistaprodutos.codfab LIKE '${filter.text}%' )`; ` OR esvlistaprodutos.nomeecommerce LIKE '${textSearch}%' ` +
` OR esvlistaprodutos.nomemarca = '${textSearch}' ` +
` OR esvlistaprodutos.codfab LIKE '${textSearch}%' `;
if (numbers.length > 0) {
where += ` OR esvlistaprodutos.codprod = ${numbers} ` +
` OR esvlistaprodutos.codauxiliar = '${numbers}' `;
}
where += ` )`;
}
} }
if (filter.urlCategory != null) { if (filter.urlCategory != null && filter.urlCategory.length > 0) {
where += ` AND esvlistaprodutos.urlcategoria like '${filter.urlCategory}%'`; where += ` AND esvlistaprodutos.urlcategoria like '${escapeSqlText(filter.urlCategory)}%'`;
}
if (store !== '99') {
where += ` AND esvlistaprodutos.codfilial = '${escapeSqlText(store)}' `;
}
if (normalizedFilter.markdown) {
where += ` AND esvlistaprodutos.produto_com_reducao_preco = 'S' `;
}
if (normalizedFilter.offers) {
where += ` AND esvlistaprodutos.produto_em_campanha = 'D' `;
}
if (normalizedFilter.oportunity) {
where += ` AND esvlistaprodutos.produto_oportunidade = 'O' `;
}
if (normalizedFilter.promotion) {
where += ` AND esvlistaprodutos.produto_em_promocao = 'S' `;
} }
where += ` AND (esvlistaprodutos.codfilial = '${store}' OR '${store}' = '99') `;
where += ` AND (esvlistaprodutos.produto_com_reducao_preco = '${(filter.markdown ? 'S' : 'N')}' OR '${(filter.markdown ? 'S' : 'N')}' = 'N') `;
where += ` AND (esvlistaprodutos.produto_em_campanha = '${(filter.offers ? 'D' : 'N')}' OR '${(filter.offers ? 'S' : 'N')}' = 'N') `;
where += ` AND (esvlistaprodutos.produto_em_campanha = '${(filter.oportunity ? 'O' : 'N')}' OR '${(filter.oportunity ? 'O' : 'N')}' = 'N') `;
where += ` AND (esvlistaprodutos.produto_em_promocao = '${(filter.promotion ? 'S' : 'N')}' OR '${(filter.promotion ? 'S' : 'N')}' = 'N') `;
if (filter.onlyWithStock) { if (normalizedFilter.onlyWithStock) {
if (filter.storeStock == '' || filter.storeStock == null) { if (filter.storeStock == '' || filter.storeStock == null) {
where += ` AND EXISTS( SELECT P.CODPROD FROM ESVLISTAPRODUTOS P ` + where += ` AND EXISTS( SELECT P.CODPROD FROM ESVLISTAPRODUTOS P ` +
` WHERE P.CODPROD = ESVLISTAPRODUTOS.CODPROD ` + ` WHERE P.CODPROD = ESVLISTAPRODUTOS.CODPROD ` +
` AND P.QTESTOQUE_DISPONIVEL > 0 ) `; ` AND P.QTESTOQUE_DISPONIVEL > 0 ) `;
} else { } else {
where += ` AND EXISTS( SELECT P.CODPROD FROM ESVLISTAPRODUTOS P ` + where += ` AND EXISTS( SELECT P.CODPROD FROM ESVLISTAPRODUTOS P ` +
` WHERE P.CODPROD = ESVLISTAPRODUTOS.CODPROD AND P.CODFILIAL = '${filter.storeStock}' ` + ` WHERE P.CODPROD = ESVLISTAPRODUTOS.CODPROD AND P.CODFILIAL = '${escapeSqlText(filter.storeStock)}' ` +
` AND P.ESTOQUE_DISP_LOJA > 0 ) `; ` AND P.ESTOQUE_DISP_LOJA > 0 ) `;
} }
} }
if (filter.percentOffMin > 0) { if (Number(filter.percentOffMin) > 0) {
where += ` AND esvlistaprodutos.PERCENTUALDESCONTO >= ${filter.percentOffMin}`; where += ` AND esvlistaprodutos.PERCENTUALDESCONTO >= ${Number(filter.percentOffMin)}`;
} }
if (filter.percentOffMax > 0) { if (Number(filter.percentOffMax) > 0) {
where += ` AND esvlistaprodutos.PERCENTUALDESCONTO <= ${filter.percentOffMax}`; where += ` AND esvlistaprodutos.PERCENTUALDESCONTO <= ${Number(filter.percentOffMax)}`;
} }
let xbrands = ''; if (normalizedFilter.brands.length > 0) {
if (filter && filter.brands && filter.brands.length > 0) { const xbrands = normalizedFilter.brands
const brands = filter.brands; .filter(b => b != null && b.length > 0)
brands.forEach(b => { .map(b => `'${escapeSqlText(b)}'`)
if (xbrands.length > 0) { .join(', ');
xbrands += ', ' + '\'' + b + '\'';
} else {
xbrands = '\'' + b + '\'';
}
});
where += ` AND esvlistaprodutos.nomemarca in ( ${xbrands} )`; where += ` AND esvlistaprodutos.nomemarca in ( ${xbrands} )`;
} }
const orderBy = `ORDER BY esvlistaprodutos.${(filter.orderBy == null) ? 'DESCRICAO' : filter.orderBy}`; const orderByColumn = orderByColumns[filter.orderBy] || 'DESCRICAO';
const orderBy = `ORDER BY esvlistaprodutos.${orderByColumn}`;
const skipReg = ((pageNumber - 1) * pageSize); const skipReg = ((pageNumber - 1) * pageSize);
const pagination = ` OFFSET ${skipReg} ROWS FETCH NEXT ${pageSize} ROWS ONLY`; const pagination = ` OFFSET ${skipReg} ROWS FETCH NEXT ${pageSize} ROWS ONLY`;
@@ -1352,11 +1407,14 @@ export class SalesService {
await queryRunner.connect(); await queryRunner.connect();
try { try {
console.log("hora de buscar produtos: " + new Date().toISOString())
let products = await queryRunner.manager.query(sql + where + orderBy + pagination) as SalesProduct[]; let products = await queryRunner.manager.query(sql + where + orderBy + pagination) as SalesProduct[];
console.log("hora final de buscar produtos: " + new Date().toISOString())
products = this.createListImages(products); products = this.createListImages(products);
console.log("hora de buscar imagens: " + new Date().toISOString())
console.log("Total de produtos: " + products.length); console.log("Total de produtos: " + products.length);
await this.redisClient.set(cacheKey, JSON.stringify(products), 'EX', 900); try { await this.redisClient.set(cacheKey, JSON.stringify(products), 'EX', 900); } catch (cacheErr) { console.error('Erro ao salvar cache searchProduct2:', cacheErr?.message || cacheErr); }
return products; return products;
} catch (err) { } catch (err) {
@@ -1455,7 +1513,7 @@ export class SalesService {
await this.updatePriorityDelivery(dataDeliveryTax.cartId, dataDeliveryTax.priorityDelivery); await this.updatePriorityDelivery(dataDeliveryTax.cartId, dataDeliveryTax.priorityDelivery);
if (cityId == 0) { if (cityId == 0) {
cityId = Number.parseInt(dataDeliveryTax.ibgeCode); cityId = Number.parseInt(dataDeliveryTax.ibgeCode ?? dataDeliveryTax.cityId);
// throw new HttpException('Cidade não localiza para cálculo da taxa de entrega', HttpStatus.BAD_REQUEST); // throw new HttpException('Cidade não localiza para cálculo da taxa de entrega', HttpStatus.BAD_REQUEST);
} }
const connectionDb = new Connection(connectionOptions); const connectionDb = new Connection(connectionOptions);

View File

@@ -149,16 +149,25 @@ export class ShoppingService {
await this.createShopping(idCart, itemShopping.invoiceStore, itemShopping.seller, queryRunner); await this.createShopping(idCart, itemShopping.invoiceStore, itemShopping.seller, queryRunner);
} }
console.log('Incluindo item 04 - ' + Date.now().toString()); console.log('Incluindo item 04 - ' + Date.now().toString());
const item = await this.getItemCartById(itemShopping.idCart, itemShopping.id); const item = await queryRunner.manager
.getRepository(ShoppingItens)
.createQueryBuilder('estprevendai')
.where("\"estprevendai\".idcart = :idcart", { idcart: idCart })
.andWhere("\"estprevendai\".id = :id", { id: itemShopping.id })
.andWhere("\"estprevendai\".dtcancel is null")
.getOne();
console.log('Incluindo item 05 - ' + Date.now().toString()); console.log('Incluindo item 05 - ' + Date.now().toString());
if (item) { if (item) {
itemShopping.id = item.id; itemShopping.id = item.id;
this.updateItem(itemShopping); await this.updateItemCart(itemShopping, queryRunner);
return await this.getItemCartById(itemShopping.idCart, itemShopping.id); await this.calculateProfitShoppping(idCart, queryRunner);
await this.updateTotalShopping(idCart, queryRunner);
await queryRunner.commitTransaction();
return itemShopping;
} }
console.log('Incluindo item 06 - ' + Date.now().toString()); console.log('Incluindo item 06 - ' + Date.now().toString());
const recordItens = await queryRunner.query(`SELECT COUNT(1) as "recordNo" FROM ESTPREVENDAI WHERE IDCART = '${itemShopping.idCart}'`); const recordItens = await queryRunner.query(`SELECT COUNT(1) as "recordNo" FROM ESTPREVENDAI WHERE IDCART = :1`, [idCart]);
let numSeq = 1; let numSeq = 1;
if (recordItens != null && recordItens.length > 0) { if (recordItens != null && recordItens.length > 0) {
numSeq = recordItens[0].recordNo + 1; numSeq = recordItens[0].recordNo + 1;
@@ -266,10 +275,10 @@ export class ShoppingService {
console.log('Incluindo item 07 - ' + Date.now().toString()); console.log('Incluindo item 07 - ' + Date.now().toString());
// await queryRunner.manager.save(createItemShopping); // await queryRunner.manager.save(createItemShopping);
console.log('Incluindo item 08 - ' + Date.now().toString()); console.log('Incluindo item 08 - ' + Date.now().toString());
await this.calculateProfitShoppping(idCart, queryRunner);
await this.updateTotalShopping(idCart, queryRunner);
await queryRunner.commitTransaction(); await queryRunner.commitTransaction();
console.log('Incluindo item 09 - ' + Date.now().toString()); console.log('Incluindo item 09 - ' + Date.now().toString());
await this.calculateProfitShoppping(idCart, queryRunner);
await this.updateTotalShopping(idCart);
return createItemShopping; return createItemShopping;
@@ -300,36 +309,19 @@ export class ShoppingService {
} }
async updateTotalShopping(idCart: string) { async updateTotalShopping(idCart: string, activeQueryRunner?: QueryRunner) {
if (activeQueryRunner) {
await this.updateTotalShoppingWithRunner(idCart, activeQueryRunner);
return;
}
const connection = new Connection(connectionOptions); const connection = new Connection(connectionOptions);
await connection.connect(); await connection.connect();
const queryRunner = connection.createQueryRunner(); const queryRunner = connection.createQueryRunner();
await queryRunner.connect(); await queryRunner.connect();
await queryRunner.startTransaction(); await queryRunner.startTransaction();
try { try {
const prevenda = await queryRunner await this.updateTotalShoppingWithRunner(idCart, queryRunner);
.query('SELECT SUM(ESTPREVENDAI.PTABELA * ESTPREVENDAI.QT) as "vltabela" ' +
' ,SUM(ESTPREVENDAI.PVENDA * ESTPREVENDAI.QT) as "vlatend" ' +
' ,SUM(ESTPREVENDAI.VLDESCONTO * ESTPREVENDAI.QT) as "vldesconto" ' +
' ,SUM(PCPRODUT.PESOBRUTO * ESTPREVENDAI.QT) as "totpeso" ' +
' FROM ESTPREVENDAI, PCPRODUT ' +
' WHERE ESTPREVENDAI.CODPROD = PCPRODUT.CODPROD ' +
' AND ESTPREVENDAI.IDCART = :1', [idCart]);
if (prevenda != null) {
const sql = ` UPDATE ESTPREVENDAC SET ` +
` VLPEDIDO = :1 ` +
` ,VLDESCONTO = :2 ` +
` ,TOTPESO = :3 ` +
` ,VLTABELA = :4 ` +
` WHERE ID = :5 `;
await queryRunner.query(sql, [
prevenda[0].vlatend,
prevenda[0].vldesconto,
prevenda[0].totpeso,
prevenda[0].vltabela,
idCart
]);
}
await queryRunner.commitTransaction(); await queryRunner.commitTransaction();
} catch (error) { } catch (error) {
@@ -342,6 +334,55 @@ export class ShoppingService {
} }
private async updateTotalShoppingWithRunner(idCart: string, queryRunner: QueryRunner) {
const prevenda = await queryRunner
.query('SELECT SUM(ESTPREVENDAI.PTABELA * ESTPREVENDAI.QT) as "vltabela" ' +
' ,SUM(ESTPREVENDAI.PVENDA * ESTPREVENDAI.QT) as "vlatend" ' +
' ,SUM(ESTPREVENDAI.VLDESCONTO * ESTPREVENDAI.QT) as "vldesconto" ' +
' ,SUM(PCPRODUT.PESOBRUTO * ESTPREVENDAI.QT) as "totpeso" ' +
' FROM ESTPREVENDAI, PCPRODUT ' +
' WHERE ESTPREVENDAI.CODPROD = PCPRODUT.CODPROD ' +
' AND ESTPREVENDAI.IDCART = :1', [idCart]);
if (prevenda != null) {
const sql = ` UPDATE ESTPREVENDAC SET ` +
` VLPEDIDO = :1 ` +
` ,VLDESCONTO = :2 ` +
` ,TOTPESO = :3 ` +
` ,VLTABELA = :4 ` +
` WHERE ID = :5 `;
await queryRunner.query(sql, [
prevenda[0].vlatend,
prevenda[0].vldesconto,
prevenda[0].totpeso,
prevenda[0].vltabela,
idCart
]);
}
}
private async updateItemCart(itemShopping: ShoppingItem, queryRunner: QueryRunner) {
const sql = ' UPDATE ESTPREVENDAI SET ' +
' PVENDA = :1 ' +
' ,PTABELA = :2 ' +
' ,QT = :3 ' +
' ,TIPOENTREGA = :4 ' +
' ,CODFILIALRETIRA = :5 ' +
' ,DESCRICAOAUXILIAR = :6 ' +
' ,AMBIENTE = :7 ' +
' WHERE ID = :8 ';
await queryRunner.query(sql, [
itemShopping.price,
itemShopping.listPrice,
itemShopping.quantity,
itemShopping.deliveryType,
itemShopping.stockStore,
itemShopping.auxDescription,
itemShopping.environment,
itemShopping.id
]);
}
async updateItem(itemShopping: ShoppingItem) { async updateItem(itemShopping: ShoppingItem) {
const connection = new Connection(connectionOptions); const connection = new Connection(connectionOptions);
await connection.connect(); await connection.connect();
@@ -350,31 +391,10 @@ export class ShoppingService {
await queryRunner.startTransaction(); await queryRunner.startTransaction();
try { try {
const sql = ' UPDATE ESTPREVENDAI SET ' + await this.updateItemCart(itemShopping, queryRunner);
' PVENDA = :1 ' + await this.updateTotalShopping(itemShopping.idCart, queryRunner);
' ,PTABELA = :2 ' +
' ,QT = :3 ' +
' ,TIPOENTREGA = :4 ' +
' ,CODFILIALRETIRA = :5 ' +
' ,DESCRICAOAUXILIAR = :6 ' +
' ,AMBIENTE = :7 ' +
' WHERE ID = :8 ';
await queryRunner.query(sql, [
itemShopping.price,
itemShopping.listPrice,
itemShopping.quantity,
itemShopping.deliveryType,
itemShopping.stockStore,
itemShopping.auxDescription,
itemShopping.environment,
itemShopping.id
]);
await queryRunner.commitTransaction();
await this.updateTotalShopping(itemShopping.idCart);
await this.calculateProfitShoppping(itemShopping.idCart, queryRunner); await this.calculateProfitShoppping(itemShopping.idCart, queryRunner);
await queryRunner.commitTransaction();
} catch (erro) { } catch (erro) {
await queryRunner.rollbackTransaction(); await queryRunner.rollbackTransaction();
@@ -419,9 +439,9 @@ export class ShoppingService {
// }) // })
// .where("\"ESTPREVENDAI\".id = :id", { id: itemShopping.id }) // .where("\"ESTPREVENDAI\".id = :id", { id: itemShopping.id })
// .execute(); // .execute();
await queryRunner.commitTransaction();
await this.calculateProfitShoppping(itemShopping.idCart, queryRunner); await this.calculateProfitShoppping(itemShopping.idCart, queryRunner);
await this.updateTotalShopping(itemShopping.idCart); await this.updateTotalShopping(itemShopping.idCart, queryRunner);
await queryRunner.commitTransaction();
return itemShopping; return itemShopping;
} catch (erro) { } catch (erro) {
await queryRunner.rollbackTransaction(); await queryRunner.rollbackTransaction();
@@ -450,9 +470,11 @@ export class ShoppingService {
id id
]); ]);
} }
if (item != null) {
await this.updateTotalShopping(item.idCart, queryRunner);
await this.calculateProfitShoppping(item.idCart, queryRunner);
}
await queryRunner.commitTransaction(); await queryRunner.commitTransaction();
await this.updateTotalShopping(item.idCart);
await this.calculateProfitShoppping(item.idCart, queryRunner);
} catch (erro) { } catch (erro) {
await queryRunner.rollbackTransaction(); await queryRunner.rollbackTransaction();
throw erro; throw erro;
@@ -484,30 +506,29 @@ export class ShoppingService {
' ,VLDESCONTO = :3 ' + ' ,VLDESCONTO = :3 ' +
' ,CODFUNCDESC = :4 ' + ' ,CODFUNCDESC = :4 ' +
' WHERE ID = :5 '; ' WHERE ID = :5 ';
items.filter(item => item.promotion == null || item.promotion == 0) for (const item of items.filter(item => item.promotion == null || item.promotion == 0)) {
.forEach(async (item) => { const price = (item.listPrice * (1 - (order.percentDiscount / 100))).toFixed(2);
const price = (item.listPrice * (1 - (order.percentDiscount / 100))).toFixed(2); listPriceTotal += (item.listPrice * item.quantity);
listPriceTotal += (item.listPrice * item.quantity); discountTotal += ((Number.parseFloat(item.listPrice.toFixed(2)) - Number.parseFloat(price)) * item.quantity);
discountTotal += ((Number.parseFloat(item.listPrice.toFixed(2)) - Number.parseFloat(price)) * item.quantity); await queryRunner.query(sql, [
await queryRunner.query(sql, [ Number.parseFloat(price),
Number.parseFloat(price), order.percentDiscount,
order.percentDiscount, (item.listPrice - Number.parseFloat(price)),
(item.listPrice - Number.parseFloat(price)), order.idUserAuth,
order.idUserAuth, item.id
item.id ]);
]); // await queryRunner.manager
// await queryRunner.manager // .createQueryBuilder()
// .createQueryBuilder() // .update(ShoppingItens)
// .update(ShoppingItens) // .set({
// .set({ // price: Number.parseFloat(price),
// price: Number.parseFloat(price), // discount: order.percentDiscount,
// discount: order.percentDiscount, // discountValue: (item.listPrice - Number.parseFloat(price)),
// discountValue: (item.listPrice - Number.parseFloat(price)), // userDiscount: order.idUserAuth
// userDiscount: order.idUserAuth // })
// }) // .where("\"ESTPREVENDAI\".id = :id", { id: item.id })
// .where("\"ESTPREVENDAI\".id = :id", { id: item.id }) // .execute();
// .execute(); }
});
console.log('total de desconto: ' + discountTotal); console.log('total de desconto: ' + discountTotal);
const sqlOrder = 'UPDATE ESTPREVENDAC SET ' + const sqlOrder = 'UPDATE ESTPREVENDAC SET ' +
' VLPEDIDO = :1 ' + ' VLPEDIDO = :1 ' +
@@ -529,9 +550,9 @@ export class ShoppingService {
// }) // })
// .where("\"ESTPREVENDAC\".id = :id", { id: order.id }) // .where("\"ESTPREVENDAC\".id = :id", { id: order.id })
// .execute(); // .execute();
await queryRunner.commitTransaction();
await this.calculateProfitShoppping(order.id, queryRunner); await this.calculateProfitShoppping(order.id, queryRunner);
await this.updateTotalShopping(order.id); await this.updateTotalShopping(order.id, queryRunner);
await queryRunner.commitTransaction();
} catch (err) { } catch (err) {
console.log(err); console.log(err);
await queryRunner.rollbackTransaction(); await queryRunner.rollbackTransaction();