refactor(erp): integração direta com banco ERP — schema sar

Revoga ADR 0006 (BD-por-workspace separado). O SAR agora conecta ao
banco PostgreSQL do ERP (módulo SIG) e usa o schema `sar` para tudo.

PRISMA
- Remove: Client, Product, Order, OrderItem, OrderStatusHistory,
  RepTarget, RepDiscountLimit, PushSubscription (modelos isolados)
- Adiciona: Pedido, PedidoItem, HistoricoPedido, AlcadaDesconto,
  MetaRepresentante, PushSubscription (mapeados para sar.*)
- IDs: id_cliente/cod_vendedor/id_empresa são INTEGER (ERP)
- situa: Int (1=Pendente 2=Aprovado 3=Cancelado 4=Faturado)
- JWT: workspace_id:string → id_empresa:number
- URL: inclui ?schema=sar para Prisma rotear ao schema ERP

SERVICES
- ClientsService: $queryRawUnsafe contra sar.vw_clientes + sar.pedidos
- CatalogService: $queryRawUnsafe contra sar.vw_produtos + sar.vw_estoque
- OrdersService: Prisma models Pedido/PedidoItem/HistoricoPedido/AlcadaDesconto
- DashboardService: MetaRepresentante + queries raw para inativos
- NotificationsService: PushSubscription com codVendedor + idEmpresa

CONTRATOS (api-interface)
- client.contract: campos ERP (idCliente, nome, cgcpf, cod_vendedor…)
- order.contract: PedidoSummary/PedidoDetail/CreatePedido + SITUA_LABEL
- product.contract: ProdutoSummary/ProdutoDetail (vw_produtos)
- auth.contract: workspaceId:string → idEmpresa:number

WEB
- Todos os cockpits e queries atualizados para os novos tipos

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-28 21:51:16 +00:00
parent 246eb28bb1
commit b0b60d7a14
39 changed files with 1433 additions and 1544 deletions

View File

@@ -1,25 +1,25 @@
import { useQuery } from '@tanstack/react-query';
import {
ProductListResponseSchema,
type ProductListQuery,
type ProductListResponse,
ProdutoListResponseSchema,
type ProdutoListQuery,
type ProdutoListResponse,
} from '@sar/api-interface';
import { apiFetch } from '../api-client';
export function useCatalog(params: Partial<ProductListQuery> = {}) {
export function useCatalog(params: Partial<ProdutoListQuery> = {}) {
const search = new URLSearchParams();
if (params.q) search.set('q', params.q);
if (params.category) search.set('category', params.category);
if (params.codGrupo) search.set('codGrupo', String(params.codGrupo));
if (params.page) search.set('page', String(params.page));
if (params.limit) search.set('limit', String(params.limit));
const qs = search.toString();
return useQuery<ProductListResponse>({
return useQuery<ProdutoListResponse>({
queryKey: ['catalog', params],
queryFn: async () => {
const res = await apiFetch(`/catalog${qs ? `?${qs}` : ''}`);
if (!res.ok) throw new Error(`catalog error ${res.status}`);
return ProductListResponseSchema.parse(await res.json());
return ProdutoListResponseSchema.parse(await res.json());
},
staleTime: 4 * 60 * 60 * 1000, // TTL 4h — FR-4.4
});