PHP 8.2.30
Preview: schema.prisma Size: 16.69 KB
/home/byroehnu/easepaybiz.easetack.com/prisma/schema.prisma

generator client {
  provider = "prisma-client-js"
}

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

model User {
  id            String         @id @default(uuid())
  email         String         @unique
  password      String
  firstName     String
  lastName      String
  phone         String         @unique
  businessName  String?
  role          UserRole       @default(OWNER)
  isActive      Boolean        @default(true)
  emailVerified Boolean        @default(false)
  
  // OTP Fields
  otp               String?
  otpVerificationId String?    // OTP.dev verification ID
  otpExpiresAt      DateTime?
  otpAttempts       Int        @default(0)
  otpLockedUntil    DateTime?
  lastOtpSentAt     DateTime?
  phoneVerified     Boolean    @default(false)
  
  // Authentication & Security
  pin           String?        // 4-6 digit PIN for quick login
  pinSetAt      DateTime?      // When PIN was set
  biometricEnabled Boolean     @default(false)
  biometricHash String?        // Hashed biometric template reference
  lastLoginAt   DateTime?      // Last successful login
  sessionToken  String?        // Current active session
  sessionExpiresAt DateTime?   // Session expiration
  
  // Security Settings
  autoLoginEnabled Boolean     @default(true)
  requirePinForTransactions Boolean @default(true)
  loginAttempts Int           @default(0)
  lockedUntil   DateTime?     // Account lockout
  
  // Device Management
  deviceTokens  DeviceToken[]
  
  // Relations
  businesses    Business[]
  sales         Sale[]
  expenses      Expense[]
  
  // New Invoice Relations
  invoices      Invoice[]
  inventoryItems InventoryItem[]
  
  // Subscription Details
  subscription  UserSubscription?
  subscriptions Subscription[]
  creditScores  CreditScore[]
  
  // Usage Tracking for Middleware
  usage         UserUsage?
  
  // User Settings
  settings      UserSettings?
  
  // Plan Limits (Set based on subscription plan)
  limits        UserLimits?
  
  // Staff Roles (User can be staff in multiple businesses)
  staffRoles    BusinessStaff[]
  
  // Invitations
  sentInvitations     StaffInvitation[] @relation("InvitedBy")
  acceptedInvitations StaffInvitation[] @relation("AcceptedBy")

  createdAt     DateTime       @default(now())
  updatedAt     DateTime       @updatedAt
}

model DeviceToken {
  id           String    @id @default(uuid())
  userId       String
  user         User      @relation(fields: [userId], references: [id], onDelete: Cascade)
  deviceId     String    // Unique device identifier
  deviceName   String?   // User-friendly device name
  deviceType   String    // ios, android, web
  fcmToken     String?   // Firebase/push notification token
  biometricId  String?   // Device biometric identifier
  isActive     Boolean   @default(true)
  lastUsedAt   DateTime  @default(now())
  createdAt    DateTime  @default(now())
  
  @@unique([userId, deviceId])
}

model Business {
  id            String        @id @default(uuid())
  name          String
  type          BusinessType
  
  ownerId       String
  owner         User          @relation(fields: [ownerId], references: [id])
  
  vatRegistered Boolean       @default(false)
  hasStaff      Boolean       @default(false)
  
  sales         Sale[]
  expenses      Expense[]
  
  // New Invoice Relations
  invoices      Invoice[]
  inventoryItems InventoryItem[]
  profile       BusinessProfile?
  
  // Staff
  staff         BusinessStaff[]
  invitations   StaffInvitation[]

  createdAt     DateTime      @default(now())
  updatedAt     DateTime      @updatedAt
}

model StaffInvitation {
  id            String           @id @default(uuid())
  token         String           @unique
  email         String?
  phone         String?
  role          String           @default("Staff")
  permissions   String[]
  status        InvitationStatus @default(PENDING)
  expiresAt     DateTime
  
  businessId    String
  business      Business         @relation(fields: [businessId], references: [id])
  
  invitedById   String
  invitedBy     User             @relation("InvitedBy", fields: [invitedById], references: [id])

  acceptedById  String?
  acceptedBy    User?            @relation("AcceptedBy", fields: [acceptedById], references: [id])

  createdAt     DateTime         @default(now())
  updatedAt     DateTime         @updatedAt
}

