feat: adiciona endpoint oferta-8026 para buscar ofertas promocionais

This commit is contained in:
JuruSysadmin
2025-11-24 16:57:46 -03:00
parent 17bec31bf8
commit 8cfcaf3910
4 changed files with 216 additions and 3 deletions

View File

@@ -0,0 +1,36 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsArray, IsNotEmpty, IsNumber, IsString, Matches } from 'class-validator';
/**
* DTO para requisição de ofertas 8026
*/
export class Oferta8026QueryDto {
@ApiProperty({
description: 'Data de início da vigência da promoção (formato DD/MM/YYYY)',
example: '19/11/2025',
})
@IsString()
@Matches(/^\d{2}\/\d{2}\/\d{4}$/, {
message: 'Data deve estar no formato DD/MM/YYYY',
})
@IsNotEmpty()
data: string;
@ApiProperty({
description: 'Array de códigos de produtos',
example: [62602],
type: [Number],
})
@IsArray()
@IsNotEmpty()
codprod: number[];
@ApiProperty({
description: 'Número da região',
example: 1,
})
@IsNumber()
@IsNotEmpty()
numregiao: number;
}

View File

@@ -0,0 +1,76 @@
import { ApiProperty } from '@nestjs/swagger';
/**
* DTO para resposta de ofertas 8026
*/
export class Oferta8026ResponseDto {
@ApiProperty({
description: 'Código do produto',
example: 12345,
})
codprod: number;
@ApiProperty({
description: 'Descrição do produto',
example: 'PRODUTO EXEMPLO',
})
descricao: string;
@ApiProperty({
description: 'Marca do produto',
example: 'MARCA EXEMPLO',
})
marca: string;
@ApiProperty({
description: 'Unidade do produto',
example: 'UN',
})
unidade: string;
@ApiProperty({
description: 'Preço de venda 1',
example: 99.9,
})
pvenda1: number;
@ApiProperty({
description: 'Preço fixo promocional',
example: 79.9,
})
precofixo: number;
@ApiProperty({
description: 'Percentual de desconto',
example: 20,
})
percdesconto: number;
@ApiProperty({
description: 'Data de fim da vigência',
example: '2024-12-31',
})
dtfimvigencia: Date;
@ApiProperty({
description: 'Mensagem para débito',
example: 'DEBITO',
required: false,
})
mensagem2: string | null;
@ApiProperty({
description: 'Mensagem para à vista',
example: 'À VISTA',
required: false,
})
mensagem3: string | null;
@ApiProperty({
description: 'Mensagem para 10x',
example: '10X',
required: false,
})
mensagem4: string | null;
}

View File

@@ -27,6 +27,8 @@ import { ProductDetailResponseDto } from './dto/product-detail-response.dto';
import { RotinaA4QueryDto } from './dto/rotina-a4-query.dto';
import { RotinaA4ResponseDto } from './dto/rotina-a4-response.dto';
import { UnifiedProductSearchDto } from './dto/unified-product-search.dto';
import { Oferta8026QueryDto } from './dto/oferta-8026-query.dto';
import { Oferta8026ResponseDto } from './dto/oferta-8026-response.dto';
//@ApiBearerAuth()
//@UseGuards(JwtAuthGuard)
@@ -151,4 +153,23 @@ export class ProductsController {
): Promise<ProductDetailResponseDto[]> {
return this.productsService.unifiedProductSearch(query);
}
/**
* Endpoint para buscar ofertas 8026
*/
@Post('oferta-8026')
@ApiOperation({ summary: 'Busca ofertas 8026 conforme parâmetros específicos' })
@ApiBody({ type: Oferta8026QueryDto })
@ApiResponse({
status: 200,
description: 'Lista de ofertas retornada com sucesso.',
type: Oferta8026ResponseDto,
isArray: true,
})
@ApiResponse({ status: 400, description: 'Parâmetros inválidos.' })
async getOferta8026(
@Body() query: Oferta8026QueryDto,
): Promise<Oferta8026ResponseDto[]> {
return this.productsService.getOferta8026(query);
}
}

View File

