📝 Đặc Tả Kỹ Thuật Bảng Nhu Cầu Tổng Cho 1 Đơn Hàng (Unified Master Demand Spec)

Cấu trúc Prisma DDL Spec chuẩn hóa Bảng Mẹ `Demand` (Header) và Bảng Con `DemandItem` (Lines chứa `execution_method`).

1. Nguyên Tắc Bảng Nhu Cầu Tổng (Unified Demand Items Architecture)

Mỗi Đơn hàng (`Order`) sở hữu đúng 1 Bảng Nhu Cầu Tổng (`Demand`) gom tất cả các record chi tiết (`DemandItem`):

  • `demand_category`: `SEMI_FINISHED` (BTP Level 1), `RAW_MATERIAL` (NVL Thô BOM), `PACKAGING_SUPPLY` (Bao Bì), `BOX_SPEC` (Quy cách thùng).
  • `execution_method`: `MANUFACTURE` (Sản xuất nội bộ), `PURCHASE` (Mua hàng PO), `SUBCONTRACT` (Gia công ngoài), `PACKAGING` (Đóng gói kho).
  • `assigned_entity`: Phân Xưởng đảm nhận / Nhà Cung Cấp / Đối Tác Gia Công.

2. Prisma DDL Spec Unified Models Schema (`demand.prisma`)

// ==========================================
// 1. UNIFIED PARENT DEMAND HEADER MODEL
// ==========================================

model Demand {
  id           Int          @id @default(autoincrement()) @map("id")
  isDeleted    Boolean      @default(false) @map("is_deleted")
  orderId      Int          @unique @map("order_id")
  
  version      Int          @default(1) @map("version")           // v1, v2, v3
  status       String       @default("DRAFT") @map("status")     // DRAFT, PENDING_APPROVAL, APPROVED, RELEASED
  
  approvedBy   Int?         @map("approved_by")
  approvedAt   DateTime?    @map("approved_at")
  changeReason String?      @map("change_reason")
  
  createdAt    DateTime     @default(now()) @map("created_at")
  updatedAt    DateTime     @updatedAt @map("updated_at")

  order        Order        @relation(fields: [orderId], references: [id])
  items        DemandItem[]

  @@map("demands")
}

enum ExecutionMethod {
  MANUFACTURE  // 1. Sản xuất nội bộ (WorkOrder)
  PURCHASE     // 2. Mua hàng từ NCC (PurchaseOrder)
  SUBCONTRACT  // 3. Gia công ngoài (SubcontractOrder)
  PACKAGING    // 4. Đóng gói kho (OrderBoxSpec)
}

enum DemandCategory {
  SEMI_FINISHED    // Bán Thành Phẩm Level 1
  RAW_MATERIAL     // Nguyên Vật Liệu Thô (BOM)
  PACKAGING_SUPPLY // Vật Tư Bao Bì
  BOX_SPEC         // Quy Cách Thùng
}

// ==========================================
// 2. UNIFIED DEMAND ITEM LINE MODEL (TẤT CẢ RECORD TRÊN 1 BẢNG)
// ==========================================

model DemandItem {
  id               Int             @id @default(autoincrement()) @map("id")
  demandId         Int             @map("demand_id")
  orderId          Int             @map("order_id")
  
  demandCategory   DemandCategory  @map("demand_category")
  executionMethod  ExecutionMethod @map("execution_method")
  
  refType          String          @map("ref_type")          // COMPONENT, MATERIAL, BOX_SPEC
  refId            Int             @map("ref_id")
  itemName         String          @map("item_name")
  
  requiredQty      Decimal         @map("required_qty") @db.Decimal(10,2)
  inventoryQty     Decimal         @default(0) @map("inventory_qty") @db.Decimal(10,2)
  shortageQty      Decimal         @default(0) @map("shortage_qty") @db.Decimal(10,2)
  
  workshopId       Int?            @map("workshop_id")       // Nếu SX Nội bộ
  vendorId         Int?            @map("vendor_id")         // Nếu Mua hàng hoặc Gia công ngoài
  assignedName     String?         @map("assigned_name")     // Tên Xưởng / NCC / ĐT Gia Công
  
  requiredDate     DateTime        @map("required_date")
  leadTimeDays     Int             @default(1) @map("lead_time_days")
  earliestStartDate DateTime       @map("earliest_start_date")
  
  status           String          @default("PENDING") @map("status") // PENDING, APPROVED, RELEASED

  demand   Demand    @relation(fields: [demandId], references: [id])
  order    Order     @relation(fields: [orderId], references: [id])
  workshop Workshop? @relation(fields: [workshopId], references: [id])
  vendor   Vendor?   @relation(fields: [vendorId], references: [id])

  @@index([demandId])
  @@index([executionMethod])
  @@map("demand_items")
}

