feat(orders): detalhe completo de pedidos ERP com produtos e pagamento

- Endpoint GET /orders/erp/:idPedido para pedidos do histórico ERP
  (endpoint estático antes de /:id com ParseUUIDPipe, sem conflito)
- JOIN vw_peditens_erp + vw_produtos: itens com codigo + descricao do produto
- forma_pagamento direto da vw_pedidos_erp (ex: "28/35/42 DIAS")
- Retorna PedidoDetail completo: totais, ipi, icmsst, comissao, obs
- Frontend: useOrderDetail detecta 'erp-*' → chama /orders/erp/{id}
- OrderDetailPage: Cond. Pagamento nas Descriptions; oculta botões
  Transmitir/Aprovar/Recusar para pedidos ERP (read-only)
- PedidoItemSchema.id relaxado de uuid() para string() (ERP usa '{id}-{ordem}')
- PedidoDetailSchema: campo formaPagamento opcional adicionado

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-30 21:49:53 +00:00
parent a3c68f9f05
commit 6fbf8bfb8e
5 changed files with 148 additions and 4 deletions

View File

@@ -5,6 +5,7 @@ import {
Get,
HttpCode,
Param,
ParseIntPipe,
ParseUUIDPipe,
Patch,
Post,
@@ -79,6 +80,11 @@ export class OrdersController {
return this.orders.reject(id, parsed);
}
@Get('erp/:idPedido')
findOneErp(@Param('idPedido', ParseIntPipe) idPedido: number): Promise<PedidoDetail> {
return this.orders.findOneErp(idPedido);
}
@Get(':id')
findOne(@Param('id', ParseUUIDPipe) id: string): Promise<PedidoDetail> {
return this.orders.findOne(id);

View File

@@ -595,4 +595,131 @@ export class OrdersService {
})),
};
}
async findOneErp(idPedido: number): Promise<PedidoDetail> {
const prisma = this.cls.get('prisma');
if (!prisma) throw new Error('prisma não disponível no CLS');
const idEmpresa = this.cls.get('idEmpresa');
const role = this.cls.get('role');
const userId = this.cls.get('userId') ?? '0';
const codVendedor = parseInt(userId, 10);
interface ErpHeader {
id_pedido: number;
num_ped_sar: string;
numero: number;
id_cliente: number;
cod_vendedor: number;
situa: number;
status_descr: string;
dt_pedido: Date;
total_produtos: string;
total_ipi: string;
total_icmsst: string;
total: string;
desconto_perc: string;
desconto_valor: string;
acrescimo: string;
comissao: string;
ped_flex: string;
obs: string | null;
forma_pagamento: string | null;
nome_cliente: string | null;
razao_cliente: string | null;
nome_vendedor: string | null;
}
interface ErpItem {
ordem: number;
id_produto: number;
codigo: string | null;
descricao: string | null;
qtd: string;
preco_unitario: string;
desconto_perc: string;
total: string;
}
const vendedorFilter = role === 'rep' ? `AND e.cod_vendedor = ${codVendedor}` : '';
const idMatriz = idEmpresa > 9000 ? idEmpresa - 9000 : idEmpresa;
const [headerRows, itemRows] = await Promise.all([
prisma.$queryRawUnsafe<ErpHeader[]>(`
SELECT e.id_pedido, e.num_ped_sar, e.numero, e.id_cliente, e.cod_vendedor,
e.situa, e.status_descr, e.dt_pedido,
e.total_produtos::text, e.total_ipi::text, e.total_icmsst::text,
e.total::text, e.desconto_perc::text, e.desconto_valor::text,
e.acrescimo::text, e.comissao::text, e.ped_flex::text, e.obs,
TRIM(e.forma_pagamento) AS forma_pagamento,
c.nome AS nome_cliente, c.razao AS razao_cliente,
(SELECT r.nome FROM vw_representantes r
WHERE r.codigo = e.cod_vendedor LIMIT 1) AS nome_vendedor
FROM vw_pedidos_erp e
LEFT JOIN vw_clientes c ON c.id_cliente = e.id_cliente
WHERE e.id_empresa = ${idEmpresa}
AND e.id_pedido = ${idPedido}
${vendedorFilter}
LIMIT 1
`),
prisma.$queryRawUnsafe<ErpItem[]>(`
SELECT ei.ordem, ei.id_produto,
TRIM(p.codigo) AS codigo, TRIM(p.descricao) AS descricao,
ei.qtd::text, ei.preco_unitario::text,
ei.desconto_perc::text, ei.total::text
FROM vw_peditens_erp ei
LEFT JOIN vw_produtos p
ON p.id_erp = ei.id_produto
AND p.id_empresa = ${idMatriz}
WHERE ei.id_pedido = ${idPedido}
ORDER BY ei.ordem
`),
]);
if (!headerRows[0]) throw new NotFoundException(`Pedido ERP ${idPedido} não encontrado`);
const h = headerRows[0];
return {
id: `erp-${h.id_pedido}`,
numPedSar: (h.num_ped_sar ?? '').trim(),
numero: Number(h.numero),
idCliente: Number(h.id_cliente),
nomeCliente: h.nome_cliente ?? null,
razaoCliente: h.razao_cliente ?? null,
codVendedor: Number(h.cod_vendedor),
nomeVendedor: h.nome_vendedor ?? null,
situa: sigToSar(Number(h.situa)),
statusDescr: h.status_descr,
dtPedido: new Date(h.dt_pedido).toISOString(),
total: h.total ?? '0',
descontoPerc: h.desconto_perc ?? '0',
obs: h.obs?.trim() || null,
createdAt: new Date(h.dt_pedido).toISOString(),
updatedAt: new Date(h.dt_pedido).toISOString(),
fonte: 'erp' as const,
formaPagamento: h.forma_pagamento || null,
totalProdutos: h.total_produtos ?? '0',
totalIpi: h.total_ipi ?? '0',
totalIcmsst: h.total_icmsst ?? '0',
descontoValor: h.desconto_valor ?? '0',
acrescimo: h.acrescimo ?? '0',
comissao: h.comissao ?? '0',
pedFlex: h.ped_flex ?? '0',
aprovadoPor: null,
aprovadoEm: null,
motivoRecusa: null,
idempotencyKey: null,
itens: itemRows.map((it) => ({
id: `${idPedido}-${it.ordem}`,
idProduto: Number(it.id_produto),
codProduto: it.codigo ?? null,
descProduto: it.descricao ?? null,
ordem: Number(it.ordem),
qtd: it.qtd,
precoUnitario: it.preco_unitario,
descontoPerc: it.desconto_perc,
total: it.total,
})),
historico: [],
};
}
}