📦 Đặc Tả Kiến Trúc Nghiệp Vụ & Kỹ Thuật: Nhu Cầu Đóng Gói Đa Thùng (Multi-Box Packaging Specification)
Tài liệu đặc tả toàn diện cho phân hệ Đóng gói của KingstonVN. Bao gồm bài toán chia thùng linh hoạt, gom BOM Pool bộ sản phẩm Set, cơ chế kiểm chéo BOM (Strict Validation Hard Block), mô hình dữ liệu Prisma MySQL và luồng API backflushing trừ kho tức thời.
📌 1. Bài Toán Nghiệp Vụ & Mục Tiêu Cốt Lõi
📦 Đóng Gói Đa Thùng Linh Hoạt
Cho phép 1 dòng đơn hàng (OrderItem) hoặc 1 đơn vị sản phẩm (Set/Variant) được phân rã thành nhiều thùng carton khác nhau (Thùng 1, Thùng 2...) để tối ưu hóa thể tích vận chuyển container và giảm thiểu rủi ro va đập hàng cồng kềnh.
⚙️ Giới Hạn 1 Đơn Vị Bộ Sản Phẩm
Cấu hình chia thùng và định mức linh kiện được thiết lập cho đúng 1 đơn vị sản phẩm (1 bộ hoặc 1 set). Khi số lượng đơn hàng là $Q$, tổng số thùng cần đóng thực tế tại xưởng PKG sẽ nhân lên tương ứng ($Q \times N$ thùng).
🍽️ Gom Set BOM Pool
Đối với bộ sản phẩm (ví dụ: Bộ bàn ăn Net 1+4 gồm 1 bàn + 4 ghế), hệ thống tự động gom toàn bộ linh kiện của các sản phẩm con vào một **BOM Pool chung** để Planner tự do xáo trộn (shuffle) phân chia vào các thùng của bộ Set đó.
🛡️ Hard Block Strict Validation
Kiểm soát và bắt buộc tổng số lượng linh kiện/vật tư đã chia vào các thùng phải khớp chính xác 100% so với định mức BOM gốc tiêu chuẩn. Hệ thống khóa cứng chức năng quét barcode tại xưởng nếu phát hiện sai lệch.
📖 2. Ngôn Ngữ Chung (Ubiquitous Language)
| Thuật Ngữ (Term) | Tên Tiếng Anh / Mã Code | Mô Tả Nghiệp Vụ Chi Tiết |
|---|---|---|
| Cấu Hình Đóng Gói Tiêu Chuẩn | VariantPackingSpec |
Master Data khai báo quy cách vỏ thùng carton và linh kiện đính kèm tiêu chuẩn cho 1 bộ thành phẩm Variant lẻ. |
| Cấu Hình Đóng Gói Đơn Hàng | OrderPackingSpec |
Cấu hình chia thùng thực tế áp dụng cho cả đơn hàng thương mại (Order), cho phép Planner tùy biến kéo thả gộp chéo linh kiện/vật tư giữa các dòng đơn hàng. |
| Kho Bán Thành Phẩm & Linh Kiện | BOM Pool |
Tập hợp danh mục toàn bộ linh kiện phôi chế tạo và phụ kiện đính kèm thuộc định mức 1 bộ sản phẩm/Set cần được phân bổ hết vào các thùng. |
| Tiến Độ Cấp Độ Thùng | Box-Level Progress |
Tỷ lệ % lũy kế số lượng thùng $S_i$ đã được xưởng PKG quét barcode đóng gói hoàn thành so với tổng quy mô đơn hàng ($Q_{\text{order}}$). |
| Tiến Độ Cấp Độ Sản Phẩm | Unit-Level Progress |
Số lượng bộ sản phẩm hoàn chỉnh sẵn sàng giao hàng, bằng $\min(Q_{\text{packed\_box\_i}})$. Khi đạt 100%, đơn hàng chuyển trạng thái `PACKED`. |
| Trừ Kho Tức Thời | Real-time Backflushing |
Nghiệp vụ tự động trừ tồn kho vỏ thùng carton và các linh kiện/phụ kiện ngay khi từng thùng được công nhân quét báo cáo hoàn thành. |
🔄 3. Quy Trình Nghiệp Vụ Happy Path (4 Bước)
Khởi Tạo Đơn Hàng
Khi đơn bán hàng được duyệt, hệ thống tự động kiểm tra `refType` của `OrderItem`. Với sản phẩm lẻ, tự động clone cấu hình đóng gói tiêu chuẩn. Với Set, gom linh kiện vào BOM Pool chờ Planner.
Lập Cấu Hình Chia Thùng
Planner truy cập Configurator đóng gói, kéo thả phân chia linh kiện vào các thùng (Thùng 1, Thùng 2...). Hệ thống kiểm chéo Strict Validation xem khớp 100% BOM hay chưa.
Duyệt & Cấp Quyền PKG
Khi cấu hình hợp lệ (khớp 100%), hệ thống lưu cấu hình và đổi trạng thái `OrderItem.status = CONFIGURED`. Cấp quyền cho xưởng PKG quét barcode đóng gói.
Quét Barcode & Backflush
Công nhân tại xưởng PKG quét hoàn thành từng thùng. Hệ thống trừ kho real-time vỏ thùng & linh kiện, tính tiến độ 2 cấp độ và đổi trạng thái `PACKED` khi đủ 100%.
💾 4. Mô Hình Cơ Sở Dữ Liệu (Prisma MySQL Schema)
File cấu hình schema đặt tại: prisma/providers/mysql/prisma-models/packing.prisma
/// Thùng đóng gói thực tế được thiết lập ở cấp Đơn hàng (Order Custom Config)
model OrderPackingSpec {
id String @id @default(uuid())
orderId Int @map("order_id") // Foreign Key -> orders (ID kiểu Int)
boxMaterialId Int @map("box_material_id") // Foreign Key -> materials (Kiểu Int)
boxIndex Int @map("box_index") // Số thứ tự thùng
name String @db.VarChar(100)
expectedQuantity Int @default(1) @map("expected_quantity")
note String? @db.Text
createdAt DateTime @default(now())
order Order @relation(fields: [orderId], references: [id])
boxMaterial Material @relation(fields: [boxMaterialId], references: [id])
packingItems OrderPackingItem[]
progress OrderPackingProgress?
@@unique([orderId, boxIndex])
@@map("order_packing_specs")
}
/// Chi tiết linh kiện hoặc vật tư đóng gói nằm trong 1 loại thùng của đơn hàng
model OrderPackingItem {
id String @id @default(uuid())
packingSpecId String @map("packing_spec_id")
orderItemId Int @map("order_item_id") // Thuộc về dòng đơn hàng nào (để đối chiếu định mức)
itemType String @db.VarChar(20) @map("item_type") // "COMPONENT" hoặc "MATERIAL"
itemId Int @map("item_id") // Foreign Key -> components(id) hoặc materials(id)
quantityPerUnit Decimal @db.Decimal(10, 2) @map("quantity_per_unit")
totalQuantity Decimal @db.Decimal(10, 2) @map("total_quantity")
packingSpec OrderPackingSpec @relation(fields: [packingSpecId], references: [id], onDelete: Cascade)
orderItem OrderItem @relation(fields: [orderItemId], references: [id])
@@map("order_packing_items")
}
/// Ghi nhận tiến độ đóng gói lũy kế của từng loại thùng thực tế trong đơn hàng
model OrderPackingProgress {
id String @id @default(uuid())
packingSpecId String @unique @map("packing_spec_id")
quantityPacked Int @default(0) @map("quantity_packed") // Lũy kế số thùng đã đóng hoàn thành
updatedAt DateTime @default(now()) @updatedAt
updatedBy Int @map("updated_by")
packingSpec OrderPackingSpec @relation(fields: [packingSpecId], references: [id])
@@map("order_packing_progress")
}
🔌 5. Đặc Tả RESTful APIs Endpoints (NestJS Backend)
/api/admin/orders/:orderId/packing-specs
Lấy cấu hình đóng gói thực tế và tiến độ của toàn bộ đơn hàng. Nếu chưa cấu hình, API trả về mảng rỗng cùng trạng thái PENDING_PACKING_CONFIG.
/api/admin/orders/:orderId/packing-specs
Planner lưu cấu hình đóng gói thực tế cho đơn hàng (cho phép gộp hoặc phân chia chéo linh kiện/vật tư của các dòng đơn hàng khác nhau). Bắt buộc chạy qua kiểm tra validateOrderPackingConfig trước khi lưu.
/api/admin/orders/packing-specs/:specId/report-progress
Công nhân tại xưởng PKG quét barcode báo cáo hoàn thành thùng đóng gói. Body: { quantityPackedChange: number }. Hệ thống tự động trừ kho backflushing vỏ thùng & linh kiện trong thùng, đồng thời tính toán tiến độ bộ sản phẩm hoàn chỉnh của các dòng đơn hàng bị ảnh hưởng.
⚙️ 6. Thuật Toán Trừ Kho Real-time (Backflushing) & Cập Nhật Tiến Độ
Mỗi khi ghi nhận báo cáo đóng gói thùng $S_i$ thành công thêm $\Delta q$ thùng:
itemType = COMPONENT):• Tìm bản ghi
WorkstationInventory tại trạm PKG có bomComponentId = item.itemId.• Khấu trừ số lượng:
inventory.quantity -= Δq * item.quantityPerUnit.• Tạo log
WorkstationInventoryLog loại OUT_WIP ghi nhận lịch sử.2. Tính toán tiến độ bộ sản phẩm hoàn chỉnh:
• Với mỗi linh kiện/vật tư $C$ thuộc dòng hàng $J$, tính tỷ lệ đóng gói: $\text{CompletionRatio}_C = \frac{\sum (\text{quantityPacked} \times \text{quantityPerUnit})}{\text{Nhu cầu định mức}}$.
• Số lượng bộ hoàn thành của dòng hàng: $Q_{\text{completed\_units}, J} = Q_{\text{order}, J} \times \min_{C \in J} (\text{CompletionRatio}_C)$.
• Nếu $Q_{\text{completed\_units}, J} == Q_{\text{order}, J}$, tự động cập nhật
OrderItem.status = 'PACKED'.