// ==========================================
// 3. PACKAGING DEMANDS & BOX PROGRESS TABLES (NHU CẦU ĐÓNG GÓI M:N)
// ==========================================

model OrderLine {
  id             Int                @id @default(autoincrement()) @map("id")
  orderId        Int                @map("order_id")
  itemName       String             @map("item_name")       // Tên sản phẩm hoặc Set sản phẩm
  itemType       String             @default("PRODUCT") @map("item_type") // PRODUCT hoặc SET
  quantity       Decimal            @map("quantity") @db.Decimal(10,2)    // Số lượng đặt hàng
  
  order          Order              @relation(fields: [orderId], references: [id])
  boxLines       OrderBoxSpecLine[] // Cấu hình đóng gói liên kết

  @@index([orderId])
  @@map("order_lines")
}

model OrderBoxSpec {
  id              Int                @id @default(autoincrement()) @map("id")
  orderId         Int                @map("order_id")
  boxCode         String             @map("box_code") // e.g. BOX-A, COMBO-NET
  boxName         String             @map("box_name")
  boxType         String             @map("box_type") // Carton 5 Lớp, Thùng Gỗ...
  totalBoxes      Int                @map("total_boxes") // Tổng số lượng thùng loại này cần đóng
  
  // Thuộc tính phục vụ xuất nhập khẩu & logistics (XNK)
  length          Decimal?           @map("length") @db.Decimal(10,2) // Chiều dài (cm)
  width           Decimal?           @map("width") @db.Decimal(10,2)  // Chiều rộng (cm)
  height          Decimal?           @map("height") @db.Decimal(10,2) // Chiều cao (cm)
  cbm             Decimal?           @map("cbm") @db.Decimal(10,4)    // Thể tích m3 (L*W*H / 1.000.000)
  netWeight       Decimal?           @map("net_weight") @db.Decimal(10,2) // Trọng lượng tịnh sản phẩm (kg)
  grossWeight     Decimal?           @map("gross_weight") @db.Decimal(10,2) // Trọng lượng cả bì (kg)
  shippingMarks   String?            @map("shipping_marks") @db.Text  // Nhãn hiệu in ngoài vỏ thùng

  order           Order              @relation(fields: [orderId], references: [id])
  boxLines        OrderBoxSpecLine[] // Các line sản phẩm/BTP nằm trong thùng (M:N)
  packages        OrderPackage[]     // Quản lý định danh từng kiện hàng vật lý thực tế
  progress        OrderBoxProgress?

  @@index([orderId])
  @@map("order_box_specs")
}

model OrderBoxSpecLine {
  id             Int          @id @default(autoincrement()) @map("id")
  boxSpecId      Int          @map("box_spec_id")
  demandItemId   Int          @map("demand_item_id") // Ánh xạ sang các BTP / Vật tư trong Bảng Nhu Cầu
  qtyPerBox      Decimal      @map("qty_per_box") @db.Decimal(10,2) // Định mức: 1 thùng này chứa bao nhiêu BTP/Vật tư này

  boxSpec        OrderBoxSpec @relation(fields: [boxSpecId], references: [id])
  demandItem     DemandItem   @relation(fields: [demandItemId], references: [id])

  @@index([boxSpecId])
  @@index([demandItemId])
  @@map("order_box_spec_lines")
}

model OrderPackage {
  id             Int          @id @default(autoincrement()) @map("id")
  boxSpecId      Int          @map("box_spec_id")
  packageCode    String       @unique @map("package_code") // Mã vạch duy nhất (QR/Barcode/SSCC): e.g. PKG-ORD20268899-001
  cartonNo       Int          @map("carton_no") // Số thứ tự thùng (ví dụ: Thùng số 1 của 50 thùng)
  weightScanned  Decimal?     @map("weight_scanned") @db.Decimal(10,2) // Trọng lượng cân thực tế tại trạm đóng gói (kg)
  status         String       @default("PACKED") @map("status") // PACKED (đã đóng), SCANNED_IN (đã vào kho), SHIPPED (đã lên tàu)
  createdAt      DateTime     @default(now()) @map("created_at")

  boxSpec        OrderBoxSpec @relation(fields: [boxSpecId], references: [id])

  @@index([boxSpecId])
  @@map("order_packages")
}

model OrderBoxProgress {
  id             Int          @id @default(autoincrement()) @map("id")
  boxSpecId      Int          @unique @map("box_spec_id")
  totalBoxes     Int          @map("total_boxes")
  syncedBoxes    Int          @default(0) @map("synced_boxes")
  packedBoxes    Int          @default(0) @map("packed_boxes")
  deliveredBoxes Int          @default(0) @map("delivered_boxes")
  status         String       @default("SYNCED") @map("status")

  boxSpec        OrderBoxSpec @relation(fields: [boxSpecId], references: [id])

  @@map("order_box_progress")
}