api.ts 10.1 KB
// vibe_erp REST API types.
//
// Hand-written rather than codegen'd from /v3/api-docs to keep the
// build pipeline simple — the framework already has springdoc-openapi
// serving the live spec, but adding @openapitools/openapi-generator-cli
// would pull in another Java toolchain into the npm build. v1 SPA can
// stay typed by hand; v1.x can revisit codegen if drift becomes a
// real problem. Every type here mirrors the matching @RestController's
// response DTO under pbc/*/http/.
//
// **BigDecimal as string.** The Spring Boot Jackson default serializes
// java.math.BigDecimal as a JSON number, which JavaScript would coerce
// to a 64-bit float and lose precision on quantities like 12345.6789.
// In practice the framework configures `spring.jackson.write-bigdecimal-as-plain`
// (default for Spring Boot 3) which still emits a JSON *number*; the SPA
// stores them as `string` because we never do client-side arithmetic on
// them — display only. If a future SPA needs sums it should round-trip
// through decimal.js, not Number().

export interface MetaInfo {
  name: string
  apiVersion: string
  implementationVersion: string
  buildTime: string | null
  activeProfiles: string[]
}

// ─── Auth (pbc-identity AuthController) ──────────────────────────────

export interface LoginRequest {
  username: string
  password: string
}

export interface TokenPair {
  accessToken: string
  accessExpiresAt: string
  refreshToken: string
  refreshExpiresAt: string
  tokenType: string
}

// ─── Identity (pbc-identity) ─────────────────────────────────────────

export interface User {
  id: string
  username: string
  displayName: string
  email: string | null
  enabled: boolean
}

export interface Role {
  id: string
  code: string
  name: string
  description: string | null
}

// ─── Catalog (pbc-catalog) ───────────────────────────────────────────

export type ItemType = 'GOOD' | 'SERVICE' | 'DIGITAL'

export interface Item {
  id: string
  code: string
  name: string
  description: string | null
  itemType: ItemType
  baseUomCode: string
  active: boolean
  ext: Record<string, unknown>
}

export interface Uom {
  id: string
  code: string
  name: string
  dimension: string
}

// ─── Partners (pbc-partners) ─────────────────────────────────────────

export type PartnerType = 'CUSTOMER' | 'SUPPLIER' | 'BOTH'

export interface Partner {
  id: string
  code: string
  name: string
  type: PartnerType
  taxId: string | null
  website: string | null
  email: string | null
  phone: string | null
  active: boolean
  ext: Record<string, unknown>
}

// ─── Inventory (pbc-inventory) ───────────────────────────────────────

export type LocationType = 'WAREHOUSE' | 'BIN' | 'VIRTUAL'

export interface Location {
  id: string
  code: string
  name: string
  type: LocationType
  active: boolean
  ext: Record<string, unknown>
}

// Note: balances/movements use a UUID `locationId`, not `locationCode`.
// The list pages join against `Location.id → code` client-side.

export interface StockBalance {
  id: string
  itemCode: string
  locationId: string
  quantity: string | number
}

export type MovementReason =
  | 'RECEIPT'
  | 'ISSUE'
  | 'ADJUSTMENT'
  | 'SALES_SHIPMENT'
  | 'PURCHASE_RECEIPT'
  | 'TRANSFER_OUT'
  | 'TRANSFER_IN'
  | 'MATERIAL_ISSUE'
  | 'PRODUCTION_RECEIPT'

export interface StockMovement {
  id: string
  itemCode: string
  locationId: string
  delta: string | number
  reason: MovementReason
  reference: string | null
  occurredAt: string
  resultingQuantity?: string | number
}

// ─── Sales orders (pbc-orders-sales) ─────────────────────────────────

export type SalesOrderStatus = 'DRAFT' | 'CONFIRMED' | 'SHIPPED' | 'CANCELLED'

export interface SalesOrderLine {
  id: string
  lineNo: number
  itemCode: string
  quantity: string | number
  unitPrice: string | number
  currencyCode: string
  lineTotal: string | number
}

export interface SalesOrder {
  id: string
  code: string
  partnerCode: string
  status: SalesOrderStatus
  orderDate: string
  currencyCode: string
  totalAmount: string | number
  lines: SalesOrderLine[]
  ext: Record<string, unknown>
}

// ─── Purchase orders (pbc-orders-purchase) ───────────────────────────

export type PurchaseOrderStatus = 'DRAFT' | 'CONFIRMED' | 'RECEIVED' | 'CANCELLED'

export interface PurchaseOrderLine {
  id: string
  lineNo: number
  itemCode: string
  quantity: string | number
  unitPrice: string | number
  currencyCode: string
  lineTotal: string | number
}

