From 25975fc0b005a6551d844a3d31303936856fc458 Mon Sep 17 00:00:00 2001 From: JurTI-BR Date: Mon, 3 Nov 2025 18:05:14 -0300 Subject: [PATCH] =?UTF-8?q?feat:=20suporte=20a=20m=C3=BAltiplos=20sellerId?= =?UTF-8?q?=20e=20melhorias=20no=20c=C3=B3digo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Adicionado suporte para sellerId como string separada por vírgula (ex: 270,431) - Melhorias em deb.repository: tipagem e documentação - Melhorias em deb.service: remoção de validação redundante - Melhorias em deb.controller: remoção de try/catch duplicado - Melhorias em orders.service: early returns e tipagem melhorada - Aplicado early returns para reduzir aninhamento - Melhorias de tipagem em todos os métodos --- src/app.module.ts | 2 + src/auth/auth/dto/login.dto.ts | 2 - src/auth/services/rate-limiting.service.ts | 8 +- .../configs/cache/redis-client.adapter.ts | 9 +- src/core/configs/cache/redis.provider.ts | 2 +- src/orders/application/deb.service.ts | 46 +++---- src/orders/application/orders.service.ts | 121 ++++++++++-------- src/orders/controllers/deb.controller.ts | 91 +++++++------ src/orders/controllers/orders.controller.ts | 92 ++----------- src/orders/dto/DebDto.ts | 78 +++++++++++ .../dto/find-orders-by-delivery-date.dto.ts | 8 +- src/orders/dto/find-orders.dto.ts | 29 ++--- src/orders/dto/order-response.dto.ts | 52 +------- src/orders/interface/deb.interface.ts | 6 + src/orders/modules/deb.module.ts | 18 +++ src/orders/repositories/deb.repository.ts | 7 +- src/orders/repositories/orders.repository.ts | 20 +-- 17 files changed, 291 insertions(+), 300 deletions(-) create mode 100644 src/orders/dto/DebDto.ts create mode 100644 src/orders/interface/deb.interface.ts create mode 100644 src/orders/modules/deb.module.ts diff --git a/src/app.module.ts b/src/app.module.ts index 6caa18d..9d4fed3 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -13,6 +13,7 @@ import { OccurrencesModule } from './crm/occurrences/occurrences.module'; import { ReasonTableModule } from './crm/reason-table/reason-table.module'; import { NegotiationsModule } from './crm/negotiations/negotiations.module'; import { HttpModule } from '@nestjs/axios'; +import { DebModule } from './orders/modules/deb.module'; import { LogisticController } from './logistic/logistic.controller'; import { LogisticService } from './logistic/logistic.service'; import { LoggerModule } from './Log/logger.module'; @@ -67,6 +68,7 @@ import { PartnersModule } from './partners/partners.module'; LoggerModule, DataConsultModule, AuthModule, + DebModule, OrdersModule, HealthModule, PartnersModule, diff --git a/src/auth/auth/dto/login.dto.ts b/src/auth/auth/dto/login.dto.ts index 91a24a4..6a01a0d 100644 --- a/src/auth/auth/dto/login.dto.ts +++ b/src/auth/auth/dto/login.dto.ts @@ -3,7 +3,6 @@ import { ApiProperty } from '@nestjs/swagger'; export class LoginDto { @ApiProperty({ - example: 'joelson.r', description: 'Usuário de login', }) @IsString() @@ -11,7 +10,6 @@ export class LoginDto { username: string; @ApiProperty({ - example: '1010', description: 'Senha do usuário', }) @IsString() diff --git a/src/auth/services/rate-limiting.service.ts b/src/auth/services/rate-limiting.service.ts index 1918430..9309b03 100644 --- a/src/auth/services/rate-limiting.service.ts +++ b/src/auth/services/rate-limiting.service.ts @@ -11,9 +11,9 @@ export interface RateLimitConfig { @Injectable() export class RateLimitingService { private readonly defaultConfig: RateLimitConfig = { - maxAttempts: 5, // 5 tentativas - windowMs: 15 * 60 * 1000, // 15 minutos - blockDurationMs: 30 * 60 * 1000, // 30 minutos de bloqueio + maxAttempts: 15, // 15 tentativas + windowMs: 1 * 60 * 1000, // 1 minuto + blockDurationMs: 1 * 60 * 1000, // 1 minuto de bloqueio }; constructor( @@ -115,7 +115,7 @@ export class RateLimitingService { const blockKey = this.buildBlockKey(ip); const attempts = await this.redis.get(key); - const isBlocked = await this.redis.get(blockKey); + const isBlocked = await this.redis.get(blockKey); const ttl = await this.redis.ttl(blockKey); return { diff --git a/src/core/configs/cache/redis-client.adapter.ts b/src/core/configs/cache/redis-client.adapter.ts index e10d386..fd0d308 100644 --- a/src/core/configs/cache/redis-client.adapter.ts +++ b/src/core/configs/cache/redis-client.adapter.ts @@ -11,7 +11,14 @@ export class RedisClientAdapter implements IRedisClient { async get(key: string): Promise { const data = await this.redis.get(key); - return data ? JSON.parse(data) : null; + if (!data) return null; + + try { + return JSON.parse(data); + } catch (error) { + // If it's not valid JSON, return the raw string value + return data as T; + } } async set(key: string, value: T, ttlSeconds = 300): Promise { diff --git a/src/core/configs/cache/redis.provider.ts b/src/core/configs/cache/redis.provider.ts index 3d4baff..34e2550 100644 --- a/src/core/configs/cache/redis.provider.ts +++ b/src/core/configs/cache/redis.provider.ts @@ -6,7 +6,7 @@ provide: 'REDIS_CLIENT', useFactory: (configService: ConfigService) => { const redis = new Redis({ - host: configService.get('REDIS_HOST', '10.1.1.124'), + host: configService.get('REDIS_HOST', '10.1.1.109'), port: configService.get('REDIS_PORT', 6379), password: configService.get('REDIS_PASSWORD', '1234'), }); diff --git a/src/orders/application/deb.service.ts b/src/orders/application/deb.service.ts index b1fd695..a60e11b 100644 --- a/src/orders/application/deb.service.ts +++ b/src/orders/application/deb.service.ts @@ -1,34 +1,26 @@ - -import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { DebRepository } from '../repositories/deb.repository'; import { DebDto } from '../dto/DebDto'; @Injectable() export class DebService { - constructor( - private readonly debRepository: DebRepository, - ) {} + constructor( + private readonly debRepository: DebRepository, + ) {} - /** - * Busca débitos por CPF ou CGCENT - * @param cpfCgcent - CPF ou CGCENT do cliente - * @param matricula - Matrícula do funcionário (opcional) - * @param cobranca - Código de cobrança (opcional) - * @returns Lista de débitos do cliente - */ - async findByCpfCgcent(cpfCgcent: string, matricula?: number, cobranca?: string): Promise { - if (!cpfCgcent) { - throw new HttpException('CPF/CGCENT é obrigatório', HttpStatus.BAD_REQUEST); - } - - try { - const result = await this.debRepository.findByCpfCgcent(cpfCgcent, matricula, cobranca); - return result as DebDto[]; - } catch (error) { - throw new HttpException( - error.message || 'Erro ao buscar débitos', - error.status || HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } + /** + * Busca débitos por CPF ou CGCENT + * @param cpfCgcent - CPF ou CGCENT do cliente (validado pelo DTO) + * @param matricula - Matrícula do funcionário (opcional) + * @param cobranca - Código de cobrança (opcional) + * @returns Lista de débitos do cliente + * @throws {Error} Erro ao buscar débitos no banco de dados + */ + async findByCpfCgcent( + cpfCgcent: string, + matricula?: number, + cobranca?: string, + ): Promise { + return await this.debRepository.findByCpfCgcent(cpfCgcent, matricula, cobranca); + } } \ No newline at end of file diff --git a/src/orders/application/orders.service.ts b/src/orders/application/orders.service.ts index 8c65852..c638138 100644 --- a/src/orders/application/orders.service.ts +++ b/src/orders/application/orders.service.ts @@ -1,5 +1,6 @@ import { Injectable, Inject, HttpStatus } from '@nestjs/common'; import { FindOrdersDto } from '../dto/find-orders.dto'; +import { FindOrdersByDeliveryDateDto } from '../dto/find-orders-by-delivery-date.dto'; import { InvoiceDto } from '../dto/find-invoice.dto'; import { CutItemDto } from '../dto/CutItemDto'; import { OrdersRepository } from '../repositories/orders.repository'; @@ -19,20 +20,22 @@ import { MarkData } from '../interface/markdata'; import { EstLogTransferFilterDto, EstLogTransferResponseDto } from '../dto/estlogtransfer.dto'; import { DeliveryCompletedQuery } from '../dto/delivery-completed-query.dto'; import { DeliveryCompleted } from '../dto/delivery-completed.dto'; - +import { OrderResponseDto } from '../dto/order-response.dto'; @Injectable() export class OrdersService { - private readonly TTL_ORDERS = 60; // 60 segundos - private readonly TTL_INVOICE = 60; // 60 segundos - private readonly TTL_ITENS = 60; // 60 segundos - private readonly TTL_LEADTIME = 60; // 60 segundos - private readonly TTL_DELIVERIES = 60; // 60 segundos - private readonly TTL_TRANSFER = 60; // 60 segundos - private readonly TTL_STATUS = 60; // 60 segundos - private readonly TTL_CARRIERS = 60; // 60 segundos - private readonly TTL_MARKS = 60; // 60 segundos - private readonly TTL_COMPLETED_DELIVERIES = 60; // 60 segundos + // Cache TTL em segundos + private static readonly DEFAULT_TTL = 60; + private readonly TTL_ORDERS = OrdersService.DEFAULT_TTL; + private readonly TTL_INVOICE = OrdersService.DEFAULT_TTL; + private readonly TTL_ITENS = OrdersService.DEFAULT_TTL; + private readonly TTL_LEADTIME = OrdersService.DEFAULT_TTL; + private readonly TTL_DELIVERIES = OrdersService.DEFAULT_TTL; + private readonly TTL_TRANSFER = OrdersService.DEFAULT_TTL; + private readonly TTL_STATUS = OrdersService.DEFAULT_TTL; + private readonly TTL_CARRIERS = OrdersService.DEFAULT_TTL; + private readonly TTL_MARKS = OrdersService.DEFAULT_TTL; + private readonly TTL_COMPLETED_DELIVERIES = OrdersService.DEFAULT_TTL; constructor( private readonly ordersRepository: OrdersRepository, @@ -41,8 +44,10 @@ export class OrdersService { /** * Buscar pedidos com cache baseado nos filtros + * @param query - Filtros para busca de pedidos + * @returns Lista de pedidos */ - async findOrders(query: FindOrdersDto) { + async findOrders(query: FindOrdersDto): Promise { const key = `orders:query:${this.hashObject(query)}`; return getOrSetCache( @@ -52,21 +57,23 @@ export class OrdersService { async () => { const orders = await this.ordersRepository.findOrders(query); - if (query.includeCompletedDeliveries) { - for (const order of orders) { - const deliveryQuery = { - orderNumber: order.invoiceNumber, - limit: 10, - offset: 0 - }; - - try { - const deliveries = await this.ordersRepository.getCompletedDeliveries(deliveryQuery); - order.completedDeliveries = deliveries; - } catch (error) { - // Se houver erro, definir como array vazio - order.completedDeliveries = []; - } + if (!query.includeCompletedDeliveries) { + return orders; + } + + for (const order of orders) { + const deliveryQuery = { + orderNumber: order.invoiceNumber, + limit: 10, + offset: 0 + }; + + try { + const deliveries = await this.ordersRepository.getCompletedDeliveries(deliveryQuery); + order.completedDeliveries = deliveries; + } catch (error) { + // Se houver erro, definir como array vazio + order.completedDeliveries = []; } } @@ -77,8 +84,10 @@ export class OrdersService { /** * Buscar pedidos por data de entrega com cache + * @param query - Filtros para busca por data de entrega + * @returns Lista de pedidos */ - async findOrdersByDeliveryDate(query: any) { + async findOrdersByDeliveryDate(query: FindOrdersByDeliveryDateDto): Promise { const key = `orders:delivery:${this.hashObject(query)}`; return getOrSetCache( this.redisClient, @@ -90,8 +99,10 @@ export class OrdersService { /** * Buscar pedidos com resultados de fechamento de caixa + * @param query - Filtros para busca de pedidos + * @returns Lista de pedidos com dados de fechamento de caixa */ - async findOrdersWithCheckout(query: FindOrdersDto) { + async findOrdersWithCheckout(query: FindOrdersDto): Promise<(OrderResponseDto & { checkout: any })[]> { const key = `orders:checkout:${this.hashObject(query)}`; return getOrSetCache( this.redisClient, @@ -236,28 +247,30 @@ export class OrdersService { return null; } - if (includeCompletedDeliveries) { - try { - // Buscar entregas realizadas usando o transactionId do pedido - // Primeiro precisamos obter o NUMTRANSVENDA do pedido - const transactionId = await this.ordersRepository.getOrderTransactionId(orderId); - - if (transactionId) { - const deliveryQuery = { - transactionId: transactionId, - limit: 10, - offset: 0 - }; - - const deliveries = await this.ordersRepository.getCompletedDeliveriesByTransactionId(deliveryQuery); - orderDelivery.completedDeliveries = deliveries; - } else { - orderDelivery.completedDeliveries = []; - } - } catch (error) { - // Se houver erro, definir como array vazio + if (!includeCompletedDeliveries) { + return orderDelivery; + } + + try { + // Buscar entregas realizadas usando o transactionId do pedido + const transactionId = await this.ordersRepository.getOrderTransactionId(orderId); + + if (!transactionId) { orderDelivery.completedDeliveries = []; + return orderDelivery; } + + const deliveryQuery = { + transactionId: transactionId, + limit: 10, + offset: 0 + }; + + const deliveries = await this.ordersRepository.getCompletedDeliveriesByTransactionId(deliveryQuery); + orderDelivery.completedDeliveries = deliveries; + } catch (error) { + // Se houver erro, definir como array vazio + orderDelivery.completedDeliveries = []; } return orderDelivery; @@ -322,10 +335,14 @@ export class OrdersService { } /** - * Utilitário para gerar hash MD5 de objetos + * Utilitário para gerar hash MD5 de objetos para chaves de cache + * @param obj - Objeto a ser serializado e hasheado + * @returns Hash MD5 do objeto serializado */ - private hashObject(obj: any): string { - const str = JSON.stringify(obj, Object.keys(obj).sort()); + private hashObject(obj: unknown): string { + const objRecord = obj as Record; + const sortedKeys = Object.keys(objRecord).sort(); + const str = JSON.stringify(objRecord, sortedKeys); return createHash('md5').update(str).digest('hex'); } diff --git a/src/orders/controllers/deb.controller.ts b/src/orders/controllers/deb.controller.ts index 9f75514..42a81c5 100644 --- a/src/orders/controllers/deb.controller.ts +++ b/src/orders/controllers/deb.controller.ts @@ -1,49 +1,46 @@ import { - Controller, - Get, - Query, - UsePipes, - HttpException, - HttpStatus, - ValidationPipe, - } from '@nestjs/common'; - import { ApiOperation, ApiTags, ApiResponse } from '@nestjs/swagger'; - import { DebService } from '../application/deb.service'; - import { DebDto } from '../dto/DebDto'; - import { FindDebDto } from '../dto/find-deb.dto'; - - @ApiTags('Débitos') - @Controller('api/v1/deb') - export class DebController { - constructor(private readonly debService: DebService) {} - - @Get('find-by-cpf') - @ApiOperation({ - summary: 'Busca débitos por CPF/CGCENT', - description: 'Busca débitos de um cliente usando CPF ou CGCENT. Opcionalmente pode filtrar por matrícula do funcionário ou código de cobrança.', - }) - @ApiResponse({ - status: 200, - description: 'Lista de débitos retornada com sucesso', - type: [DebDto], - }) - @ApiResponse({ - status: 400, - description: 'CPF/CGCENT é obrigatório', - }) - @UsePipes(new ValidationPipe({ transform: true })) - async findByCpfCgcent( - @Query() query: FindDebDto, - ): Promise { - try { - return await this.debService.findByCpfCgcent(query.cpfCgcent, query.matricula, query.cobranca); - } catch (error) { - throw new HttpException( - error.message || 'Erro ao buscar débitos', - error.status || HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - } + Controller, + Get, + Query, + UsePipes, + ValidationPipe, +} from '@nestjs/common'; +import { ApiOperation, ApiTags, ApiResponse } from '@nestjs/swagger'; +import { DebService } from '../application/deb.service'; +import { DebDto } from '../dto/DebDto'; +import { FindDebDto } from '../dto/find-deb.dto'; + +@ApiTags('Débitos') +@Controller('api/v1/deb') +export class DebController { + constructor(private readonly debService: DebService) {} + + @Get('find-by-cpf') + @ApiOperation({ + summary: 'Busca débitos por CPF/CGCENT', + description: 'Busca débitos de um cliente usando CPF ou CGCENT. Opcionalmente pode filtrar por matrícula do funcionário ou código de cobrança.', + }) + @ApiResponse({ + status: 200, + description: 'Lista de débitos retornada com sucesso', + type: [DebDto], + }) + @ApiResponse({ + status: 400, + description: 'CPF/CGCENT é obrigatório', + }) + @ApiResponse({ + status: 500, + description: 'Erro interno do servidor', + }) + @UsePipes(new ValidationPipe({ transform: true })) + async findByCpfCgcent( + @Query() query: FindDebDto, + ): Promise { + return await this.debService.findByCpfCgcent( + query.cpfCgcent, + query.matricula, + query.cobranca, + ); } - - \ No newline at end of file +} diff --git a/src/orders/controllers/orders.controller.ts b/src/orders/controllers/orders.controller.ts index 0ac473e..9928fa0 100644 --- a/src/orders/controllers/orders.controller.ts +++ b/src/orders/controllers/orders.controller.ts @@ -13,7 +13,6 @@ import { HttpStatus, DefaultValuePipe, ParseBoolPipe, - Optional, } from '@nestjs/common'; import { ApiBearerAuth, ApiOperation, ApiTags, ApiQuery, ApiParam, ApiResponse } from '@nestjs/swagger'; import { ResponseInterceptor } from '../../common/response.interceptor'; @@ -34,8 +33,6 @@ import { CarrierDto } from 'src/data-consult/dto/carrier.dto'; import { OrderResponseDto } from '../dto/order-response.dto'; import { MarkResponseDto } from '../dto/mark-response.dto'; import { EstLogTransferResponseDto } from '../dto/estlogtransfer.dto'; -import { DeliveryCompletedQuery } from '../dto/delivery-completed-query.dto'; -import { DeliveryCompleted } from '../dto/delivery-completed.dto'; @ApiTags('Orders') @@ -51,9 +48,6 @@ export class OrdersController { description: 'Busca pedidos com filtros avançados. Suporta filtros por data, cliente, vendedor, status, tipo de entrega e status de transferência.' }) @ApiQuery({ name: 'includeCheckout', required: false, type: 'boolean', description: 'Incluir dados de checkout' }) - @ApiQuery({ name: 'includeCompletedDeliveries', required: false, type: 'boolean', description: 'Incluir dados de entregas realizadas para cada pedido' }) - @ApiQuery({ name: 'limit', required: false, type: 'number', description: 'Limite de registros por página', example: 100 }) - @ApiQuery({ name: 'offset', required: false, type: 'number', description: 'Offset para paginação', example: 0 }) @ApiQuery({ name: 'statusTransfer', required: false, type: 'string', description: 'Filtrar por status de transferência (Em Trânsito, Em Separação, Aguardando Separação, Concluída)' }) @ApiQuery({ name: 'markId', required: false, type: 'number', description: 'ID da marca para filtrar pedidos' }) @ApiQuery({ name: 'markName', required: false, type: 'string', description: 'Nome da marca para filtrar pedidos (busca parcial)' }) @@ -90,7 +84,7 @@ export class OrdersController { @Get(':orderId/checkout') @ApiOperation({ summary: 'Busca fechamento de caixa para um pedido' }) - @ApiParam({ name: 'orderId', example: 236001388 }) + @ApiParam({ name: 'orderId' }) @UsePipes(new ValidationPipe({ transform: true })) getOrderCheckout( @Param('orderId', ParseIntPipe) orderId: number, @@ -104,7 +98,6 @@ export class OrdersController { name: 'chavenfe', required: true, description: 'Chave da Nota Fiscal (44 dígitos)', - example: '35191234567890000123550010000000011000000010', }) @ApiOperation({ summary: 'Busca NF pela chave' }) @@ -122,7 +115,7 @@ export class OrdersController { @Get('itens/:orderId') @ApiOperation({ summary: 'Busca PELO numero do pedido' }) - @ApiParam({ name: 'orderId', example: '236001388' }) + @ApiParam({ name: 'orderId' }) @UsePipes(new ValidationPipe({ transform: true })) async getItens(@Param('orderId', ParseIntPipe) orderId: number): Promise { try { @@ -136,7 +129,7 @@ export class OrdersController { } @Get('cut-itens/:orderId') @ApiOperation({ summary: 'Busca itens cortados do pedido' }) - @ApiParam({ name: 'orderId', example: '236001388' }) + @ApiParam({ name: 'orderId' }) @UsePipes(new ValidationPipe({ transform: true })) async getCutItens(@Param('orderId', ParseIntPipe) orderId: number): Promise { try { @@ -151,16 +144,11 @@ export class OrdersController { @Get('delivery/:orderId') @ApiOperation({ summary: 'Busca dados de entrega do pedido' }) - @ApiParam({ name: 'orderId', example: '236001388' }) - @ApiQuery({ name: 'includeCompletedDeliveries', required: false, type: 'boolean', description: 'Incluir dados de entregas realizadas para o pedido' }) + @ApiParam({ name: 'orderId' }) @UsePipes(new ValidationPipe({ transform: true })) - async getOrderDelivery( - @Param('orderId', ParseIntPipe) orderId: number, - @Query('includeCompletedDeliveries', new DefaultValuePipe(false), ParseBoolPipe) - includeCompletedDeliveries: boolean, - ): Promise { + async getOrderDelivery(@Param('orderId', ParseIntPipe) orderId: number): Promise { try { - return await this.ordersService.getOrderDelivery(orderId.toString(), includeCompletedDeliveries); + return await this.ordersService.getOrderDelivery(orderId.toString()); } catch (error) { throw new HttpException( error.message || 'Erro ao buscar dados de entrega', @@ -171,7 +159,7 @@ export class OrdersController { @Get('transfer/:orderId') @ApiOperation({ summary: 'Consulta pedidos de transferência' }) - @ApiParam({ name: 'orderId', example: 236001388 }) + @ApiParam({ name: 'orderId' }) @UsePipes(new ValidationPipe({ transform: true })) async getTransfer(@Param('orderId', ParseIntPipe) orderId: number): Promise { try { @@ -186,7 +174,7 @@ export class OrdersController { @Get('status/:orderId') @ApiOperation({ summary: 'Consulta status do pedido' }) - @ApiParam({ name: 'orderId', example: 236001388 }) + @ApiParam({ name: 'orderId' }) @UsePipes(new ValidationPipe({ transform: true })) async getStatusOrder(@Param('orderId', ParseIntPipe) orderId: number): Promise { try { @@ -202,7 +190,7 @@ export class OrdersController { @Get(':orderId/deliveries') @ApiOperation({ summary: 'Consulta entregas do pedido' }) - @ApiParam({ name: 'orderId', example: '236001388' }) + @ApiParam({ name: 'orderId' }) @ApiQuery({ name: 'createDateIni', required: false, description: 'Data inicial para filtro (formato YYYY-MM-DD)' }) @ApiQuery({ name: 'createDateEnd', required: false, description: 'Data final para filtro (formato YYYY-MM-DD)' }) async getOrderDeliveries( @@ -223,7 +211,7 @@ export class OrdersController { @Get('leadtime/:orderId') @ApiOperation({ summary: 'Consulta leadtime do pedido' }) - @ApiParam({ name: 'orderId', example: '236001388' }) + @ApiParam({ name: 'orderId' }) @UsePipes(new ValidationPipe({ transform: true })) async getLeadtime(@Param('orderId', ParseIntPipe) orderId: number): Promise { try { @@ -315,7 +303,7 @@ async getMarksByName(@Query('name') markName: string): Promise Boolean) @IsBoolean() @ApiPropertyOptional({ - description: 'Filtrar pedidos que tenham registros na tabela de transfer log', - example: true + description: 'Filtrar pedidos que tenham registros na tabela de transfer log' }) hasPreBox?: boolean; @IsOptional() @IsString() @ApiPropertyOptional({ - description: 'Código da filial de origem da transferência (Pre-Box)', - example: '5' + description: 'Código da filial de origem da transferência (Pre-Box)' }) preBoxFilial?: string; @IsOptional() @IsString() @ApiPropertyOptional({ - description: 'Código da filial de destino da transferência', - example: '6' + description: 'Código da filial de destino da transferência' }) transferDestFilial?: string; @IsOptional() @IsDateString() @ApiPropertyOptional({ - description: 'Data de transferência inicial (formato YYYY-MM-DD)', - example: '2024-01-01' + description: 'Data de transferência inicial (formato YYYY-MM-DD)' }) transferDateIni?: string; @IsOptional() @IsDateString() @ApiPropertyOptional({ - description: 'Data de transferência final (formato YYYY-MM-DD)', - example: '2024-12-31' + description: 'Data de transferência final (formato YYYY-MM-DD)' }) transferDateEnd?: string; @@ -223,8 +217,7 @@ sellerName?: string; @Type(() => Boolean) @IsBoolean() @ApiPropertyOptional({ - description: 'Incluir dados de entregas realizadas para cada pedido', - example: false + description: 'Incluir dados de entregas realizadas para cada pedido' }) includeCompletedDeliveries?: boolean; @@ -232,8 +225,7 @@ sellerName?: string; @Type(() => Number) @IsNumber() @ApiPropertyOptional({ - description: 'Limite de registros por página', - example: 100 + description: 'Limite de registros por página' }) limit?: number; @@ -241,8 +233,7 @@ sellerName?: string; @Type(() => Number) @IsNumber() @ApiPropertyOptional({ - description: 'Offset para paginação', - example: 0 + description: 'Offset para paginação' }) offset?: number; } diff --git a/src/orders/dto/order-response.dto.ts b/src/orders/dto/order-response.dto.ts index f7a1b14..1f80baf 100644 --- a/src/orders/dto/order-response.dto.ts +++ b/src/orders/dto/order-response.dto.ts @@ -3,308 +3,258 @@ import { ApiProperty } from '@nestjs/swagger'; export class OrderResponseDto { @ApiProperty({ description: 'Data de criação do pedido', - example: '2024-04-02T10:00:00Z', }) createDate: Date; @ApiProperty({ description: 'ID da loja', - example: '001 - Pre-Box (002)', }) storeId: string; @ApiProperty({ description: 'ID do pedido', - example: 12345, }) orderId: number; @ApiProperty({ description: 'ID do cliente', - example: '12345', }) customerId: string; @ApiProperty({ description: 'Nome do cliente', - example: '12345 - João da Silva', }) customerName: string; @ApiProperty({ description: 'ID do vendedor', - example: '001', }) sellerId: string; @ApiProperty({ description: 'Nome do vendedor', - example: '001 - Maria Santos', }) sellerName: string; @ApiProperty({ description: 'Nome da loja', - example: 'Loja Centro', }) store: string; @ApiProperty({ description: 'Tipo de entrega', - example: 'Entrega (EN)', }) deliveryType: string; @ApiProperty({ description: 'Local de entrega', - example: '001-Centro', }) deliveryLocal: string; @ApiProperty({ description: 'Local de entrega principal', - example: '001-Rota Centro', }) masterDeliveryLocal: string; @ApiProperty({ description: 'Tipo do pedido', - example: 'TV8 - Entrega (EN)', }) orderType: string; @ApiProperty({ description: 'Valor total do pedido', - example: 1000.00, }) amount: number; @ApiProperty({ description: 'Data de entrega', - example: '2024-04-05T10:00:00Z', }) deliveryDate: Date; @ApiProperty({ description: 'Prioridade de entrega', - example: 'Alta', }) deliveryPriority: string; @ApiProperty({ description: 'ID do carregamento', - example: 123, }) shipmentId: number; @ApiProperty({ description: 'Data de liberação', - example: '2024-04-02T10:00:00Z', }) releaseDate: Date; @ApiProperty({ description: 'Usuário de liberação', - example: '001', }) releaseUser: string; @ApiProperty({ description: 'Nome do usuário de liberação', - example: '001 - João Silva', }) releaseUserName: string; @ApiProperty({ description: 'Data de saída do carregamento', - example: '2024-04-03T10:00:00Z', }) shipmentDate: Date; @ApiProperty({ description: 'Data de criação do carregamento', - example: '2024-04-02T10:00:00Z', }) shipmentDateCreate: Date; @ApiProperty({ description: 'Data de fechamento do carregamento', - example: '2024-04-03T18:00:00Z', }) shipmentCloseDate: Date; @ApiProperty({ description: 'ID do plano de pagamento', - example: '001', }) paymentId: string; @ApiProperty({ description: 'Nome do plano de pagamento', - example: 'Cartão de Crédito', }) paymentName: string; @ApiProperty({ description: 'ID da cobrança', - example: '001', }) billingId: string; @ApiProperty({ description: 'Nome da cobrança', - example: '001 - Cartão de Crédito', }) billingName: string; @ApiProperty({ description: 'Data de faturamento', - example: '2024-04-02T10:00:00Z', }) invoiceDate: Date; @ApiProperty({ description: 'Hora de faturamento', - example: 10, }) invoiceHour: number; @ApiProperty({ description: 'Minuto de faturamento', - example: 30, }) invoiceMinute: number; @ApiProperty({ description: 'Número da nota fiscal', - example: 123456, }) invoiceNumber: number; @ApiProperty({ description: 'Descrição do bloqueio', - example: 'Cliente com restrição', }) BloqDescription: string; @ApiProperty({ description: 'Data de confirmação de entrega', - example: '2024-04-05T15:00:00Z', }) confirmDeliveryDate: Date; @ApiProperty({ description: 'Peso total', - example: 50.5, }) totalWeigth: number; @ApiProperty({ description: 'Processo do pedido', - example: 3, }) processOrder: number; @ApiProperty({ description: 'Status do pedido', - example: 'FATURADO', }) status: string; @ApiProperty({ description: 'Pagamento', - example: 0, }) payment: number; @ApiProperty({ description: 'Motorista', - example: '001 - João Silva', }) driver: string; @ApiProperty({ description: 'ID do pedido de venda', - example: 12346, }) orderSaleId: number; @ApiProperty({ description: 'Descrição do carro', - example: 'Fiat Fiorino (ABC1234)', }) carDescription: string; @ApiProperty({ description: 'Identificação do carro', - example: 'ABC1234', }) carIdentification: string; @ApiProperty({ description: 'Transportadora', - example: '001 - Transportadora XYZ', }) carrier: string; @ApiProperty({ description: 'Status da transferência', - example: 'Em Trânsito', nullable: true, }) statusTransfer: string | null; @ApiProperty({ description: 'Loja Pre-Box', - example: '002', }) storePreBox: string; @ApiProperty({ description: 'Código do emitente da nota fiscal', - example: 32, nullable: true, }) codEmitente: number | null; @ApiProperty({ description: 'Matrícula do funcionário emitente', - example: 32, nullable: true, }) emitenteMatricula: number | null; @ApiProperty({ description: 'Nome do funcionário emitente', - example: 'João Silva', nullable: true, }) emitenteNome: string | null; @ApiProperty({ description: 'Código do funcionário de faturamento', - example: 1336, nullable: true, }) fatUserCode: number | null; @ApiProperty({ description: 'Nome do funcionário de faturamento', - example: 'ADRIANO COSTA DA SILVA', nullable: true, }) fatUserName: string | null; @ApiProperty({ description: 'Descrição completa do funcionário de faturamento', - example: '1336-ADRIANO COSTA DA SILVA', nullable: true, }) fatUserDescription: string | null; @ApiProperty({ description: 'Entrega agendada', - example: 'ENTREGA NORMAL', }) schedulerDelivery: string; -} \ No newline at end of file +} diff --git a/src/orders/interface/deb.interface.ts b/src/orders/interface/deb.interface.ts new file mode 100644 index 0000000..62a3dd7 --- /dev/null +++ b/src/orders/interface/deb.interface.ts @@ -0,0 +1,6 @@ +export interface DebQueryParams { + cpfCgcent: string; + matricula?: number; + } + + \ No newline at end of file diff --git a/src/orders/modules/deb.module.ts b/src/orders/modules/deb.module.ts new file mode 100644 index 0000000..5270883 --- /dev/null +++ b/src/orders/modules/deb.module.ts @@ -0,0 +1,18 @@ +import { Module } from '@nestjs/common'; +import { DebController } from '../controllers/deb.controller'; +import { DebService } from '../application/deb.service'; +import { DebRepository } from '../repositories/deb.repository'; +import { DatabaseModule } from '../../core/database/database.module'; +import { ConfigModule } from '@nestjs/config'; + +@Module({ + imports: [ + ConfigModule, + DatabaseModule, + ], + controllers: [DebController], + providers: [DebService, DebRepository], + exports: [DebService], +}) +export class DebModule {} + diff --git a/src/orders/repositories/deb.repository.ts b/src/orders/repositories/deb.repository.ts index 4184a4e..aac6875 100644 --- a/src/orders/repositories/deb.repository.ts +++ b/src/orders/repositories/deb.repository.ts @@ -1,7 +1,7 @@ - -import { Injectable } from '@nestjs/common'; + import { Injectable } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { InjectDataSource } from '@nestjs/typeorm'; +import { DebDto } from '../dto/DebDto'; @Injectable() export class DebRepository { @@ -15,8 +15,9 @@ export class DebRepository { * @param matricula - Matrícula do funcionário (opcional) * @param cobranca - Código de cobrança (opcional) * @returns Lista de débitos do cliente + * @throws {Error} Erro ao executar a query no banco de dados */ - async findByCpfCgcent(cpfCgcent: string, matricula?: number, cobranca?: string) { + async findByCpfCgcent(cpfCgcent: string, matricula?: number, cobranca?: string): Promise { const queryRunner = this.oracleDataSource.createQueryRunner(); await queryRunner.connect(); try { diff --git a/src/orders/repositories/orders.repository.ts b/src/orders/repositories/orders.repository.ts index a0f529f..65564c9 100644 --- a/src/orders/repositories/orders.repository.ts +++ b/src/orders/repositories/orders.repository.ts @@ -403,7 +403,12 @@ WHERE ); } if (query.sellerId) { - conditions.push(`AND PCPEDC.CODUSUR = :sellerId`); + const sellerIds = query.sellerId + .split(",") + .map((s) => s.trim()) + .filter((s) => s) + .join(","); + conditions.push(`AND PCPEDC.CODUSUR IN (${sellerIds})`); } if (query.customerId) { conditions.push(`AND PCPEDC.CODCLI = :customerId`); @@ -572,9 +577,6 @@ WHERE if (query.filialretira) { parameters.storeStockId = query.filialretira; } - if (query.sellerId) { - parameters.sellerId = query.sellerId; - } if (query.customerId) { parameters.customerId = query.customerId; } @@ -805,7 +807,12 @@ WHERE conditions.push(`AND PCPEDC.CODFILIAL = :storeId`); } if (query.sellerId) { - conditions.push(`AND PCPEDC.CODUSUR = :sellerId`); + const sellerIds = query.sellerId + .split(",") + .map((s) => s.trim()) + .filter((s) => s) + .join(","); + conditions.push(`AND PCPEDC.CODUSUR IN (${sellerIds})`); } if (query.customerId) { conditions.push(`AND PCPEDC.CODCLI = :customerId`); @@ -889,9 +896,6 @@ WHERE if (query.codfilial) { parameters.storeId = query.codfilial; } - if (query.sellerId) { - parameters.sellerId = query.sellerId; - } if (query.customerId) { parameters.customerId = query.customerId; }