model Sale {
  id            String        @id @default(uuid())
  amount        Decimal       @db.Decimal(12, 2)
  description   String?
  paymentMethod String        @default("CASH") // CASH, TRANSFER, POS
  date          DateTime      @default(now())
  
  userId        String
  user          User          @relation(fields: [userId], references: [id])
  
  businessId    String?
  business      Business?     @relation(fields: [businessId], references: [id])

  createdAt     DateTime      @default(now())
  updatedAt     DateTime      @updatedAt
}

model Expense {
  id            String        @id @default(uuid())
  amount        Decimal       @db.Decimal(12, 2)
  description   String
  category      String?
  paymentMethod String        @default("CASH") // CASH, TRANSFER, POS
  date          DateTime      @default(now())
  
  userId        String
  user          User          @relation(fields: [userId], references: [id])
  
  businessId    String?
  business      Business?     @relation(fields: [businessId], references: [id])

  createdAt     DateTime      @default(now())
  updatedAt     DateTime      @updatedAt
}

// --- Enhanced Subscription Models with Flutterwave Integration ---

model UserSubscription {
  id                String    @id @default(uuid())
  userId            String    @unique
  user              User      @relation(fields: [userId], references: [id], onDelete: Cascade)
  
  // Flutterwave Integration
  planId            String?   // Flutterwave Plan ID
  planName          String    @default("Free")
  flwCustomerId     String?   // Flutterwave Customer ID
  flwSubscriptionId String?   // Flutterwave Subscription ID
  
  // Subscription Status
  status            SubscriptionStatus @default(INACTIVE)
  nextBillingDate   DateTime?
  gracePeriodEnd    DateTime? // 7 days after payment failure
  
  // Billing
  amount            Decimal?  @db.Decimal(10, 2)
  currency          String    @default("NGN")
  interval          String?   // "monthly", "yearly"
  
  createdAt         DateTime  @default(now())
  updatedAt         DateTime  @updatedAt
}

model UserSettings {
  id                String    @id @default(uuid())
  userId            String    @unique
  user              User      @relation(fields: [userId], references: [id], onDelete: Cascade)
  
  // Preferences
  language          String    @default("en")
  fontSize          String    @default("medium")
  currency          String    @default("NGN")
  dateFormat        String    @default("DD/MM/YYYY")
  
  // Toggles
  lowStockAlert     Boolean   @default(true)
  pushNotifications Boolean   @default(true)
  smsAlerts         Boolean   @default(false)
  usePinCode        Boolean   @default(false)
  useBiometrics     Boolean   @default(false)
  
  createdAt         DateTime  @default(now())
  updatedAt         DateTime  @updatedAt
}

model UserUsage {
  id                String    @id @default(uuid())
  userId            String    @unique
  user              User      @relation(fields: [userId], references: [id], onDelete: Cascade)
  
  // Usage Counters
  invoiceCount      Int       @default(0)
  teamMemberCount   Int       @default(1)
  reportExports     Int       @default(0)
  
  // Monthly Reset Tracking
  lastResetDate     DateTime  @default(now())
  
  createdAt         DateTime  @default(now())
  updatedAt         DateTime  @updatedAt
}

model UserLimits {
  id                String    @id @default(uuid())
  userId            String    @unique
  user              User      @relation(fields: [userId], references: [id], onDelete: Cascade)
  
  // Plan Limits
  maxInvoices       Int       @default(5)   // Free tier limit
  maxTeamMembers    Int       @default(1)
  maxReportExports  Int       @default(2)
  hasAdvancedReports Boolean  @default(false)
  hasAPIAccess      Boolean   @default(false)
  
  createdAt         DateTime  @default(now())
  updatedAt         DateTime  @updatedAt
}

model Plan {
  id            String        @id @default(uuid())
  name          String        @unique // e.g., "Free", "Basic", "Pro", "Enterprise"
  description   String?
  
  // Pricing
  priceMonthly  Decimal       @db.Decimal(10, 2)
  priceYearly   Decimal       @db.Decimal(10, 2)
  currency      String        @default("NGN")
  
  // Flutterwave Integration
  flwPlanIdMonthly String?     // Flutterwave Plan ID for monthly
  flwPlanIdYearly  String?     // Flutterwave Plan ID for yearly
  
  // Plan Features
  maxInvoices      Int
  maxTeamMembers   Int
  maxReportExports Int
  hasAdvancedReports Boolean   @default(false)
  hasAPIAccess     Boolean     @default(false)
  hasPrioritySupport Boolean   @default(false)
  
  // Status
  isActive      Boolean       @default(true)
  sortOrder     Int           @default(0)
  
  features      Feature[]
  subscriptions Subscription[]

  createdAt     DateTime      @default(now())
  updatedAt     DateTime      @updatedAt
}