export interface PurchaseOrder {
  id: string
  code: string
  partnerCode: string
  status: PurchaseOrderStatus
  orderDate: string
  expectedDate: string | null
  currencyCode: string
  totalAmount: string | number
  lines: PurchaseOrderLine[]
  ext: Record<string, unknown>
}

// ─── Production (pbc-production) ─────────────────────────────────────

export type WorkOrderStatus = 'DRAFT' | 'IN_PROGRESS' | 'COMPLETED' | 'CANCELLED'
export type WorkOrderOperationStatus = 'PENDING' | 'IN_PROGRESS' | 'COMPLETED'

export interface WorkOrderInput {
  id: string
  lineNo: number
  itemCode: string
  quantityPerUnit: string | number
  sourceLocationCode: string
}

export interface WorkOrderOperation {
  id: string
  lineNo: number
  operationCode: string
  workCenter: string
  standardMinutes: string | number
  status: WorkOrderOperationStatus
  actualMinutes: string | number | null
  startedAt: string | null
  completedAt: string | null
}

export interface WorkOrder {
  id: string
  code: string
  outputItemCode: string
  outputQuantity: string | number
  status: WorkOrderStatus
  dueDate: string | null
  sourceSalesOrderCode: string | null
  inputs: WorkOrderInput[]
  operations: WorkOrderOperation[]
  ext: Record<string, unknown>
}

export interface ShopFloorEntry {
  workOrderId: string
  workOrderCode: string
  outputItemCode: string
  outputQuantity: string | number
  sourceSalesOrderCode: string | null
  currentOperationLineNo: number | null
  currentOperationCode: string | null
  currentWorkCenter: string | null
  currentOperationStatus: WorkOrderOperationStatus | null
  operationsCompleted: number
  operationsTotal: number
  totalStandardMinutes: string | number
  totalActualMinutes: string | number
}

// ─── Finance (pbc-finance) ───────────────────────────────────────────

export type AccountType = 'ASSET' | 'LIABILITY' | 'EQUITY' | 'REVENUE' | 'EXPENSE'

export interface Account {
  id: string
  code: string
  name: string
  accountType: AccountType
  description: string | null
  active: boolean
}

export type JournalEntryType = 'AR' | 'AP'
export type JournalEntryStatus = 'POSTED' | 'SETTLED' | 'REVERSED'

export interface JournalEntryLine {
  lineNo: number
  accountCode: string
  debit: string | number
  credit: string | number
  description: string | null
}

export interface JournalEntry {
  id: string
  code: string
  type: JournalEntryType
  status: JournalEntryStatus
  partnerCode: string
  orderCode: string
  amount: string | number
  currencyCode: string
  postedAt: string
  lines: JournalEntryLine[]
}

// ─── Workflow (user tasks) ──────────────────────────────────────────

export interface UserTaskSummary {
  taskId: string
  taskName: string
  formKey: string | null
  processDefinitionKey: string
  processInstanceId: string
  createTime: string
  assignee: string | null
}

export interface UserTaskDetail extends UserTaskSummary {
  variables: Record<string, unknown>
}

// ─── Rules ─────────────────────────────────────────────────────────

export interface RuleCondition {
  field: string
  operator: string
  value: string
}

export interface RuleAction {
  type: string
  config: Record<string, string>
}

export interface RuleDefinition {
  slug: string
  name: string
  description?: string
  enabled: boolean
  triggerEvent: string
  conditions: RuleCondition[]
  conditionLogic: 'AND' | 'OR'
  actions: RuleAction[]
  version: number
  source?: string
}

// ─── Metadata Definitions ───────────────────────────────────────────

export type FormPurpose = 'create' | 'edit' | 'user-task' | 'view'

export interface FormDefinition {
  entityName: string
  slug: string
  title: string
  purpose: FormPurpose
  jsonSchema: Record<string, unknown>
  uiSchema: Record<string, unknown>
  version: number
  source?: string
}

export interface ListViewColumnDef {
  field: string
  label: string
  width?: string
  sortable: boolean
  format?: 'date' | 'money' | 'status-badge' | 'link'
}

export interface ListViewDefinition {
  entityName: string
  slug: string
  title: string
  columns: ListViewColumnDef[]
  defaultSort?: { field: string; direction: 'asc' | 'desc' }
  filters?: { field: string; operator: string; label: string }[]
  pageSize: number
  version: number
  source?: string
}

export interface CustomFieldType {
  kind: string
  maxLength?: number
  precision?: number
  scale?: number
  targetEntity?: string
  allowedValues?: string[]
}

export interface CustomFieldDef {
  key: string
  targetEntity: string
  type: CustomFieldType
  required: boolean
  pii: boolean
  labelTranslations: Record<string, string>
  source?: string
}

export interface MetadataEntity {
  name: string
  pbc: string
  table: string
  description?: string
  source?: string
}

export interface MetadataPermission {
  key: string
  description: string
  source?: string
}