Merge branch 'feature/seller-id-array-and-code-improvements' into feature/deb-query-builder
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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<string>(key);
|
||||
const isBlocked = await this.redis.get(blockKey);
|
||||
const isBlocked = await this.redis.get<string>(blockKey);
|
||||
const ttl = await this.redis.ttl(blockKey);
|
||||
|
||||
return {
|
||||
|
||||
@@ -11,7 +11,14 @@ export class RedisClientAdapter implements IRedisClient {
|
||||
|
||||
async get<T>(key: string): Promise<T | null> {
|
||||
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<T>(key: string, value: T, ttlSeconds = 300): Promise<void> {
|
||||
|
||||
2
src/core/configs/cache/redis.provider.ts
vendored
2
src/core/configs/cache/redis.provider.ts
vendored
@@ -6,7 +6,7 @@
|
||||
provide: 'REDIS_CLIENT',
|
||||
useFactory: (configService: ConfigService) => {
|
||||
const redis = new Redis({
|
||||
host: configService.get<string>('REDIS_HOST', '10.1.1.124'),
|
||||
host: configService.get<string>('REDIS_HOST', '10.1.1.109'),
|
||||
port: configService.get<number>('REDIS_PORT', 6379),
|
||||
password: configService.get<string>('REDIS_PASSWORD', '1234'),
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { DebRepository } from '../repositories/deb.repository';
|
||||
import { DebDto } from '../dto/DebDto';
|
||||
|
||||
@@ -11,24 +10,17 @@ export class DebService {
|
||||
|
||||
/**
|
||||
* Busca débitos por CPF ou CGCENT
|
||||
* @param cpfCgcent - CPF ou CGCENT do cliente
|
||||
* @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<DebDto[]> {
|
||||
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,
|
||||
);
|
||||
}
|
||||
async findByCpfCgcent(
|
||||
cpfCgcent: string,
|
||||
matricula?: number,
|
||||
cobranca?: string,
|
||||
): Promise<DebDto[]> {
|
||||
return await this.debRepository.findByCpfCgcent(cpfCgcent, matricula, cobranca);
|
||||
}
|
||||
}
|
||||
@@ -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<OrderResponseDto[]> {
|
||||
const key = `orders:query:${this.hashObject(query)}`;
|
||||
|
||||
return getOrSetCache(
|
||||
@@ -52,7 +57,10 @@ export class OrdersService {
|
||||
async () => {
|
||||
const orders = await this.ordersRepository.findOrders(query);
|
||||
|
||||
if (query.includeCompletedDeliveries) {
|
||||
if (!query.includeCompletedDeliveries) {
|
||||
return orders;
|
||||
}
|
||||
|
||||
for (const order of orders) {
|
||||
const deliveryQuery = {
|
||||
orderNumber: order.invoiceNumber,
|
||||
@@ -68,7 +76,6 @@ export class OrdersService {
|
||||
order.completedDeliveries = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return orders;
|
||||
},
|
||||
@@ -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<OrderResponseDto[]> {
|
||||
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,13 +247,19 @@ export class OrdersService {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (includeCompletedDeliveries) {
|
||||
if (!includeCompletedDeliveries) {
|
||||
return orderDelivery;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!transactionId) {
|
||||
orderDelivery.completedDeliveries = [];
|
||||
return orderDelivery;
|
||||
}
|
||||
|
||||
const deliveryQuery = {
|
||||
transactionId: transactionId,
|
||||
limit: 10,
|
||||
@@ -251,14 +268,10 @@ export class OrdersService {
|
||||
|
||||
const deliveries = await this.ordersRepository.getCompletedDeliveriesByTransactionId(deliveryQuery);
|
||||
orderDelivery.completedDeliveries = deliveries;
|
||||
} else {
|
||||
orderDelivery.completedDeliveries = [];
|
||||
}
|
||||
} 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<string, unknown>;
|
||||
const sortedKeys = Object.keys(objRecord).sort();
|
||||
const str = JSON.stringify(objRecord, sortedKeys);
|
||||
return createHash('md5').update(str).digest('hex');
|
||||
}
|
||||
|
||||
|
||||
@@ -3,18 +3,16 @@ import {
|
||||
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';
|
||||
} 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 {
|
||||
@ApiTags('Débitos')
|
||||
@Controller('api/v1/deb')
|
||||
export class DebController {
|
||||
constructor(private readonly debService: DebService) {}
|
||||
|
||||
@Get('find-by-cpf')
|
||||
@@ -31,19 +29,18 @@ import {
|
||||
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<DebDto[]> {
|
||||
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,
|
||||
return await this.debService.findByCpfCgcent(
|
||||
query.cpfCgcent,
|
||||
query.matricula,
|
||||
query.cobranca,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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<OrderItemDto[]> {
|
||||
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<CutItemDto[]> {
|
||||
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<OrderDeliveryDto | null> {
|
||||
async getOrderDelivery(@Param('orderId', ParseIntPipe) orderId: number): Promise<OrderDeliveryDto | null> {
|
||||
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<OrderTransferDto[] | null> {
|
||||
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<OrderStatusDto[] | null> {
|
||||
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<LeadtimeDto[]> {
|
||||
try {
|
||||
@@ -315,7 +303,7 @@ async getMarksByName(@Query('name') markName: string): Promise<MarkResponseDto[]
|
||||
|
||||
@Get('transfer-log/:orderId')
|
||||
@ApiOperation({ summary: 'Busca log de transferência por ID do pedido' })
|
||||
@ApiParam({ name: 'orderId', example: 153068638, description: 'ID do pedido para buscar log de transferência' })
|
||||
@ApiParam({ name: 'orderId', description: 'ID do pedido para buscar log de transferência' })
|
||||
@ApiQuery({ name: 'dttransf', required: false, type: 'string', description: 'Data de transferência (formato YYYY-MM-DD)' })
|
||||
@ApiQuery({ name: 'codfilial', required: false, type: 'number', description: 'Código da filial de origem' })
|
||||
@ApiQuery({ name: 'codfilialdest', required: false, type: 'number', description: 'Código da filial de destino' })
|
||||
@@ -391,62 +379,4 @@ async getTransferLogs(
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Get('completed-deliveries')
|
||||
@ApiOperation({
|
||||
summary: 'Busca entregas realizadas',
|
||||
description: 'Busca entregas realizadas com filtros opcionais por data, cliente, motorista, status e outros critérios.'
|
||||
})
|
||||
@ApiQuery({ name: 'startDate', required: false, type: 'string', description: 'Data de início (formato YYYY-MM-DD)' })
|
||||
@ApiQuery({ name: 'endDate', required: false, type: 'string', description: 'Data de fim (formato YYYY-MM-DD)' })
|
||||
@ApiQuery({ name: 'outId', required: false, type: 'number', description: 'ID da saída' })
|
||||
@ApiQuery({ name: 'driverName', required: false, type: 'string', description: 'Nome do motorista' })
|
||||
@ApiQuery({ name: 'customerId', required: false, type: 'number', description: 'ID do cliente' })
|
||||
@ApiQuery({ name: 'customerName', required: false, type: 'string', description: 'Nome do cliente' })
|
||||
@ApiQuery({ name: 'orderNumber', required: false, type: 'string', description: 'Número da nota fiscal' })
|
||||
@ApiQuery({ name: 'status', required: false, type: 'string', description: 'Status da entrega' })
|
||||
@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 })
|
||||
@UsePipes(new ValidationPipe({ transform: true }))
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'Entregas realizadas encontradas com sucesso',
|
||||
type: [DeliveryCompleted]
|
||||
})
|
||||
@ApiResponse({ status: 400, description: 'Parâmetros inválidos' })
|
||||
@ApiResponse({ status: 500, description: 'Erro interno do servidor' })
|
||||
async getCompletedDeliveries(
|
||||
@Query('startDate') startDate?: string,
|
||||
@Query('endDate') endDate?: string,
|
||||
@Query('outId') outId?: string,
|
||||
@Query('driverName') driverName?: string,
|
||||
@Query('customerId') customerId?: string,
|
||||
@Query('customerName') customerName?: string,
|
||||
@Query('orderNumber') orderNumber?: string,
|
||||
@Query('status') status?: string,
|
||||
@Query('limit', new DefaultValuePipe(100), ParseIntPipe) limit: number = 100,
|
||||
@Query('offset', new DefaultValuePipe(0), ParseIntPipe) offset: number = 0,
|
||||
) {
|
||||
try {
|
||||
const query: DeliveryCompletedQuery = {
|
||||
startDate,
|
||||
endDate,
|
||||
outId: outId ? parseInt(outId, 10) : undefined,
|
||||
driverName,
|
||||
customerId: customerId ? parseInt(customerId, 10) : undefined,
|
||||
customerName,
|
||||
orderNumber,
|
||||
status,
|
||||
limit,
|
||||
offset,
|
||||
};
|
||||
|
||||
return await this.ordersService.getCompletedDeliveries(query);
|
||||
} catch (error) {
|
||||
throw new HttpException(
|
||||
error.message || 'Erro ao buscar entregas realizadas',
|
||||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,12 +38,12 @@ export class FindOrdersByDeliveryDateDto {
|
||||
codfilial?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@IsString()
|
||||
@ApiPropertyOptional({
|
||||
description: 'ID do vendedor',
|
||||
example: 123
|
||||
description: 'ID do vendedor (separado por vírgula para múltiplos valores)',
|
||||
example: '270,431'
|
||||
})
|
||||
sellerId?: number;
|
||||
sellerId?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
|
||||
@@ -66,10 +66,10 @@ export class FindOrdersDto {
|
||||
stockId?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@IsString()
|
||||
@ApiPropertyOptional()
|
||||
|
||||
sellerId?: number;
|
||||
sellerId?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@@ -158,7 +158,6 @@ sellerName?: string;
|
||||
@IsString()
|
||||
@ApiPropertyOptional({
|
||||
description: 'Filtrar por status de transferência',
|
||||
example: 'Em Trânsito,Em Separação,Aguardando Separação,Concluída',
|
||||
enum: ['Em Trânsito', 'Em Separação', 'Aguardando Separação', 'Concluída']
|
||||
})
|
||||
statusTransfer?: string;
|
||||
@@ -182,40 +181,35 @@ sellerName?: string;
|
||||
@Type(() => 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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
6
src/orders/interface/deb.interface.ts
Normal file
6
src/orders/interface/deb.interface.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface DebQueryParams {
|
||||
cpfCgcent: string;
|
||||
matricula?: number;
|
||||
}
|
||||
|
||||
|
||||
18
src/orders/modules/deb.module.ts
Normal file
18
src/orders/modules/deb.module.ts
Normal file
@@ -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 {}
|
||||
|
||||
@@ -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<DebDto[]> {
|
||||
const queryRunner = this.oracleDataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
try {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user