prisma: modelo Client + migração 20260527225728_add_client + seed dev (10 clientes)
api: GET /clients (list, busca, filtro atividade/financeiro, paginação) + GET /clients/:id
rep vê carteira própria; supervisor/admin vê tudo; activityStatus calculado de lastOrderAt
@sar/api-interface: ClientSummarySchema, ClientDetailSchema, ClientListResponseSchema
web: ClientsPage (tabela AntD, busca, filtro), DevLogin (token dev), authStore, Bearer no apiFetch
oq-4 resolvida: creditLimit gerenciado no SAR
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
72 lines
3.0 KiB
Plaintext
72 lines
3.0 KiB
Plaintext
// SAR — Workspace Database Schema
|
|
// Stack canon: Prisma 7 · PostgreSQL 18 · BD-por-workspace (ADR 0006)
|
|
//
|
|
// Este schema roda em CADA workspace DB (sar_workspace_<id>).
|
|
// NÃO há workspaceId/tenantId em nenhum modelo — o isolamento é físico.
|
|
// O banco master (sar_master) é gerenciado pelo master-login (IdP JCS), não por este schema.
|
|
//
|
|
// CODING-RULES PGD-DB-004: moduleFormat = "cjs" (NestJS é CJS)
|
|
// CODING-RULES PGD-DB-001: MIGRATION_DATABASE_URL aponta direto ao PG (sem PgBouncer)
|
|
|
|
generator client {
|
|
provider = "prisma-client-js"
|
|
output = "../../../node_modules/.prisma/client"
|
|
moduleFormat = "cjs"
|
|
}
|
|
|
|
// Prisma 7: url removida do schema — conexão em prisma.config.ts (migrate)
|
|
// e no WorkspacePrismaPool via PrismaPg adapter (runtime).
|
|
datasource db {
|
|
provider = "postgresql"
|
|
}
|
|
|
|
// ─── Enums ───────────────────────────────────────────────────────────────────
|
|
|
|
// Situação financeira resumida do cliente — cacheável offline (FR-2.4, FR-2.5).
|
|
// Valor numérico de crédito e inadimplência requerem conexão.
|
|
enum FinancialStatus {
|
|
regular
|
|
attention
|
|
blocked
|
|
}
|
|
|
|
// ─── Client (C2) ─────────────────────────────────────────────────────────────
|
|
//
|
|
// Cadastro sincronizado do ERP legado (FR-2.6). Rep não cria/edita no MVP.
|
|
// creditLimit: gerenciado no SAR — admin/supervisor define (OQ-4 resolvido 2026-05-27).
|
|
// lastOrderAt/lastOrderValue: desnormalizados, atualizados ao sincronizar Orders (C3/C4).
|
|
// activityStatus: calculado em runtime a partir de lastOrderAt (não persiste — evita drift).
|
|
|
|
model Client {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
name String // razão social / nome completo
|
|
tradeName String? // nome fantasia
|
|
taxId String @unique // CNPJ (14 dígitos) ou CPF (11 dígitos), sem máscara
|
|
email String?
|
|
phone String?
|
|
address Json? // { street, number, complement?, district, city, state, zip }
|
|
|
|
// Situação financeira — resumo cacheável; detalhes numéricos requerem conexão
|
|
financialStatus FinancialStatus @default(regular)
|
|
creditLimit Decimal? @db.Decimal(15, 2)
|
|
|
|
// Desnormalizados de Orders (atualizados em C3/C4)
|
|
repId String // userId do Rep responsável (JWT sub)
|
|
lastOrderAt DateTime?
|
|
lastOrderValue Decimal? @db.Decimal(15, 2)
|
|
openOrdersCount Int @default(0)
|
|
|
|
// Controle de sync com ERP
|
|
erpCode String? // código no ERP legado
|
|
syncedAt DateTime?
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
deletedAt DateTime? // soft delete — não remove fisicamente
|
|
|
|
@@index([repId])
|
|
@@index([taxId])
|
|
@@index([name])
|
|
@@index([deletedAt]) // filtragem de soft delete eficiente
|
|
}
|