generator client {
  provider      = "prisma-client-js"
  binaryTargets = ["native", "debian-openssl-1.0.x"]
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model Branch {
  id          String      @id @default(cuid())
  code        String      @unique
  name        String
  timezone    String      @default("Asia/Colombo")
  createdAt   DateTime    @default(now())
  updatedAt   DateTime    @updatedAt
  users       User[]
  sales       Sale[]
  inventory   Inventory[]
  auditLogs   AuditLog[]
  syncEvents  SyncOutbox[]
  accountingEntries AccountingEntry[]
  dailyClosings     DailyClosing[]
  taxPeriods        TaxPeriod[]
  reportSchedules   ReportSchedule[]
  reportAuditLogs   ReportAuditLog[]
  reportSubscriptions ReportSubscription[]
  notifications    NotificationEvent[]
  userSessions     UserSession[]
  authSessions     AuthSession[]
  warehouses       Warehouse[]
  purchases        Purchase[]
  goodsReceipts    GoodsReceiptNote[]
  userBranchAccesses UserBranchAccess[]
  branchSequences  BranchSequence[]
  saleReturns      SaleReturn[]
  customers        Customer[]
  suppliers        Supplier[]
}

model User {
  id           String      @id @default(cuid())
  username     String      @unique
  displayName  String
  passwordHash String
  role         UserRole
  branchId     String
  branchScope  UserBranchScope @default(SINGLE_BRANCH)
  branch       Branch      @relation(fields: [branchId], references: [id])
  sales        Sale[]      @relation("CashierSales")
  auditLogs    AuditLog[]
  accountingEntries AccountingEntry[]
  accountingEntryApprovals AccountingEntryApproval[]
  dailyClosings     DailyClosing[]
  reportSchedules   ReportSchedule[]
  reportAuditLogs   ReportAuditLog[]
  reportSubscriptions ReportSubscription[]
  notificationReads NotificationRead[]
  userSessions      UserSession[]
  authSessions      AuthSession[]
  authSecurity      AuthUserSecurity?
  branchAccesses    UserBranchAccess[]
  createdAt    DateTime    @default(now())
  updatedAt    DateTime    @updatedAt
}

model UserBranchAccess {
  id         String   @id @default(cuid())
  userId     String
  branchId   String
  canOperate Boolean  @default(true)
  canView    Boolean  @default(true)
  isDefault  Boolean  @default(false)
  createdAt  DateTime @default(now())
  updatedAt  DateTime @updatedAt
  user       User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  branch     Branch   @relation(fields: [branchId], references: [id], onDelete: Cascade)

  @@unique([userId, branchId])
  @@index([branchId, canOperate, canView])
}

model AppSetting {
  id        String   @id @default(cuid())
  namespace String   @unique
  payload    Json
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model BackupArchive {
  id           String   @id @default(cuid())
  label        String
  filePath     String
  sizeBytes    Int
  checksum     String
  backupType   String
  status       String
  encrypted    Boolean  @default(true)
  compressed   Boolean  @default(true)
  cloudStored  Boolean  @default(false)
  restorePoint Boolean  @default(false)
  createdAt    DateTime @default(now())
  restoredAt   DateTime?

  @@index([createdAt])
  @@index([backupType, status])
}

model NotificationEvent {
  id          String   @id @default(cuid())
  branchId    String?
  type        NotificationType
  channel     NotificationChannel
  title       String
  message     String
  severity    NotificationSeverity
  status      NotificationDispatchStatus @default(QUEUED)
  recipient   String?
  dedupeKey   String?
  payload     Json
  provider    String?
  externalId  String?
  errorMessage String?
  retryCount   Int      @default(0)
  lastAttemptAt DateTime?
  nextAttemptAt DateTime?
  createdAt   DateTime @default(now())
  sentAt      DateTime?
  readAt      DateTime?
  branch      Branch?  @relation(fields: [branchId], references: [id])
  reads       NotificationRead[]

  @@index([status, createdAt])
  @@index([type, severity, createdAt])
  @@index([branchId, createdAt])
  @@unique([channel, dedupeKey])
}

model NotificationRead {
  id             String            @id @default(cuid())
  notificationId String
  userId         String
  readAt         DateTime          @default(now())
  createdAt      DateTime          @default(now())
  notification   NotificationEvent @relation(fields: [notificationId], references: [id], onDelete: Cascade)
  user           User              @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([notificationId, userId])
  @@index([userId, readAt])
}

model UserSession {
  id                 String   @id
  userId             String
  branchId           String
  deviceId           String
  deviceName         String
  machineFingerprint String?
  currentScreen      String?
  socketId           String?
  tokenHash          String   @unique
  rememberMe         Boolean  @default(false)
  status             String   @default("online")
  createdAt          DateTime @default(now())
  lastSeenAt         DateTime @default(now())
  lastHeartbeatAt    DateTime?
  expiresAt          DateTime
  revokedAt          DateTime?
  user               User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  branch             Branch   @relation(fields: [branchId], references: [id], onDelete: Cascade)

  @@index([userId, lastSeenAt])
  @@index([branchId, status, lastSeenAt])
  @@index([socketId])
}

model AuthSession {
  id                 String   @id
  userId             String
  branchId           String
  deviceId           String
  deviceName         String
  machineFingerprint String?
  currentScreen      String?
  socketId           String?
  tokenHash          String   @unique
  rememberMe         Boolean  @default(false)
  createdAt          DateTime @default(now())
  lastSeenAt         DateTime @default(now())
  lastHeartbeatAt    DateTime?
  expiresAt          DateTime
  revokedAt          DateTime?
  user               User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  branch             Branch   @relation(fields: [branchId], references: [id], onDelete: Cascade)

  @@index([userId, lastSeenAt])
  @@index([branchId, lastSeenAt])
  @@index([socketId])
}

model AuthUserSecurity {
  userId         String   @id
  failedAttempts Int      @default(0)
  lockedUntil    DateTime?
  lastLoginAt    DateTime?
  lastFailedAt   DateTime?
  updatedAt      DateTime @default(now())
  user           User     @relation(fields: [userId], references: [id], onDelete: Cascade)
}

model BillingApprovalRequest {
  id                String   @id
  branchId          String
  requesterUserId   String
  approverUserId    String
  actionType        String
  targetType        String
  targetId          String?
  reason            String
  context           Json
  status            String
  createdAt         DateTime @default(now())
  decidedAt         DateTime?

  @@index([branchId, createdAt])
  @@index([requesterUserId, createdAt])
  @@index([approverUserId, createdAt])
}

model ActivityLog {
  id        String   @id
  userId    String?
  branchId  String?
  sessionId String?
  deviceId  String?
  screen    String?
  status    String
  payload   Json
  createdAt DateTime @default(now())

  @@index([userId, createdAt])
  @@index([branchId, createdAt])
  @@index([sessionId, createdAt])
}

model EventLog {
  id             String   @id
  sequence       BigInt   @default(autoincrement()) @unique
  eventName      String
  eventClass     String   @default("domain")
  aggregateType  String
  aggregateId    String
  branchId       String?
  actorUserId    String?
  payload        Json     @default("{}")
  occurredAt     DateTime
  deliveryStatus String   @default("pending")
  publishedAt    DateTime?
  processedAt    DateTime?
  createdAt      DateTime @default(now())

  @@index([eventName, occurredAt(sort: Desc)])
  @@index([aggregateType, aggregateId, occurredAt(sort: Desc)])
  @@index([branchId, occurredAt(sort: Desc)])
  @@index([eventClass, sequence(sort: Desc)])
}

model EventDeliveryCursor {
  consumer     String   @id
  lastSequence BigInt   @default(0)
  updatedAt    DateTime @default(now())
}

model SocketConnection {
  socketId        String   @id
  userId          String?
  branchId        String?
  sessionId       String?
  deviceId        String?
  deviceName      String?
  clientVersion   String?
  currentScreen   String?
  status          String   @default("connected")
  connectedAt     DateTime @default(now())
  lastSeenAt      DateTime @default(now())
  disconnectedAt  DateTime?

  @@index([userId, lastSeenAt])
  @@index([branchId, lastSeenAt])
  @@index([sessionId, lastSeenAt])
}

model EventDeadLetter {
  id           String   @id
  consumer     String
  eventId      String
  eventName    String
  sequence     BigInt?
  branchId     String?
  aggregateType String
  aggregateId  String
  errorMessage String
  payload      Json
  failedAt     DateTime @default(now())

  @@index([consumer, failedAt(sort: Desc)])
  @@index([eventName, failedAt(sort: Desc)])
}

model Customer {
  id           String      @id @default(cuid())
  code         String      @unique
  name         String
  phone        String?
  email        String?
  customerType CustomerType @default(WALK_IN)
  institutionName String?
  gradeClass   String?
  studentId    String?
  preferredLanguage String?
  creditLimit  Decimal?    @db.Decimal(14, 2)
  branchId     String?
  archivedAt   DateTime?
  createdAt    DateTime    @default(now())
  updatedAt    DateTime    @updatedAt
  branch       Branch?     @relation(fields: [branchId], references: [id])
  sales        Sale[]
  loyaltyLedgers CustomerLoyaltyLedger[]
  dueLedgers      CustomerDueLedger[]
  notificationLogs CustomerNotificationLog[]

  @@index([branchId, createdAt])
  @@index([archivedAt, createdAt])
}

model CustomerLoyaltyLedger {
  id            String              @id @default(cuid())
  customerId    String
  saleId        String?
  pointsDelta   Int
  balanceAfter  Int?
  reason        String
  note          String?
  createdAt     DateTime            @default(now())
  customer      Customer            @relation(fields: [customerId], references: [id])
  sale          Sale?               @relation(fields: [saleId], references: [id])

  @@index([customerId, createdAt])
  @@index([saleId])
}

model CustomerDueLedger {
  id            String              @id @default(cuid())
  customerId    String
  saleId        String?
  amount        Decimal             @db.Decimal(14, 2)
  entryType     CustomerDueEntryType
  balanceAfter  Decimal?            @db.Decimal(14, 2)
  dueDate       DateTime?
  note          String?
  createdAt     DateTime            @default(now())
  customer      Customer            @relation(fields: [customerId], references: [id])
  sale          Sale?               @relation(fields: [saleId], references: [id])

  @@index([customerId, createdAt])
  @@index([saleId])
  @@index([entryType, dueDate])
}

model CustomerNotificationLog {
  id            String                    @id @default(cuid())
  customerId    String
  saleId        String?
  channel       CustomerNotificationChannel
  template      String
  recipient     String
  preview       String
  provider      String
  status        CustomerNotificationStatus
  payload       Json
  externalId    String?
  errorMessage  String?
  createdAt     DateTime                  @default(now())
  sentAt        DateTime?
  customer      Customer                  @relation(fields: [customerId], references: [id])
  sale          Sale?                     @relation(fields: [saleId], references: [id])

  @@index([customerId, createdAt])
  @@index([status, createdAt])
}

model Supplier {
  id          String       @id @default(cuid())
  code        String       @unique
  name        String
  phone       String?
  email       String?
  supplierType SupplierType @default(GENERAL_SUPPLIER)
  contactPerson String?
  paymentTerms String?
  supplierNotes String?
  branchId    String?
  createdAt   DateTime     @default(now())
  updatedAt   DateTime     @updatedAt
  branch      Branch?      @relation(fields: [branchId], references: [id])
  purchases   Purchase[]
  invoices    PurchaseInvoice[]
  payments    PurchasePayment[]
  customGrns  GoodsReceiptNote[]
  contactLogs SupplierContactLog[]
  settlementEvents SupplierSettlementEvent[]

  @@index([branchId, createdAt])
  @@index([supplierType, createdAt])
}

model SupplierContactLog {
  id             String                 @id @default(cuid())
  supplierId     String
  channel        SupplierContactChannel
  summary        String
  agent          String
  outcome        SupplierContactOutcome
  createdAt      DateTime               @default(now())
  nextFollowUpAt DateTime?
  supplier       Supplier               @relation(fields: [supplierId], references: [id])

  @@index([supplierId, createdAt])
  @@index([outcome, nextFollowUpAt])
}

model SupplierSettlementEvent {
  id                 String          @id @default(cuid())
  supplierId         String
  purchaseId         String
  paymentId          String
  eventType          String
  previousPaidAmount Decimal         @db.Decimal(14, 2)
  newPaidAmount      Decimal         @db.Decimal(14, 2)
  previousStatus     String?
  newStatus          String?
  method             String?
  referenceNo        String?
  note               String?
  actor              String?
  createdAt          DateTime        @default(now())
  supplier           Supplier        @relation(fields: [supplierId], references: [id])
  purchase           Purchase        @relation(fields: [purchaseId], references: [id])
  payment            PurchasePayment @relation(fields: [paymentId], references: [id])

  @@index([supplierId, createdAt])
  @@index([purchaseId, createdAt])
  @@index([paymentId, createdAt])
}

model Product {
  id           String        @id @default(cuid())
  sku          String        @unique
  barcode      String?       @unique
  isbn         String?       @unique
  name         String
  description  String?       @db.Text
  author       String?
  publisher    String?
  edition      String?
  language     String?
  subject      String?
  gradeLevel   String?
  itemType     String?
  shelfLocation String?
  modelNumber  String?
  partNumber   String?
  material     String?
  size         String?
  color        String?
  warrantyMonths Int?
  rackLocation String?
  binLocation  String?
  wholesalePrice Decimal?    @db.Decimal(14, 2)
  minSellPrice Decimal?      @db.Decimal(14, 2)
  preferredReorderQuantity Decimal? @db.Decimal(14, 3)
  isSerialized Boolean      @default(false)
  unit         String
  sellPrice    Decimal       @db.Decimal(14, 2)
  costPrice    Decimal       @db.Decimal(14, 2)
  taxRate      Decimal       @default(0.18) @db.Decimal(8, 4)
  discountType String        @default("amount")
  discountValue Decimal      @default(0) @db.Decimal(14, 2)
  isActive     Boolean       @default(true)
  imageUrl     String?
  modifierGroups Json?
  comboItems    Json?
  categoryId   String?
  brandId      String?
  warehouseId  String?
  reorderLevel Decimal?      @db.Decimal(14, 3)
  batchTracked Boolean       @default(false)
  expiryTracked Boolean      @default(false)
  hasVariants  Boolean       @default(false)
  expiryDate   DateTime?
  createdAt    DateTime      @default(now())
  updatedAt    DateTime      @updatedAt
  inventory    Inventory[]
  saleItems    SaleItem[]
  purchaseItems PurchaseItem[]
  category     Category?     @relation(fields: [categoryId], references: [id])
  brand        Brand?        @relation(fields: [brandId], references: [id])
  warehouse    Warehouse?    @relation(fields: [warehouseId], references: [id])
  variants     ProductVariant[]
  batches      InventoryBatch[]
  damagedItems DamagedItem[]
  inventoryHistory InventoryHistory[]
  stockTransferLines StockTransferLine[]
  goodsReceiptLines GoodsReceiptLine[]
  saleReturnLines SaleReturnLine[]

  @@index([isActive, name])
  @@index([author])
  @@index([publisher])
  @@index([subject, gradeLevel])
  @@index([itemType, isActive])
  @@index([shelfLocation])
  @@index([modelNumber])
  @@index([partNumber])
  @@index([rackLocation])
  @@index([binLocation])
  @@index([isSerialized, isActive])
  @@index([categoryId, brandId, warehouseId])
  @@index([expiryDate])
}

model Inventory {
  id           String      @id @default(cuid())
  branchId     String
  productId    String
  onHand       Decimal     @db.Decimal(14, 3)
  reorderLevel Decimal     @db.Decimal(14, 3)
  updatedAt    DateTime    @updatedAt
  branch       Branch      @relation(fields: [branchId], references: [id])
  product      Product     @relation(fields: [productId], references: [id])

  @@unique([branchId, productId])
  @@index([productId])
  @@index([branchId, reorderLevel, updatedAt])
}

model Category {
  id          String    @id @default(cuid())
  code        String    @unique
  name        String    @unique
  description String?
  createdAt   DateTime  @default(now())
  updatedAt   DateTime  @updatedAt
  products    Product[]

  @@index([name])
}

model Brand {
  id          String    @id @default(cuid())
  code        String    @unique
  name        String    @unique
  description String?
  createdAt   DateTime  @default(now())
  updatedAt   DateTime  @updatedAt
  products    Product[]

  @@index([name])
}

model Warehouse {
  id          String             @id @default(cuid())
  code        String             @unique
  branchId    String
  name        String
  location    String?
  manager     String?
  createdAt   DateTime           @default(now())
  updatedAt   DateTime           @updatedAt
  branch      Branch             @relation(fields: [branchId], references: [id])
  products    Product[]
  variants    ProductVariant[]
  batches     InventoryBatch[]
  transfersFrom StockTransfer[]  @relation("TransferFromWarehouse")
  transfersTo   StockTransfer[]  @relation("TransferToWarehouse")
  damagedItems DamagedItem[]
  history     InventoryHistory[]
  goodsReceiptLines GoodsReceiptLine[]

  @@index([name])
  @@index([branchId, name])
}

model ProductVariant {
  id           String      @id @default(cuid())
  productId     String
  warehouseId   String?
  sku           String      @unique
  barcode       String?     @unique
  name          String
  attributes    Json
  price         Decimal     @db.Decimal(14, 2)
  stock         Decimal     @db.Decimal(14, 3)
  createdAt     DateTime    @default(now())
  updatedAt     DateTime    @updatedAt
  product       Product     @relation(fields: [productId], references: [id])
  warehouse     Warehouse?  @relation(fields: [warehouseId], references: [id])

  @@index([productId])
}

model InventoryBatch {
  id          String      @id @default(cuid())
  productId    String
  warehouseId  String
  batchNo      String
  qty          Decimal     @db.Decimal(14, 3)
  receivedAt   DateTime    @default(now())
  expiryDate   DateTime?
  createdAt    DateTime    @default(now())
  updatedAt    DateTime    @updatedAt
  product      Product     @relation(fields: [productId], references: [id])
  warehouse    Warehouse   @relation(fields: [warehouseId], references: [id])
  history      InventoryHistory[]

  @@index([productId, warehouseId])
  @@unique([batchNo, warehouseId])
  @@index([expiryDate, warehouseId])
}

model StockTransfer {
  id               String                @id @default(cuid())
  referenceNo      String                @unique
  fromWarehouseId  String
  toWarehouseId    String
  note             String?
  createdAt        DateTime              @default(now())
  fromWarehouse    Warehouse             @relation("TransferFromWarehouse", fields: [fromWarehouseId], references: [id])
  toWarehouse      Warehouse             @relation("TransferToWarehouse", fields: [toWarehouseId], references: [id])
  lines            StockTransferLine[]

  @@index([createdAt, fromWarehouseId, toWarehouseId])
}

model StockTransferLine {
  id              String         @id @default(cuid())
  stockTransferId String
  productId       String
  quantity        Decimal        @db.Decimal(14, 3)
  stockTransfer   StockTransfer  @relation(fields: [stockTransferId], references: [id])
  product         Product        @relation(fields: [productId], references: [id])

  @@index([stockTransferId])
  @@index([productId])
}

model DamagedItem {
  id           String      @id @default(cuid())
  productId     String
  warehouseId   String
  quantity      Decimal     @db.Decimal(14, 3)
  reason        String
  createdAt     DateTime    @default(now())
  product       Product     @relation(fields: [productId], references: [id])
  warehouse     Warehouse   @relation(fields: [warehouseId], references: [id])

  @@index([warehouseId, createdAt])
  @@index([productId, createdAt])
}

model InventoryHistory {
  id           String      @id @default(cuid())
  productId     String
  warehouseId   String?
  batchId       String?
  action        String
  quantity      Decimal     @db.Decimal(14, 3)
  note          String?
  actor         String?
  createdAt     DateTime    @default(now())
  product       Product     @relation(fields: [productId], references: [id])
  warehouse     Warehouse?  @relation(fields: [warehouseId], references: [id])
  batch         InventoryBatch? @relation(fields: [batchId], references: [id])

  @@index([productId, createdAt])
}

model Sale {
  id          String      @id @default(cuid())
  branchId    String
  cashierId   String
  customerId  String?
  receiptNumber String?
  idempotencyKey String?
  returnOriginalSaleId String?
  returnReason String?
  subtotal    Decimal     @db.Decimal(14, 2)
  discount    Decimal     @db.Decimal(14, 2)
  tax         Decimal     @db.Decimal(14, 2)
  total       Decimal     @db.Decimal(14, 2)
  createdAt   DateTime    @default(now())
  branch      Branch      @relation(fields: [branchId], references: [id])
  cashier     User        @relation("CashierSales", fields: [cashierId], references: [id])
  customer    Customer?   @relation(fields: [customerId], references: [id])
  items       SaleItem[]
  payments    Payment[]
  loyaltyEntries CustomerLoyaltyLedger[]
  dueEntries     CustomerDueLedger[]
  notificationLogs CustomerNotificationLog[]

  @@unique([branchId, receiptNumber])
  @@unique([idempotencyKey])
  @@index([branchId, createdAt])
  @@index([returnOriginalSaleId])
}

model SaleItem {
  id          String      @id @default(cuid())
  saleId       String
  productId    String
  returnOriginalSaleItemId String?
  qty          Decimal     @db.Decimal(14, 3)
  unitPrice    Decimal     @db.Decimal(14, 2)
  discount     Decimal     @db.Decimal(14, 2)
  taxRate      Decimal     @default(0) @db.Decimal(8, 4)
  lineTotal    Decimal     @db.Decimal(14, 2)
  sale         Sale        @relation(fields: [saleId], references: [id])
  product      Product     @relation(fields: [productId], references: [id])

  @@index([saleId])
  @@index([productId])
  @@index([returnOriginalSaleItemId])
}

model Payment {
  id         String        @id @default(cuid())
  saleId      String
  method      PaymentMethod
  amount      Decimal       @db.Decimal(14, 2)
  reference   String?
  sale        Sale          @relation(fields: [saleId], references: [id])
}

model BranchSequence {
  id        String   @id @default(cuid())
  branchId  String
  scope     String
  nextValue Int      @default(1)
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  branch    Branch   @relation(fields: [branchId], references: [id])

  @@unique([branchId, scope])
}

model SaleReturn {
  id             String   @id @default(cuid())
  branchId       String
  originalSaleId String
  returnSaleId   String   @unique
  receiptNumber  String
  status         String   @default("POSTED")
  reason         String?
  total          Decimal  @db.Decimal(14, 2)
  createdAt      DateTime @default(now())
  lines          SaleReturnLine[]
  branch         Branch   @relation(fields: [branchId], references: [id])

  @@index([branchId, createdAt])
  @@index([originalSaleId])
}

model SaleReturnLine {
  id                 String     @id @default(cuid())
  saleReturnId       String
  originalSaleItemId String?
  productId          String
  qty                Decimal    @db.Decimal(14, 3)
  amount             Decimal    @db.Decimal(14, 2)
  saleReturn         SaleReturn @relation(fields: [saleReturnId], references: [id])
  product            Product    @relation(fields: [productId], references: [id])

  @@index([saleReturnId])
  @@index([originalSaleItemId])
  @@index([productId])
}

model Purchase {
  id                    String              @id @default(cuid())
  supplierId            String
  branchId              String
  referenceNo           String              @unique
  status                PurchaseStatus      @default(DRAFT)
  approvalStatus        ApprovalStatus      @default(PENDING)
  invoiceNo             String?
  invoiceAttachmentName String?
  branchName            String              @default("Main Branch")
  note                  String?
  expectedAt            DateTime?
  receivedAt            DateTime?
  subtotal              Decimal             @db.Decimal(14, 2)
  taxTotal              Decimal             @db.Decimal(14, 2)
  total                 Decimal             @db.Decimal(14, 2)
  paidAmount            Decimal             @default(0) @db.Decimal(14, 2)
  createdAt             DateTime            @default(now())
  updatedAt             DateTime            @updatedAt
  supplier              Supplier            @relation(fields: [supplierId], references: [id])
  branch                Branch              @relation(fields: [branchId], references: [id])
  items                 PurchaseItem[]
  grns                  GoodsReceiptNote[]
  invoices              PurchaseInvoice[]
  payments              PurchasePayment[]
  approvals             PurchaseApproval[]
  settlementEvents      SupplierSettlementEvent[]

  @@index([branchId, createdAt])
  @@index([supplierId, status, createdAt])
  @@index([approvalStatus, createdAt])
}

model PurchaseItem {
  id            String      @id @default(cuid())
  purchaseId     String
  productId      String
  qty            Decimal     @db.Decimal(14, 3)
  receivedQty    Decimal     @default(0) @db.Decimal(14, 3)
  unitCost       Decimal     @db.Decimal(14, 2)
  taxRate        Decimal     @default(0) @db.Decimal(8, 2)
  lineDiscount   Decimal     @default(0) @db.Decimal(14, 2)
  lineTotal      Decimal     @db.Decimal(14, 2)
  purchase       Purchase    @relation(fields: [purchaseId], references: [id])
  product        Product     @relation(fields: [productId], references: [id])

  @@index([purchaseId, productId])
}

model GoodsReceiptNote {
  id              String            @id @default(cuid())
  purchaseId       String?
  supplierId       String?
  branchId         String?
  grnNo            String            @unique
  sourceType       GrnSourceType     @default(PURCHASE_ORDER)
  supplierName     String?
  branchName       String            @default("Main Branch")
  customReference  String?
  receivedBy       String
  note             String?
  receivedAt       DateTime          @default(now())
  purchase         Purchase?         @relation(fields: [purchaseId], references: [id])
  supplier         Supplier?         @relation(fields: [supplierId], references: [id])
  branch           Branch?           @relation(fields: [branchId], references: [id])
  lines            GoodsReceiptLine[]

  @@index([branchId, receivedAt])
  @@index([purchaseId, receivedAt])
  @@index([supplierId, receivedAt])
}

model GoodsReceiptLine {
  id             String            @id @default(cuid())
  goodsReceiptId String
  productId      String
  warehouseId    String?
  quantity       Decimal           @db.Decimal(14, 3)
  unitCost       Decimal?          @db.Decimal(14, 2)
  sellPrice      Decimal?          @db.Decimal(14, 2)
  batchNo        String?
  expiryDate     DateTime?
  goodsReceipt   GoodsReceiptNote  @relation(fields: [goodsReceiptId], references: [id])
  product        Product           @relation(fields: [productId], references: [id])
  warehouse      Warehouse?        @relation(fields: [warehouseId], references: [id])

  @@index([goodsReceiptId])
  @@index([productId, warehouseId])
}

model PurchaseInvoice {
  id              String      @id @default(cuid())
  purchaseId       String
  supplierId       String
  invoiceNo        String
  attachmentName   String?
  fileUrl          String?
  storagePath      String?
  uploadedAt       DateTime    @default(now())
  amount           Decimal     @db.Decimal(14, 2)
  dueDate          DateTime?
  purchase         Purchase    @relation(fields: [purchaseId], references: [id])
  supplier         Supplier    @relation(fields: [supplierId], references: [id])

  @@index([supplierId, dueDate])
  @@index([purchaseId, uploadedAt])
}

model PurchasePayment {
  id            String             @id @default(cuid())
  purchaseId     String
  supplierId     String
  amount         Decimal            @db.Decimal(14, 2)
  paidAmount     Decimal            @default(0) @db.Decimal(14, 2)
  dueDate        DateTime?
  paidAt         DateTime?
  status         DuePaymentStatus   @default(CURRENT)
  method         String?
  referenceNo    String?
  note           String?
  purchase       Purchase           @relation(fields: [purchaseId], references: [id])
  supplier       Supplier           @relation(fields: [supplierId], references: [id])
  settlementEvents SupplierSettlementEvent[]

  @@index([supplierId, status, dueDate])
  @@index([purchaseId, paidAt])
}

model PurchaseApproval {
  id            String           @id @default(cuid())
  purchaseId     String
  requestedBy    String
  status         ApprovalStatus  @default(PENDING)
  approvedBy     String?
  requestedAt    DateTime        @default(now())
  decidedAt      DateTime?
  note           String?
  purchase       Purchase        @relation(fields: [purchaseId], references: [id])

  @@index([status, requestedAt])
  @@index([purchaseId, status])
}

model AccountingEntry {
  id            String               @id @default(cuid())
  branchId      String?
  createdById   String?
  entryDate     DateTime             @default(now())
  direction     AccountingDirection
  sourceType    AccountingSourceType
  category      String
  description   String
  amount        Decimal              @db.Decimal(14, 2)
  taxAmount     Decimal              @default(0) @db.Decimal(14, 2)
  referenceType String?
  referenceId   String?
  status        AccountingEntryStatus @default(POSTED)
  requiredApprovals Int               @default(2)
  createdAt     DateTime             @default(now())
  updatedAt     DateTime             @updatedAt
  branch        Branch?              @relation(fields: [branchId], references: [id])
  createdBy     User?                @relation(fields: [createdById], references: [id])
  approvals     AccountingEntryApproval[]

  @@index([entryDate, direction])
  @@index([branchId, entryDate])
  @@index([sourceType, referenceId])
}

model AccountingEntryApproval {
  id                String                    @id @default(cuid())
  accountingEntryId String
  approverId        String?
  stage             Int
  decision          AccountingApprovalDecision @default(PENDING)
  note              String?
  createdAt         DateTime                  @default(now())
  decidedAt         DateTime?
  accountingEntry   AccountingEntry           @relation(fields: [accountingEntryId], references: [id])
  approver          User?                     @relation(fields: [approverId], references: [id])

  @@index([accountingEntryId, createdAt])
  @@index([approverId, decision])
}

model DailyClosing {
  id            String      @id @default(cuid())
  branchId      String
  closedById    String?
  businessDate  DateTime
  openingCash   Decimal     @default(0) @db.Decimal(14, 2)
  cashSales     Decimal     @default(0) @db.Decimal(14, 2)
  cashExpenses  Decimal     @default(0) @db.Decimal(14, 2)
  expectedCash  Decimal     @default(0) @db.Decimal(14, 2)
  actualCash    Decimal     @default(0) @db.Decimal(14, 2)
  variance      Decimal     @default(0) @db.Decimal(14, 2)
  note          String?
  closedAt      DateTime    @default(now())
  branch        Branch      @relation(fields: [branchId], references: [id])
  closedBy      User?       @relation(fields: [closedById], references: [id])

  @@unique([branchId, businessDate])
  @@index([businessDate, branchId])
}

model TaxPeriod {
  id            String          @id @default(cuid())
  branchId      String?
  label         String
  periodStart   DateTime
  periodEnd     DateTime
  taxCollected  Decimal         @default(0) @db.Decimal(14, 2)
  taxPaid       Decimal         @default(0) @db.Decimal(14, 2)
  netTax        Decimal         @default(0) @db.Decimal(14, 2)
  status        TaxPeriodStatus @default(OPEN)
  note          String?
  filedAt       DateTime?
  lockedAt      DateTime?
  createdAt     DateTime        @default(now())
  updatedAt     DateTime        @updatedAt
  branch        Branch?         @relation(fields: [branchId], references: [id])

  @@index([periodStart, periodEnd])
  @@index([branchId, status])
}

model ReportSchedule {
  id          String                 @id @default(cuid())
  branchId    String?
  createdById String?
  name        String
  reportKey   String
  format      ReportExportFormat
  channel     ReportDeliveryChannel
  frequency   ReportScheduleFrequency
  dateRange   String
  locale      String                 @default("en")
  recipients  Json
  filters     Json?
  isActive    Boolean                @default(true)
  lastRunAt   DateTime?
  nextRunAt   DateTime?
  createdAt   DateTime               @default(now())
  updatedAt   DateTime               @updatedAt
  branch      Branch?                @relation(fields: [branchId], references: [id])
  createdBy   User?                  @relation(fields: [createdById], references: [id])
  auditLogs   ReportAuditLog[]
  snapshots   ReportingSnapshot[]

  @@index([isActive, nextRunAt])
  @@index([reportKey, createdAt])
  @@index([branchId, frequency])
}

model ReportSubscription {
  id              String                 @id @default(cuid())
  branchId        String?
  createdById     String?
  name            String
  reportKey       String
  format          ReportExportFormat
  frequency       ReportScheduleFrequency
  locale          String                 @default("en")
  recipients      Json
  channels        Json
  isActive        Boolean                @default(true)
  lastDeliveredAt DateTime?
  nextRunAt       DateTime?
  createdAt       DateTime               @default(now())
  updatedAt       DateTime               @updatedAt
  branch          Branch?                @relation(fields: [branchId], references: [id])
  createdBy       User?                  @relation(fields: [createdById], references: [id])

  @@index([isActive, nextRunAt])
  @@index([reportKey, createdAt])
}

model ReportBuilder {
  id          String    @id @default(cuid())
  name        String
  baseReportKey String
  chartType   String
  columns     Json
  groupBy     Json
  filters     Json
  createdAt   DateTime  @default(now())
  updatedAt   DateTime  @updatedAt

  @@index([baseReportKey, createdAt])
}

model ReportSavedView {
  id            String    @id @default(cuid())
  name          String
  reportKey     String
  pageSize      Int       @default(50)
  visibleColumns Json
  filters       Json
  createdAt     DateTime  @default(now())
  updatedAt     DateTime  @updatedAt

  @@index([reportKey, createdAt])
}

model DashboardLayout {
  id         String    @id @default(cuid())
  name       String
  widgets    Json
  isDefault  Boolean   @default(false)
  createdAt  DateTime  @default(now())
  updatedAt  DateTime  @updatedAt
}

model ReportAuditLog {
  id         String                @id @default(cuid())
  branchId   String?
  userId     String?
  scheduleId String?
  reportKey  String
  action     String
  format     ReportExportFormat?
  channel    ReportDeliveryChannel?
  payload    Json
  createdAt  DateTime              @default(now())
  branch     Branch?               @relation(fields: [branchId], references: [id])
  user       User?                 @relation(fields: [userId], references: [id])
  schedule   ReportSchedule?       @relation(fields: [scheduleId], references: [id])

  @@index([reportKey, createdAt])
  @@index([scheduleId, createdAt])
  @@index([branchId, createdAt])
}

model ReportingSnapshot {
  id          String      @id @default(cuid())
  scheduleId  String?
  reportKey   String
  dateRange   String
  branchId    String?
  locale      String      @default("en")
  payload     Json
  generatedAt DateTime    @default(now())
  expiresAt   DateTime
  schedule    ReportSchedule? @relation(fields: [scheduleId], references: [id])

  @@index([reportKey, generatedAt])
  @@index([expiresAt])
  @@unique([reportKey, dateRange, branchId, locale])
}

model AuditLog {
  id          String      @id @default(cuid())
  branchId    String
  userId      String
  entity      String
  action      String
  payload     Json
  createdAt   DateTime    @default(now())
  branch      Branch      @relation(fields: [branchId], references: [id])
  user        User        @relation(fields: [userId], references: [id])

  @@index([branchId, createdAt])
}

model SyncOutbox {
  id           String         @id @default(cuid())
  branchId      String
  aggregateType String
  aggregateId   String
  eventType     String
  payload       Json
  status        SyncStatus    @default(PENDING)
  retryCount    Int           @default(0)
  createdAt     DateTime      @default(now())
  processedAt   DateTime?
  branch        Branch        @relation(fields: [branchId], references: [id])

  @@index([status, createdAt])
}

enum UserRole {
  ADMIN
  SUPER_ADMIN
  MANAGER
  CASHIER
  ACCOUNTANT
  INVENTORY_CLERK
}

enum UserBranchScope {
  SINGLE_BRANCH
  MULTI_BRANCH
  ALL_BRANCHES
}

enum PaymentMethod {
  CASH
  CARD
  CREDIT
  WALLET
  BANK
}

enum SyncStatus {
  PENDING
  PROCESSING
  SYNCED
  FAILED
}

enum NotificationType {
  LOW_STOCK
  SALE
  SYSTEM
}

enum NotificationChannel {
  DESKTOP
  SMS
  WHATSAPP
  EMAIL
}

enum NotificationSeverity {
  INFO
  WARNING
  CRITICAL
}

enum NotificationDispatchStatus {
  QUEUED
  SENT
  FAILED
  SKIPPED
}

enum PurchaseStatus {
  DRAFT
  SUBMITTED
  APPROVED
  PARTIAL_RECEIVED
  RECEIVED
  BILLED
  PARTIAL_PAID
  PAID
  CANCELLED
}

enum ApprovalStatus {
  PENDING
  APPROVED
  REJECTED
}

enum DuePaymentStatus {
  CURRENT
  OVERDUE
  PAID
}

enum GrnSourceType {
  PURCHASE_ORDER
  CUSTOM
}

enum CustomerDueEntryType {
  CHARGE
  PAYMENT
  ADJUSTMENT
  REFUND
}

enum CustomerType {
  WALK_IN
  REGULAR
  CONTRACTOR
  BUILDER
  ELECTRICIAN
  PLUMBER
  BUSINESS
  STUDENT
  PARENT
  TEACHER
  SCHOOL
  INSTITUTE
  LIBRARY
  CORPORATE
}

enum CustomerNotificationChannel {
  SMS
  WHATSAPP
}

enum CustomerNotificationStatus {
  PENDING
  SENT
  FAILED
  SKIPPED
}

enum SupplierContactChannel {
  CALL
  SMS
  EMAIL
  WHATSAPP
  VISIT
  NOTE
}

enum SupplierType {
  PUBLISHER
  DISTRIBUTOR
  STATIONERY_WHOLESALER
  IMPORTER
  LOCAL_PRINTER
  LOCAL_WHOLESALER
  ELECTRICAL_SUPPLIER
  PLUMBING_SUPPLIER
  PAINT_DISTRIBUTOR
  TOOL_IMPORTER
  CONSTRUCTION_MATERIAL_SUPPLIER
  BRAND_DISTRIBUTOR
  GENERAL_SUPPLIER
}

enum SupplierContactOutcome {
  COMPLETED
  PENDING
  FAILED
}

enum AccountingDirection {
  INCOME
  EXPENSE
}

enum AccountingSourceType {
  MANUAL
  ACCRUAL
  SALE
  PURCHASE
  PURCHASE_PAYMENT
  DAILY_CLOSING
}

enum AccountingEntryStatus {
  DRAFT
  PENDING_APPROVAL
  POSTED
  VOIDED
}

enum AccountingApprovalDecision {
  PENDING
  APPROVED
  REJECTED
}

enum ReportExportFormat {
  PDF
  EXCEL
  CSV
  JSON
}

enum ReportDeliveryChannel {
  DOWNLOAD
  EMAIL
  WHATSAPP
}

enum ReportScheduleFrequency {
  DAILY
  WEEKLY
  MONTHLY
  QUARTERLY
  YEARLY
}

enum TaxPeriodStatus {
  OPEN
  LOCKED
  FILED
}