model Feature {
  id            String        @id @default(uuid())
  name          String        @unique // e.g., "add_staff", "export_reports"
  description   String?
  
  plans         Plan[]        // Many-to-many: Plans have features

  createdAt     DateTime      @default(now())
  updatedAt     DateTime      @updatedAt
}

model Subscription {
  id            String             @id @default(uuid())
  userId        String
  user          User               @relation(fields: [userId], references: [id])
  
  planId        String
  plan          Plan               @relation(fields: [planId], references: [id])
  
  status        SubscriptionStatus @default(ACTIVE)
  frequency     BillingFrequency   @default(MONTHLY)
  
  startDate     DateTime           @default(now())
  endDate       DateTime           // Calculated based on frequency
  
  autoRenew     Boolean            @default(true)

  createdAt     DateTime           @default(now())
  updatedAt     DateTime           @updatedAt
}

// --- RBAC / Staff Models ---

model BusinessStaff {
  id            String        @id @default(uuid())
  
  businessId    String
  business      Business      @relation(fields: [businessId], references: [id])
  
  userId        String
  user          User          @relation(fields: [userId], references: [id])
  
  role          String        // e.g., "Manager", "Cashier", "Accountant"
  permissions   String[]      // Array of permission strings e.g., ["create_sale", "view_reports"]
  isActive      Boolean       @default(true)

  createdAt     DateTime      @default(now())
  updatedAt     DateTime      @updatedAt

  @@unique([businessId, userId]) // User can only have one staff entry per business
}

// --- Enums ---

enum UserRole {
  OWNER
  STAFF
  ADMIN
}

enum InvitationStatus {
  PENDING
  ACCEPTED
  EXPIRED
  CANCELLED
}

enum SubscriptionStatus {
  ACTIVE
  INACTIVE
  EXPIRED
  CANCELLED
  PAST_DUE
  SUSPENDED
}

enum BillingFrequency {
  MONTHLY
  YEARLY
}

enum BusinessType {
  RETAIL
  SERVICE
  MANUFACTURING
  TECHNOLOGY
  CONSULTING
  RESTAURANT
  HEALTHCARE
  EDUCATION
  REAL_ESTATE
  OTHER
}

model CreditScore {
  id                      String   @id @default(uuid())
  userId                  String
  user                    User     @relation(fields: [userId], references: [id])
  score                   Int
  factors                 Json
  recommendations         Json
  paymentHistoryScore     Int
  revenueStabilityScore   Int
  businessAgeScore        Int
  expenseManagementScore  Int
  
  createdAt               DateTime @default(now())
  updatedAt               DateTime @updatedAt
}

// --- Invoice Management Models ---

model Invoice {
  id                String         @id @default(uuid())
  invoiceNumber     String         @unique
  
  // Customer Details
  customerName      String
  customerEmail     String?
  customerPhone     String?
  customerAddress   String?
  
  // Business Details
  businessId        String?
  business          Business?      @relation(fields: [businessId], references: [id])
  userId            String
  user              User           @relation(fields: [userId], references: [id])
  
  // Invoice Details
  invoiceDate       DateTime       @default(now())
  dueDate           DateTime?
  paymentMethod     String?        // Cash, Bank Transfer, Card, etc.
  
  // Financial Details
  subtotal          Decimal        @db.Decimal(15, 2)
  discount          Decimal        @default(0) @db.Decimal(15, 2)
  discountType      DiscountType   @default(FIXED) // FIXED or PERCENTAGE
  vatRate           Decimal        @default(0) @db.Decimal(5, 2)
  vatAmount         Decimal        @default(0) @db.Decimal(15, 2)
  total             Decimal        @db.Decimal(15, 2)
  
  // Status & Metadata
  status            InvoiceStatus  @default(CREATED)
  notes             String?
  currency          String         @default("NGN")
  
  // Digital Features
  qrCodeUrl         String?
  qrCodeData        String?        // Base64 encoded QR code
  downloadUrl       String?
  shareUrl          String?
  
  // Payment Tracking
  paidAmount        Decimal        @default(0) @db.Decimal(15, 2)
  paidAt            DateTime?
  
  // Relations
  items             InvoiceItem[]
  payments          InvoicePayment[]
  
  createdAt         DateTime       @default(now())
  updatedAt         DateTime       @updatedAt
  
  @@index([businessId])
  @@index([userId])
  @@index([status])
  @@index([invoiceDate])
}

