ajuste orders

This commit is contained in:
Joelson
2025-09-25 22:31:29 -03:00
parent 7b5fa2e78d
commit 893a7c5b0a
7 changed files with 432 additions and 9 deletions

View File

@@ -50,15 +50,13 @@ async function bootstrap() {
);
app.enableCors({
origin: process.env.NODE_ENV === 'production'
? ['https://www.jurunense.com', 'https://*.jurunense.com']
: ['http://localhost:9602 add '],
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
origin: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
credentials: true,
allowedHeaders: ['Content-Type', 'Authorization', 'Accept'],
maxAge: 3600,
});
const config = new DocumentBuilder()
.setTitle('Portal Jurunense API')
.setDescription('Documentação da API do Portal Jurunense')

View File

@@ -13,6 +13,7 @@ import {
HttpStatus,
DefaultValuePipe,
ParseBoolPipe,
Optional,
} from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiTags, ApiQuery, ApiParam, ApiResponse } from '@nestjs/swagger';
import { ResponseInterceptor } from '../../common/response.interceptor';
@@ -33,11 +34,13 @@ 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')
//@ApiBearerAuth()
//@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@Controller('api/v1/orders')
export class OrdersController {
constructor(private readonly ordersService: OrdersService) {}
@@ -48,6 +51,9 @@ 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)' })
@@ -146,10 +152,15 @@ 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' })
@UsePipes(new ValidationPipe({ transform: true }))
async getOrderDelivery(@Param('orderId', ParseIntPipe) orderId: number): Promise<OrderDeliveryDto | null> {
async getOrderDelivery(
@Param('orderId', ParseIntPipe) orderId: number,
@Query('includeCompletedDeliveries', new DefaultValuePipe(false), ParseBoolPipe)
includeCompletedDeliveries: boolean,
): Promise<OrderDeliveryDto | null> {
try {
return await this.ordersService.getOrderDelivery(orderId.toString());
return await this.ordersService.getOrderDelivery(orderId.toString(), includeCompletedDeliveries);
} catch (error) {
throw new HttpException(
error.message || 'Erro ao buscar dados de entrega',
@@ -380,4 +391,62 @@ 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

@@ -1,3 +1,5 @@
import { DeliveryCompleted } from './delivery-completed.dto';
export class OrderDeliveryDto {
placeId: number;
placeName: string;
@@ -24,5 +26,6 @@ export class OrderDeliveryDto {
separatorName: string;
confName: string;
releaseDate: Date;
completedDeliveries?: DeliveryCompleted[];
}

View File

@@ -0,0 +1,54 @@
import { IsOptional, IsString, IsNumber, IsDateString } from 'class-validator';
import { ApiPropertyOptional } from '@nestjs/swagger';
export class DeliveryCompletedQuery {
@ApiPropertyOptional({ description: 'Data de início para filtro (formato YYYY-MM-DD)' })
@IsOptional()
@IsDateString()
startDate?: string;
@ApiPropertyOptional({ description: 'Data de fim para filtro (formato YYYY-MM-DD)' })
@IsOptional()
@IsDateString()
endDate?: string;
@ApiPropertyOptional({ description: 'ID da saída' })
@IsOptional()
@IsNumber()
outId?: number;
@ApiPropertyOptional({ description: 'Nome do motorista' })
@IsOptional()
@IsString()
driverName?: string;
@ApiPropertyOptional({ description: 'ID do cliente' })
@IsOptional()
@IsNumber()
customerId?: number;
@ApiPropertyOptional({ description: 'Nome do cliente' })
@IsOptional()
@IsString()
customerName?: string;
@ApiPropertyOptional({ description: 'Número da nota fiscal' })
@IsOptional()
@IsString()
orderNumber?: string;
@ApiPropertyOptional({ description: 'Status da entrega' })
@IsOptional()
@IsString()
status?: string;
@ApiPropertyOptional({ description: 'Limite de registros por página', default: 100 })
@IsOptional()
@IsNumber()
limit?: number;
@ApiPropertyOptional({ description: 'Offset para paginação', default: 0 })
@IsOptional()
@IsNumber()
offset?: number;
}

View File

@@ -0,0 +1,23 @@
export class DeliveryCompleted {
outId: number;
transactionId: number;
deliveryDate: Date;
invoiceNumber: string;
customerId: number;
customerName: string;
deliveryDoc: string;
deliveryName: string;
lat: number;
lng: number;
existBreakdown: string;
existReturn: string;
obsReturn: string;
dataCanhoto: Date;
transactionIdInvoice: number;
invoiceUserId: number;
invoiceUserName: string;
deliveryStatus: string;
driverId: number;
driverName: string;
urlImages: string[];
}

View File

@@ -218,4 +218,31 @@ sellerName?: string;
example: '2024-12-31'
})
transferDateEnd?: string;
@IsOptional()
@Type(() => Boolean)
@IsBoolean()
@ApiPropertyOptional({
description: 'Incluir dados de entregas realizadas para cada pedido',
example: false
})
includeCompletedDeliveries?: boolean;
@IsOptional()
@Type(() => Number)
@IsNumber()
@ApiPropertyOptional({
description: 'Limite de registros por página',
example: 100
})
limit?: number;
@IsOptional()
@Type(() => Number)
@IsNumber()
@ApiPropertyOptional({
description: 'Offset para paginação',
example: 0
})
offset?: number;
}

View File

@@ -11,6 +11,8 @@ import { OrderStatusDto } from '../dto/OrderStatusDto';
import { InvoiceCheckDto } from '../dto/invoice-check.dto';
import { LeadtimeDto } from "../dto/leadtime.dto";
import { MarkData } from "../interface/markdata";
import { DeliveryCompletedQuery } from "../dto/delivery-completed-query.dto";
import { DeliveryCompleted } from "../dto/delivery-completed.dto";
@Injectable()
@@ -1569,4 +1571,251 @@ WHERE
`;
return await this.oracleDataSource.query(sql, [markName]);
}
/**
* Busca o NUMTRANSVENDA de um pedido específico
* @param orderId - ID do pedido
* @returns NUMTRANSVENDA do pedido ou null se não encontrado
*/
async getOrderTransactionId(orderId: string): Promise<number | null> {
const sql = `
SELECT NUMTRANSVENDA
FROM PCPEDC
WHERE NUMPED = :1
`;
const result = await this.oracleDataSource.query(sql, [orderId]);
return result.length > 0 ? result[0].NUMTRANSVENDA : null;
}
/**
* Busca entregas realizadas por transactionId
* @param query - Filtros para a consulta de entregas realizadas incluindo transactionId
* @returns Lista de entregas realizadas
*/
async getCompletedDeliveriesByTransactionId(query: { transactionId: number; limit: number; offset: number }): Promise<DeliveryCompleted[]> {
const sql = `
SELECT
ESTENTREGAS.CODSAIDA AS "outId"
,ESTENTREGAS.NUMTRANSVENDA AS "transactionId"
,ESTENTREGAS.DATA AS "deliveryDate"
,PCNFSAID.NUMNOTA AS "invoiceNumber"
,PCNFSAID.CODCLI AS "customerId"
,PCCLIENT.CLIENTE AS "customerName"
,ESTENTREGAS.DOCUMENTORECEBEDOR AS "deliveryDoc"
,ESTENTREGAS.NOMERECEBEDOR AS "deliveryName"
,ESTENTREGAS.LAT AS "lat"
,ESTENTREGAS.LNG AS "lng"
,ESTENTREGAS.AVARIA AS "existBreakdown"
,ESTENTREGAS.DEVOLUCAO AS "existReturn"
,ESTENTREGAS.MOTIVODEVOLUCAO AS "obsReturn"
,PCNFSAID.DTCANHOTO AS "dataCanhoto"
,PCNFSAID.NUMTRANSVENDA AS "transactionIdInvoice"
,PCNFSAID.CODFUNCCANHOTO AS "invoiceUserId"
,PCEMPR.NOME AS "invoiceUserName"
,ESTSITUACAOENTREGA.SITUACAO AS "deliveryStatus"
,PCCARREG.CODMOTORISTA AS "driverId"
,MOTORISTA.NOME AS "driverName"
FROM ESTENTREGAS, PCNFSAID, PCCLIENT, PCEMPR, ESTSITUACAOENTREGA, ESTSAIDAVEICULOCARREG, PCCARREG, PCEMPR MOTORISTA
WHERE ESTENTREGAS.NUMTRANSVENDA = PCNFSAID.NUMTRANSVENDA
AND ESTENTREGAS.CODSAIDA = ESTSAIDAVEICULOCARREG.CODSAIDA
AND PCNFSAID.NUMCAR = ESTSAIDAVEICULOCARREG.NUMCAR
AND PCNFSAID.NUMCAR = PCCARREG.NUMCAR
AND PCCARREG.CODMOTORISTA = MOTORISTA.MATRICULA (+)
AND PCNFSAID.CODCLI = PCCLIENT.CODCLI
AND PCNFSAID.CODFUNCCANHOTO = PCEMPR.MATRICULA (+)
AND ESTENTREGAS.CODSAIDA = ESTSITUACAOENTREGA.CODSAIDA (+)
AND PCNFSAID.CODCLI = ESTSITUACAOENTREGA.CODCLI (+)
AND ESTENTREGAS.NUMTRANSVENDA = :1
ORDER BY ESTENTREGAS.DATA DESC
OFFSET :2 ROWS FETCH NEXT :3 ROWS ONLY
`;
const deliveries = await this.oracleDataSource.query(sql, [
query.transactionId,
query.offset,
query.limit
]);
// Buscar imagens para cada entrega
for (let index = 0; index < deliveries.length; index++) {
const delivery = deliveries[index];
const sqlImages = `
SELECT URL
FROM ESTENTREGASIMAGENS
WHERE CODSAIDA = :1
AND NUMTRANSVENDA = :2
`;
const images = await this.oracleDataSource.query(sqlImages, [delivery.outId, delivery.transactionId]);
delivery.urlImages = images.map((image: any) => image.URL);
}
// Converter os resultados para o formato esperado
return deliveries.map((row: any) => ({
outId: row.outId,
transactionId: row.transactionId,
deliveryDate: row.deliveryDate,
invoiceNumber: row.invoiceNumber,
customerId: row.customerId,
customerName: row.customerName,
deliveryDoc: row.deliveryDoc,
deliveryName: row.deliveryName,
lat: row.lat,
lng: row.lng,
existBreakdown: row.existBreakdown,
existReturn: row.existReturn,
obsReturn: row.obsReturn,
dataCanhoto: row.dataCanhoto,
transactionIdInvoice: row.transactionIdInvoice,
invoiceUserId: row.invoiceUserId,
invoiceUserName: row.invoiceUserName,
deliveryStatus: row.deliveryStatus,
driverId: row.driverId,
driverName: row.driverName,
urlImages: [...row.urlImages],
}));
}
/**
* Busca entregas realizadas com filtros opcionais
* @param query - Filtros para a consulta de entregas realizadas
* @returns Lista de entregas realizadas
*/
async getCompletedDeliveries(query: DeliveryCompletedQuery): Promise<DeliveryCompleted[]> {
let sql = `
SELECT
ESTENTREGAS.CODSAIDA AS "outId"
,ESTENTREGAS.NUMTRANSVENDA AS "transactionId"
,ESTENTREGAS.DATA AS "deliveryDate"
,PCNFSAID.NUMNOTA AS "invoiceNumber"
,PCNFSAID.CODCLI AS "customerId"
,PCCLIENT.CLIENTE AS "customerName"
,ESTENTREGAS.DOCUMENTORECEBEDOR AS "deliveryDoc"
,ESTENTREGAS.NOMERECEBEDOR AS "deliveryName"
,ESTENTREGAS.LAT AS "lat"
,ESTENTREGAS.LNG AS "lng"
,ESTENTREGAS.AVARIA AS "existBreakdown"
,ESTENTREGAS.DEVOLUCAO AS "existReturn"
,ESTENTREGAS.MOTIVODEVOLUCAO AS "obsReturn"
,PCNFSAID.DTCANHOTO AS "dataCanhoto"
,PCNFSAID.NUMTRANSVENDA AS "transactionIdInvoice"
,PCNFSAID.CODFUNCCANHOTO AS "invoiceUserId"
,PCEMPR.NOME AS "invoiceUserName"
,ESTSITUACAOENTREGA.SITUACAO AS "deliveryStatus"
,PCCARREG.CODMOTORISTA AS "driverId"
,MOTORISTA.NOME AS "driverName"
FROM ESTENTREGAS, PCNFSAID, PCCLIENT, PCEMPR, ESTSITUACAOENTREGA, ESTSAIDAVEICULOCARREG, PCCARREG, PCEMPR MOTORISTA
WHERE ESTENTREGAS.NUMTRANSVENDA = PCNFSAID.NUMTRANSVENDA
AND ESTENTREGAS.CODSAIDA = ESTSAIDAVEICULOCARREG.CODSAIDA
AND PCNFSAID.NUMCAR = ESTSAIDAVEICULOCARREG.NUMCAR
AND PCNFSAID.NUMCAR = PCCARREG.NUMCAR
AND PCCARREG.CODMOTORISTA = MOTORISTA.MATRICULA (+)
AND PCNFSAID.CODCLI = PCCLIENT.CODCLI
AND PCNFSAID.CODFUNCCANHOTO = PCEMPR.MATRICULA (+)
AND ESTENTREGAS.CODSAIDA = ESTSITUACAOENTREGA.CODSAIDA (+)
AND PCNFSAID.CODCLI = ESTSITUACAOENTREGA.CODCLI (+)
`;
const parameters: any[] = [];
let paramIndex = 1;
// Filtros opcionais
if (query.startDate) {
sql += ` AND TRUNC(ESTENTREGAS.DATA) >= TO_DATE(:${paramIndex},'YYYY-MM-DD')`;
parameters.push(query.startDate);
paramIndex++;
}
if (query.endDate) {
sql += ` AND TRUNC(ESTENTREGAS.DATA) <= TO_DATE(:${paramIndex},'YYYY-MM-DD')`;
parameters.push(query.endDate);
paramIndex++;
}
if (query.outId) {
sql += ` AND ESTENTREGAS.CODSAIDA = :${paramIndex}`;
parameters.push(query.outId);
paramIndex++;
}
if (query.driverName) {
sql += ` AND UPPER(MOTORISTA.NOME) LIKE UPPER(:${paramIndex})`;
parameters.push(`%${query.driverName}%`);
paramIndex++;
}
if (query.customerId && query.customerId > 0) {
sql += ` AND PCCLIENT.CODCLI = :${paramIndex}`;
parameters.push(query.customerId);
paramIndex++;
}
if (query.customerName) {
sql += ` AND UPPER(PCCLIENT.CLIENTE) LIKE UPPER(:${paramIndex})`;
parameters.push(`%${query.customerName}%`);
paramIndex++;
}
if (query.orderNumber) {
sql += ` AND PCNFSAID.NUMNOTA = :${paramIndex}`;
parameters.push(query.orderNumber);
paramIndex++;
}
if (query.status) {
sql += ` AND ESTSITUACAOENTREGA.SITUACAO = :${paramIndex}`;
parameters.push(query.status);
paramIndex++;
}
// Ordenação
sql += ` ORDER BY ESTENTREGAS.DATA DESC`;
// Paginação
const limit = query.limit || 100;
const offset = query.offset || 0;
sql += ` OFFSET :${paramIndex} ROWS FETCH NEXT :${paramIndex + 1} ROWS ONLY`;
parameters.push(offset);
parameters.push(limit);
const deliveries = await this.oracleDataSource.query(sql, parameters);
// Buscar imagens para cada entrega
for (let index = 0; index < deliveries.length; index++) {
const delivery = deliveries[index];
const sqlImages = `
SELECT URL
FROM ESTENTREGASIMAGENS
WHERE CODSAIDA = :1
AND NUMTRANSVENDA = :2
`;
const images = await this.oracleDataSource.query(sqlImages, [delivery.outId, delivery.transactionId]);
delivery.urlImages = images.map((image: any) => image.URL);
}
// Converter os resultados para o formato esperado
return deliveries.map((row: any) => ({
outId: row.outId,
transactionId: row.transactionId,
deliveryDate: row.deliveryDate,
invoiceNumber: row.invoiceNumber,
customerId: row.customerId,
customerName: row.customerName,
deliveryDoc: row.deliveryDoc,
deliveryName: row.deliveryName,
lat: row.lat,
lng: row.lng,
existBreakdown: row.existBreakdown,
existReturn: row.existReturn,
obsReturn: row.obsReturn,
dataCanhoto: row.dataCanhoto,
transactionIdInvoice: row.transactionIdInvoice,
invoiceUserId: row.invoiceUserId,
invoiceUserName: row.invoiceUserName,
deliveryStatus: row.deliveryStatus,
driverId: row.driverId,
driverName: row.driverName,
urlImages: [...row.urlImages],
}));
}
}