Adiciona busca por codauxiliar em findProducts e cria API de busca unificada
- Modifica findProducts para buscar por CODPROD e CODAUXILIAR - Adiciona testes para o método products - Cria endpoint unified-search para busca unificada por nome, código de barras ou codprod - Adiciona @IsOptional aos campos opcionais do ProductDetailQueryDto - Adiciona testes para products.service
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { DataConsultService } from '../data-consult.service';
|
||||
import { DataConsultRepository } from '../data-consult.repository';
|
||||
import { IRedisClient } from '../../core/configs/cache/IRedisClient';
|
||||
@@ -14,6 +15,8 @@ export const createMockRepository = (
|
||||
findSellers: jest.fn(),
|
||||
findBillings: jest.fn(),
|
||||
findCustomers: jest.fn(),
|
||||
findProducts: jest.fn(),
|
||||
findProductsByCodauxiliar: jest.fn(),
|
||||
findAllProducts: jest.fn(),
|
||||
findAllCarriers: jest.fn(),
|
||||
findRegions: jest.fn(),
|
||||
@@ -31,6 +34,9 @@ export interface DataConsultServiceTestContext {
|
||||
mockRepository: jest.Mocked<DataConsultRepository>;
|
||||
mockRedisClient: jest.Mocked<IRedisClient>;
|
||||
mockDataSource: jest.Mocked<DataSource>;
|
||||
mockLogger: {
|
||||
error: jest.Mock;
|
||||
};
|
||||
}
|
||||
|
||||
export async function createDataConsultServiceTestModule(
|
||||
@@ -64,10 +70,21 @@ export async function createDataConsultServiceTestModule(
|
||||
|
||||
const service = module.get<DataConsultService>(DataConsultService);
|
||||
|
||||
const mockLogger = {
|
||||
error: jest.fn(),
|
||||
};
|
||||
|
||||
jest.spyOn(Logger.prototype, 'error').mockImplementation(
|
||||
(message: any, ...optionalParams: any[]) => {
|
||||
mockLogger.error(message, ...optionalParams);
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
service,
|
||||
mockRepository,
|
||||
mockRedisClient,
|
||||
mockDataSource,
|
||||
mockLogger,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -124,6 +124,23 @@ describe('DataConsultService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should filter out sellers with null id', async () => {
|
||||
context.mockRepository.findSellers.mockResolvedValue([
|
||||
{ id: null, name: 'Vendedor 1' },
|
||||
{ id: '002', name: 'Vendedor 2' },
|
||||
{ id: null, name: 'Vendedor 3' },
|
||||
] as any);
|
||||
|
||||
const result = await context.service.sellers();
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].id).toBe('002');
|
||||
expect(result[0].name).toBe('Vendedor 2');
|
||||
result.forEach((seller) => {
|
||||
expect(seller.id).not.toBeNull();
|
||||
expect(seller.id).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('should log error when repository throws exception', async () => {
|
||||
const repositoryError = new Error('Database connection failed');
|
||||
context.mockRepository.findSellers.mockRejectedValue(repositoryError);
|
||||
@@ -510,4 +527,124 @@ describe('DataConsultService', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('products', () => {
|
||||
let context: Awaited<ReturnType<typeof createDataConsultServiceTestModule>>;
|
||||
|
||||
beforeEach(async () => {
|
||||
context = await createDataConsultServiceTestModule();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('Tests that expose problems', () => {
|
||||
it('should search products by CODPROD', async () => {
|
||||
context.mockRepository.findProducts.mockResolvedValue([
|
||||
{
|
||||
id: '12345',
|
||||
name: 'PRODUTO EXEMPLO',
|
||||
manufacturerCode: 'FAB001',
|
||||
},
|
||||
] as any);
|
||||
|
||||
const result = await context.service.products('12345');
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].id).toBe('12345');
|
||||
expect(result[0].name).toBe('PRODUTO EXEMPLO');
|
||||
expect(context.mockRepository.findProducts).toHaveBeenCalledWith(
|
||||
'12345',
|
||||
);
|
||||
});
|
||||
|
||||
it('should search products by CODAUXILIAR', async () => {
|
||||
context.mockRepository.findProducts.mockResolvedValue([
|
||||
{
|
||||
id: '12345',
|
||||
name: 'PRODUTO EXEMPLO',
|
||||
manufacturerCode: 'FAB001',
|
||||
},
|
||||
] as any);
|
||||
|
||||
const result = await context.service.products('7891234567890');
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].id).toBe('12345');
|
||||
expect(context.mockRepository.findProducts).toHaveBeenCalledWith(
|
||||
'7891234567890',
|
||||
);
|
||||
});
|
||||
|
||||
it('should search products by CODPROD or CODAUXILIAR', async () => {
|
||||
context.mockRepository.findProducts.mockResolvedValue([
|
||||
{
|
||||
id: '12345',
|
||||
name: 'PRODUTO EXEMPLO',
|
||||
manufacturerCode: 'FAB001',
|
||||
},
|
||||
{
|
||||
id: '12346',
|
||||
name: 'OUTRO PRODUTO',
|
||||
manufacturerCode: 'FAB002',
|
||||
},
|
||||
] as any);
|
||||
|
||||
const result = await context.service.products('12345');
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0].id).toBe('12345');
|
||||
expect(result[1].id).toBe('12346');
|
||||
});
|
||||
|
||||
it('should handle empty result from repository', async () => {
|
||||
context.mockRepository.findProducts.mockResolvedValue([]);
|
||||
|
||||
const result = await context.service.products('99999');
|
||||
|
||||
expect(result).toHaveLength(0);
|
||||
expect(Array.isArray(result)).toBe(true);
|
||||
});
|
||||
|
||||
it('should validate that all products have required properties (id, name)', async () => {
|
||||
context.mockRepository.findProducts.mockResolvedValue([
|
||||
{ id: '12345', name: 'PRODUTO 1' },
|
||||
{ id: '12346', name: 'PRODUTO 2' },
|
||||
{ id: '12347', name: 'PRODUTO 3' },
|
||||
] as any);
|
||||
|
||||
const result = await context.service.products('12345');
|
||||
|
||||
result.forEach((product) => {
|
||||
expect(product.id).toBeDefined();
|
||||
expect(product.name).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw error when filter is invalid', async () => {
|
||||
await expect(
|
||||
context.service.products(null as any),
|
||||
).rejects.toThrow(HttpException);
|
||||
await expect(
|
||||
context.service.products(undefined as any),
|
||||
).rejects.toThrow(HttpException);
|
||||
await expect(
|
||||
context.service.products('' as any),
|
||||
).rejects.toThrow(HttpException);
|
||||
});
|
||||
|
||||
it('should log error when repository throws exception', async () => {
|
||||
const repositoryError = new Error('Database connection failed');
|
||||
context.mockRepository.findProducts.mockRejectedValue(repositoryError);
|
||||
await expect(context.service.products('12345')).rejects.toThrow(
|
||||
HttpException,
|
||||
);
|
||||
expect(context.mockLogger.error).toHaveBeenCalledWith(
|
||||
'Erro ao buscar produtos',
|
||||
repositoryError,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -116,13 +116,15 @@ export class DataConsultRepository {
|
||||
}
|
||||
|
||||
async findProducts(filter: string): Promise<ProductDto[]> {
|
||||
const cleanedFilter = filter.replace(/\D/g, '');
|
||||
const sql = `
|
||||
SELECT PCPRODUT.CODPROD as "id",
|
||||
PCPRODUT.CODPROD || ' - ' || PCPRODUT.DESCRICAO || ' ( ' || PCPRODUT.CODFAB || ' )' as "description"
|
||||
FROM PCPRODUT
|
||||
WHERE PCPRODUT.CODPROD = :filter
|
||||
WHERE PCPRODUT.CODPROD = :0
|
||||
OR REGEXP_REPLACE(PCPRODUT.CODAUXILIAR, '[^0-9]', '') = :1
|
||||
`;
|
||||
const results = await this.executeQuery<ProductDto[]>(sql, [filter]);
|
||||
const results = await this.executeQuery<ProductDto[]>(sql, [filter, cleanedFilter]);
|
||||
return results.map((result) => new ProductDto(result));
|
||||
}
|
||||
|
||||
|
||||
41
src/products/__tests__/products.service.spec.helper.ts
Normal file
41
src/products/__tests__/products.service.spec.helper.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ProductsService } from '../products.service';
|
||||
import { DataSource } from 'typeorm';
|
||||
import { getDataSourceToken } from '@nestjs/typeorm';
|
||||
|
||||
export const createMockDataSource = () =>
|
||||
({
|
||||
query: jest.fn(),
|
||||
} as any);
|
||||
|
||||
export interface ProductsServiceTestContext {
|
||||
service: ProductsService;
|
||||
mockDataSource: jest.Mocked<DataSource>;
|
||||
}
|
||||
|
||||
export async function createProductsServiceTestModule(
|
||||
dataSourceMethods: Partial<DataSource> = {},
|
||||
): Promise<ProductsServiceTestContext> {
|
||||
const mockDataSource = {
|
||||
...createMockDataSource(),
|
||||
...dataSourceMethods,
|
||||
} as any;
|
||||
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
ProductsService,
|
||||
{
|
||||
provide: getDataSourceToken('oracle'),
|
||||
useValue: mockDataSource,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
const service = module.get<ProductsService>(ProductsService);
|
||||
|
||||
return {
|
||||
service,
|
||||
mockDataSource,
|
||||
};
|
||||
}
|
||||
|
||||
175
src/products/__tests__/products.service.spec.ts
Normal file
175
src/products/__tests__/products.service.spec.ts
Normal file
@@ -0,0 +1,175 @@
|
||||
import { HttpException } from '@nestjs/common';
|
||||
import { createProductsServiceTestModule } from './products.service.spec.helper';
|
||||
import { ProductDetailQueryDto } from '../dto/product-detail-query.dto';
|
||||
import { ProductDetailResponseDto } from '../dto/product-detail-response.dto';
|
||||
|
||||
describe('ProductsService', () => {
|
||||
describe('getProductDetails', () => {
|
||||
let context: Awaited<ReturnType<typeof createProductsServiceTestModule>>;
|
||||
|
||||
beforeEach(async () => {
|
||||
context = await createProductsServiceTestModule();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('busca por codauxiliar', () => {
|
||||
it('deve buscar produtos por codauxiliar com sucesso', async () => {
|
||||
const query: ProductDetailQueryDto = {
|
||||
numregiao: 1,
|
||||
codauxiliar: ['7891234567890', '7891234567891'],
|
||||
codfilial: '1',
|
||||
};
|
||||
|
||||
const mockProducts = [
|
||||
{
|
||||
codprod: 12345,
|
||||
descricao: 'PRODUTO 1 - MARCA 1',
|
||||
embalagem: 'UN',
|
||||
codauxiliar: '7891234567890',
|
||||
marca: 'MARCA 1',
|
||||
preco: 99.9,
|
||||
filial: 'FILIAL MATRIZ',
|
||||
regiao: 'REGIÃO SUL',
|
||||
},
|
||||
{
|
||||
codprod: 12346,
|
||||
descricao: 'PRODUTO 2 - MARCA 2',
|
||||
embalagem: 'UN',
|
||||
codauxiliar: '7891234567891',
|
||||
marca: 'MARCA 2',
|
||||
preco: 149.9,
|
||||
filial: 'FILIAL MATRIZ',
|
||||
regiao: 'REGIÃO SUL',
|
||||
},
|
||||
];
|
||||
|
||||
context.mockDataSource.query.mockResolvedValue(mockProducts);
|
||||
|
||||
const result = await context.service.getProductDetails(query);
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0].codprod).toBe(12345);
|
||||
expect(result[0].codauxiliar).toBe('7891234567890');
|
||||
expect(result[0].descricao).toBe('PRODUTO 1 - MARCA 1');
|
||||
expect(result[0].preco).toBe('99,90');
|
||||
expect(result[1].codprod).toBe(12346);
|
||||
expect(result[1].codauxiliar).toBe('7891234567891');
|
||||
expect(result[1].preco).toBe('149,90');
|
||||
|
||||
expect(context.mockDataSource.query).toHaveBeenCalledTimes(1);
|
||||
const callArgs = context.mockDataSource.query.mock.calls[0];
|
||||
expect(callArgs[0]).toContain('REGEXP_REPLACE(PCPRODUT.CODAUXILIAR');
|
||||
expect(callArgs[1]).toContain(1);
|
||||
expect(callArgs[1]).toContain('1');
|
||||
expect(callArgs[1]).toContain('7891234567890');
|
||||
expect(callArgs[1]).toContain('7891234567891');
|
||||
});
|
||||
|
||||
it('deve remover caracteres não numéricos do codauxiliar na query', async () => {
|
||||
const query: ProductDetailQueryDto = {
|
||||
numregiao: 1,
|
||||
codauxiliar: ['789.123.456.789-0', '789-123-456-789-1'],
|
||||
codfilial: '1',
|
||||
};
|
||||
|
||||
const mockProducts = [
|
||||
{
|
||||
codprod: 12345,
|
||||
descricao: 'PRODUTO 1 - MARCA 1',
|
||||
embalagem: 'UN',
|
||||
codauxiliar: '7891234567890',
|
||||
marca: 'MARCA 1',
|
||||
preco: 99.9,
|
||||
filial: 'FILIAL MATRIZ',
|
||||
regiao: 'REGIÃO SUL',
|
||||
},
|
||||
];
|
||||
|
||||
context.mockDataSource.query.mockResolvedValue(mockProducts);
|
||||
|
||||
const result = await context.service.getProductDetails(query);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(context.mockDataSource.query).toHaveBeenCalledTimes(1);
|
||||
const callArgs = context.mockDataSource.query.mock.calls[0];
|
||||
expect(callArgs[1]).toContain('7891234567890');
|
||||
expect(callArgs[1]).toContain('7891234567891');
|
||||
});
|
||||
|
||||
it('deve retornar array vazio quando nenhum produto é encontrado', async () => {
|
||||
const query: ProductDetailQueryDto = {
|
||||
numregiao: 1,
|
||||
codauxiliar: ['9999999999999'],
|
||||
codfilial: '1',
|
||||
};
|
||||
|
||||
context.mockDataSource.query.mockResolvedValue([]);
|
||||
|
||||
const result = await context.service.getProductDetails(query);
|
||||
|
||||
expect(result).toHaveLength(0);
|
||||
expect(context.mockDataSource.query).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('deve formatar o preço corretamente', async () => {
|
||||
const query: ProductDetailQueryDto = {
|
||||
numregiao: 1,
|
||||
codauxiliar: ['7891234567890'],
|
||||
codfilial: '1',
|
||||
};
|
||||
|
||||
const mockProducts = [
|
||||
{
|
||||
codprod: 12345,
|
||||
descricao: 'PRODUTO 1 - MARCA 1',
|
||||
embalagem: 'UN',
|
||||
codauxiliar: '7891234567890',
|
||||
marca: 'MARCA 1',
|
||||
preco: 1234.56,
|
||||
filial: 'FILIAL MATRIZ',
|
||||
regiao: 'REGIÃO SUL',
|
||||
},
|
||||
];
|
||||
|
||||
context.mockDataSource.query.mockResolvedValue(mockProducts);
|
||||
|
||||
const result = await context.service.getProductDetails(query);
|
||||
|
||||
expect(result[0].preco).toBe('1.234,56');
|
||||
});
|
||||
|
||||
it('deve lançar exceção quando codprod e codauxiliar não são informados', async () => {
|
||||
const query: ProductDetailQueryDto = {
|
||||
numregiao: 1,
|
||||
codfilial: '1',
|
||||
};
|
||||
|
||||
await expect(
|
||||
context.service.getProductDetails(query),
|
||||
).rejects.toThrow(HttpException);
|
||||
await expect(
|
||||
context.service.getProductDetails(query),
|
||||
).rejects.toThrow('É necessário informar codprod ou codauxiliar.');
|
||||
});
|
||||
|
||||
it('deve lançar exceção quando codauxiliar é array vazio', async () => {
|
||||
const query: ProductDetailQueryDto = {
|
||||
numregiao: 1,
|
||||
codauxiliar: [],
|
||||
codfilial: '1',
|
||||
};
|
||||
|
||||
await expect(
|
||||
context.service.getProductDetails(query),
|
||||
).rejects.toThrow(HttpException);
|
||||
await expect(
|
||||
context.service.getProductDetails(query),
|
||||
).rejects.toThrow('É necessário informar codprod ou codauxiliar.');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsArray, IsNotEmpty, IsNumber, IsString } from 'class-validator';
|
||||
import { IsArray, IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
/**
|
||||
* DTO para requisição de detalhes de produtos
|
||||
@@ -19,6 +19,7 @@ export class ProductDetailQueryDto {
|
||||
type: [Number],
|
||||
required: false,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
codprod?: number[];
|
||||
|
||||
@@ -28,6 +29,7 @@ export class ProductDetailQueryDto {
|
||||
type: [String],
|
||||
required: false,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
codauxiliar?: string[];
|
||||
|
||||
|
||||
32
src/products/dto/unified-product-search.dto.ts
Normal file
32
src/products/dto/unified-product-search.dto.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsNumber, IsString } from 'class-validator';
|
||||
|
||||
/**
|
||||
* DTO para busca unificada de produtos
|
||||
*/
|
||||
export class UnifiedProductSearchDto {
|
||||
@ApiProperty({
|
||||
description: 'Termo de busca (nome, código de barras ou codprod)',
|
||||
example: '7891234567890',
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
search: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Código da região para buscar o preço',
|
||||
example: 1,
|
||||
})
|
||||
@IsNumber()
|
||||
@IsNotEmpty()
|
||||
numregiao: number;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Código da filial',
|
||||
example: '1',
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
codfilial: string;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { ProductDetailQueryDto } from './dto/product-detail-query.dto';
|
||||
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';
|
||||
|
||||
|
||||
//@ApiBearerAuth()
|
||||
@@ -95,4 +96,21 @@ export class ProductsController {
|
||||
async getRotinaA4(@Body() query: RotinaA4QueryDto): Promise<RotinaA4ResponseDto> {
|
||||
return this.productsService.getRotinaA4(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoint para busca unificada de produtos por nome, código de barras ou codprod
|
||||
*/
|
||||
@Post('unified-search')
|
||||
@ApiOperation({ summary: 'Busca unificada de produtos por nome, código de barras ou codprod' })
|
||||
@ApiBody({ type: UnifiedProductSearchDto })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'Lista de produtos encontrados retornada com sucesso.',
|
||||
type: ProductDetailResponseDto,
|
||||
isArray: true
|
||||
})
|
||||
@ApiResponse({ status: 400, description: 'Parâmetros inválidos.' })
|
||||
async unifiedProductSearch(@Body() query: UnifiedProductSearchDto): Promise<ProductDetailResponseDto[]> {
|
||||
return this.productsService.unifiedProductSearch(query);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ProductDetailQueryDto } from './dto/product-detail-query.dto';
|
||||
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';
|
||||
|
||||
@Injectable()
|
||||
export class ProductsService {
|
||||
@@ -306,6 +307,83 @@ export class ProductsService {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Busca unificada de produtos por nome, código de barras ou codprod
|
||||
*/
|
||||
async unifiedProductSearch(
|
||||
query: UnifiedProductSearchDto,
|
||||
): Promise<ProductDetailResponseDto[]> {
|
||||
const { search, numregiao, codfilial } = query;
|
||||
|
||||
if (!search || search.trim().length === 0) {
|
||||
throw new HttpException(
|
||||
'É necessário informar um termo de busca.',
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
const searchTerm = search.trim();
|
||||
const numericOnly = searchTerm.replace(/\D/g, '');
|
||||
const isNumeric = numericOnly.length > 0 && /^\d+$/.test(numericOnly);
|
||||
|
||||
const baseParams: any[] = [numregiao, codfilial, numregiao];
|
||||
let whereCondition: string;
|
||||
let params: any[];
|
||||
|
||||
if (isNumeric) {
|
||||
const numericSearch = numericOnly;
|
||||
whereCondition = `(
|
||||
PCPRODUT.CODPROD = :4
|
||||
OR REGEXP_REPLACE(PCPRODUT.CODAUXILIAR, '[^0-9]', '') = :5
|
||||
OR PCPRODUT.DESCRICAO LIKE '%' || :6 || '%'
|
||||
)`;
|
||||
params = [
|
||||
...baseParams,
|
||||
parseInt(numericSearch, 10),
|
||||
numericSearch,
|
||||
searchTerm,
|
||||
];
|
||||
} else {
|
||||
whereCondition = `PCPRODUT.DESCRICAO LIKE '%' || :4 || '%'`;
|
||||
params = [...baseParams, searchTerm];
|
||||
}
|
||||
|
||||
const sql = `
|
||||
SELECT
|
||||
PCPRODUT.CODPROD AS "codprod",
|
||||
PCPRODUT.DESCRICAO || ' - ' || PCMARCA.MARCA AS "descricao",
|
||||
PCPRODUT.EMBALAGEM AS "embalagem",
|
||||
PCPRODUT.CODAUXILIAR AS "codauxiliar",
|
||||
PCMARCA.MARCA AS "marca",
|
||||
(
|
||||
SELECT PCTABPR.PVENDA1
|
||||
FROM PCTABPR
|
||||
WHERE PCTABPR.CODPROD = PCPRODUT.CODPROD
|
||||
AND PCTABPR.NUMREGIAO = :1
|
||||
) AS "preco",
|
||||
(
|
||||
SELECT TRIM(REPLACE(RAZAOSOCIAL, 'LTDA', ''))
|
||||
FROM PCFILIAL F
|
||||
WHERE CODIGO = :2
|
||||
) AS "filial",
|
||||
(
|
||||
SELECT REGIAO
|
||||
FROM PCREGIAO
|
||||
WHERE NUMREGIAO = :3
|
||||
) AS "regiao"
|
||||
FROM PCPRODUT
|
||||
LEFT JOIN PCMARCA ON PCPRODUT.CODMARCA = PCMARCA.CODMARCA
|
||||
WHERE ${whereCondition}
|
||||
`;
|
||||
|
||||
const products = await this.dataSource.query(sql, params);
|
||||
|
||||
return products.map((product) => ({
|
||||
...product,
|
||||
preco: this.formatarMoedaBrasileira(product.preco),
|
||||
}));
|
||||
}
|
||||
|
||||
private buildRotinaA4WhereCondition(
|
||||
codprod: number | undefined,
|
||||
codauxiliar: string | undefined,
|
||||
|
||||
Reference in New Issue
Block a user