Merge branch 'feature/seller-id-array-and-code-improvements' into feature/deb-query-builder

This commit is contained in:
Joelson
2025-11-05 16:40:24 -03:00
committed by GitHub
16 changed files with 213 additions and 300 deletions

View File

@@ -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,

View File

@@ -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()

View File

@@ -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 {

View File

@@ -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> {

View File

@@ -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'),
});

View File

@@ -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<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,
);
}
}
/**
* 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<DebDto[]> {
return await this.debRepository.findByCpfCgcent(cpfCgcent, matricula, cobranca);
}
}

View File

@@ -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,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
};
if (!query.includeCompletedDeliveries) {
return orders;
}
try {
const deliveries = await this.ordersRepository.getCompletedDeliveries(deliveryQuery);
order.completedDeliveries = deliveries;
} catch (error) {
// Se houver erro, definir como array vazio
order.completedDeliveries = [];
}
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<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,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 (!includeCompletedDeliveries) {
return orderDelivery;
}
if (transactionId) {
const deliveryQuery = {
transactionId: transactionId,
limit: 10,
offset: 0
};
try {
// Buscar entregas realizadas usando o transactionId do pedido
const transactionId = await this.ordersRepository.getOrderTransactionId(orderId);
const deliveries = await this.ordersRepository.getCompletedDeliveriesByTransactionId(deliveryQuery);
orderDelivery.completedDeliveries = deliveries;
} else {
orderDelivery.completedDeliveries = [];
}
} catch (error) {
// Se houver erro, definir como array vazio
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<string, unknown>;
const sortedKeys = Object.keys(objRecord).sort();
const str = JSON.stringify(objRecord, sortedKeys);
return createHash('md5').update(str).digest('hex');
}

View File

@@ -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';
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) {}
@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<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,
);
}
}
@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<DebDto[]> {
return await this.debService.findByCpfCgcent(
query.cpfCgcent,
query.matricula,
query.cobranca,
);
}
}

View File

@@ -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,
);
}
}
}

View File

@@ -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()

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -0,0 +1,6 @@
export interface DebQueryParams {
cpfCgcent: string;
matricula?: number;
}

View 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 {}

View File

@@ -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 {

View File

@@ -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;
}