model InvoiceItem {
  id            String      @id @default(uuid())
  invoiceId     String
  invoice       Invoice     @relation(fields: [invoiceId], references: [id], onDelete: Cascade)
  
  // Item Details
  itemName      String
  description   String?
  quantity      Decimal     @db.Decimal(10, 2)
  unitPrice     Decimal     @db.Decimal(15, 2)
  total         Decimal     @db.Decimal(15, 2)
  
  // Inventory Reference (optional)
  inventoryItemId String?
  inventoryItem   InventoryItem? @relation(fields: [inventoryItemId], references: [id])
  
  createdAt     DateTime    @default(now())
}

model InvoicePayment {
  id            String         @id @default(uuid())
  invoiceId     String
  invoice       Invoice        @relation(fields: [invoiceId], references: [id])
  
  amount        Decimal        @db.Decimal(15, 2)
  paymentMethod String
  paymentDate   DateTime       @default(now())
  reference     String?        // Payment gateway reference
  
  createdAt     DateTime       @default(now())
}

model InventoryItem {
  id            String      @id @default(uuid())
  businessId    String?
  business      Business?   @relation(fields: [businessId], references: [id])
  userId        String
  user          User        @relation(fields: [userId], references: [id])
  
  // Item Details
  name          String
  description   String?
  category      String?
  
  // Pricing
  unitPrice     Decimal     @db.Decimal(15, 2)
  costPrice     Decimal?    @db.Decimal(15, 2)
  
  // Inventory
  stock         Decimal     @default(0) @db.Decimal(10, 2)
  unit          String?     // piece, kg, liter, etc.
  minStock      Decimal?    @db.Decimal(10, 2) // Reorder level
  
  // Status
  isActive      Boolean     @default(true)
  
  // Relations
  invoiceItems  InvoiceItem[]
  
  createdAt     DateTime    @default(now())
  updatedAt     DateTime    @updatedAt
  
  @@index([businessId])
  @@index([userId])
  @@index([category])
}

model BusinessProfile {
  id                String      @id @default(uuid())
  businessId        String      @unique
  business          Business    @relation(fields: [businessId], references: [id])
  
  // Business Information
  registrationName  String?
  tradingName       String?
  email             String?
  phone             String?
  website           String?
  
  // Address
  address           String?
  city              String?
  state             String?
  country           String      @default("Nigeria")
  postalCode        String?
  
  // Tax Information
  vatRegistration   String?
  tinNumber         String?
  rcNumber          String?
  bnNumber          String?
  
  // Operations
  receiveMoneyMethods String[] @default(["CASH", "TRANSFER", "POS"])
  
  // Settings
  defaultCurrency   String      @default("NGN")
  defaultVatRate    Decimal     @default(7.5) @db.Decimal(5, 2)
  
  // Invoice Settings
  invoicePrefix     String      @default("INV")
  nextInvoiceNumber Int         @default(1)
  
  // Logo & Branding
  logoUrl           String?
  brandColor        String?
  
  createdAt         DateTime    @default(now())
  updatedAt         DateTime    @updatedAt
}



// --- New Enums ---

enum InvoiceStatus {
  DRAFT
  CREATED
  SENT
  VIEWED
  PAID
  PARTIALLY_PAID
  OVERDUE
  CANCELLED
  REFUNDED
}

enum DiscountType {
  FIXED
  PERCENTAGE
}

Directory Contents

Dirs: 1 × Files: 2

Name Size Perms Modified Actions
- drwxr-xr-x 2026-03-14 01:49:04
Edit Download
127 B lr--r--r-- 2026-03-14 01:49:04
Edit Download
16.69 KB lrw-r--r-- 2026-03-01 11:05:52
Edit Download

If ZipArchive is unavailable, a .tar will be created (no compression).