chore: organize project and update service

This commit is contained in:
Joelbrit0 2026-01-29 11:42:23 -03:00
parent 66adb39b9b
commit fc2045da89
5 changed files with 322 additions and 5 deletions

View File

@ -2,13 +2,15 @@ import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { PrinterController } from './controller/printer.controller';
import { PedidoSevenController } from './controller/pedido-seven.controller';
import { PrinterService } from './services/printer.service';
import { PrinterProcessor } from './services/printer.processor';
import { InfrastructureModule } from './infrastructure/infrastructure.module';
import { PedidoSevenService } from './services/pedido-seven.service';
@Module({
imports: [InfrastructureModule],
controllers: [AppController, PrinterController],
providers: [AppService, PrinterService, PrinterProcessor],
controllers: [AppController, PrinterController, PedidoSevenController],
providers: [AppService, PrinterService, PrinterProcessor, PedidoSevenService],
})
export class AppModule {}

View File

@ -8,11 +8,11 @@ export function createTypeOrmOptions(
): TypeOrmModuleOptions {
const baseOptions: DataSourceOptions = {
type: 'oracle',
username: configService.get<string>('DB_USERNAME') || 'teste',
password: configService.get<string>('DB_PASSWORD') || 'teste',
username: configService.get<string>('DB_USERNAME') || 'SEVEN',
password: configService.get<string>('DB_PASSWORD') || 'USR54SEV',
connectString:
configService.get<string>('DB_CONNECT_STRING') ||
'(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=10.1.1.241)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=BDTESTE)))',
'(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=10.1.1.241)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=WINT)))',
synchronize: false,
logging: true,
entities: [__dirname + '/../**/*.{entity,view}.{js,ts}'],

View File

@ -0,0 +1,40 @@
import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common';
import {
ApiNotFoundResponse,
ApiOkResponse,
ApiOperation,
ApiParam,
ApiServiceUnavailableResponse,
ApiTags,
} from '@nestjs/swagger';
import { PedidoSevenService } from '../services/pedido-seven.service';
import { PedidoCondVenda7RowDto } from '../dto/pedido-condvenda7-row.dto';
@ApiTags('Pedidos')
@Controller('pedidos')
export class PedidoSevenController {
constructor(private readonly pedidoSevenService: PedidoSevenService) {}
@Get('condvenda-7/:numped7')
@ApiOperation({
summary: 'Consulta pedido (CONDVENDA=7) por NUMPEDENTFUT',
})
@ApiParam({
name: 'numped7',
example: 123456,
description: 'Valor para PCPEDC.NUMPEDENTFUT',
})
@ApiOkResponse({
description: 'Linhas retornadas pela consulta (pedido x itens)',
type: [PedidoCondVenda7RowDto],
})
@ApiNotFoundResponse({
description: 'Pedido nao encontrado para o NUMPEDENTFUT informado',
})
@ApiServiceUnavailableResponse({
description: 'Banco de dados indisponivel (DataSource nao inicializado)',
})
async consultar(@Param('numped7', ParseIntPipe) numped7: number) {
return this.pedidoSevenService.consultarPorNumped7(numped7);
}
}

View File

@ -0,0 +1,74 @@
import { ApiProperty } from '@nestjs/swagger';
export class PedidoCondVenda7RowDto {
@ApiProperty({ example: 123456 })
numped!: number;
@ApiProperty({ example: '12345678000199' })
cgc!: string;
@ApiProperty({ example: 'MINHA EMPRESA LTDA' })
razaosocial!: string;
@ApiProperty({ description: 'SYSDATE', type: String, format: 'date-time' })
dtemissao!: Date;
@ApiProperty({ example: 1 })
codfilial!: number;
@ApiProperty({ example: 'FILIAL CENTRO' })
filialvenda!: string;
@ApiProperty({ example: 2 })
codfilialretira!: number;
@ApiProperty({ example: 'FILIAL RETIRA' })
filialretira!: string;
@ApiProperty({ type: String, format: 'date-time' })
data!: Date;
@ApiProperty({ example: 999 })
numcar!: number;
@ApiProperty({ example: 123 })
codcli!: number;
@ApiProperty({ example: 'CLIENTE TESTE' })
cliente!: string;
@ApiProperty({ example: 10 })
codusur!: number;
@ApiProperty({ example: 'VENDEDOR' })
vendedor!: string;
@ApiProperty({ example: 25.5 })
vlfrete!: number;
@ApiProperty({ example: 199.9 })
valor!: number;
@ApiProperty({ example: 'ENTREGA' })
tipoentrega!: string;
@ApiProperty({
type: String,
format: 'date-time',
required: false,
nullable: true,
})
dtentrega!: Date | null;
@ApiProperty({ example: 98765 })
codprod!: number;
@ApiProperty({ example: 'PRODUTO X' })
descricao!: string;
@ApiProperty({ example: 2 })
qt!: number;
@ApiProperty({ example: 'e10adc3949ba59abbe56e057f20f883e' })
hash!: string;
}

View File

@ -0,0 +1,201 @@
import {
Injectable,
InternalServerErrorException,
NotFoundException,
Optional,
ServiceUnavailableException,
} from '@nestjs/common';
import { DataSource } from 'typeorm';
import { PedidoCondVenda7RowDto } from '../dto/pedido-condvenda7-row.dto';
@Injectable()
export class PedidoSevenService {
constructor(@Optional() private readonly dataSource?: DataSource) {}
private static toNumber(value: unknown, field: string): number {
if (typeof value === 'number' && Number.isFinite(value)) return value;
if (typeof value === 'string' && value.trim() !== '') {
const n = Number(value);
if (Number.isFinite(n)) return n;
}
throw new InternalServerErrorException(`Campo ${field} invalido`);
}
private static asString(value: unknown, field: string): string {
if (typeof value === 'string') return value;
if (value == null) return '';
if (typeof value === 'number' || typeof value === 'boolean') {
return String(value);
}
throw new InternalServerErrorException(`Campo ${field} invalido`);
}
private static toDate(value: unknown, field: string): Date {
if (value instanceof Date && !Number.isNaN(value.getTime())) return value;
if (typeof value === 'string' || typeof value === 'number') {
const d = new Date(value);
if (!Number.isNaN(d.getTime())) return d;
}
throw new InternalServerErrorException(`Campo ${field} invalido`);
}
private static toNullableDate(value: unknown): Date | null {
if (value == null) return null;
if (value instanceof Date && !Number.isNaN(value.getTime())) return value;
if (typeof value === 'string' || typeof value === 'number') {
const d = new Date(value);
if (!Number.isNaN(d.getTime())) return d;
}
return null;
}
private static pick(row: Record<string, unknown>, key: string): unknown {
return (
row[key] ??
row[key.toUpperCase()] ??
row[key.toLowerCase()] ??
row[key.replace(/_/g, '')] ??
row[key.toUpperCase().replace(/_/g, '')]
);
}
private static mapRow(row: Record<string, unknown>): PedidoCondVenda7RowDto {
const dto = new PedidoCondVenda7RowDto();
dto.numped = this.toNumber(this.pick(row, 'NUMPED'), 'NUMPED');
dto.cgc = this.asString(this.pick(row, 'CGC'), 'CGC');
dto.razaosocial = this.asString(
this.pick(row, 'RAZAOSOCIAL'),
'RAZAOSOCIAL',
);
dto.dtemissao = this.toDate(this.pick(row, 'DTEMISSAO'), 'DTEMISSAO');
dto.codfilial = this.toNumber(this.pick(row, 'CODFILIAL'), 'CODFILIAL');
dto.filialvenda = this.asString(
this.pick(row, 'FILIALVENDA'),
'FILIALVENDA',
);
dto.codfilialretira = this.toNumber(
this.pick(row, 'CODFILIALRETIRA'),
'CODFILIALRETIRA',
);
dto.filialretira = this.asString(
this.pick(row, 'FILIALRETIRA'),
'FILIALRETIRA',
);
dto.data = this.toDate(this.pick(row, 'DATA'), 'DATA');
dto.numcar = this.toNumber(this.pick(row, 'NUMCAR'), 'NUMCAR');
dto.codcli = this.toNumber(this.pick(row, 'CODCLI'), 'CODCLI');
dto.cliente = this.asString(this.pick(row, 'CLIENTE'), 'CLIENTE');
dto.codusur = this.toNumber(this.pick(row, 'CODUSUR'), 'CODUSUR');
dto.vendedor = this.asString(this.pick(row, 'VENDEDOR'), 'VENDEDOR');
dto.vlfrete = this.toNumber(this.pick(row, 'VLFRETE'), 'VLFRETE');
dto.valor = this.toNumber(this.pick(row, 'VALOR'), 'VALOR');
dto.tipoentrega = this.asString(
this.pick(row, 'TIPOENTREGA'),
'TIPOENTREGA',
);
dto.dtentrega = this.toNullableDate(this.pick(row, 'DTENTREGA'));
dto.codprod = this.toNumber(this.pick(row, 'CODPROD'), 'CODPROD');
dto.descricao = this.asString(this.pick(row, 'DESCRICAO'), 'DESCRICAO');
dto.qt = this.toNumber(this.pick(row, 'QT'), 'QT');
dto.hash = this.asString(this.pick(row, 'HASH'), 'HASH');
return dto;
}
async consultarPorNumped7(
numped7: number,
): Promise<PedidoCondVenda7RowDto[]> {
if (!this.dataSource) {
throw new ServiceUnavailableException('DataSource nao disponivel');
}
if (!this.dataSource.isInitialized) {
throw new ServiceUnavailableException(
'Banco de dados indisponivel (DataSource nao inicializado)',
);
}
const sql = `
SELECT
PCPEDC.NUMPED,
PCFILIAL.CGC,
PCFILIAL.RAZAOSOCIAL,
SYSDATE DTEMISSAO,
PCPEDC.CODFILIAL,
PCFILIAL.FANTASIA FILIALVENDA,
PCPEDI.CODFILIALRETIRA,
RETIRA.FANTASIA FILIALRETIRA,
PCPEDC.DATA,
PCPEDC.NUMCAR,
PCPEDC.CODCLI,
PCCLIENT.CLIENTE CLIENTE,
PCNFSAID.CODUSUR,
PCUSUARI.NOME VENDEDOR,
PCPEDC.VLFRETE,
DECODE(PCNFSAID.VLTOTGER, 0, PCNFSAID.VLTOTAL, PCNFSAID.VLTOTAL) VALOR,
DECODE(
PCPEDI.TIPOENTREGA,
'EN', 'ENTREGA',
'EF', 'ENCOMENDA',
'RP', 'RETIRA POSTERIOR',
'RI', 'RETIRA IMEDIATA',
'RETIRA IMEDIATA'
) TIPOENTREGA,
PCPEDC.DTENTREGA,
PCPEDI.CODPROD,
PCPRODUT.DESCRICAO,
PCPEDI.QT,
MD5(PCNFSAID.NUMPED || '@Juru2025$') HASH
FROM
PCPEDC,
PCPEDI,
PCNFSAID,
PCCLIENT,
PCUSUARI,
PCPRODUT,
PCFILIAL,
PCFILIAL RETIRA
WHERE
PCNFSAID.NUMPED = PCPEDC.NUMPEDENTFUT
AND PCPEDC.CODFILIAL = PCFILIAL.CODIGO
AND PCPEDI.CODFILIALRETIRA = RETIRA.CODIGO
AND PCPEDI.CODPROD = PCPRODUT.CODPROD
AND PCPEDC.CODCLI = PCCLIENT.CODCLI
AND PCNFSAID.CODUSUR = PCUSUARI.CODUSUR
AND PCPEDC.NUMPED = PCPEDI.NUMPED
AND PCNFSAID.DTCANCEL IS NULL
AND PCNFSAID.CONDVENDA = 7
AND PCPEDC.NUMPEDENTFUT = :1
`;
try {
const rows = (await this.dataSource.query(sql, [numped7])) as unknown;
if (!Array.isArray(rows)) {
throw new InternalServerErrorException('Resposta invalida do banco');
}
if (rows.length === 0) {
throw new NotFoundException(
`Pedido nao encontrado para NUMPEDENTFUT=${numped7}`,
);
}
return rows.map((r) => {
if (r && typeof r === 'object' && !Array.isArray(r)) {
return PedidoSevenService.mapRow(r as Record<string, unknown>);
}
throw new InternalServerErrorException(
'Linha invalida retornada pelo banco',
);
});
} catch (err) {
throw new InternalServerErrorException(
`Erro ao executar a consulta: ${err instanceof Error ? err.message : String(err)}`,
);
}
}
}