📝 Đặ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")
}