Compare commits
No commits in common. "feat/pedido-seven-grouping-20260317" and "main" have entirely different histories.
feat/pedid
...
main
|
|
@ -1,250 +0,0 @@
|
||||||
# Migracao do Frontend: `/pedidos/condvenda-7/:numped7`
|
|
||||||
|
|
||||||
## O que mudou
|
|
||||||
|
|
||||||
Antes, a API retornava uma lista achatada de itens:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
PedidoCondVenda7RowDto[]
|
|
||||||
```
|
|
||||||
|
|
||||||
Agora, a API retorna uma lista de grupos logisticos:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
PedidoCondVenda7GroupDto[]
|
|
||||||
```
|
|
||||||
|
|
||||||
Cada grupo ja vem separado no backend pela chave:
|
|
||||||
|
|
||||||
```txt
|
|
||||||
numped|tipoentrega|dtentrega|codfilialretira
|
|
||||||
```
|
|
||||||
|
|
||||||
## Regra para o frontend
|
|
||||||
|
|
||||||
- Nao agrupar por conta propria.
|
|
||||||
- Nao usar `hash` como chave de separacao.
|
|
||||||
- Nao inferir `tipoentrega` pelo primeiro item de uma lista mista.
|
|
||||||
- Gerar 1 etiqueta por grupo retornado pela API.
|
|
||||||
|
|
||||||
## Novo formato de resposta
|
|
||||||
|
|
||||||
```ts
|
|
||||||
type PedidoCondVenda7RowDto = {
|
|
||||||
numped: number
|
|
||||||
cgc: string
|
|
||||||
razaosocial: string
|
|
||||||
dtemissao: string
|
|
||||||
codfilial: number
|
|
||||||
filialvenda: string
|
|
||||||
codfilialretira: number
|
|
||||||
filialretira: string
|
|
||||||
data: string
|
|
||||||
numcar: number
|
|
||||||
codcli: number
|
|
||||||
cliente: string
|
|
||||||
codusur: number
|
|
||||||
vendedor: string
|
|
||||||
vlfrete: number
|
|
||||||
valor: number
|
|
||||||
tipoentrega: string
|
|
||||||
dtentrega: string | null
|
|
||||||
codprod: number
|
|
||||||
descricao: string
|
|
||||||
qt: number
|
|
||||||
hash: string
|
|
||||||
}
|
|
||||||
|
|
||||||
type PedidoCondVenda7GroupDto = {
|
|
||||||
groupKey: string
|
|
||||||
numped: number
|
|
||||||
tipoentrega: string
|
|
||||||
dtentrega: string | null
|
|
||||||
codfilialretira: number
|
|
||||||
filialretira: string
|
|
||||||
cgc: string
|
|
||||||
razaosocial: string
|
|
||||||
dtemissao: string
|
|
||||||
codfilial: number
|
|
||||||
filialvenda: string
|
|
||||||
data: string
|
|
||||||
numcar: number
|
|
||||||
codcli: number
|
|
||||||
cliente: string
|
|
||||||
codusur: number
|
|
||||||
vendedor: string
|
|
||||||
vlfrete: number
|
|
||||||
valor: number
|
|
||||||
hash: string
|
|
||||||
items: PedidoCondVenda7RowDto[]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Exemplo de resposta
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"groupKey": "157073710|RETIRA POSTERIOR|2026-03-11T00:00:00.000Z|4",
|
|
||||||
"numped": 157073710,
|
|
||||||
"tipoentrega": "RETIRA POSTERIOR",
|
|
||||||
"dtentrega": "2026-03-11T00:00:00.000Z",
|
|
||||||
"codfilialretira": 4,
|
|
||||||
"filialretira": "JURUNENSE BR",
|
|
||||||
"cgc": "13772792000407",
|
|
||||||
"razaosocial": "JURUNENSE HOME CENTER LTDA",
|
|
||||||
"dtemissao": "2026-03-17T18:58:51.000Z",
|
|
||||||
"codfilial": 4,
|
|
||||||
"filialvenda": "JURUNENSE BR",
|
|
||||||
"data": "2026-03-04T00:00:00.000Z",
|
|
||||||
"numcar": 0,
|
|
||||||
"codcli": 247485,
|
|
||||||
"cliente": "SORAIA CRISTINA SILVA DA COSTA",
|
|
||||||
"codusur": 157,
|
|
||||||
"vendedor": "BR - LOURIVAL EDVALDO COSTA FERREIRA",
|
|
||||||
"vlfrete": 0,
|
|
||||||
"valor": 827.83,
|
|
||||||
"hash": "4368D18F69C36357A1FD52271EF58861",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"numped": 157073710,
|
|
||||||
"tipoentrega": "RETIRA POSTERIOR",
|
|
||||||
"codprod": 12586,
|
|
||||||
"descricao": "ARG COZ E BANHEIROS 20KG QUARTZ",
|
|
||||||
"qt": 7,
|
|
||||||
"hash": "4368D18F69C36357A1FD52271EF58861"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"groupKey": "157073714|ENTREGA|2026-03-11T00:00:00.000Z|12",
|
|
||||||
"numped": 157073714,
|
|
||||||
"tipoentrega": "ENTREGA",
|
|
||||||
"dtentrega": "2026-03-11T00:00:00.000Z",
|
|
||||||
"codfilialretira": 12,
|
|
||||||
"filialretira": "JURUNENSE DISTRITO",
|
|
||||||
"cgc": "13772792001217",
|
|
||||||
"razaosocial": "JURUNENSE HOME CENTER LTDA",
|
|
||||||
"dtemissao": "2026-03-17T18:58:51.000Z",
|
|
||||||
"codfilial": 12,
|
|
||||||
"filialvenda": "JURUNENSE DISTRITO",
|
|
||||||
"data": "2026-03-04T00:00:00.000Z",
|
|
||||||
"numcar": 8899569,
|
|
||||||
"codcli": 247485,
|
|
||||||
"cliente": "SORAIA CRISTINA SILVA DA COSTA",
|
|
||||||
"codusur": 157,
|
|
||||||
"vendedor": "BR - LOURIVAL EDVALDO COSTA FERREIRA",
|
|
||||||
"vlfrete": 0,
|
|
||||||
"valor": 0,
|
|
||||||
"hash": "4368D18F69C36357A1FD52271EF58861",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"numped": 157073714,
|
|
||||||
"tipoentrega": "ENTREGA",
|
|
||||||
"codprod": 54001,
|
|
||||||
"descricao": "CER 66X66RT MILANO BG GR 2,18M",
|
|
||||||
"qt": 4,
|
|
||||||
"hash": "4368D18F69C36357A1FD52271EF58861"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"numped": 157073714,
|
|
||||||
"tipoentrega": "ENTREGA",
|
|
||||||
"codprod": 54067,
|
|
||||||
"descricao": "CER 45X45 BEGE 45 2,00M",
|
|
||||||
"qt": 14,
|
|
||||||
"hash": "4368D18F69C36357A1FD52271EF58861"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
## O que mudar no frontend
|
|
||||||
|
|
||||||
### 1. Tipagem
|
|
||||||
|
|
||||||
Trocar o tipo esperado da resposta:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const data: PedidoCondVenda7RowDto[] = await response.json()
|
|
||||||
```
|
|
||||||
|
|
||||||
por:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const groups: PedidoCondVenda7GroupDto[] = await response.json()
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Renderizacao
|
|
||||||
|
|
||||||
Antes:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
renderLabel(data)
|
|
||||||
```
|
|
||||||
|
|
||||||
Agora:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
groups.forEach((group) => {
|
|
||||||
renderLabel(group)
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Cabecalho da etiqueta
|
|
||||||
|
|
||||||
Os dados do cabecalho devem vir do grupo:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
group.numped
|
|
||||||
group.tipoentrega
|
|
||||||
group.dtentrega
|
|
||||||
group.cliente
|
|
||||||
group.filialretira
|
|
||||||
group.vendedor
|
|
||||||
group.hash
|
|
||||||
group.valor
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Itens da etiqueta
|
|
||||||
|
|
||||||
A tabela de produtos deve vir de:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
group.items
|
|
||||||
```
|
|
||||||
|
|
||||||
Exemplo:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
group.items.map((item) => ({
|
|
||||||
codprod: item.codprod,
|
|
||||||
descricao: item.descricao,
|
|
||||||
qt: item.qt,
|
|
||||||
}))
|
|
||||||
```
|
|
||||||
|
|
||||||
## Erro antigo que nao pode voltar
|
|
||||||
|
|
||||||
Se existir algo assim:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const firstItem = data[0]
|
|
||||||
const tipoEntrega = firstItem.tipoentrega
|
|
||||||
```
|
|
||||||
|
|
||||||
isso precisa sair se `data` representar mais de um pedido misturado.
|
|
||||||
|
|
||||||
Agora o correto e:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const tipoEntrega = group.tipoentrega
|
|
||||||
const items = group.items
|
|
||||||
```
|
|
||||||
|
|
||||||
## Resumo operacional
|
|
||||||
|
|
||||||
- 1 grupo retornado = 1 etiqueta
|
|
||||||
- cabecalho vem do grupo
|
|
||||||
- produtos vem de `group.items`
|
|
||||||
- separacao ja vem pronta do backend
|
|
||||||
|
|
@ -8,8 +8,7 @@ import {
|
||||||
ApiTags,
|
ApiTags,
|
||||||
} from '@nestjs/swagger';
|
} from '@nestjs/swagger';
|
||||||
import { PedidoSevenService } from '../services/pedido-seven.service';
|
import { PedidoSevenService } from '../services/pedido-seven.service';
|
||||||
import { PedidoCondVenda7GroupDto } from '../dto/pedido-condvenda7-group.dto';
|
import { PedidoCondVenda7RowDto } from '../dto/pedido-condvenda7-row.dto';
|
||||||
import { PedidoSevenParamDto } from '../dto/pedido-seven-param.dto';
|
|
||||||
|
|
||||||
@ApiTags('Pedidos')
|
@ApiTags('Pedidos')
|
||||||
@Controller('pedidos')
|
@Controller('pedidos')
|
||||||
|
|
@ -22,13 +21,12 @@ export class PedidoSevenController {
|
||||||
})
|
})
|
||||||
@ApiParam({
|
@ApiParam({
|
||||||
name: 'numped7',
|
name: 'numped7',
|
||||||
type: Number,
|
|
||||||
example: 123456,
|
example: 123456,
|
||||||
description: 'Valor para PCPEDC.NUMPEDENTFUT',
|
description: 'Valor para PCPEDC.NUMPEDENTFUT',
|
||||||
})
|
})
|
||||||
@ApiOkResponse({
|
@ApiOkResponse({
|
||||||
description: 'Grupos logisticos retornados pela consulta (pedido x itens)',
|
description: 'Linhas retornadas pela consulta (pedido x itens)',
|
||||||
type: [PedidoCondVenda7GroupDto],
|
type: [PedidoCondVenda7RowDto],
|
||||||
})
|
})
|
||||||
@ApiNotFoundResponse({
|
@ApiNotFoundResponse({
|
||||||
description: 'Pedido nao encontrado para o NUMPEDENTFUT informado',
|
description: 'Pedido nao encontrado para o NUMPEDENTFUT informado',
|
||||||
|
|
@ -36,9 +34,7 @@ export class PedidoSevenController {
|
||||||
@ApiServiceUnavailableResponse({
|
@ApiServiceUnavailableResponse({
|
||||||
description: 'Banco de dados indisponivel (DataSource nao inicializado)',
|
description: 'Banco de dados indisponivel (DataSource nao inicializado)',
|
||||||
})
|
})
|
||||||
async consultar(
|
async consultar(@Param('numped7', ParseIntPipe) numped7: number) {
|
||||||
@Param('numped7', ParseIntPipe) numped7: PedidoSevenParamDto['numped7'],
|
|
||||||
) {
|
|
||||||
return this.pedidoSevenService.consultarPorNumped7(numped7);
|
return this.pedidoSevenService.consultarPorNumped7(numped7);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
export * from './pedido-condvenda7-row.dto';
|
|
||||||
export * from './pedido-condvenda7-row.mapper';
|
|
||||||
export * from './pedido-seven-param.dto';
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
|
||||||
import { PedidoCondVenda7RowDto } from './pedido-condvenda7-row.dto';
|
|
||||||
|
|
||||||
export class PedidoCondVenda7GroupDto {
|
|
||||||
@ApiProperty({
|
|
||||||
example: '157073710|RETIRA POSTERIOR|2026-03-11T00:00:00.000Z|4',
|
|
||||||
})
|
|
||||||
groupKey!: string;
|
|
||||||
|
|
||||||
@ApiProperty({ example: 157073710 })
|
|
||||||
numped!: number;
|
|
||||||
|
|
||||||
@ApiProperty({ example: 'ENTREGA' })
|
|
||||||
tipoentrega!: string;
|
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
type: String,
|
|
||||||
format: 'date-time',
|
|
||||||
required: false,
|
|
||||||
nullable: true,
|
|
||||||
})
|
|
||||||
dtentrega!: Date | null;
|
|
||||||
|
|
||||||
@ApiProperty({ example: 4 })
|
|
||||||
codfilialretira!: number;
|
|
||||||
|
|
||||||
@ApiProperty({ example: 'JURUNENSE BR' })
|
|
||||||
filialretira!: string;
|
|
||||||
|
|
||||||
@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({ 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: 'e10adc3949ba59abbe56e057f20f883e' })
|
|
||||||
hash!: string;
|
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
type: [PedidoCondVenda7RowDto],
|
|
||||||
description: 'Itens pertencentes ao grupo logistico',
|
|
||||||
})
|
|
||||||
items!: PedidoCondVenda7RowDto[];
|
|
||||||
}
|
|
||||||
|
|
@ -1,124 +0,0 @@
|
||||||
import { InternalServerErrorException } from '@nestjs/common';
|
|
||||||
import { z, ZodError } from 'zod';
|
|
||||||
import { PedidoCondVenda7RowDto } from './pedido-condvenda7-row.dto';
|
|
||||||
|
|
||||||
function normalizeRowKeys(row: Record<string, unknown>): Record<string, unknown> {
|
|
||||||
return Object.entries(row).reduce<Record<string, unknown>>((acc, [key, value]) => {
|
|
||||||
acc[key.replace(/_/g, '').toUpperCase()] = value;
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
function coerceNumber(field: string) {
|
|
||||||
return z.preprocess((value) => {
|
|
||||||
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
||||||
if (typeof value === 'string' && value.trim() !== '') {
|
|
||||||
const parsed = Number(value);
|
|
||||||
if (Number.isFinite(parsed)) return parsed;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}, z.number({ invalid_type_error: `Campo ${field} invalido` }));
|
|
||||||
}
|
|
||||||
|
|
||||||
function coerceString(field: string) {
|
|
||||||
return z.preprocess((value) => {
|
|
||||||
if (typeof value === 'string') return value;
|
|
||||||
if (value == null) return '';
|
|
||||||
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
||||||
return String(value);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}, z.string({ invalid_type_error: `Campo ${field} invalido` }));
|
|
||||||
}
|
|
||||||
|
|
||||||
function coerceDate(field: string) {
|
|
||||||
return z.preprocess((value) => {
|
|
||||||
if (value instanceof Date && !Number.isNaN(value.getTime())) return value;
|
|
||||||
if (typeof value === 'string' || typeof value === 'number') {
|
|
||||||
const parsed = new Date(value);
|
|
||||||
if (!Number.isNaN(parsed.getTime())) return parsed;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}, z.date({ invalid_type_error: `Campo ${field} invalido` }));
|
|
||||||
}
|
|
||||||
|
|
||||||
const pedidoCondVenda7RowSchema = z.object({
|
|
||||||
NUMPED: coerceNumber('NUMPED'),
|
|
||||||
CGC: coerceString('CGC'),
|
|
||||||
RAZAOSOCIAL: coerceString('RAZAOSOCIAL'),
|
|
||||||
DTEMISSAO: coerceDate('DTEMISSAO'),
|
|
||||||
CODFILIAL: coerceNumber('CODFILIAL'),
|
|
||||||
FILIALVENDA: coerceString('FILIALVENDA'),
|
|
||||||
CODFILIALRETIRA: coerceNumber('CODFILIALRETIRA'),
|
|
||||||
FILIALRETIRA: coerceString('FILIALRETIRA'),
|
|
||||||
DATA: coerceDate('DATA'),
|
|
||||||
NUMCAR: coerceNumber('NUMCAR'),
|
|
||||||
CODCLI: coerceNumber('CODCLI'),
|
|
||||||
CLIENTE: coerceString('CLIENTE'),
|
|
||||||
CODUSUR: coerceNumber('CODUSUR'),
|
|
||||||
VENDEDOR: coerceString('VENDEDOR'),
|
|
||||||
VLFRETE: coerceNumber('VLFRETE'),
|
|
||||||
VALOR: z.preprocess((value) => {
|
|
||||||
if (value == null) return 0;
|
|
||||||
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
||||||
if (typeof value === 'string' && value.trim() !== '') {
|
|
||||||
const parsed = Number(value);
|
|
||||||
if (Number.isFinite(parsed)) return parsed;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}, z.number()),
|
|
||||||
TIPOENTREGA: coerceString('TIPOENTREGA'),
|
|
||||||
DTENTREGA: z.preprocess((value) => {
|
|
||||||
if (value == null) return null;
|
|
||||||
if (value instanceof Date && !Number.isNaN(value.getTime())) return value;
|
|
||||||
if (typeof value === 'string' || typeof value === 'number') {
|
|
||||||
const parsed = new Date(value);
|
|
||||||
if (!Number.isNaN(parsed.getTime())) return parsed;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}, z.date().nullable()),
|
|
||||||
CODPROD: coerceNumber('CODPROD'),
|
|
||||||
DESCRICAO: coerceString('DESCRICAO'),
|
|
||||||
QT: coerceNumber('QT'),
|
|
||||||
HASH: coerceString('HASH'),
|
|
||||||
});
|
|
||||||
|
|
||||||
export function mapPedidoCondVenda7Row(
|
|
||||||
row: Record<string, unknown>,
|
|
||||||
): PedidoCondVenda7RowDto {
|
|
||||||
try {
|
|
||||||
const parsed = pedidoCondVenda7RowSchema.parse(normalizeRowKeys(row));
|
|
||||||
|
|
||||||
return Object.assign(new PedidoCondVenda7RowDto(), {
|
|
||||||
numped: parsed.NUMPED,
|
|
||||||
cgc: parsed.CGC,
|
|
||||||
razaosocial: parsed.RAZAOSOCIAL,
|
|
||||||
dtemissao: parsed.DTEMISSAO,
|
|
||||||
codfilial: parsed.CODFILIAL,
|
|
||||||
filialvenda: parsed.FILIALVENDA,
|
|
||||||
codfilialretira: parsed.CODFILIALRETIRA,
|
|
||||||
filialretira: parsed.FILIALRETIRA,
|
|
||||||
data: parsed.DATA,
|
|
||||||
numcar: parsed.NUMCAR,
|
|
||||||
codcli: parsed.CODCLI,
|
|
||||||
cliente: parsed.CLIENTE,
|
|
||||||
codusur: parsed.CODUSUR,
|
|
||||||
vendedor: parsed.VENDEDOR,
|
|
||||||
vlfrete: parsed.VLFRETE,
|
|
||||||
valor: parsed.VALOR,
|
|
||||||
tipoentrega: parsed.TIPOENTREGA,
|
|
||||||
dtentrega: parsed.DTENTREGA,
|
|
||||||
codprod: parsed.CODPROD,
|
|
||||||
descricao: parsed.DESCRICAO,
|
|
||||||
qt: parsed.QT,
|
|
||||||
hash: parsed.HASH,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof ZodError) {
|
|
||||||
const message = error.issues[0]?.message ?? 'Linha invalida retornada pelo banco';
|
|
||||||
throw new InternalServerErrorException(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
|
||||||
|
|
||||||
export class PedidoSevenParamDto {
|
|
||||||
@ApiProperty({
|
|
||||||
example: 123456,
|
|
||||||
description: 'Valor para PCPEDC.NUMPEDENTFUT',
|
|
||||||
})
|
|
||||||
numped7!: number;
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import {
|
import {
|
||||||
HttpException,
|
|
||||||
Injectable,
|
Injectable,
|
||||||
InternalServerErrorException,
|
InternalServerErrorException,
|
||||||
NotFoundException,
|
NotFoundException,
|
||||||
|
|
@ -7,17 +6,119 @@ import {
|
||||||
ServiceUnavailableException,
|
ServiceUnavailableException,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { DataSource } from 'typeorm';
|
import { DataSource } from 'typeorm';
|
||||||
import { PedidoCondVenda7GroupDto } from '../dto/pedido-condvenda7-group.dto';
|
|
||||||
import { PedidoCondVenda7RowDto } from '../dto/pedido-condvenda7-row.dto';
|
import { PedidoCondVenda7RowDto } from '../dto/pedido-condvenda7-row.dto';
|
||||||
import { mapPedidoCondVenda7Row } from '../dto/pedido-condvenda7-row.mapper';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PedidoSevenService {
|
export class PedidoSevenService {
|
||||||
constructor(@Optional() private readonly dataSource?: DataSource) {}
|
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 toNullableNumber(value: unknown): number {
|
||||||
|
if (value == null) return 0;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.toNullableNumber(this.pick(row, '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(
|
async consultarPorNumped7(
|
||||||
numped7: number,
|
numped7: number,
|
||||||
): Promise<PedidoCondVenda7GroupDto[]> {
|
): Promise<PedidoCondVenda7RowDto[]> {
|
||||||
if (!this.dataSource) {
|
if (!this.dataSource) {
|
||||||
throw new ServiceUnavailableException('DataSource nao disponivel');
|
throw new ServiceUnavailableException('DataSource nao disponivel');
|
||||||
}
|
}
|
||||||
|
|
@ -41,39 +142,45 @@ SELECT
|
||||||
PCPEDC.DATA,
|
PCPEDC.DATA,
|
||||||
PCPEDC.NUMCAR,
|
PCPEDC.NUMCAR,
|
||||||
PCPEDC.CODCLI,
|
PCPEDC.CODCLI,
|
||||||
PCCLIENT.CLIENTE,
|
PCCLIENT.CLIENTE CLIENTE,
|
||||||
PCNFSAID.CODUSUR,
|
PCNFSAID.CODUSUR,
|
||||||
PCUSUARI.NOME VENDEDOR,
|
PCUSUARI.NOME VENDEDOR,
|
||||||
PCPEDC.VLFRETE,
|
PCPEDC.VLFRETE,
|
||||||
CASE
|
CASE
|
||||||
WHEN ROW_NUMBER() OVER (
|
WHEN ROW_NUMBER() OVER (
|
||||||
PARTITION BY PCPEDC.NUMPED
|
PARTITION BY PCPEDC.NUMPEDENTFUT
|
||||||
ORDER BY PCPEDI.ROWID
|
ORDER BY PCPEDC.NUMPED, PCPEDI.ROWID
|
||||||
) = 1
|
) = 1
|
||||||
THEN SUM(PCPEDI.QT * PCPEDI.PVENDA) OVER (PARTITION BY PCPEDC.NUMPED)
|
THEN CASE
|
||||||
|
WHEN NVL(PCNFSAID.VLTOTGER, 0) = 0 THEN PCNFSAID.VLTOTAL
|
||||||
|
ELSE PCNFSAID.VLTOTGER
|
||||||
|
END
|
||||||
ELSE NULL
|
ELSE NULL
|
||||||
END AS VALOR,
|
END VALOR,
|
||||||
CASE PCPEDI.TIPOENTREGA
|
DECODE(
|
||||||
WHEN 'EN' THEN 'ENTREGA'
|
PCPEDI.TIPOENTREGA,
|
||||||
WHEN 'EF' THEN 'ENCOMENDA'
|
'EN', 'ENTREGA',
|
||||||
WHEN 'RP' THEN 'RETIRA POSTERIOR'
|
'EF', 'ENCOMENDA',
|
||||||
WHEN 'RI' THEN 'RETIRA IMEDIATA'
|
'RP', 'RETIRA POSTERIOR',
|
||||||
ELSE 'RETIRA IMEDIATA'
|
'RI', 'RETIRA IMEDIATA',
|
||||||
END AS TIPOENTREGA,
|
'RETIRA IMEDIATA'
|
||||||
|
) TIPOENTREGA,
|
||||||
PCPEDC.DTENTREGA,
|
PCPEDC.DTENTREGA,
|
||||||
PCPEDI.CODPROD,
|
PCPEDI.CODPROD,
|
||||||
PCPRODUT.DESCRICAO,
|
PCPRODUT.DESCRICAO,
|
||||||
PCPEDI.QT,
|
PCPEDI.QT,
|
||||||
MD5(PCNFSAID.NUMPED || '@Juru2025$') HASH
|
MD5(PCNFSAID.NUMPED || '@Juru2025$') HASH
|
||||||
FROM PCPEDC,
|
FROM
|
||||||
PCPEDI,
|
PCPEDC,
|
||||||
PCNFSAID,
|
PCPEDI,
|
||||||
PCCLIENT,
|
PCNFSAID,
|
||||||
PCUSUARI,
|
PCCLIENT,
|
||||||
PCPRODUT,
|
PCUSUARI,
|
||||||
PCFILIAL,
|
PCPRODUT,
|
||||||
PCFILIAL RETIRA
|
PCFILIAL,
|
||||||
WHERE PCNFSAID.NUMPED = PCPEDC.NUMPEDENTFUT
|
PCFILIAL RETIRA
|
||||||
|
WHERE
|
||||||
|
PCNFSAID.NUMPED = PCPEDC.NUMPEDENTFUT
|
||||||
AND PCPEDC.CODFILIAL = PCFILIAL.CODIGO
|
AND PCPEDC.CODFILIAL = PCFILIAL.CODIGO
|
||||||
AND PCPEDI.CODFILIALRETIRA = RETIRA.CODIGO
|
AND PCPEDI.CODFILIALRETIRA = RETIRA.CODIGO
|
||||||
AND PCPEDI.CODPROD = PCPRODUT.CODPROD
|
AND PCPEDI.CODPROD = PCPRODUT.CODPROD
|
||||||
|
|
@ -83,7 +190,9 @@ WHERE PCNFSAID.NUMPED = PCPEDC.NUMPEDENTFUT
|
||||||
AND PCNFSAID.DTCANCEL IS NULL
|
AND PCNFSAID.DTCANCEL IS NULL
|
||||||
AND PCNFSAID.CONDVENDA = 7
|
AND PCNFSAID.CONDVENDA = 7
|
||||||
AND PCPEDC.NUMPEDENTFUT = :1
|
AND PCPEDC.NUMPEDENTFUT = :1
|
||||||
ORDER BY PCPEDC.NUMPED, PCPEDI.CODPROD
|
ORDER BY
|
||||||
|
PCPEDC.NUMPED,
|
||||||
|
PCPEDI.CODPROD
|
||||||
`;
|
`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -98,80 +207,18 @@ ORDER BY PCPEDC.NUMPED, PCPEDI.CODPROD
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const mappedRows = rows.map((r) => {
|
return rows.map((r) => {
|
||||||
if (r && typeof r === 'object' && !Array.isArray(r)) {
|
if (r && typeof r === 'object' && !Array.isArray(r)) {
|
||||||
return mapPedidoCondVenda7Row(r as Record<string, unknown>);
|
return PedidoSevenService.mapRow(r as Record<string, unknown>);
|
||||||
}
|
}
|
||||||
throw new InternalServerErrorException(
|
throw new InternalServerErrorException(
|
||||||
'Linha invalida retornada pelo banco',
|
'Linha invalida retornada pelo banco',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.groupRows(mappedRows);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof HttpException) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new InternalServerErrorException(
|
throw new InternalServerErrorException(
|
||||||
`Erro ao executar a consulta: ${err instanceof Error ? err.message : String(err)}`,
|
`Erro ao executar a consulta: ${err instanceof Error ? err.message : String(err)}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private groupRows(rows: PedidoCondVenda7RowDto[]): PedidoCondVenda7GroupDto[] {
|
|
||||||
const groups = new Map<string, PedidoCondVenda7GroupDto>();
|
|
||||||
|
|
||||||
for (const row of rows) {
|
|
||||||
const groupKey = this.buildGroupKey(row);
|
|
||||||
const existingGroup = groups.get(groupKey);
|
|
||||||
|
|
||||||
if (existingGroup) {
|
|
||||||
existingGroup.items.push(row);
|
|
||||||
existingGroup.valor += row.valor ?? 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
groups.set(
|
|
||||||
groupKey,
|
|
||||||
Object.assign(new PedidoCondVenda7GroupDto(), {
|
|
||||||
groupKey,
|
|
||||||
numped: row.numped,
|
|
||||||
tipoentrega: row.tipoentrega,
|
|
||||||
dtentrega: row.dtentrega,
|
|
||||||
codfilialretira: row.codfilialretira,
|
|
||||||
filialretira: row.filialretira,
|
|
||||||
cgc: row.cgc,
|
|
||||||
razaosocial: row.razaosocial,
|
|
||||||
dtemissao: row.dtemissao,
|
|
||||||
codfilial: row.codfilial,
|
|
||||||
filialvenda: row.filialvenda,
|
|
||||||
data: row.data,
|
|
||||||
numcar: row.numcar,
|
|
||||||
codcli: row.codcli,
|
|
||||||
cliente: row.cliente,
|
|
||||||
codusur: row.codusur,
|
|
||||||
vendedor: row.vendedor,
|
|
||||||
vlfrete: row.vlfrete,
|
|
||||||
valor: row.valor ?? 0,
|
|
||||||
hash: row.hash,
|
|
||||||
items: [row],
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Array.from(groups.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildGroupKey(row: PedidoCondVenda7RowDto): string {
|
|
||||||
const dtentrega =
|
|
||||||
row.dtentrega instanceof Date ? row.dtentrega.toISOString() : 'null';
|
|
||||||
|
|
||||||
return [
|
|
||||||
row.numped,
|
|
||||||
row.tipoentrega,
|
|
||||||
dtentrega,
|
|
||||||
row.codfilialretira,
|
|
||||||
].join('|');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue