From 28a1cee8765b468935422c5d8348a22fcd38fcf9 Mon Sep 17 00:00:00 2001 From: JurTI-BR Date: Tue, 1 Apr 2025 16:42:05 -0300 Subject: [PATCH] impl redis cloud --- src/core/configs/cache/redis.provider.ts | 7 +- src/data-consult/data-consult.controller.ts | 8 + src/data-consult/data-consult.repository.ts | 18 +- src/data-consult/data-consult.service.ts | 20 ++ .../dist/data-consult.controller.js | 140 +++++++++ src/orders/application/dist/orders.service.js | 208 ++++++++++++++ src/orders/application/orders.service.ts | 21 ++ src/orders/controllers/orders.controller.ts | 65 +++++ src/orders/dto/OrderDeliveryDto.ts | 28 ++ src/orders/dto/OrderStatusDto.ts | 8 + src/orders/dto/OrderTransferDto.ts | 13 + src/orders/dto/invoice-check-item.dto.ts | 7 + src/orders/dto/invoice-check.dto.ts | 11 + src/orders/repositories/orders.repository.ts | 271 ++++++++++++++++++ 14 files changed, 819 insertions(+), 6 deletions(-) create mode 100644 src/data-consult/dist/data-consult.controller.js create mode 100644 src/orders/application/dist/orders.service.js create mode 100644 src/orders/dto/OrderDeliveryDto.ts create mode 100644 src/orders/dto/OrderStatusDto.ts create mode 100644 src/orders/dto/OrderTransferDto.ts create mode 100644 src/orders/dto/invoice-check-item.dto.ts create mode 100644 src/orders/dto/invoice-check.dto.ts diff --git a/src/core/configs/cache/redis.provider.ts b/src/core/configs/cache/redis.provider.ts index 23b13e7..a17b1cd 100644 --- a/src/core/configs/cache/redis.provider.ts +++ b/src/core/configs/cache/redis.provider.ts @@ -6,9 +6,10 @@ provide: 'REDIS_CLIENT', useFactory: (configService: ConfigService) => { const redis = new Redis({ - host: configService.get('REDIS_HOST', '10.1.1.109'), - port: configService.get('REDIS_PORT', 6379), - // password: configService.get('REDIS_PASSWORD', ''), + host: configService.get('REDIS_HOST', 'redis-17317.crce181.sa-east-1-2.ec2.redns.redis-cloud.com'), + port: configService.get('REDIS_PORT', 17317), + username: configService.get('REDIS_USERNAME', 'default' ), + password: configService.get('REDIS_PASSWORD', 'd8sVttpJdNxrWjYRK43QGAKzEt3I8HVc'), }); redis.on('error', (err) => { diff --git a/src/data-consult/data-consult.controller.ts b/src/data-consult/data-consult.controller.ts index ffea607..f76bfb3 100644 --- a/src/data-consult/data-consult.controller.ts +++ b/src/data-consult/data-consult.controller.ts @@ -2,6 +2,7 @@ import { Controller, Get, Param,UseGuards } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiParam, ApiBearerAuth } from '@nestjs/swagger'; import { DataConsultService } from './data-consult.service'; import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard' +import { ProductDto } from './dto/product.dto'; @ApiTags('DataConsult') @Controller('api/v1/data-consult') @@ -48,4 +49,11 @@ export class DataConsultController { async products(@Param('filter') filter: string) { return this.dataConsultService.products(filter); } + + @Get('Buscar 500 produtos') + @ApiOperation({ summary: 'VIEW DE 500 PRODUTOS' }) + async getAllProducts(): Promise { + return this.dataConsultService.getAllProducts(); + } + } \ No newline at end of file diff --git a/src/data-consult/data-consult.repository.ts b/src/data-consult/data-consult.repository.ts index 1157fbc..39162d7 100644 --- a/src/data-consult/data-consult.repository.ts +++ b/src/data-consult/data-consult.repository.ts @@ -129,7 +129,7 @@ export class DataConsultRepository { PCPRODUT.CODPROD || ' - ' || PCPRODUT.DESCRICAO || ' ( ' || REGEXP_REPLACE(PCPRODUT.CODAUXILIAR, '[^0-9]', '') || ' )' as "description" FROM PCPRODUT - WHERE PCPRODUT.CODPROD = ? + WHERE PCPRODUT.CODPROD = :1 ORDER BY PCPRODUT.DESCRICAO `, params: [cleanedFilter], @@ -140,7 +140,7 @@ export class DataConsultRepository { PCPRODUT.CODPROD || ' - ' || PCPRODUT.DESCRICAO || ' ( ' || REGEXP_REPLACE(PCPRODUT.CODAUXILIAR, '[^0-9]', '') || ' )' as "description" FROM PCPRODUT - WHERE PCPRODUT.CODAUXILIAR = ? + WHERE PCPRODUT.CODAUXILIAR = :1 ORDER BY PCPRODUT.DESCRICAO `, params: [cleanedFilter], @@ -151,7 +151,7 @@ export class DataConsultRepository { PCPRODUT.CODPROD || ' - ' || PCPRODUT.DESCRICAO || ' ( ' || REGEXP_REPLACE(PCPRODUT.CODAUXILIAR, '[^0-9]', '') || ' )' as "description" FROM PCPRODUT - WHERE PCPRODUT.DESCRICAO LIKE ? + WHERE PCPRODUT.DESCRICAO LIKE :1 ORDER BY PCPRODUT.DESCRICAO `, params: [likeFilter], @@ -167,4 +167,16 @@ export class DataConsultRepository { return []; } + + async findAllProducts(): Promise { + const sql = ` + SELECT PCPRODUT.CODPROD as "id", + PCPRODUT.CODPROD || ' - ' || PCPRODUT.DESCRICAO || + ' ( ' || REGEXP_REPLACE(PCPRODUT.CODAUXILIAR, '[^0-9]', '') || ' )' as "description" + FROM PCPRODUT + WHERE ROWNUM <= 1000 + ORDER BY PCPRODUT.DESCRICAO + `; + return this.executeQuery(sql); + } } \ No newline at end of file diff --git a/src/data-consult/data-consult.service.ts b/src/data-consult/data-consult.service.ts index d63e37d..d609e01 100644 --- a/src/data-consult/data-consult.service.ts +++ b/src/data-consult/data-consult.service.ts @@ -17,6 +17,10 @@ export class DataConsultService { private readonly SELLERS_CACHE_KEY = 'data-consult:sellers'; private readonly SELLERS_TTL = 3600; // 1 hora + private readonly STORES_TTL = 3600; + private readonly BILLINGS_TTL = 3600; + private readonly ALL_PRODUCTS_CACHE_KEY = 'data-consult:products:all'; + private readonly ALL_PRODUCTS_TTL = 600; // 10 minutos (ajustável) constructor( @@ -59,8 +63,11 @@ export class DataConsultService { async billings(): Promise { this.logger.log('Buscando todos os faturamentos'); return this.repository.findBillings(); + } + + /** * Obter clientes filtrados por termo de pesquisa * @param filter - Termo de pesquisa para filtrar clientes @@ -95,4 +102,17 @@ export class DataConsultService { ); } } + + async getAllProducts(): Promise { + this.logger.log('Buscando produtos com cache Redis...'); + return getOrSetCache( + this.redisClient, + this.ALL_PRODUCTS_CACHE_KEY, + this.ALL_PRODUCTS_TTL, + async () => { + this.logger.log('Cache de produtos vazio. Buscando no banco...'); + return this.repository.findAllProducts(); + } + ); + } } diff --git a/src/data-consult/dist/data-consult.controller.js b/src/data-consult/dist/data-consult.controller.js new file mode 100644 index 0000000..e9a8bdd --- /dev/null +++ b/src/data-consult/dist/data-consult.controller.js @@ -0,0 +1,140 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +exports.__esModule = true; +exports.DataConsultController = void 0; +var common_1 = require("@nestjs/common"); +var swagger_1 = require("@nestjs/swagger"); +var jwt_auth_guard_1 = require("src/auth/guards/jwt-auth.guard"); +var DataConsultController = /** @class */ (function () { + function DataConsultController(dataConsultService) { + this.dataConsultService = dataConsultService; + } + DataConsultController.prototype.stores = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + return [2 /*return*/, this.dataConsultService.stores()]; + }); + }); + }; + DataConsultController.prototype.sellers = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + return [2 /*return*/, this.dataConsultService.sellers()]; + }); + }); + }; + DataConsultController.prototype.billings = function () { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + return [2 /*return*/, this.dataConsultService.billings()]; + }); + }); + }; + DataConsultController.prototype.customer = function (filter) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + return [2 /*return*/, this.dataConsultService.customers(filter)]; + }); + }); + }; + DataConsultController.prototype.products = function (filter) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + return [2 /*return*/, this.dataConsultService.products(filter)]; + }); + }); + }; + DataConsultController.prototype.getAllProducts = function () { + return __awaiter(this, void 0, Promise, function () { + return __generator(this, function (_a) { + return [2 /*return*/, this.dataConsultService.getAllProducts()]; + }); + }); + }; + __decorate([ + common_1.UseGuards(jwt_auth_guard_1.JwtAuthGuard), + swagger_1.ApiBearerAuth(), + common_1.Get('stores'), + swagger_1.ApiOperation({ summary: 'Lista todas as lojas' }) + ], DataConsultController.prototype, "stores"); + __decorate([ + common_1.UseGuards(jwt_auth_guard_1.JwtAuthGuard), + swagger_1.ApiBearerAuth(), + common_1.Get('sellers'), + swagger_1.ApiOperation({ summary: 'Lista todos os vendedores' }) + ], DataConsultController.prototype, "sellers"); + __decorate([ + common_1.UseGuards(jwt_auth_guard_1.JwtAuthGuard), + swagger_1.ApiBearerAuth(), + common_1.Get('billings'), + swagger_1.ApiOperation({ summary: 'Retorna informações de faturamento' }) + ], DataConsultController.prototype, "billings"); + __decorate([ + common_1.UseGuards(jwt_auth_guard_1.JwtAuthGuard), + swagger_1.ApiBearerAuth(), + common_1.Get('customers/:filter'), + swagger_1.ApiOperation({ summary: 'Filtra clientes pelo parâmetro fornecido' }), + swagger_1.ApiParam({ name: 'filter', description: 'Filtro de busca para clientes' }), + __param(0, common_1.Param('filter')) + ], DataConsultController.prototype, "customer"); + __decorate([ + common_1.Get('products/:filter'), + swagger_1.ApiOperation({ summary: 'Busca produtos filtrados' }), + swagger_1.ApiParam({ name: 'filter', description: 'Filtro de busca' }), + __param(0, common_1.Param('filter')) + ], DataConsultController.prototype, "products"); + __decorate([ + common_1.Get('Buscar 500 produtos'), + swagger_1.ApiOperation({ summary: 'VIEW DE 500 PRODUTOS' }) + ], DataConsultController.prototype, "getAllProducts"); + DataConsultController = __decorate([ + swagger_1.ApiTags('DataConsult'), + common_1.Controller('api/v1/data-consult') + ], DataConsultController); + return DataConsultController; +}()); +exports.DataConsultController = DataConsultController; diff --git a/src/orders/application/dist/orders.service.js b/src/orders/application/dist/orders.service.js new file mode 100644 index 0000000..3251db9 --- /dev/null +++ b/src/orders/application/dist/orders.service.js @@ -0,0 +1,208 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +exports.__esModule = true; +exports.OrdersService = void 0; +var common_1 = require("@nestjs/common"); +var redis_client_adapter_provider_1 = require("src/core/configs/cache/redis-client.adapter.provider"); +var cache_util_1 = require("src/shared/cache.util"); +var crypto_1 = require("crypto"); +var OrdersService = /** @class */ (function () { + function OrdersService(ordersRepository, redisClient) { + this.ordersRepository = ordersRepository; + this.redisClient = redisClient; + this.TTL_ORDERS = 60 * 10; // 10 minutos + this.TTL_INVOICE = 60 * 60; // 1 hora + this.TTL_ITENS = 60 * 10; // 10 minutos + } + /** + * Buscar pedidos com cache baseado nos filtros + */ + OrdersService.prototype.findOrders = function (query) { + return __awaiter(this, void 0, void 0, function () { + var key; + var _this = this; + return __generator(this, function (_a) { + key = "orders:query:" + this.hashObject(query); + return [2 /*return*/, cache_util_1.getOrSetCache(this.redisClient, key, this.TTL_ORDERS, function () { return _this.ordersRepository.findOrders(query); })]; + }); + }); + }; + /** + * Buscar nota fiscal por chave NFe com cache + */ + OrdersService.prototype.findInvoice = function (chavenfe) { + return __awaiter(this, void 0, Promise, function () { + var key; + var _this = this; + return __generator(this, function (_a) { + key = "orders:invoice:" + chavenfe; + return [2 /*return*/, cache_util_1.getOrSetCache(this.redisClient, key, this.TTL_INVOICE, function () { return __awaiter(_this, void 0, void 0, function () { + var invoiceData, invoice; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.ordersRepository.findInvoice(chavenfe)]; + case 1: + invoiceData = _a.sent(); + invoice = { + storeId: invoiceData.storeId, + invoiceDate: new Date(invoiceData.invoiceDate), + orderId: invoiceData.orderId, + invoiceId: invoiceData.invoiceId, + transactionId: invoiceData.transactionId, + customerId: invoiceData.customerId, + customer: invoiceData.customer, + sellerId: invoiceData.sellerId, + sellerName: invoiceData.sellerName, + itensQt: invoiceData.itensQt, + itens: invoiceData.itens + }; + return [2 /*return*/, invoice]; + } + }); + }); })]; + }); + }); + }; + /** + * Buscar itens de pedido com cache + */ + OrdersService.prototype.getItens = function (orderId) { + return __awaiter(this, void 0, Promise, function () { + var key; + var _this = this; + return __generator(this, function (_a) { + key = "orders:itens:" + orderId; + return [2 /*return*/, cache_util_1.getOrSetCache(this.redisClient, key, this.TTL_ITENS, function () { return __awaiter(_this, void 0, void 0, function () { + var itens; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.ordersRepository.getItens(orderId)]; + case 1: + itens = _a.sent(); + return [2 /*return*/, itens.map(function (item) { return ({ + productId: Number(item.productId), + description: item.description, + pacth: item.pacth, + color: item.color, + stockId: Number(item.stockId), + quantity: Number(item.quantity), + salePrice: Number(item.salePrice), + deliveryType: item.deliveryType, + total: Number(item.total), + weight: Number(item.weight), + department: item.department, + brand: item.brand + }); })]; + } + }); + }); })]; + }); + }); + }; + OrdersService.prototype.getCutItens = function (orderId) { + return __awaiter(this, void 0, Promise, function () { + var itens; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.ordersRepository.getCutItens(orderId)]; + case 1: + itens = _a.sent(); + return [2 /*return*/, itens.map(function (item) { return ({ + productId: Number(item.productId), + description: item.description, + pacth: item.pacth, + stockId: Number(item.stockId), + saleQuantity: Number(item.saleQuantity), + cutQuantity: Number(item.cutQuantity), + separedQuantity: Number(item.separedQuantity) + }); })]; + } + }); + }); + }; + OrdersService.prototype.getOrderDelivery = function (orderId) { + return __awaiter(this, void 0, Promise, function () { + return __generator(this, function (_a) { + return [2 /*return*/, this.ordersRepository.getOrderDelivery(orderId)]; + }); + }); + }; + OrdersService.prototype.getTransfer = function (orderId) { + return __awaiter(this, void 0, Promise, function () { + return __generator(this, function (_a) { + return [2 /*return*/, this.ordersRepository.getTransfer(orderId)]; + }); + }); + }; + OrdersService.prototype.getStatusOrder = function (orderId) { + return __awaiter(this, void 0, Promise, function () { + return __generator(this, function (_a) { + return [2 /*return*/, this.ordersRepository.getStatusOrder(orderId)]; + }); + }); + }; + /** + * Utilitário para gerar hash MD5 de objetos + */ + OrdersService.prototype.hashObject = function (obj) { + var str = JSON.stringify(obj, Object.keys(obj).sort()); + return crypto_1.createHash('md5').update(str).digest('hex'); + }; + OrdersService.prototype.createInvoiceCheck = function (invoice) { + return __awaiter(this, void 0, Promise, function () { + return __generator(this, function (_a) { + return [2 /*return*/, this.ordersRepository.createInvoiceCheck(invoice)]; + }); + }); + }; + OrdersService = __decorate([ + common_1.Injectable(), + __param(1, common_1.Inject(redis_client_adapter_provider_1.RedisClientToken)) + ], OrdersService); + return OrdersService; +}()); +exports.OrdersService = OrdersService; diff --git a/src/orders/application/orders.service.ts b/src/orders/application/orders.service.ts index eabba0e..3f0e995 100644 --- a/src/orders/application/orders.service.ts +++ b/src/orders/application/orders.service.ts @@ -8,6 +8,12 @@ import { IRedisClient } from 'src/core/configs/cache/IRedisClient'; import { RedisClientToken } from 'src/core/configs/cache/redis-client.adapter.provider'; import { getOrSetCache } from 'src/shared/cache.util'; import { createHash } from 'crypto'; +import { OrderDeliveryDto } from '../dto/OrderDeliveryDto'; +import { OrderTransferDto } from '../dto/OrderTransferDto'; +import { OrderStatusDto } from '../dto/OrderStatusDto'; +import { InvoiceCheckDto } from '../dto/invoice-check.dto'; + + @Injectable() export class OrdersService { @@ -101,7 +107,17 @@ export class OrdersService { })); } + async getOrderDelivery(orderId: string): Promise { + return this.ordersRepository.getOrderDelivery(orderId); + } + async getTransfer(orderId: number): Promise { + return this.ordersRepository.getTransfer(orderId); + } + + async getStatusOrder(orderId: number): Promise { + return this.ordersRepository.getStatusOrder(orderId); + } /** * Utilitário para gerar hash MD5 de objetos */ @@ -110,4 +126,9 @@ export class OrdersService { return createHash('md5').update(str).digest('hex'); } + +async createInvoiceCheck(invoice: InvoiceCheckDto): Promise<{ message: string }> { + return this.ordersRepository.createInvoiceCheck(invoice); +} + } \ No newline at end of file diff --git a/src/orders/controllers/orders.controller.ts b/src/orders/controllers/orders.controller.ts index aa6fc3e..0540566 100644 --- a/src/orders/controllers/orders.controller.ts +++ b/src/orders/controllers/orders.controller.ts @@ -1,6 +1,8 @@ import { Controller, Get, + Post, + Body, Param, Query, UsePipes, @@ -18,6 +20,13 @@ import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard'; import { InvoiceDto } from '../dto/find-invoice.dto'; import { OrderItemDto } from "../dto/OrderItemDto"; import { CutItemDto } from '../dto/CutItemDto'; +import { OrderDeliveryDto } from '../dto/OrderDeliveryDto'; +import { OrderTransferDto } from '../dto/OrderTransferDto'; +import { OrderStatusDto } from '../dto/OrderStatusDto'; +import { InvoiceCheckDto } from '../dto/invoice-check.dto'; + + + @ApiTags('Orders') @@ -73,4 +82,60 @@ export class OrdersController { ); } } + + @Get('delivery/:orderId') + @ApiOperation({ summary: 'Busca dados de entrega do pedido' }) + @UsePipes(new ValidationPipe({ transform: true })) + async getOrderDelivery(@Param('orderId') orderId: string): Promise { + try { + return await this.ordersService.getOrderDelivery(orderId); + } catch (error) { + throw new HttpException( + error.message || 'Erro ao buscar dados de entrega', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + +@Get('transfer/:orderId') +@ApiOperation({ summary: 'Consulta pedidos de transferência' }) +@UsePipes(new ValidationPipe({ transform: true })) +async getTransfer(@Param('orderId') orderId: number): Promise { + try { + return await this.ordersService.getTransfer(orderId); + } catch (error) { + throw new HttpException( + error.message || 'Erro ao buscar transferências do pedido', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + +@Get('status/:orderId') +@ApiOperation({ summary: 'Consulta status do pedido' }) +@UsePipes(new ValidationPipe({ transform: true })) +async getStatusOrder(@Param('orderId') orderId: number): Promise { + try { + return await this.ordersService.getStatusOrder(orderId); + } catch (error) { + throw new HttpException( + error.message || 'Erro ao buscar status do pedido', + error.status || HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + +@Post('invoice/check') +@ApiOperation({ summary: 'Cria conferência de nota fiscal' }) +@UsePipes(new ValidationPipe({ transform: true })) +async createInvoiceCheck(@Body() invoice: InvoiceCheckDto): Promise<{ message: string }> { + try { + return await this.ordersService.createInvoiceCheck(invoice); + } catch (error) { + throw new HttpException( + error.message || 'Erro ao salvar conferência', + error.status || HttpStatus.INTERNAL_SERVER_ERROR + ); + } + } } diff --git a/src/orders/dto/OrderDeliveryDto.ts b/src/orders/dto/OrderDeliveryDto.ts new file mode 100644 index 0000000..8d92f42 --- /dev/null +++ b/src/orders/dto/OrderDeliveryDto.ts @@ -0,0 +1,28 @@ +export class OrderDeliveryDto { + placeId: number; + placeName: string; + street: string; + addressNumber: string; + bairro: string; + city: string; + state: string; + addressComplement: string; + cep: string; + commentOrder1: string; + commentOrder2: string; + commentDelivery1: string; + commentDelivery2: string; + commentDelivery3: string; + commentDelivery4: string; + shippimentId: number; + shippimentDate: Date; + shippimentComment: string; + place: string; + driver: string; + car: string; + closeDate: Date; + separatorName: string; + confName: string; + releaseDate: Date; + } + \ No newline at end of file diff --git a/src/orders/dto/OrderStatusDto.ts b/src/orders/dto/OrderStatusDto.ts new file mode 100644 index 0000000..fc10d37 --- /dev/null +++ b/src/orders/dto/OrderStatusDto.ts @@ -0,0 +1,8 @@ +export class OrderStatusDto { + orderId: number; + status: string; + statusDate: Date; + userName: string; + comments: string | null; + } + \ No newline at end of file diff --git a/src/orders/dto/OrderTransferDto.ts b/src/orders/dto/OrderTransferDto.ts new file mode 100644 index 0000000..e409206 --- /dev/null +++ b/src/orders/dto/OrderTransferDto.ts @@ -0,0 +1,13 @@ +export class OrderTransferDto { + orderId: number; + transferDate: Date; + invoiceId: number; + transactionId: number; + oldShipment: number; + newShipment: number; + transferText: string; + cause: string; + userName: string; + program: string; + } + \ No newline at end of file diff --git a/src/orders/dto/invoice-check-item.dto.ts b/src/orders/dto/invoice-check-item.dto.ts new file mode 100644 index 0000000..d4aeb14 --- /dev/null +++ b/src/orders/dto/invoice-check-item.dto.ts @@ -0,0 +1,7 @@ +export class InvoiceCheckItemDto { + productId: number; + seq: number; + qt: number; + confDate: string; + } + \ No newline at end of file diff --git a/src/orders/dto/invoice-check.dto.ts b/src/orders/dto/invoice-check.dto.ts new file mode 100644 index 0000000..b404316 --- /dev/null +++ b/src/orders/dto/invoice-check.dto.ts @@ -0,0 +1,11 @@ +import { InvoiceCheckItemDto } from './invoice-check-item.dto'; + +export class InvoiceCheckDto { + transactionId: number; + storeId: number; + invoiceId: number; + startDate: string; + endDate: string; + userId: number; + itens: InvoiceCheckItemDto[]; +} diff --git a/src/orders/repositories/orders.repository.ts b/src/orders/repositories/orders.repository.ts index f817faf..b3eef6c 100644 --- a/src/orders/repositories/orders.repository.ts +++ b/src/orders/repositories/orders.repository.ts @@ -4,6 +4,11 @@ import { InjectDataSource } from "@nestjs/typeorm"; import { FindOrdersDto } from "../dto/find-orders.dto"; import { OrderItemDto } from "../dto/OrderItemDto"; import { CutItemDto } from '../dto/CutItemDto'; +import { OrderDeliveryDto } from '../dto/OrderDeliveryDto'; +import { OrderTransferDto } from '../dto/OrderTransferDto'; +import { OrderStatusDto } from '../dto/OrderStatusDto'; +import { InvoiceCheckDto } from '../dto/invoice-check.dto'; + @@ -365,4 +370,270 @@ export class OrdersRepository { await queryRunner.release(); } } + + async getOrderDelivery(orderId: string): Promise { + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + + try { + const sql = ` + SELECT PCPRACA.CODPRACA as "placeId", + PCPRACA.PRACA as "placeName", + NVL(ENDENT.ENDERENT, PCCLIENT.ENDERENT) as "street", + NVL(ENDENT.NUMEROENT, PCCLIENT.NUMEROENT) as "addressNumber", + NVL(ENDENT.BAIRROENT, PCCLIENT.BAIRROENT) as "bairro", + NVL(ENDENT.MUNICENT, PCCLIENT.MUNICENT) as "city", + NVL(ENDENT.ESTENT, PCCLIENT.ESTENT) as "state", + NVL(ENDENT.COMPLEMENTOENT, PCCLIENT.COMPLEMENTOENT) as "addressComplement", + NVL(ENDENT.CEPENT, PCCLIENT.CEPENT) as "cep", + PCPEDC.OBS1 as "commentOrder1", + PCPEDC.OBS2 as "commentOrder2", + PCPEDC.OBSENTREGA1 as "commentDelivery1", + PCPEDC.OBSENTREGA2 as "commentDelivery2", + PCPEDC.OBSENTREGA3 as "commentDelivery3", + PCPEDC.OBSENTREGA4 as "commentDelivery4", + PCPEDC.NUMCAR as "shippimentId", + PCCARREG.DTSAIDA as "shippimentDate", + PCCARREG.DESTINO as "shippimentComment", + PCROTAEXP.DESCRICAO as "place", + PCEMPR.MATRICULA||'-'||PCEMPR.NOME as "driver", + PCVEICUL.PLACA||' - '||PCVEICUL.DESCRICAO as "car", + CASE WHEN PCCARREG.DTFECHA < PCCARREG.DTSAIDA THEN NULL ELSE PCCARREG.DTFECHA END as "closeDate", + SEPARADOR.NOME as "separatorName", + CONFERENTE.NOME as "confName", + PCPEDC.DTLIBERA as "releaseDate" + FROM PCPEDC, PCCLIENT, PCPRACA, PCCLIENTENDENT ENDENT, PCCARREG, PCVEICUL, PCEMPR, PCROTAEXP, + PCEMPR SEPARADOR, PCEMPR CONFERENTE + WHERE NVL(ENDENT.CODPRACAENT, PCPEDC.CODPRACA) = PCPRACA.CODPRACA + AND PCPEDC.CODCLI = PCCLIENT.CODCLI + AND PCPEDC.CODENDENTCLI = ENDENT.CODENDENTCLI (+) + AND PCPEDC.CODCLI = ENDENT.CODCLI (+) + AND PCPEDC.NUMCAR = PCCARREG.NUMCAR (+) + AND PCCARREG.CODMOTORISTA = PCEMPR.MATRICULA (+) + AND PCCARREG.CODVEICULO = PCVEICUL.CODVEICULO (+) + AND PCCARREG.CODROTAPRINC = PCROTAEXP.CODROTA (+) + AND PCPEDC.CODFUNCSEP = SEPARADOR.MATRICULA (+) + AND PCPEDC.CODFUNCCONF = CONFERENTE.MATRICULA (+) + AND PCPEDC.NUMPED = ${orderId} + `; + + const result = await queryRunner.manager.query(sql); + return result.length > 0 ? result[0] : null; + } finally { + await queryRunner.release(); + } + } + async getTransfer(orderId: number): Promise { + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + + try { + const sql = ` + SELECT + L.NUMPED as "orderId", + L.DTTRANSF as "transferDate", + L.NUMNOTA as "invoiceId", + L.NUMTRANSVENDA as "transactionId", + L.NUMCARANTERIOR as "oldShipment", + L.NUMCARATUAL as "newShipment", + L.MOTIVOTRANSF as "transferText", + L.codmotivo || '-' || PCTABDEV.MOTIVO as "cause", + L.codfunctransf || '-' || PCEMPR.NOME as "userName", + L.rotinatransf as "program" + FROM PCLOGTRANSFNFCARREG L + LEFT JOIN PCTABDEV ON L.CODMOTIVO = PCTABDEV.coddevol + LEFT JOIN PCEMPR ON L.CODFUNCTRANSF = PCEMPR.MATRICULA + WHERE L.NUMTRANSVENDA IN ( + SELECT NUMTRANSVENDA FROM PCPEDC WHERE PCPEDC.NUMPED = ${orderId} + ) + `; + + const result = await queryRunner.manager.query(sql); + return result.length > 0 ? result : null; + } finally { + await queryRunner.release(); + } + } + + async getStatusOrder(orderId: number): Promise { + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + + try { + const sql = `SELECT pcpedc.numped AS "orderId", + 'Digitação pedido' AS "status", + TO_DATE (TO_CHAR(pcpedc.data, 'DD/MM/YYYY') || ' ' || pcpedc.hora || ':' || pcpedc.minuto, + 'DD/MM/YYYY HH24:MI') + AS "statusDate", + pcpedc.codemitente || '-' || pcempr.nome "userName" + ,NULL as "comments" + FROM pcpedc, pcempr + WHERE pcpedc.numped = ${orderId} AND pcpedc.codemitente = pcempr.matricula(+) + UNION ALL + SELECT pcpedc.numped AS "orderId", + 'Montagem de carga' AS "status", + TO_DATE ( + TO_CHAR(pccarreg.datamon, 'DD/MM/YYYY') + || ' ' + || pccarreg.horamon + || ':' + || pccarreg.minutomon, + 'DD/MM/YYYY HH24:MI') + AS "statusDate", + pccarreg.codfuncmon || '-' || pcempr.nome "userName" + ,NULL as "comments" + FROM pcpedc, pcempr, pccarreg + WHERE pcpedc.numped = ${orderId} + AND pccarreg.codfuncmon = pcempr.matricula(+) + AND pcpedc.numcar = pccarreg.numcar + AND pcpedc.numcar > 0 + AND pccarreg.datamon is not null + UNION ALL + SELECT pcpedc.numped AS "orderId", + 'Emissão do mapa' AS "status", + CASE WHEN PCPEDC.DTEMISSAOMAPA IS NOT NULL THEN + TO_DATE ( + TO_CHAR(pcpedc.dtemissaomapa, 'DD/MM/YYYY') + || ' ' + || NVL(pcpedc.horaemissaomapa, '00') + || ':' + || nvl(pcpedc.minutoemissaomapa, '01'), + 'DD/MM/YYYY HH24:MI') ELSE NULL END + AS "statusDate", + pcpedc.codfuncemissaomapa || '-' || pcempr.nome AS "userName" + ,NULL as "comments" + FROM pcpedc, pcempr + WHERE pcpedc.numped = ${orderId} + AND pcpedc.codfuncemissaomapa = pcempr.matricula(+) + AND PCPEDC.DTEMISSAOMAPA IS NOT NULL + UNION ALL + SELECT pcpedc.numped AS "orderId", + 'Inicio de Separação' AS "status", + pcpedc.dtinicialsep as "statusDate", + pcpedc.codfuncsep || '-' || pcempr.nome "userName" + ,NULL as "comments" + FROM pcpedc, pcempr + WHERE pcpedc.numped = ${orderId} + AND pcpedc.codfuncsep = pcempr.matricula(+) + AND PCPEDC.dtinicialsep IS NOT NULL + UNION ALL + SELECT pcpedc.numped AS "orderId", + 'Fim de Separação' AS "status", + pcpedc.dtfinalsep as "statusDate", + pcpedc.codfuncsep || '-' || pcempr.nome "userName" + ,NULL as "comments" + FROM pcpedc, pcempr + WHERE pcpedc.numped = ${orderId} AND pcpedc.codfuncsep = pcempr.matricula(+) + and pcpedc.dtfinalsep is not null + UNION ALL + SELECT pcpedc.numped AS "orderId", + 'Inicio conferência' AS "status", + pcpedc.dtinicialcheckout AS "statusDate", + pcpedc.codfuncconf || '-' || pcempr.nome "userName" + ,NULL as "comments" + FROM pcpedc, pcempr + WHERE pcpedc.numped = ${orderId} + AND pcpedc.codfuncconf = pcempr.matricula(+) + AND pcpedc.dtinicialcheckout IS NOT NULL + UNION ALL + SELECT pcpedc.numped AS "orderId", + 'Fim conferência' AS "status", + pcpedc.dtfinalcheckout AS "statusDate", + pcpedc.codfuncconf || '-' || pcempr.nome "userName" + ,NULL as "comments" + FROM pcpedc, pcempr + WHERE pcpedc.numped = ${orderId} + AND pcpedc.codfuncconf = pcempr.matricula(+) + AND pcpedc.dtfinalcheckout IS NOT NULL + UNION ALL + SELECT pcpedc.numped AS "orderId", + 'Faturamento' AS "status", + TO_DATE ( + TO_CHAR(pcpedc.dtfat, 'DD/MM/YYYY') || ' ' || pcpedc.horafat || ':' || pcpedc.minutofat, + 'DD/MM/YYYY HH24:MI') + AS "statusDate", + pcempr.matricula || '-' || pcempr.nome "userName" + ,NULL as "comments" + FROM pcpedc, pcempr, pccarreg, pcnfsaid + WHERE pcpedc.numped = ${orderId} + AND pcpedc.numcar = pccarreg.numcar + and pcpedc.numtransvenda = pcnfsaid.numtransvenda + AND nvl(pcnfsaid.codemitente, decode(pcpedc.numcar,0,-1,pccarreg.codfuncfat)) = pcempr.matricula(+) + AND pcpedc.dtfat IS NOT NULL + UNION ALL + SELECT pcpedc.numped AS "orderId", + 'Entrega' AS "status", + pcnfsaid.dtcanhoto AS "statusDate", + CASE WHEN PCNFSAID.NUMCAR > 0 THEN + pcnfsaid.codfunccanhoto || '-' || pcempr.nome + ELSE '' END "userName" + ,NULL as "comments" + FROM pcpedc, pcnfsaid, pcempr, pccarreg + WHERE pcpedc.numped = ${orderId} + AND pcpedc.numtransvenda = pcnfsaid.numtransvenda + AND pcnfsaid.numcar = pccarreg.numcar (+) + AND pcnfsaid.codfunccanhoto = pcempr.matricula(+) + AND pcnfsaid.dtcanhoto IS NOT NULL + UNION ALL + SELECT pcpedc.numped AS "orderId", + 'Transf entre carregamento' AS "status", + pclogtransfnfcarreg.dttransf AS "statusDate", + pclogtransfnfcarreg.codfunctransf || '-' || pcempr.nome "userName", + 'ORIG: '||pclogtransfnfcarreg.numcaranterior || ' -> + DEST.: ' ||pclogtransfnfcarreg.numcaratual as "comments" + FROM pclogtransfnfcarreg, pcpedc, pcempr + WHERE pclogtransfnfcarreg.numnota = pcpedc.numnota (+) + AND pcpedc.numped = ${orderId} + AND pclogtransfnfcarreg.codfunctransf = pcempr.matricula(+) + ORDER BY 3`; + + const result = await queryRunner.manager.query(sql); + return result.length > 0 ? result : null; + } finally { + await queryRunner.release(); + } + } + async createInvoiceCheck(invoice: InvoiceCheckDto): Promise<{ message: string }> { + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction(); + + try { + const sequenceSql = 'SELECT ESSCONFERENCIANF.NEXTVAL as "id" FROM DUAL'; + const result = await queryRunner.manager.query(sequenceSql); + const checkId = result[0].id; + + const sqlMain = ` + INSERT INTO ESTCONFERENCIANF ( + ID, NUMTRANSVENDA, CODFILIAL, NUMNOTA, DTINICIO, DTFIM, CODFUNCCONF + ) VALUES ( + ${checkId}, ${invoice.transactionId}, ${invoice.storeId}, ${invoice.invoiceId}, + TO_DATE(SUBSTR(REPLACE(REPLACE('${invoice.startDate}', 'T', ' '), 'Z', ''),1,18), 'YYYY-MM-DD HH24:MI:SS'), + TO_DATE(SUBSTR(REPLACE(REPLACE('${invoice.endDate}', 'T', ' '), 'Z', ''),1,18), 'YYYY-MM-DD HH24:MI:SS'), + ${invoice.userId} + ) + `; + await queryRunner.manager.query(sqlMain); + + for (const item of invoice.itens) { + const sqlItem = ` + INSERT INTO ESTCONFERENCIANFITENS ( + IDCONF, NUMTRANSVENDA, CODPROD, NUMSEQ, QT, DTCONF + ) VALUES ( + ${checkId}, ${invoice.transactionId}, ${item.productId}, ${item.seq}, ${item.qt}, + TO_DATE(SUBSTR(REPLACE(REPLACE('${item.confDate}', 'T', ' '), 'Z', ''),1,18), 'YYYY-MM-DD HH24:MI:SS') + ) + `; + await queryRunner.manager.query(sqlItem); + } + + await queryRunner.commitTransaction(); + return { message: 'Conferência salva com sucesso!' }; + } catch (error) { + await queryRunner.rollbackTransaction(); + throw new HttpException(error.message, HttpStatus.BAD_REQUEST); + } finally { + await queryRunner.release(); + } + } } \ No newline at end of file