@@ -9,6 +9,8 @@ import { ProductDetailResponseDto } from './dto/product-detail-response.dto';
import { RotinaA4QueryDto } from './dto/rotina-a4-query.dto';
import { RotinaA4ResponseDto } from './dto/rotina-a4-response.dto';
import { UnifiedProductSearchDto } from './dto/unified-product-search.dto';
import { Oferta8026QueryDto } from './dto/oferta-8026-query.dto';
import { Oferta8026ResponseDto } from './dto/oferta-8026-response.dto';
@Injectable()
export class ProductsService {
@@ -322,9 +324,6 @@ export class ProductsService {
}));
}
/**
* Busca unificada de produtos por nome, código de barras ou codprod
*/
async unifiedProductSearch(
query: UnifiedProductSearchDto,
): Promise<ProductDetailResponseDto[]> {
@@ -548,4 +547,85 @@ export class ProductsService {
return produto;
}
async getOferta8026(
query: Oferta8026QueryDto,
): Promise<Oferta8026ResponseDto[]> {
const { data, codprod, numregiao } = query;
if (!codprod || codprod.length === 0) {
throw new HttpException(
'É necessário informar pelo menos um código de produto.',
HttpStatus.BAD_REQUEST,
);
}
let dataFormatada = data;
const dateMatch = data.match(/^(\d{2})\/(\d{2})\/(\d{4})$/);
if (dateMatch) {
const [, part1, part2, year] = dateMatch;
const num2 = parseInt(part2, 10);
dataFormatada = num2 > 12
? `${part2}/${part1}/${year}`
: `${part1}/${part2}/${year}`;
}
const codprodPlaceholders: string[] = [];
const params: any[] = [];
let paramIndex = 0;
params.push(dataFormatada);
paramIndex++;
codprod.forEach((cod) => {
codprodPlaceholders.push(`:${paramIndex}`);
params.push(cod);
paramIndex++;
});
params.push(numregiao);
const sql = `
SELECT
pctabpr.codprod,
pcprodut.descricao,
pcmarca.marca,
pcprodut.unidade,
pctabpr.pvenda1,
pcprecoprom.precofixo,
TRUNC(((pctabpr.pvenda1 - pcprecoprom.precofixo) / pctabpr.pvenda1) * 100, 0) percdesconto,
pcprecoprom.dtfimvigencia,
CASE WHEN pcprecoprom.codplpagmax = 2 THEN 'DEBITO' ELSE NULL END mensagem2,
CASE WHEN pcprecoprom.codplpagmax = 10 THEN 'À VISTA' ELSE NULL END mensagem3,
CASE WHEN pcprecoprom.codplpagmax = 42 OR pcprecoprom.codplpagmax = 46 THEN '10X' ELSE NULL END mensagem4
FROM pctabpr, pcprecoprom, pcplpag, pcprodut, pcmarca
WHERE pctabpr.codprod = pcprecoprom.codprod
AND pctabpr.numregiao = pcprecoprom.numregiao
AND pctabpr.codprod = pcprodut.codprod
AND pcprodut.codmarca = pcmarca.codmarca (+)
AND pcprecoprom.codplpagmax = pcplpag.codplpag (+)
AND TRUNC(pcprecoprom.dtiniciovigencia) = TRUNC(TO_DATE(:0, 'DD/MM/YYYY'))
AND pcprecoprom.codprod IN (${codprodPlaceholders.join(',')})
AND PCPRECOPROM.DTFIMVIGENCIA >= TRUNC(SYSDATE)
AND pcprecoprom.codplpagmax IN (42, 46)
AND pctabpr.numregiao = :${paramIndex}
`;
const result = await this.dataSource.query(sql, params);
return result.map((row) => ({
codprod: row.CODPROD,
descricao: row.DESCRICAO,
marca: row.MARCA,
unidade: row.UNIDADE,
pvenda1: row.PVENDA1,
precofixo: row.PRECOFIXO,
percdesconto: row.PERCDESCONTO,
dtfimvigencia: row.DTFIMVIGENCIA,
mensagem2: row.MENSAGEM2,
mensagem3: row.MENSAGEM3,
mensagem4: row.MENSAGEM4,
}));
}
}