👋 Bắt đầu tại đây
Sổ tay nhập môn hệ thống VErp cho lập trình viên mới
Hệ thống ERP cho doanh nghiệp sản xuất, kiến trúc microservices .NET (backend verp-api) + frontend Angular (verp-web). Mỗi phân hệ nghiệp vụ là một service với CSDL riêng, giao tiếp qua gRPC và bảng tham chiếu Ref*.
Hệ thống gồm 9 phân hệ
| 01. Stock | Kho – Sản phẩm – BOM – Kiểm kê – Vị trí lưu trữ |
| 02. Manufacturing | Sản xuất – Lệnh SX – Công đoạn – Bàn giao – Gia công ngoài – Kế hoạch |
| 03. PurchaseOrder | Mua hàng (Procurement) + Bán hàng/Voucher (Ordering) |
| 04. Accountancy | Kế toán – Phiếu nhập liệu động – Tính giá/phí – P&L – GL – Phân bổ hóa đơn |
| 05. QcManagement | Quản lý chất lượng – Phiếu kiểm – Tiêu chí/Tiêu chuẩn – Lấy mẫu AQL – Lỗi – CAPA |
| 06. Organization | Nhân sự – Phòng ban – Lương – Ca làm – Khách hàng – Chi nhánh |
| 07. Master | Master data dùng chung – User/Role/Phân quyền – Danh mục động – Gen code – Tiền tệ – Menu/Module |
| 08. ReportConfig | Cấu hình báo cáo – Loại BC/View/Cột/Bộ lọc – Dashboard – Scripting SQL/JS – Template xuất |
| 09. StatisticReportData | Dữ liệu báo cáo thống kê – Lương ngày – Cache dashboard – Bảng tạm báo cáo |
→ Xem chi tiết entity & quan hệ ở mục 🗺️ Sơ đồ ER.
3 cơ chế kết nối xuyên suốt
Gần như mọi bảng có SubsidiaryId để tách dữ liệu theo chi nhánh (multi-tenant). Luôn lọc theo nó.
Service không join DB service khác; giữ bản sao read-only (RefUser, RefDepartment, RefProduct…) đồng bộ qua gRPC.
Khớp nối lỏng qua mã chuỗi: ProductionOrderCode, Pocode, OrderCode… thay vì FK cứng xuyên service.
Pattern lặp lại — nắm 1 lần, hiểu nhiều module
Cùng mô hình cấu hình động dùng ở Accountancy (InputType…), Ordering (VoucherType…), Organization (HrType…), Master (Category…). Hiểu 1 là hiểu cả 4.
Sinh mã tự động (số phiếu/đơn) cấu hình ở Master (CustomGenCode).
Không xóa cứng: IsDeleted. Mọi bảng có CreatedByUserId/UpdatedByUserId/…DatetimeUtc; RowVersion chống ghi đè.
Tác vụ nặng (tính giá thành, phân bổ) chạy nền theo job thay vì đồng bộ.
Lộ trình đọc cho người mới
- Đọc hết trang này để nắm khung & thuật ngữ cốt lõi.
- Mở 🔗 Luồng end-to-end để thấy dữ liệu chạy xuyên các phân hệ trong 1 quy trình thật.
- Mở 🗺️ Sơ đồ ER, bắt đầu từ Stock và Manufacturing; bấm bảng để xem mô tả · quan hệ · cột (+enum).
- Gặp thuật ngữ lạ → tra 📖 Từ điển nghiệp vụ.
- Muốn chạy thử → làm theo ⚙️ Quickstart dev.
Mẹo dùng công cụ này
- Menu trái: chuyển giữa các phần.
- Ô tìm kiếm (trong Sơ đồ ER): gõ tên bảng/cột/enum → nhảy tới & ghim popup.
- Hover bảng = xem nhanh; click = ghim popup (mô tả, quan hệ, cột + giá trị enum).
- Mỗi module có Flow chi tiết (sơ đồ + bước) — zoom/kéo được.
Tài liệu kèm: guide entity (.md) · Excel catalog.
🔗 Quy Trình VErp ERP: Từ Đơn Hàng Bán Đến Thanh Toán Kế Toán
Kịch bản "golden path" hoàn chỉnh theo dõi vòng đời một sản phẩm từ lúc khách hàng đặt hàng qua chứng từ Ordering (VoucherBill), sản xuất qua Manufacturing (ProductionOrder), lưu kho Stock (Inventory), kiểm chất QC (InspectionSheet), đến thanh toán kế toán Accountancy (CalcFeeProduct, InputBill). Mỗi bước ghi cụ thể module, hành động tiếng Việt, và các entity/bảng PascalCase liên quan.
📖 Từ điển nghiệp vụ
74 thuật ngữ
| Thuật ngữ | Giải thích |
|---|---|
| Phân hệ Module / Domain | Một lĩnh vực chức năng độc lập trong VErp như Stock (Kho), Manufacturing (Sản xuất), Accountancy (Kế toán), QC, Purchase Orders (Mua hàng), Organization (Nhân sự). Mỗi phân hệ có service riêng, database riêng, và flow nghiệp vụ độc lập. |
| Lệnh sản xuất ProductionOrder | Chỉ thị sản xuất chứa mã, ngày bắt đầu/kết thúc, sản phẩm cần sản xuất, số lượng, và liên kết với phòng ban, kế hoạch tháng. Là thực thể chính trong quy trình sản xuất từ lập kế hoạch đến hoàn thành. |
| BOM Bill of Materials (ProductBom) | Danh sách vật liệu/thành phần cần thiết để sản xuất một sản phẩm hoàn chỉnh, bao gồm sản phẩm con, số lượng, tỷ lệ hao hụt, bước sản xuất input/output. |
| AQL Acceptable Quality Level | Mức chất lượng chấp nhận được trong kiểm tra chất lượng - xác định tỷ lệ/số lượng lỗi tối đa chấp nhận được. Được áp dụng theo mức kiểm tra (G1, G2, S1, S2) và loại lỗi (nhẹ, lớn, nghiêm trọng). |
| Kiệm hàng Package | Đơn vị vật lý quản lý hàng hóa trong kho, chứa thông tin sản phẩm, vị trí lưu trữ, số lượng tồn hiện tại, hạn sử dụng, và liên kết đến đơn hàng/sản xuất. |
| Bút toán Journal Entry / Transaction Entry (InputValueRowBt) | Ghi chép kế toán ghi nhận giao dịch với tài khoản nợ/có, số tiền VND/ngoại tệ, ngày hạch toán, và liên kết hóa đơn/khách hàng. Là đơn vị cơ sở của sổ cái. |
| Voucher Voucher / Document (VoucherBill, VoucherType) | Loại chứng từ tùy chỉnh động (hóa đơn, phiếu xuất/nhập, v.v.) với cấu hình riêng bao gồm trường dữ liệu, khu vực hiển thị, quy tắc xác thực, hook JS/SQL. VoucherBill lưu dữ liệu chứng từ thực tế. |
| Bàn giao Handover (ProductionHandover) | Bản ghi chuyển giao sản phẩm/vật liệu từ công đoạn này sang công đoạn khác (hoặc phòng ban này sang phòng ban khác), ghi nhận số lượng, ngày giờ, và trạng thái (chưa tiếp nhận/đã tiếp nhận). |
| Gia công ngoài Outsourcing (OutsourcePartRequest, OutsourceStepRequest) | Yêu cầu giao công cho bên thứ ba để gia công chi tiết sản phẩm hoặc toàn bộ công đoạn sản xuất. Liên kết với đơn đặt hàng mua và tạo thành phần của đơn hàng sản xuất. |
| Kiểm kê Stock Take (StockTake, StockTakePeriod) | Hoạt động kiểm tra, đếm tồn kho theo kỳ (hàng tháng/quý/năm) để xác nhận số lượng thực tế và so sánh với sổ sách. Tạo phiếu kiểm kê và biên bản tiếp nhận khi hoàn thành. |
| Phân bổ hóa đơn Invoice Allocation / Clearing (InvoiceManualAllocate, InvoiceClearing) | Quá trình ghi nhận thanh toán hóa đơn bằng cách kết nối khoản thanh toán (hóa đơn thanh toán) với hóa đơn gốc cần thanh toán. Có thể thủ công (Manual) hoặc tự động (Auto). Quyết toán (Clearing) là bước xác nhận thanh toán hoàn tất. |
| Yêu cầu xuất kho Inventory Requirement (InventoryRequirement) | Phiếu yêu cầu xuất hàng từ kho cho mục đích sản xuất, bán hàng, hoặc sử dụng nội bộ. Chứa danh sách sản phẩm cần xuất, số lượng, kho cấp, và trạng thái duyệt. |
| Phiếu xuất nhập kho Inventory Transaction (Inventory, InventoryDetail) | Chứng từ ghi nhận hoạt động xuất/nhập hàng hóa vào/ra khỏi kho với loại (xuất/nhập), ngày, mã hóa đơn, chi tiết sản phẩm/kiện/số lượng, và trạng thái duyệt. |
| Cân đối tồn kho Inventory Balance (InvBalance, InvBalanceDetail) | Ảnh chụp số lượng tồn kho tính đến một ngày nhất định, được dùng làm điểm mốc để tính toán số dư hiện tại qua timeline giao dịch. Bao gồm chi tiết per sản phẩm, vị trí, đơn vị. |
| Phân công sản xuất Production Assignment (ProductionAssignment) | Bản ghi gán phân công sản xuất cho một công đoạn cụ thể, phòng ban, và lệnh sản xuất, chứa số lượng, khối lượng công việc, giờ làm, ngày bắt đầu/kết thúc, và tỷ lệ tiến độ (0-100%). |
| Công đoạn sản xuất Production Step (ProductionStep, Step) | Một giai đoạn/bước trong quy trình sản xuất, được định nghĩa với tên, tỉ lệ co, loại bàn giao. Có thể là step bắt đầu hoặc kết thúc của quy trình. Sự kết nối giữa các step tạo nên quy trình hoàn chỉnh. |
| Phòng ban Department (Department, FactoryDepartment) | Đơn vị tổ chức sản xuất/kinh doanh chịu trách nhiệm thực hiện công đoạn hoặc quản lý kho. Hỗ trợ cấu trúc phân cấp (phòng cha-con) và cân đối năng lực (giờ làm, số nhân viên). |
| Công ty con Subsidiary | Chi nhánh/cơ sở kinh doanh trong hệ thống multi-tenant. Mỗi Subsidiary có dữ liệu riêng, cấu hình riêng, và được tách biệt hoàn toàn trong database bằng SubsidiaryId. Hỗ trợ cấu trúc phân cấp (chi nhánh con). |
| Multi-tenant Multi-tenant / SubsidiaryId | Kiến trúc cho phép nhiều công ty con (Subsidiary) hoạt động trên cùng một ứng dụng, với dữ liệu được tách biệt qua SubsidiaryId. VErp sử dụng multi-tenant tại row-level, không phải database-level. |
| Soft-delete Soft Delete (IsDeleted) | Thay vì xóa dữ liệu vật lý (xóa dòng), đánh dấu IsDeleted=true để giữ lại audit trail và khôi phục dữ liệu nếu cần. Phần lớn entity trong VErp hỗ trợ soft-delete. |
| Gen-code Auto Code Generation (CustomGenCode, CustomGenCodeValue) | Cơ chế tự động sinh mã dữ liệu (mã hóa đơn, phiếu xuất/nhập, v.v.) theo quy tắc tùy chỉnh với tiền tố, số thứ tự tăng dần, định dạng ngày. Cấu hình trong CustomGenCode, trạng thái hiện tại lưu ở CustomGenCodeValue. |
| Chứng từ động Dynamic Document / Configurable Voucher | Kiến trúc cho phép xây dựng loại chứng từ (VoucherType) mới mà không cần lập trình, bằng cách cấu hình: trường (VoucherField), khu vực hiển thị (VoucherArea), quy tắc xác thực, hook JS/SQL. Áp dụng cho hóa đơn bán hàng, yêu cầu mua, v.v. |
| Phiếu nhập liệu Input Bill (InputBill, InputType) | Loại chứng từ động cho hệ thống kế toán, dùng để nhập dữ liệu chi phí, phân bổ, hoặc bút toán tùy chỉnh. Cấu trúc tương tự VoucherType nhưng dành cho tính toán tài chính. InputBill lưu dữ liệu thực tế, InputType định nghĩa cấu trúc. |
| InputType / VoucherType Input/Voucher Type Definition | Định nghĩa cấu trúc một loại chứng từ (InputType cho kế toán, VoucherType cho bán hàng) bao gồm: trường dữ liệu, khu vực hiển thị, hook hành động (JS/SQL: BeforeSave, AfterSave, BeforeSubmit), quy tắc xác thực. |
| VoucherArea / InputArea Document Area / Section | Một khu vực/section bên trong chứng từ (ví dụ: header, chi tiết, chân trang) giúp tổ chức các trường dữ liệu. Cấu hình cột hiển thị, cho phép nhiều dòng hoặc dòng đơn, và thứ tự. |
| VoucherField / InputField Document Field Definition | Định nghĩa một trường dữ liệu trong chứng từ bao gồm: tên, kiểu dữ liệu, kiểu biểu mẫu (text/dropdown/date), quy tắc xác thực, bảng tham chiếu (Ref*), và handler sự kiện (onChange, onFocus, onBlur). |
| Bảng Ref* Reference Table (Ref prefix) | Bảng tham chiếu dùng để tìm giá trị dropdown hoặc xác thực dữ liệu đầu vào trong trường chứng từ. Tên bảng bắt đầu với 'Ref' để chỉ nó là danh mục/master data (ví dụ: RefCustomer, RefProduct, RefDepartment). |
| Phiếu kiểm tra Inspection Sheet (InspectionSheet, InspectionSheetItem) | Tài liệu QC ghi nhận kết quả kiểm tra chất lượng cho một lô hàng hoặc sản xuất, chứa danh sách mặt hàng kiểm tra, tiêu chí, mẫu kiểm tra, kết quả đạt/không đạt, và lỗi phát hiện. |
| Tiêu chí kiểm tra Quality Criteria (QualityCriteria, QualityStandardCriteria) | Một tiêu chí kiểm tra cụ thể (định tính hoặc định lượng) với tên, dụng cụ đo, công thức tính giá trị chuẩn, sai lệch cho phép. Liên kết với loại lỗi có thể xảy ra (error codes). Được gán vào Tiêu chuẩn chất lượng với AQL rate. |
| Tiêu chuẩn chất lượng Quality Standard (QualityStandard) | Bộ tiêu chí kiểm tra hoàn chỉnh áp dụng cho một sản phẩm, loại sản phẩm, hoặc công đoạn. Gồm: danh sách tiêu chí, quy tắc lấy mẫu, mức AQL cho từng tiêu chí, và phạm vi áp dụng (sản phẩm, danh mục, hoặc công đoạn). |
| Quy tắc lấy mẫu Sampling Standard (SamplingStandard, SamplingStandardSampleSize) | Tiêu chuẩn lấy mẫu định nghĩa cách chọn mẫu từ lô hàng theo lô lớn, mức kiểm tra (bình thường/thắt chặt/giảm nhẹ), ví dụ ISO 2859-1. Bao gồm bảng kích thước mẫu tương ứng với từng tổ hợp mức kiểm tra. |
| Phân loại lỗi Inspection Error Type (InspectionErrorType, InspectionError) | Hệ thống phân loại lỗi kiểm tra thành nhóm (InspectionErrorType: ví dụ kích thước, màu sắc, vết nứt) và chi tiết mã lỗi (InspectionError) với mô tả, mức độ (nhẹ/lớn/nghiêm trọng), hình ảnh minh họa. |
| Mã lỗi Error Code (InspectionError, InspectionErrorType) | Mã định danh một loại lỗi cụ thể phát hiện trong kiểm tra chất lượng (ví dụ: ERR001 - Màu sắc không đúng), ghi nhận mục đích, mức độ lỗi, và có thể kèm hình ảnh. |
| Lần chạy kiểm tra Test Run (InspectionSheetItemTestRun, InspectionSheetItemCriteriaTestRun) | Bản ghi một lần kiểm tra thực tế một sản phẩm hoặc tiêu chí cụ thể, ghi nhận số lượng kiểm tra, kết quả đạt/không đạt, kiện hàng, pallet, và danh sách lỗi phát hiện. |
| Hành động khắc phục Corrective Action Plan (CorrectiveActionPlan, CapErrorItem, CapDispositionAction) | Phiếu hành động khắc phục theo dõi sản phẩm không phù hợp, ghi nhận nguyên nhân ban đầu, phân tích nguyên nhân, hành động xử lý (hủy, sửa chữa, bán hạ giá), và đánh giá hiệu quả. Có các giai đoạn xét duyệt QC, QA, quản lý. |
| Báo lỗi khách hàng Customer Feedback (CustomerFeedback, CustomerFeedbackDetail) | Bản ghi báo lỗi từ khách hàng về sản phẩm nhận được, gồm ngày báo, đơn hàng/hóa đơn, nội dung lỗi, số lượng lỗi, yêu cầu xử lý, và trạng thái giải quyết. |
| Tính toán vật liệu Material Calculation (MaterialCalc, MaterialCalcProduct, MaterialCalcSummary) | Tính toán số lượng vật liệu cần mua từ các đơn hàng bán hàng bằng cách tính BOM của sản phẩm bán. Tóm tắt vật liệu gốc, vật liệu chuyển đổi, tỷ giá cho mỗi sản phẩm. |
| Tính toán thuộc tính Property Calculation (PropertyCalc, PropertyCalcProduct, PropertyCalcSummary) | Tính toán thuộc tính (kích thước, màu, v.v.) của vật liệu cần mua từ đơn hàng bán, dùng để cắt/tùy chỉnh vật liệu theo yêu cầu khách hàng. |
| Yêu cầu mua hàng Purchasing Request (PurchasingRequest, PurchasingRequestDetail) | Chứng từ đề xuất mua hàng từ các bộ phận nội bộ hoặc tính toán vật liệu, ghi nhận danh sách sản phẩm cần mua, số lượng, và trạng thái duyệt. |
| Gợi ý mua hàng Purchasing Suggestion (PurchasingSuggest, PurchasingSuggestDetail) | Đề xuất mua hàng sau khi duyệt yêu cầu mua, kèm theo chi phí, thuế, tỷ giá, và được phân công cho nhân viên mua hàng (PoAssignment) để tạo đơn đặt hàng. |
| Phân công đặt hàng PO Assignment (PoAssignment, PoAssignmentDetail) | Phân công mua hàng cho một nhân viên từ gợi ý mua hàng, chứa danh sách sản phẩm cần mua, giá, thuế, phí, và người được phân công. |
| Đơn đặt hàng mua Purchase Order (PurchaseOrder, PurchaseOrderDetail) | Chứng từ chính khi mua hàng từ nhà cung cấp, ghi nhận nhà cung cấp, sản phẩm, số lượng, giá, thuế, chi phí giao, và trạng thái duyệt/nhận hàng. |
| Giá nhà cung cấp Provider Pricing (PoProviderPricing, PoProviderPricingDetail) | Bảng giá từ nhà cung cấp cho các sản phẩm, ghi nhận chi phí giao hàng, thuế, tỷ giá, và các sản phẩm trong bảng giá với giá từng cái. |
| Theo dõi đơn hàng Purchase Order Tracked (PurchaseOrderTracked) | Nhật ký theo dõi tiến độ nhận hàng từ đơn đặt hàng, ghi nhận ngày, sản phẩm, số lượng nhận, và tình trạng. |
| Kế hoạch tháng Month Plan (MonthPlan, WeekPlan) | Kế hoạch sản xuất tháng chứa ngày bắt đầu/kết thúc và ghi chú, dùng để nhóm các lệnh sản xuất, tính toán công suất, và lập kế hoạch chi tiết (kế hoạch tuần). |
| Vật tư tiêu hao Consumable Materials (ProductionConsumMaterial) | Vật tư bổ sung (kim, sợi chỉ, hóa chất, v.v.) được sử dụng trong sản xuất nhưng không phải nguyên vật liệu chính, lưu trữ theo công đoạn, lệnh sản xuất, và phòng ban. |
| Tính chi phí sản phẩm Calculate Product Fees (CalcFeeProduct, CalcFeeProductDoneRow) | Tính toán chi phí sản phẩm theo kỳ (tháng/năm) gồm chi phí trực tiếp (vật liệu, lao động), chi phí gián tiếp, chi phí chung, và so sánh với giá bán để tính giá thành. |
| Tính lợi nhuận thua lỗ Calculate Profit and Loss (CalcProfitAndLost) | Tính toán báo cáo lợi nhuận và thua lỗ theo kỳ (tháng/năm), được lọc theo sản phẩm, khách hàng, hoặc bộ phận, giúp phân tích hiệu suất kinh doanh. |
| Bảng công nhân viên Time Sheet (TimeSheet, TimeSheetDetail, TimeSheetAggregate) | Bảng tính công hàng tháng ghi nhận công của nhân viên theo ngày, với chi tiết giờ vào/ra, loại ngày (thường/cuối tuần/lễ), tổng hợp công (công thường, công cuối tuần, công lễ, giờ công). |
| Lịch ca làm việc Shift Schedule (ShiftSchedule, ShiftScheduleConfiguration, ShiftScheduleDetail) | Lịch gán ca làm việc cho nhân viên, cấu hình gán ca theo ngày trong tuần, chu kỳ, hoặc danh sách ngày cụ thể. Chi tiết lịch lưu ca của từng nhân viên mỗi ngày. |
| Cấu hình ca làm việc Shift Configuration (ShiftConfiguration) | Định nghĩa thông tin ca làm việc gồm giờ vào, giờ ra, giờ ăn, loại ca (sáng/chiều/đêm), quy tắc tính muộn/sớm/không chấm công, và chi tiết xử lý. |
| Tăng ca Overtime (OvertimeConfiguration, ShiftRequestLetterOvertime) | Định nghĩa rule tính giờ tăng ca bao gồm làm tròn giờ, ngưỡng tối thiểu, bônus khi đạt mục tiêu, khung giờ áp dụng. Đơn xin tăng ca ghi nhận giờ bắt đầu/kết thúc và tổng giờ tăng ca. |
| Đơn xin nghỉ phép Leave Request (Leave, LeaveConfig, AbsenceTypeSymbol) | Đơn xin nghỉ phép ghi nhận ngày bắt đầu/kết thúc, số ngày (nửa ngày), loại vắng mặt (phép năm, phép không lương, ốm), và trạng thái phê duyệt. Cấu hình chính sách phép định nghĩa phép tối đa, tỷ lệ lương. |
| Kỳ lương Salary Period (SalaryPeriod, SalaryPeriodGroup) | Kỳ tính lương ghi nhận tháng/năm, ngày bắt đầu/kết thúc, người kiểm duyệt, và trạng thái duyệt. Liên kết với nhóm lương (SalaryGroup) qua SalaryPeriodGroup. |
| Nhóm lương Salary Group (SalaryGroup, SalaryField, SalaryGroupField) | Nhóm lương để nhân viên với các trường lương tùy chỉnh (lương cơ bản, phụ cấp, khấu trừ, v.v.), được áp dụng cho một kỳ lương cụ thể. Cấu hình điều kiện áp dụng, thứ tự tính toán, và công thức tính. |
| Lương Salary (SalaryDailyEmployee, SalaryDailyEmployeeValue, CalcFeeProduct) | Tính toán lương từ bảng công, kỳ lương, và công thức. SalaryDailyEmployee lưu tính toán hàng ngày, SalaryDailyEmployeeCurrentMonth lưu tháng hiện tại, được sử dụng để báo cáo lương thời gian thực. |
| Danh mục khách hàng Customer Category (CustomerCate, Customer) | Phân loại khách hàng theo danh mục, mỗi khách hàng ghi nhận mã, tên, loại, địa chỉ, thuế, liên hệ, hạn nợ, hạn vay, điều kiện thanh toán. |
| Sản phẩm Product (Product, ProductCate, ProductType) | Mô tả sản phẩm ghi nhận mã, tên, danh mục, loại, kích thước, trọng lượng, đơn vị cơ bản, barcode, trạng thái hoạt động. ProductCate/ProductType phân loại sản phẩm theo danh mục hoặc loại. |
| Chuyển đổi đơn vị Unit Conversion (ProductUnitConversionInfo, Unit) | Quy tắc chuyển đổi giữa đơn vị cơ bản và đơn vị phụ của sản phẩm, ghi nhận công thức chuyển đổi, số chữ số thập phân. Unit định nghĩa đơn vị tính được sử dụng trong hệ thống. |
| Kho hàng Stock / Warehouse (Stock, Location) | Quản lý kho ghi nhận mã, tên, địa chỉ, người quản lý, trạng thái. Location là vị trí lưu trữ cụ thể trong kho hỗ trợ quản lý vị trí chi tiết. |
| Vai trò người dùng Role (Role, RolePermission, RoleDataPermission) | Vai trò xác định quyền của người dùng, gồm quyền truy cập module (RolePermission) và quyền dữ liệu cấp độ đối tượng (RoleDataPermission). Hỗ trợ cấu trúc phân cấp vai trò cha-con. |
| Vai trò dữ liệu Data Permission / Row-Level Security | Quyền truy cập dữ liệu ở mức dòng (row-level), cho phép người dùng chỉ xem/chỉnh sửa dữ liệu của Subsidiary hoặc bộ phận cụ thể. Được cấu hình qua RoleDataPermission. |
| Nhóm danh mục Category Group (CategoryGroup) | Nhóm các danh mục liên quan (Product, Customer, Supplier, v.v.) để tổ chức và quản lý dễ dàng hơn trong master data. |
| Danh mục động Dynamic Category (Category, CategoryField, CategoryView, CategoryViewField) | Cơ chế tạo danh mục (lookup table) động mà không cần lập trình, bằng cách cấu hình trường dữ liệu, view hiển thị, và quy tắc xác thực. Là nền tảng cho việc mở rộng master data. |
| Báo cáo Report (ReportType, ReportTypeView, ReportSavedData) | Mẫu báo cáo động với cấu hình SQL/JS/C#, template Excel/XML, các tham số lọc, và chế độ xem khác nhau. ReportSavedData lưu báo cáo đã thực thi để chia sẻ hoặc tái sử dụng. |
| Bảng điều khiển Dashboard (DashboardType, DashboardTypeView) | Bảng điều khiển hiển thị dữ liệu kinh doanh với biểu đồ, SQL động, JS tùy chỉnh. Được cấu hình với các chế độ xem khác nhau và các tham số lọc. |
| Thông tin kinh doanh Business Info (BusinessInfo) | Lưu trữ thông tin chính thức công ty con như tên ngắn, tên công ty, tên tiếng Anh, người đại diện, địa chỉ, thuế, liên hệ, logo. |
| Audit log Activity Log / Audit Trail | Ghi nhận lịch sử thay đổi dữ liệu gồm ai, khi nào, thay đổi gì, giá trị cũ/mới. Lưu trữ trong ActivityLogDatabase và sử dụng để kiểm toán, phục hồi dữ liệu, và phân tích thay đổi. |
| Cấu hình hệ thống System Configuration (Config, EmailConfiguration, CurrencyConvert, I18nLanguage) | Các thiết lập toàn cục của hệ thống như thông tin SMTP, tỷ giá tiền tệ, ngôn ngữ giao diện, mẫu email, cấu hình in, quy tắc sinh mã. |
| Mẫu email Email Template (MailTemplate) | Mẫu email có sẵn với tiêu đề, nội dung, mã mẫu dùng để gửi email tự động trong các quy trình (xác nhận đơn hàng, thông báo phê duyệt, v.v.). |
| Hàng sửa chữa Rework / Scrap Disposition | Hành động xử lý sản phẩm lỗi phát hiện qua kiểm tra chất lượng: hủy, sửa chữa, bán hạ giá. Được ghi nhận trong CapDispositionAction và CorrectiveActionPlan. |
| Cấu hình bút toán GL Entry Config (TransactionEntryConfig) | Cấu hình quy tắc bút toán kế toán định nghĩa tài khoản nợ/có, loại bút toán, và chiều phân tích (khách hàng, bộ phận, hợp đồng, đơn hàng, sản phẩm) cho từng loại chứng từ. |
| Quyết toán Invoice Reconciliation / Clearing (InvoiceClearing) | Quá trình xác nhận hóa đơn đã được thanh toán hoàn toàn bằng cách kết nối các khoản thanh toán (allocation) với hóa đơn gốc, sau đó khóa hóa đơn. |
| Hóa đơn Invoice / Bill (VoucherBill, InputBill) | Chứng từ ghi nhận giao dịch mua, bán, hoặc kế toán. VoucherBill là hóa đơn bán hàng, InputBill là hóa đơn kế toán. Mỗi hóa đơn có mã duy nhất, ngày, và trạng thái duyệt. |
⚙️ Quickstart môi trường dev
Chạy hệ thống ở máy local
Yêu cầu môi trường
- SQL Server 2019+ (LocalDB or full instance): Stores 12 distinct databases (Master, Stock, Identity, PurchaseOrder, Accountancy Private/Public, Report, Organization, Manufacturing, ActivityLog, StatisticReportData, QcManagement)
- .NET 10.0 SDK: VErp API targets net10.0
- Node.js 18+: Frontend uses Angular 21 with npm 10+
- Redis (optional): Backend supports caching; development uses in-memory by default
- Visual Studio 2022 Community+ OR VS Code: Recommended for .NET/C# development
Chạy Backend (verp-api)
- Verify prerequisites: dotnet --version (10.0+), sqlserver instance accessible
- Clone/navigate to D:\Projects\verp\verp-api
- Create config files from templates: Copy EfScaffolding\AppSetting.Development.json to root directory (or set ASPNETCORE_ENVIRONMENT=Development)
- Update database connection strings in AppSetting config: Set ConnectionString keys for all 12 databases to point to local SQL Server instance (e.g., 'Server=.;Database=MasterDB;Trusted_Connection=true;' for each database name)
- Restore NuGet packages: dotnet restore VErp.sln
- Build solution: dotnet build VErp.sln -c Debug
- Run Entity Framework migrations (if using EF Core migrations): dotnet ef database update against each DbContext (or apply SQL scripts in SqlScripts/Database/Database-Scripts/Structure/ manually)
- Seed initial data: Optional—run SQL scripts in SqlScripts/Commons/ for permissions, configs, and test data
- Start API: dotnet run --project WebApis/VErpApi/VErpApi.csproj OR via Visual Studio (F5 with Development profile)
- Verify: Navigate to http://localhost:5000 (launchSettings.json shows applicationUrl http://localhost:5000 for Development), check Swagger UI at http://localhost:5000/swagger
Chạy Frontend (verp-web)
- Navigate to D:\Projects\verp\verp-web
- Install dependencies: npm install
- Update environment configuration: Edit src\environments\environment.development.ts and set apiEndpoint, tokenEndpoint, signalREndpoint to match backend (http://localhost:5000 or http://localhost:8001 per config)
- Start dev server: npm start (uses ng serve --port 8005 --host 0.0.0.0 --configuration=development)
- Verify: Navigate to http://localhost:8005, check browser console for API connectivity
- Optional—build for production: npm run deploy-production (creates dist/verp-web/ with optimized bundles)
Lưu ý
- Multi-tenant via subsidiary codes: Backend isolates data by subsidiary (see AppSetting.Developer.SubsidiaryCodes in config)
- Authentication: Uses IdentityServer4 (IdentityServerDB) with OAuth2/JWT; development credentials in environment.development.ts (client_id: 'web', client_secret: 'secretWeb')
- Database strategy: 12 separate SQL Server databases (not a single monolith) composed via DI in DatabaseConfigurationExtensions.cs—must configure all 12 connection strings
- API endpoints: http://localhost:5000 (backend) vs http://localhost:8005 (frontend SPA); SignalR for real-time notifications
- Memory constraints: package.json sets --max_old_space_size=8048 for ng serve (high memory demand for large Angular project)
- SignalR: Frontend subscribes to http://localhost:5000/ for live notifications (configured in environment files)
- File upload: Backend expects /usr/resources/ folder; update FileUploadFolder in AppSetting if running locally
- Certificates: Development uses certificate-free setup in AppSetting; production requires /usr/verp/certificate/cert.pfx
- Caching layers: Backend uses Redis (if configured) + in-memory (MemCache); activity logging uses ActivityLogDB for distributed locks
- Backend runs on http://localhost:5000 by default; frontend on http://localhost:8005; ensure no port conflicts
- No Docker setup required for local dev; use native SQL Server and Node.js
- Unresolved: Exact SQL script execution order for schema + seeding not documented—may need to apply scripts manually from SqlScripts/Database/ in dependency order or use EF Core migrations if available
VErp — Sơ đồ tương quan entity theo phân hệ
Sinh từ OnModelCreating (FK đã kiểm chứng). Quan hệ: cha ||--o{ con = con giữ khóa ngoại trỏ cha. Đã ẩn khóa phân vùng SubsidiaryId & audit để làm rõ quan hệ domain. FK* = khóa tổng hợp.
01. Stock (StockDBContext)
📋 Flow chi tiết (16 bước)
Mô-đun Kho quản lý toàn bộ vòng đời sản phẩm từ nhập, xuất, tồn kho cho đến kiểm kê định kỳ. Dữ liệu chảy qua các phiếu giao dịch (Inventory) để thay đổi số lượng trong kiện hàng (Package), sau đó cập nhật lịch sử tồn kho theo thời gian (InvBalance) và hỗ trợ các yêu cầu xuất kho từ sản xuất (InventoryRequirement). Mô-đun liên kết chặt với Sản xuất, QC, và Kế toán để đảm bảo dữ liệu chi phí và trạng thái hoạt động luôn đồng bộ.
flowchart TD
n1[/"1. Inventory tạo với ID duy nhất (Phiếu xuất nhập kho)"/]
n2[/"2. InventoryDetail với ProductId (Chi tiết xuất nhập hàng)"/]
n3{"3. Xác thực Package hợp lệ (Kiện hàng)"}
n4[/"4. Gửi Inventory tới kiểm tra (Phiếu xuất nhập kho) ↔QcManagement"/]
n5["5. Phê duyệt cập nhật Package (Kiện hàng)"]
n6["6. Tái tính số dư Package (Kiện hàng)"]
n7["7. Đánh dấu InvBalance cần tái tính (Cân đối hàng tồn)"]
n8["8. Tái tính InvBalance lịch sử (Cân đối hàng tồn)"]
n9[/"9. InventoryRequirement cho sản xuất (Yêu cầu xuất kho) ↔Manufacturing"/]
n10["10. InventoryRequirementDetail chi tiết (Chi tiết yêu cầu xuất hàng) ↔Manufacturing"]
n11[/"11. Tạo phiếu xuất kho từ yêu cầu ↔Manufacturing"/]
n12["12. RefInventoryId liên kết phiếu"]
n13[/"13. StockTakePeriod khởi tạo kiểm kê (Kỳ kiểm kê kho)"/]
n14[/"14. StockTake chi tiết kiểm thực tế (Phiếu kiểm kê chi tiết)"/]
n15(["15. Certificate xác nhận kiểm kê"])
n16[/"16. Thông báo Module khác (Module ứng dụng) ↔Manufacturing+1"/]
n1 --> n2
n2 --> n3
n3 --> n4
n4 --> n5
n5 --> n6
n6 --> n7
n7 --> n8
n8 --> n9
n9 --> n10
n10 --> n11
n11 --> n12
n12 --> n13
n13 --> n14
n14 --> n15
n15 --> n16
classDef cross fill:#fef3c7,stroke:#d97706,color:#7c2d12;
classDef se fill:#dcfce7,stroke:#16a34a,color:#14532d;
class n15 se;
class n4,n9,n10,n11,n16 cross;- 1. Khi nhân viên tạo phiếu giao dịch, hệ thống tạo bản ghi Inventory với InventoryCode duy nhất, InventoryTypeId (1=Nhập/2=Xuất), và StockId để xác định kho lưu trữ.
- 2. Cho mỗi dòng sản phẩm, tạo InventoryDetail với ProductId, ProductUnitConversionId (đơn vị tính), PrimaryQuantity, và FromPackageId/ToPackageId để theo dõi nguồn/đích kiện hàng.
- 3. Hệ thống xác thực Package (từ bảng Package) tồn tại và có số lượng hợp lệ qua FromPackageId, kiểm tra số dư trong kiện trước khi cho phép xuất.
- 4. Người dùng hoặc QC có thể gửi Inventory tới kiểm tra bằng SentToCensor, lúc này CensorByUserId và CensorDatetimeUtc được ghi lại cho phép QC xử lý.
- 5. Khi phiếu được phê duyệt (IsApproved=true), hệ thống cập nhật ToPackageId nếu là nhập kho, hoặc giảm FromPackageId nếu là xuất kho; TotalMoney tính từ tổng Money của InventoryDetail.
- 6. Đối với mỗi Package bị ảnh hưởng, hệ thống gọi RecalcPackageQuantityOnInvDetail để tái tính số dư dựa trên tất cả InventoryDetail liên quan.
- 7. Sau khi cập nhật Package, hệ thống đánh dấu InvBalanceDirty để chỉ ra rằng InvBalance cần tái tính, đảm bảo không phải tải lại toàn bộ lịch sử mà chỉ từ điểm bẩn.
- 8. Hệ thống tái tính InvBalance bằng cách lấy InvBalance gần nhất trước ngày thay đổi, sau đó phát lại tất cả InventoryDetail theo thứ tự (Date, InventoryTypeId, InventoryDetailId) để tích lũy số dư cho InvBalanceDetail.
- 9. Khi sản xuất yêu cầu nguyên liệu, hệ thống tạo InventoryRequirement với InventoryRequirementCode, InventoryTypeId=2 (xuất), Date, và DepartmentId của bộ phận yêu cầu.
- 10. Mỗi dòng InventoryRequirementDetail chứa ProductId, AssignStockId (kho cấp hàng), ProductUnitConversionId, tương ứng với ProductBom của sản phẩm hoàn thành để tính tỷ lệ cần hàng.
- 11. Khi InventoryRequirementDetail được phê duyệt, hệ thống tạo Inventory xuất kho với FromPackageId từ kho AssignStockId, liên kết qua InventoryRequirementDetailId và ProductionOrderCode để theo dõi nguồn gốc.
- 12. Một Inventory có thể có RefInventoryId để liên kết các phiếu liên quan (ví dụ: từ phiếu kho đầu vào thành phiếu kho xuất khi chuyển kho hoặc nhập lại), tạo chuỗi giao dịch theo dõi được.
- 13. Mỗi tháng/kỳ, quản lý tạo StockTakePeriod với StockTakePeriodCode, Date, StockId, và Status để khởi tạo phiếu kiểm kê định kỳ cho một kho cụ thể.
- 14. Nhân viên kho điền StockTake (chi tiết kiểm kê thực tế) với StockTakeId, StockTakeDetail chứa ProductId, số lượng kiểm được thực tế, so sánh với InvBalanceDetail để phát hiện chênh lệch (IsDifference).
- 15. Sau khi hoàn thành kiểm kê, quản lý tạo StockTakeAcceptanceCertificate với StockTakePeriodId, ghi nhận kết luận (ConclusionContent), xác nhận hoặc điều chỉnh số dư nếu có mất mát/lỗi.
- 16. Mô-đun thông báo tới Manufacturing để cập nhật trạng thái ProductionOrderStatus khi có biến động Inventory, và tới Accountancy để ghi chi phí dựa trên UnitPrice × Quantity trong InventoryDetail, đảm bảo báo cáo tài chính chính xác.
erDiagram
InvBalance["InvBalance (Cân đối hàng tồn)"] {
}
InvBalanceDetail["InvBalanceDetail (Chi tiết cân đối tồn kho)"] {
}
Inventory["Inventory (Phiếu xuất nhập kho)"] {
}
InventoryDetail["InventoryDetail (Chi tiết xuất nhập hàng)"] {
}
InventoryRequirement["InventoryRequirement (Yêu cầu xuất kho)"] {
}
InventoryRequirementDetail["InventoryRequirementDetail (Chi tiết yêu cầu xuất hàng)"] {
}
Location["Location (Vị trí lưu trữ)"] {
}
Package["Package (Kiện hàng)"] {
}
Product["Product (Sản phẩm)"] {
}
ProductBom["ProductBom (Danh sách vật liệu sản phẩm)"] {
}
ProductCate["ProductCate (Danh mục sản phẩm)"] {
}
ProductType["ProductType (Loại sản phẩm)"] {
}
ProductUnitConversionInfo["ProductUnitConversionInfo (Chuyển đổi đơn vị sản phẩm)"] {
}
Stock["Stock (Kho hàng)"] {
}
StockTake["StockTake (Phiếu kiểm kê chi tiết)"] {
}
StockTakeAcceptanceCertificate["StockTakeAcceptanceCertificate (Biên bản tiếp nhận kiểm kê)"] {
}
StockTakeDetail["StockTakeDetail (Chi tiết kiểm kê sản phẩm)"] {
}
StockTakePeriod["StockTakePeriod (Kỳ kiểm kê kho)"] {
}
StockTakeRepresentative["StockTakeRepresentative (Người đại diện kiểm kê)"] {
}
Unit["Unit (Đơn vị tính)"] {
}
InvBalance ||--o{ InvBalanceDetail : "InvBalanceId"
Stock ||--o{ Inventory : "StockId"
Inventory ||--o{ Inventory : "RefInventoryId"
Package ||--o{ InventoryDetail : "FromPackageId"
Inventory ||--o{ InventoryDetail : "InventoryId"
Product ||--o{ InventoryDetail : "ProductId"
ProductUnitConversionInfo ||--o{ InventoryDetail : "ProductUnitConversionId"
Package ||--o{ InventoryDetail : "ToPackageId"
Stock ||--o{ InventoryRequirementDetail : "AssignStockId"
InventoryRequirement ||--o{ InventoryRequirementDetail : "InventoryRequirementId"
Product ||--o{ InventoryRequirementDetail : "ProductId"
ProductUnitConversionInfo ||--o{ InventoryRequirementDetail : "ProductUnitConversionId"
Location ||--o{ Package : "LocationId"
ProductUnitConversionInfo ||--o{ Package : "ProductUnitConversionId"
Stock ||--o{ Package : "StockId"
ProductCate ||--o{ Product : "ProductCateId"
ProductType ||--o{ Product : "ProductTypeId"
Product ||--o{ ProductBom : "ProductId"
Product ||--o{ ProductBom : "ChildProductId"
ProductCate ||--o{ ProductCate : "ParentProductCateId"
ProductType ||--o{ ProductType : "ParentProductTypeId"
Product ||--o{ ProductUnitConversionInfo : "ProductId"
Unit ||--o{ ProductUnitConversionInfo : "RefUnitId"
StockTakePeriod ||--o{ StockTake : "StockTakePeriodId"
StockTakePeriod ||--o{ StockTakeAcceptanceCertificate : "StockTakePeriodId"
StockTake ||--o{ StockTakeDetail : "StockTakeId"
StockTakePeriod ||--o{ StockTakeRepresentative : "StockTakePeriodId"
Stock ||--o{ Location : "StockId"
Product ||--o{ Package : "ProductId"
Stock ||--o{ StockTakePeriod : "StockId"
02. Manufacturing (ManufacturingDBContext)
📋 Flow chi tiết (16 bước)
Mô-đun Manufacturing quản lý toàn bộ vòng đời sản xuất từ lập kế hoạch tháng đến hoàn thành bàn giao sản phẩm. Dữ liệu chảy từ MonthPlan → ProductionOrder → ProductionOrderDetail → ProductionAssignment (giao công đoạn cho bộ phận) → ProductionHandover (bàn giao giữa công đoạn) → hoàn thành. Mô-đun hỗ trợ gia công ngoài (OutsourcePartRequest), quản lý vật tư tiêu hao (ProductionConsumMaterial), và cập nhật tự động với Kho hàng thông qua InventoryDetail.
flowchart TD
n1(["1. Tạo MonthPlan kế hoạch (Kế hoạch tháng)"])
n2[/"2. Tạo ProductionOrder từ Sales (Lệnh sản xuất) ↔Ordering"/]
n3[/"3. Thêm ProductionOrderDetail (Chi tiết lệnh sản xuất)"/]
n4["4. Định nghĩa ProductionStep (Công đoạn sản xuất)"]
n5[/"5. Tạo ProductionStepLinkData (Dữ liệu liên kết công đoạn)"/]
n6[/"6. Gán ProductionAssignment (Phân công sản xuất)"/]
n7[/"7. Tạo ProductionOrderMaterials (Vật tư lệnh sản xuất) ↔Stock"/]
n8[/"8. Gia công ngoài OutsourcePart ↔PurchaseOrder"/]
n9["9. Cập nhật ProductionAssignmentDetail (Chi tiết phân công sản xuất)"]
n10[/"10. Theo dõi ProductionConsumMaterial (Vật tư tiêu hao)"/]
n11[/"11. Tạo ProductionHandover (Bàn giao sản xuất) ↔Stock"/]
n12{"12. Xác nhận bàn giao"}
n13[/"13. Ghi ProductionHistory (Lịch sử sản xuất)"/]
n14["14. Cập nhật tiến độ Assignment"]
n15{"15. Hoàn thành ProductionOrder (Lệnh sản xuất) ↔Stock"}
n16(["16. Đồng bộ các mô-đun ↔Stock"])
n1 --> n2
n2 --> n3
n3 --> n4
n4 --> n5
n5 --> n6
n6 --> n7
n7 --> n8
n8 --> n9
n9 --> n10
n10 --> n11
n11 --> n12
n12 --> n13
n13 --> n14
n14 --> n15
n15 --> n16
classDef cross fill:#fef3c7,stroke:#d97706,color:#7c2d12;
classDef se fill:#dcfce7,stroke:#16a34a,color:#14532d;
class n1,n16 se;
class n2,n7,n8,n11,n15,n16 cross;- 1. Tạo MonthPlan: Lập kế hoạch tháng sản xuất với StartDate, EndDate để xác định khoảng thời gian kế hoạch (dùng làm container cho các ProductionOrder).
- 2. Tạo ProductionOrder: Nhân viên tạo lệnh sản xuất từ đơn hàng bán (Sales), ghi ProductionOrderCode, StartDate, EndDate, MonthPlanId (liên kết kế hoạch tháng). Ban đầu IsDraft=true, ProductionOrderStatus=Draft.
- 3. Thêm ProductionOrderDetail: Mỗi lệnh sản xuất chứa chi tiết sản phẩm cần sản xuất, ghi ProductId, Quantity (số lượng), ReserveQuantity (bù hao), OrderDetailId (tham chiếu từ bán hàng), InvInpQuantity (nhập kho dự kiến).
- 4. Định nghĩa ProductionStep: Hệ thống tạo quy trình sản xuất bao gồm các công đoạn (Step) cần thực hiện. Mỗi ProductionStep có StepId, Title, ContainerId (liên kết ProductionOrderId), IsStartStep (công đoạn bắt đầu), IsEndStep (công đoạn cuối), CoordinateX/Y (vị trí trên bản đồ quy trình), OutsourceStepRequestId (nếu gia công ngoài).
- 5. Tạo ProductionStepLinkData: Dữ liệu liên kết chi tiết mỗi công đoạn (chi tiết kỹ thuật, bản vẽ, tham số). Hỗ trợ ProductionStepLinkDataRole để định nghĩa vai trò dữ liệu (kiểm tra, gia công, vân vân).
- 6. Gán phân công sản xuất (ProductionAssignment): Với mỗi cặp (ProductionOrder, ProductionStep, DepartmentId), tạo assignment ghi AssignmentQuantity, AssignmentWorkload, AssignmentHours, StartDate, EndDate, RateInPercent (mức độ hoàn thành 0-100%). Mỗi assignment liên kết ProductionStepLinkDataId để theo dõi kỹ thuật chi tiết.
- 7. Tạo ProductionOrderMaterials: Tính toán vật tư cần thiết cho mỗi ProductionOrder, ghi ProductId (vật tư), Quantity, ConversionRate, StepId, DepartmentId, ParentId (tạo cấu trúc BOM). Tự động tạo yêu cầu nhập kho với InventoryRequirementStatusId (liên kết với Stock module).
- 8. Gia công ngoài (nếu cần): Nhận diện công đoạn hoặc chi tiết sản phẩm cần gia công. Tạo OutsourcePartRequest từ ProductionOrderDetail, ghi OutsourcePartRequestCode, Status, tìm hoặc tạo PurchaseOrder (liên kết với Purchase module). Tạo OutsourceStepRequest từ ProductionOrder với OutsourceStepRequestData chứa các ProductionStepId cần gia công.
- 9. Cập nhật ProductionAssignmentDetail: Ghi nhận chi tiết thực hiện công đoạn tại bộ phận (lao động, thiết bị), liên kết ProductionAssignmentDetailLinkData với ProductionStepLinkDataId để theo dõi kỹ thuật thực tế.
- 10. Theo dõi tiêu hao vật tư (ProductionConsumMaterial): Khi công đoạn thực hiện, ghi vật tư tiêu hao thực tế (từ ProductionStep, ProductionAssignment), tính toán chi phí tiêu hao để so sánh dự toán.
- 11. Bàn giao giữa công đoạn (ProductionHandover): Khi công đoạn 1 hoàn thành, tạo ProductionHandover từ FromProductionStep (bộ phận phát hàng) đến ToProductionStep (bộ phận nhận), ghi HandoverQuantity, HandoverDatetime, Status=Pending. Liên kết InventoryDetailId (tự động tạo phiếu xuất/nhập kho trong Stock module). HandoverQuantity phải ≤ AssignmentQuantity.
- 12. Xác nhận bàn giao: Bộ phận tiếp nhận kiểm tra và chấp nhận handover (Status=Accepted), tạo ProductionHandoverReceipt (biên bản bàn giao). Cập nhật tự động vị trí vật tư trong Kho thông qua InventoryDetail.
- 13. Ghi lịch sử sản xuất (ProductionHistory): Mỗi lần bàn giao được chấp nhận, tạo ProductionHistory ghi FromProductionStep, ToProductionStep, ProductionHandoverReceiptId, theo dõi lịch sử di chuyển vật tư qua các công đoạn.
- 14. Cập nhật tiến độ Assignment: Theo dõi tiến độ công đoạn bằng RateInPercent (0-100%), AssignedProgressStatus (Pending/InProgress/Complete). Khi hoàn thành, AssignedProgressStatus=Complete, IsManualFinish có thể được đặt = true.
- 15. Hoàn thành ProductionOrder: Khi tất cả công đoạn xong (ProductionStep đều có status=Complete) và vật tư đã bàn giao về cuối (IsEndStep=true), cập nhật IsFinished=true, EstimateFinishDate → MaxInvOrHandoverDate (kiểm tra cân đối với Kho), MaxAccountingHandoverDate (kiểm tra với Kế toán).
- 16. Đồng bộ với các mô-đun khác: Cập nhật Sales (hoàn thành đơn hàng), Stock (tự động tạo phiếu nhập cuối sản xuất), Accountancy (ghi nhận chi phí sản xuất dựa MinAccountingHandoverDate/MaxAccountingHandoverDate). Kết thúc vòng đời khi tất cả điều kiện: đã xuất kho, đã hạch toán, Accountancy confirm = true.
erDiagram
MonthPlan["MonthPlan (Kế hoạch tháng)"] {
}
OutsourcePartRequest["OutsourcePartRequest (Yêu cầu gia công chi tiết)"] {
}
OutsourcePartRequestDetail["OutsourcePartRequestDetail (Chi tiết yêu cầu gia công)"] {
}
OutsourceStepRequest["OutsourceStepRequest (Yêu cầu gia công công đoạn)"] {
}
OutsourceStepRequestData["OutsourceStepRequestData (Dữ liệu gia công công đoạn)"] {
}
ProductionAssignment["ProductionAssignment (Phân công sản xuất)"] {
}
ProductionAssignmentDetail["ProductionAssignmentDetail (Chi tiết phân công sản xuất)"] {
}
ProductionAssignmentDetailLinkData["ProductionAssignmentDetailLinkData (Liên kết chi tiết phân công)"] {
}
ProductionConsumMaterial["ProductionConsumMaterial (Vật tư tiêu hao)"] {
}
ProductionHandover["ProductionHandover (Bàn giao sản xuất)"] {
}
ProductionHandoverReceipt["ProductionHandoverReceipt (Biên bản bàn giao)"] {
}
ProductionHistory["ProductionHistory (Lịch sử sản xuất)"] {
}
ProductionOrder["ProductionOrder (Lệnh sản xuất)"] {
}
ProductionOrderDetail["ProductionOrderDetail (Chi tiết lệnh sản xuất)"] {
}
ProductionOrderMaterials["ProductionOrderMaterials (Vật tư lệnh sản xuất)"] {
}
ProductionStep["ProductionStep (Công đoạn sản xuất)"] {
}
ProductionStepLinkData["ProductionStepLinkData (Dữ liệu liên kết công đoạn)"] {
}
ProductionStepLinkDataRole["ProductionStepLinkDataRole (Vai trò dữ liệu liên kết)"] {
}
Step["Step (Bước quy trình)"] {
}
StepGroup["StepGroup (Nhóm bước quy trình)"] {
}
WeekPlan["WeekPlan (Kế hoạch tuần)"] {
}
MonthPlan ||--o{ ProductionOrder : "MonthPlanId"
WeekPlan ||--o{ ProductionOrder : "FromWeekPlanId"
WeekPlan ||--o{ ProductionOrder : "ToWeekPlanId"
ProductionOrder ||--o{ ProductionOrderDetail : "ProductionOrderId"
ProductionOrderDetail ||--o{ OutsourcePartRequest : "ProductionOrderDetailId"
OutsourcePartRequest ||--o{ OutsourcePartRequestDetail : "OutsourcePartRequestId"
OutsourcePartRequestDetail ||--o{ ProductionStepLinkData : "OutsourceRequestDetailId"
ProductionOrder ||--o{ OutsourceStepRequest : "ProductionOrderId"
OutsourceStepRequest ||--o{ OutsourceStepRequestData : "OutsourceStepRequestId"
ProductionStep ||--o{ OutsourceStepRequestData : "ProductionStepId"
ProductionStepLinkData ||--o{ OutsourceStepRequestData : "ProductionStepLinkDataId"
OutsourceStepRequest ||--o{ ProductionStep : "OutsourceStepRequestId"
Step ||--o{ ProductionStep : "StepId"
StepGroup ||--o{ Step : "StepGroupId"
ProductionOrder ||--o{ ProductionAssignment : "ProductionOrderId"
ProductionStep ||--o{ ProductionAssignment : "ProductionStepId"
ProductionStepLinkData ||--o{ ProductionAssignment : "ProductionStepLinkDataId"
ProductionAssignment ||--o{ ProductionAssignmentDetail : "FK*"
ProductionAssignmentDetail ||--o{ ProductionAssignmentDetailLinkData : "FK*"
ProductionStepLinkData ||--o{ ProductionAssignmentDetailLinkData : "ProductionStepLinkDataId"
ProductionStep ||--o{ ProductionStepLinkDataRole : "ProductionStepId"
ProductionStepLinkData ||--o{ ProductionStepLinkDataRole : "ProductionStepLinkDataId"
ProductionOrder ||--o{ ProductionHandover : "ProductionOrderId"
ProductionStep ||--o{ ProductionHandover : "FromProductionStepId"
ProductionStep ||--o{ ProductionHandover : "ToProductionStepId"
ProductionHandoverReceipt ||--o{ ProductionHandover : "ProductionHandoverReceiptId"
ProductionHandoverReceipt ||--o{ ProductionHistory : "ProductionHandoverReceiptId"
ProductionStep ||--o{ ProductionHistory : "ProductionStepId"
ProductionStep ||--o{ ProductionConsumMaterial : "ProductionStepId"
ProductionAssignment ||--o{ ProductionConsumMaterial : "FK*"
ProductionOrder ||--o{ ProductionOrderMaterials : "ProductionOrderId"
ProductionStepLinkData ||--o{ ProductionOrderMaterials : "ProductionStepLinkDataId"
ProductionOrderMaterials ||--o{ ProductionOrderMaterials : "ParentId"
03. PurchaseOrder (PurchaseOrderDBContext)
📋 Flow chi tiết (16 bước)
Module PurchaseOrder quản lý toàn bộ quy trình mua hàng từ nhà cung cấp, bao gồm tạo yêu cầu mua, nhập giá từ nhà cung cấp, phân công đặt hàng cho nhân viên, và cuối cùng phát hành đơn đặt hàng chính thức. Dữ liệu chuyển động qua các tầng: từ PurchasingRequest (nhu cầu) → PurchasingSuggest (tổng hợp nhà cung cấp) → PoAssignment (phân công người mua) → PoProviderPricing (giá nhà cung cấp) → PurchaseOrder (đơn hàng chính thức), với các bảng phụ theo dõi vật liệu, chi phí vượt quá, và tình trạng giao hàng.
flowchart TD
n1(["1. PurchasingRequest khởi tạo (Yêu cầu mua hàng)"])
n2[/"2. PurchasingSuggestDetail tạo (Chi tiết gợi ý mua hàng)"/]
n3["3. PurchasingSuggestDetailSubCalculation thêm (Phép tính con chi tiết gợi ý)"]
n4[/"4. PoAssignment phân công (Phân công đặt hàng)"/]
n5{"5. PoAssignment xác nhận (Phân công đặt hàng)"}
n6[/"6. PoProviderPricing tạo (Giá nhà cung cấp)"/]
n7{"7. PoProviderPricing kiểm tra (Giá nhà cung cấp)"}
n8{"8. PoProviderPricing phê duyệt (Giá nhà cung cấp)"}
n9[/"9. PurchaseOrder tạo (Đơn đặt hàng mua) ↔Manufacturing"/]
n10["10. PurchaseOrderDetailSubCalculation thêm (Phép tính con chi tiết đơn hàng)"]
n11[/"11. PropertyCalc/MaterialCalc liên kết (Tính toán thuộc tính; Tính toán vật liệu) ↔Master"/]
n12[/"12. PurchaseOrderMaterials ghi nhận (Vật liệu đơn đặt hàng) ↔Stock"/]
n13[/"13. PurchaseOrder gửi kiểm tra (Đơn đặt hàng mua)"/]
n14{"14. PurchaseOrder kiểm tra (Đơn đặt hàng mua)"}
n15{"15. PurchaseOrder phê duyệt cuối (Đơn đặt hàng mua) ↔Accountancy"}
n16[/"16. PurchaseOrderTracked ghi nhận giao hàng (Theo dõi đơn đặt hàng) ↔Manufacturing"/]
n1 --> n2
n2 --> n3
n3 --> n4
n4 --> n5
n5 --> n6
n6 --> n7
n7 --> n8
n8 --> n9
n9 --> n10
n10 --> n11
n11 --> n12
n12 --> n13
n13 --> n14
n14 --> n15
n15 --> n16
classDef cross fill:#fef3c7,stroke:#d97706,color:#7c2d12;
classDef se fill:#dcfce7,stroke:#16a34a,color:#14532d;
class n1 se;
class n9,n11,n12,n15,n16 cross;- Bước 1: Tạo PurchasingRequest (yêu cầu mua hàng) - Hệ thống hoặc người dùng khởi tạo PurchasingRequest với PurchasingRequestCode, Date, NeedDate và PurchasingRequestTypeId (loại: Normal/OrderMaterial/MaterialCalc/ProductionOrder). Nếu từ MaterialCalc, trỏ đến MaterialCalcId; nếu từ SaleOrder, trỏ đến OrderDetailId; nếu từ ProductionOrder, trỏ đến ProductionOrderId. Chi tiết sản phẩm được lưu trong PurchasingRequestDetail.
- Bước 2: Tạo PurchasingSuggestDetail từ PurchasingRequestDetail - Khi người dùng chọn tạo gợi ý mua hàng, hệ thống tạo PurchasingSuggest mới với PurchasingSuggestCode, Date, CurrencyId, ExchangeRate, và TotalMoney (được tính từ chi tiết). Các dòng chi tiết được tạo trong PurchasingSuggestDetail, tham chiếu trở lại PurchasingRequestDetail qua PurchasingRequestDetailId.
- Bước 3: Thêm PurchasingSuggestDetailSubCalculation cho các chi tiết phức tạp - Nếu sản phẩm yêu cầu tính toán con (e.g., sản phẩm cắt, hỗn hợp), hệ thống tạo PurchasingSuggestDetailSubCalculation cho mỗi chi tiết, lưu dữ liệu chi tiết của phép tính con như hệ số chuyển đổi, giá chi tiết.
- Bước 4: Tạo PoAssignment (phân công đặt hàng) từ PurchasingSuggest - Người quản lý tạo PoAssignment với PoAssignmentCode, AssigneeUserId (người mua được giao), PoAssignmentStatusId, và tham chiếu PurchasingSuggestId. Các dòng chi tiết từ PurchasingSuggest được copy vào PoAssignmentDetail, sở dĩ cần PurchasingSuggestDetailId để link ngược.
- Bước 5: Người mua xác nhận PoAssignment - Người được giao (AssigneeUserId) xem danh sách PoAssignment, kiểm tra các sản phẩm cần mua, đặt IsConfirmed = true hoặc false để xác nhận hoặc từ chối phân công.
- Bước 6: Tạo PoProviderPricing (báo giá từ nhà cung cấp) - Khi nhà cung cấp gửi báo giá hoặc hệ thống nhận dữ liệu, tạo PoProviderPricing với PoProviderPricingCode, CustomerId (ID nhà cung cấp), Date, DeliveryDate, CurrencyId, ExchangeRate, TotalMoney. Chi tiết giá được lưu trong PoProviderPricingDetail (tham chiếu ProductId, giá chuyển đổi, etc.).
- Bước 7: Kiểm tra PoProviderPricing (IsChecked) - Người kiểm tra (CheckedByUserId) xem xét báo giá, kiểm tra giá vs thị trường, đặt IsChecked = true, ghi lại CheckedDatetimeUtc. Nếu báo giá không hợp lệ, có thể reject hoặc yêu cầu sửa.
- Bước 8: Phê duyệt PoProviderPricing (IsApproved) - Cấp quản lý cao hơn (CensorByUserId) xem xét báo giá đã kiểm tra, đặt IsApproved = true và CensorDatetimeUtc. Báo giá được duyệt là cơ sở để tạo PurchaseOrder.
- Bước 9: Tạo PurchaseOrder từ báo giá đã duyệt - Khi PoProviderPricing được phê duyệt, hệ thống tạo PurchaseOrder mới với PurchaseOrderCode (sinh từ CustomGenCode), CustomerId (nhà cung cấp từ PoProviderPricing), Date, DeliveryDate, DeliveryDestination, CurrencyId, ExchangeRate, TotalMoney. Chi tiết PurchaseOrder được tạo từ PoProviderPricingDetail, lưu vào PurchaseOrderDetail và trỏ RefPoAssignmentId, RefPurchasingSuggestId để khoanh vùng dữ liệu.
- Bước 10: Thêm PurchaseOrderDetailSubCalculation - Nếu chi tiết PurchaseOrder có sub-calculation (e.g., từ property calc hoặc material calc), hệ thống tạo PurchaseOrderDetailSubCalculation với dữ liệu tính toán chi tiết (phí, chiều dài, độ rộng, etc.).
- Bước 11: Liên kết với PropertyCalc hoặc MaterialCalc nếu có - Nếu PurchaseOrder được tạo từ PropertyCalc (cắt dặt) hoặc MaterialCalc (tính vật liệu), đặt PropertyCalcId hoặc tham chiếu đến MaterialCalc thông qua detail. Dữ liệu dimension/excess được lưu trong PurchaseOrderExcess nếu có chi phí vượt quá.
- Bước 12: Ghi nhận vật liệu trong PurchaseOrderMaterials - Nếu PurchaseOrder cung cấp vật liệu (e.g., từ ngoài, giao kèm), ghi nhận trong PurchaseOrderMaterials với ProductId, Quantity để theo dõi tích hợp với Stock module.
- Bước 13: Gửi PurchaseOrder để kiểm tra (SendToCensor) - Người tạo gửi PurchaseOrder cho người kiểm tra, đặt PoProcessStatusId = InProgress, CensorByUserId, CensorDatetimeUtc. Người kiểm tra xem toàn bộ chi tiết (PurchaseOrderDetail), tính toán (PurchaseOrderDetailSubCalculation).
- Bước 14: Kiểm tra PurchaseOrder - Người kiểm tra (CheckedByUserId) xác minh PurchaseOrder, đặt IsChecked = true, CheckedDatetimeUtc. Nếu có lỗi, có thể gọi RejectCheck để quay lại Draft.
- Bước 15: Phê duyệt PurchaseOrder cuối cùng - Cấp quản lý (CensorByUserId) phê duyệt PurchaseOrder, đặt IsApproved = true, PoProcessStatusId = Complete. Lúc này PurchaseOrder được coi là chính thức, kích hoạt sự kiện gửi email SendMailNotify, và chuẩn bị dữ liệu để Stock module tạo InventoryInput khi hàng đến.
- Bước 16: Theo dõi giao hàng trong PurchaseOrderTracked - Khi hàng được giao từng phần hoặc toàn bộ, hệ thống ghi nhận trong PurchaseOrderTracked (lưu ReceivedQuantity, ReceivedDate, delivery status) để quản lý tình trạng giao hàng. Đồng thời, nếu là outsource part, PurchaseOrderOutsourceMapping liên kết với OutsourcePart request từ Manufacturing module để cập nhật tình trạng sản xuất ngoài.
erDiagram
MaterialCalc["MaterialCalc (Tính toán vật liệu)"] {
}
MaterialCalcConsumptionGroup["MaterialCalcConsumptionGroup (Nhóm tiêu thụ vật liệu)"] {
}
MaterialCalcProduct["MaterialCalcProduct (Sản phẩm tính vật liệu)"] {
}
MaterialCalcProductDetail["MaterialCalcProductDetail (Chi tiết sản phẩm tính vật liệu)"] {
}
MaterialCalcProductOrder["MaterialCalcProductOrder (Đơn hàng sản phẩm tính toán)"] {
}
MaterialCalcSummary["MaterialCalcSummary (Tóm tắt vật liệu tính toán)"] {
}
MaterialCalcSummarySubCalculation["MaterialCalcSummarySubCalculation (Phép tính con tóm tắt vật liệu)"] {
}
PoAssignment["PoAssignment (Phân công đặt hàng)"] {
}
PoAssignmentDetail["PoAssignmentDetail (Chi tiết phân công đặt hàng)"] {
}
PoProviderPricing["PoProviderPricing (Giá nhà cung cấp)"] {
}
PoProviderPricingDetail["PoProviderPricingDetail (Chi tiết giá nhà cung cấp)"] {
}
PropertyCalc["PropertyCalc (Tính toán thuộc tính)"] {
}
PropertyCalcProduct["PropertyCalcProduct (Sản phẩm tính thuộc tính)"] {
}
PropertyCalcProductDetail["PropertyCalcProductDetail (Chi tiết sản phẩm tính thuộc tính)"] {
}
PropertyCalcProductOrder["PropertyCalcProductOrder (Đơn hàng sản phẩm tính thuộc tính)"] {
}
PropertyCalcProperty["PropertyCalcProperty (Thuộc tính tính toán)"] {
}
PropertyCalcSummary["PropertyCalcSummary (Tóm tắt thuộc tính tính toán)"] {
}
ProviderProductInfo["ProviderProductInfo (Thông tin sản phẩm nhà cung cấp)"] {
}
PurchaseOrder["PurchaseOrder (Đơn đặt hàng mua)"] {
}
PurchaseOrderDetail["PurchaseOrderDetail (Chi tiết đơn đặt hàng)"] {
}
PurchaseOrderDetailSubCalculation["PurchaseOrderDetailSubCalculation (Phép tính con chi tiết đơn hàng)"] {
}
PurchaseOrderExcess["PurchaseOrderExcess (Chi phí vượt quá đơn hàng)"] {
}
PurchaseOrderMaterials["PurchaseOrderMaterials (Vật liệu đơn đặt hàng)"] {
}
PurchaseOrderTracked["PurchaseOrderTracked (Theo dõi đơn đặt hàng)"] {
}
PurchasingRequest["PurchasingRequest (Yêu cầu mua hàng)"] {
}
PurchasingRequestDetail["PurchasingRequestDetail (Chi tiết yêu cầu mua)"] {
}
PurchasingSuggest["PurchasingSuggest (Gợi ý mua hàng)"] {
}
PurchasingSuggestDetail["PurchasingSuggestDetail (Chi tiết gợi ý mua hàng)"] {
}
PurchasingSuggestDetailSubCalculation["PurchasingSuggestDetailSubCalculation (Phép tính con chi tiết gợi ý)"] {
}
VoucherArea["VoucherArea (Khu vực chứng từ)"] {
}
VoucherAreaField["VoucherAreaField (Trường khu vực chứng từ)"] {
}
VoucherBill["VoucherBill (Hóa đơn chứng từ)"] {
}
VoucherField["VoucherField (Trường chứng từ)"] {
}
VoucherType["VoucherType (Loại chứng từ)"] {
}
VoucherTypeGroup["VoucherTypeGroup (Nhóm loại chứng từ)"] {
}
VoucherTypeView["VoucherTypeView (Lược đồ xem chứng từ)"] {
}
VoucherTypeViewField["VoucherTypeViewField (Trường lược đồ xem chứng từ)"] {
}
PurchasingRequest ||--o{ PurchasingRequestDetail : "PurchasingRequestId"
MaterialCalc ||--o{ PurchasingRequest : "MaterialCalcId"
PropertyCalc ||--o{ PurchasingRequest : "PropertyCalcId"
MaterialCalc ||--o{ MaterialCalcConsumptionGroup : "MaterialCalcId"
MaterialCalc ||--o{ MaterialCalcProduct : "MaterialCalcId"
MaterialCalcProduct ||--o{ MaterialCalcProductDetail : "MaterialCalcProductId"
MaterialCalcProduct ||--o{ MaterialCalcProductOrder : "MaterialCalcProductId"
MaterialCalc ||--o{ MaterialCalcSummary : "MaterialCalcId"
MaterialCalcSummary ||--o{ MaterialCalcSummarySubCalculation : "MaterialCalcSummaryId"
PropertyCalc ||--o{ PropertyCalcProduct : "PropertyCalcId"
PropertyCalcProduct ||--o{ PropertyCalcProductDetail : "PropertyCalcProductId"
PropertyCalcProduct ||--o{ PropertyCalcProductOrder : "PropertyCalcProductId"
PropertyCalc ||--o{ PropertyCalcProperty : "PropertyCalcId"
PropertyCalc ||--o{ PropertyCalcSummary : "PropertyCalcId"
PurchasingSuggest ||--o{ PoAssignment : "PurchasingSuggestId"
PoAssignment ||--o{ PoAssignmentDetail : "PoAssignmentId"
PurchasingSuggestDetail ||--o{ PoAssignmentDetail : "PurchasingSuggestDetailId"
PoProviderPricing ||--o{ PoProviderPricingDetail : "PoProviderPricingId"
PurchasingSuggest ||--o{ PurchasingSuggestDetail : "PurchasingSuggestId"
PurchasingSuggestDetail ||--o{ PurchasingSuggestDetailSubCalculation : "PurchasingSuggestDetailId"
PurchaseOrder ||--o{ PurchaseOrderDetail : "PurchaseOrderId"
PurchaseOrderDetail ||--o{ PurchaseOrderDetailSubCalculation : "PurchaseOrderDetailId"
PurchaseOrder ||--o{ PurchaseOrderExcess : "PurchaseOrderId"
PurchaseOrder ||--o{ PurchaseOrderMaterials : "PurchaseOrderId"
PurchaseOrder ||--o{ PurchaseOrderTracked : "PurchaseOrderId"
VoucherTypeGroup ||--o{ VoucherType : "VoucherTypeGroupId"
VoucherType ||--o{ VoucherArea : "VoucherTypeId"
VoucherArea ||--o{ VoucherAreaField : "VoucherAreaId"
VoucherField ||--o{ VoucherAreaField : "VoucherFieldId"
VoucherType ||--o{ VoucherAreaField : "VoucherTypeId"
VoucherType ||--o{ VoucherBill : "VoucherTypeId"
VoucherType ||--o{ VoucherTypeView : "VoucherTypeId"
VoucherTypeView ||--o{ VoucherTypeViewField : "VoucherTypeViewId"
04. Accountancy (AccountancyDBContext)
📋 Flow chi tiết (18 bước)
Module Accountancy quản lý toàn bộ quy trình phân loại, ghi chép, tính toán chi phí và lợi nhuận trong kinh doanh. Dòng chảy chính bao gồm: (1) tạo và xử lý phiếu nhập liệu (InputBill) theo loại cấu hình linh hoạt với các hooks xử lý; (2) tính toán chi phí sản phẩm (CalcFeeProduct) từ chi phí vật liệu, nhân công, chi phí chung; (3) báo cáo lợi nhuận thua lỗ (CalcProfitAndLost); (4) phân bổ hóa đơn thủ công với duyệt phê chuẩn (InvoiceManualAllocate); (5) quyết toán hóa đơn thanh toán (InvoiceClearing); (6) phân bổ hóa đơn tự động qua JobRequest bất đồng bộ; (7) tạo bút toán kế toán (InputValueRowBt) với tài khoản nợ-có và tiền tệ; (8) cấu hình GL entry (TransactionEntryConfig) và công thức tính toán (ProgramingFunction) tùy chỉnh.
flowchart TD
n1(["1. InputTypeGroup phân loại phiếu nhập (Nhóm loại phiếu)"])
n2["2. InputArea cấu hình bố cục form (Khu vực phiếu nhập)"]
n3[/"3. InputBill tạo phiếu nhập (Phiếu nhập liệu)"/]
n4["4. InputBill nhập dữ liệu (Phiếu nhập liệu)"]
n5{"5. BeforeSubmitAction xác thực"}
n6[/"6. InputBill submit lưu (Phiếu nhập liệu)"/]
n7[/"7. InputValueRowBt tạo bút toán (Dòng giá trị bút toán)"/]
n8["8. AfterSaveAction xử lý sau"]
n9[/"9. JobRequest phân bổ bất đồng bộ"/]
n10["10. JobRequest xử lý phân bổ"]
n11["11. CalcFeeProduct tính chi phí (Tính chi phí sản phẩm) ↔Manufacturing"]
n12[/"12. UpdateBillPrices đồng bộ"/]
n13["13. CalcProfitAndLost báo cáo (Tính lợi nhuận thua lỗ)"]
n14[/"14. InvoiceManualAllocate phân bổ (Phân bổ hóa đơn thủ công)"/]
n15[/"15. InvoiceManualAllocateDetail ghi tài khoản"/]
n16{"16. InvoiceManualAllocate duyệt (Phân bổ hóa đơn thủ công)"}
n17[/"17. InvoiceClearing quyết toán (Quyết toán hóa đơn)"/]
n18[/"18. InvoiceClearingDetail khớp chứng (Chi tiết quyết toán) ↔Stock"/]
n1 --> n2
n2 --> n3
n3 --> n4
n4 --> n5
n5 --> n6
n6 --> n7
n7 --> n8
n8 --> n9
n9 --> n10
n10 --> n11
n11 --> n12
n12 --> n13
n13 --> n14
n14 --> n15
n15 --> n16
n16 --> n17
n17 --> n18
classDef cross fill:#fef3c7,stroke:#d97706,color:#7c2d12;
classDef se fill:#dcfce7,stroke:#16a34a,color:#14532d;
class n1 se;
class n11,n18 cross;- Bước 1: Quản trị viên tạo InputTypeGroup để phân loại các loại phiếu nhập, sau đó tạo InputType với code, tiêu đề, hooks xử lý (PreLoadAction, AfterLoadAction, BeforeSubmitAction, BeforeSaveAction, AfterSaveAction) và cấu hình phân bổ (DataAllowcationInputTypeIds, ResultAllowcationInputTypeId, CalcResultAllowcationSqlQuery).
- Bước 2: Quản trị viên cấu hình InputArea và InputAreaField theo InputType để định nghĩa bố cục form nhập liệu động, xác định các trường dữ liệu (InputField) sẽ hiển thị.
- Bước 3: Kế toán tạo InputBill mới bằng cách chọn InputType, hệ thống tự động thực thi PreLoadAction hook để tải dữ liệu phụ thuộc.
- Bước 4: Kế toán nhập dữ liệu vào các InputArea theo cấu hình, hệ thống thực thi AfterLoadAction hook sau khi tải form.
- Bước 5: Trước khi lưu, hệ thống thực thi BeforeSubmitAction hook để xác thực dữ liệu, kiểm tra các InputBill liên quan (CheckExisted/CheckDeleted checks).
- Bước 6: Kế toán submit InputBill, hệ thống thực thi BeforeSaveAction hook, sau đó lưu InputBill với version tracking (LatestBillVersion) và tạo bản ghi InfoRowFId.
- Bước 7: Sau khi lưu, hệ thống tạo InputValueRowBt (dòng bút toán) theo TransactionEntryConfig, ghi nhận tài khoản nợ (TkNo), tài khoản có (TkCo), số tiền VND (VndNo/VndCo), ngoại tệ (NgoaiTeNo/NgoaiTeCo), tỷ giá, và thông tin hóa đơn gốc (InvoiceId, InvoiceDate, SeriHd).
- Bước 8: Hệ thống thực thi AfterSaveAction hook để xử lý sau khi ghi sổ, có thể kích hoạt các tác vụ bổ sung.
- Bước 9: Nếu InputType có cấu hình DataAllowcationInputTypeIds, hệ thống tạo JobRequest với JobName, SubsidiaryId, UnitType, UnitValue để chạy bất đồng bộ phân bổ hóa đơn tự động.
- Bước 10: JobRequest được xử lý bất đồng bộ, JobRequestEvent theo dõi trạng thái (Queued → Running → Completed/Failed), hệ thống thực thi CalcResultAllowcationSqlQuery để tạo InvoiceAutoAllocation records với OriginBillInvoiceUnitId, PayBillInvoiceUnitId, Amount.
- Bước 11: Hàng tháng, kế toán chạy CalcFeeProduct để tính toán chi phí sản phẩm: ghi nhận FromDate, ToDate, Year, Month, ProductId, FactoryDepartmentId, tính ProducingSumTotal từ chi phí vật liệu (SumTotalMaterial), nhân công (SumTotalLabor), chi phí chung (SumTotalGeneral), áp dụng ProducingProductPriceFeeFactor.
- Bước 12: Sau khi tính CalcFeeProduct, hệ thống gọi UpdateBillPrices để đồng bộ các giá phí tính được vào các InputBill tương ứng, đánh dấu IsUpdatedToInputBills=true, lưu UpdatedToInputBillsDateUtc.
- Bước 13: Kế toán tạo CalcProfitAndLost báo cáo với FromDate, ToDate, ProductId, Filter, Options để tính doanh thu từ đơn hàng (OrderCode), trừ đi chi phí vật liệu (CalcFeeProductDoneDirect), chi phí nhân công (CalcFeeProductProducingDirect), chi phí chung (CalcFeeProductProducingInDirect) theo sản phẩm.
- Bước 14: Kế toán thủ công tạo InvoiceManualAllocate để phân bổ hóa đơn thanh toán, nhập Code, Date, Description, liên kết InputBillFId, hệ thống khởi tạo InvoiceManualAllocateStatus=Pending.
- Bước 15: Kế toán tạo InvoiceManualAllocateDetail cho mỗi hóa đơn, ghi nhận tài khoản nợ (DebtAccountNumber), tài khoản có (CreditAccountNumber), số tiền theo TransactionEntryConfig (IsByCustomer, IsByDepartment, IsByProduct, IsByProductionOrder).
- Bước 16: Kiểm soát viên kiểm tra InvoiceManualAllocate, đánh dấu IsChecked=true, CheckedByUserId, CheckedDatetimeUtc, trạng thái chuyển Pending → Checked. Giám đốc phê chuẩn cuối cùng, đánh dấu IsApproved=true, CensorByUserId, CensorDatetimeUtc, trạng thái Checked → Approved, các bút toán GL Entry được khóa.
- Bước 17: Kế toán tạo InvoiceClearing để quyết toán hóa đơn thanh toán, nhập InvoiceClearingCode, Date, DiffInputBillFId (chênh lệch), ClearingInputBillFId (thanh toán), khởi tạo InvoiceManualAllocateStatus=Pending.
- Bước 18: Kế toán tạo InvoiceClearingDetail ghi nhận từng đơn hóa đơn được quyết toán, khớp số tiền thanh toán với hóa đơn gốc, chuyển trạng thái Pending → Matched, sau đó kiểm soát viên xác nhận Matched → Cleared, hệ thống tạo GL entry: Nợ Tài khoản Phải trả (DR Payable) / Có Tiền mặt hoặc Ngân hàng (CR Cash), đồng bộ với Stock hoặc Master Data để cập nhật tình trạng thanh toán.
erDiagram
CalcFeeProduct["CalcFeeProduct (Tính chi phí sản phẩm)"] {
}
CalcFeeProductDoneRow["CalcFeeProductDoneRow (Chi tiết chi phí sản phẩm)"] {
}
CalcProfitAndLost["CalcProfitAndLost (Tính lợi nhuận thua lỗ)"] {
}
InputArea["InputArea (Khu vực phiếu nhập)"] {
}
InputAreaField["InputAreaField (Trường khu vực phiếu)"] {
}
InputBill["InputBill (Phiếu nhập liệu)"] {
}
InputField["InputField (Trường dữ liệu cơ bản)"] {
}
InputType["InputType (Loại phiếu nhập)"] {
}
InputTypeGroup["InputTypeGroup (Nhóm loại phiếu)"] {
}
InputValueRowBt["InputValueRowBt (Dòng giá trị bút toán)"] {
}
InvoiceAutoAllocation["InvoiceAutoAllocation (Phân bổ hóa đơn tự động)"] {
}
InvoiceClearing["InvoiceClearing (Quyết toán hóa đơn)"] {
}
InvoiceClearingDetail["InvoiceClearingDetail (Chi tiết quyết toán)"] {
}
InvoiceManualAllocate["InvoiceManualAllocate (Phân bổ hóa đơn thủ công)"] {
}
InvoiceManualInfo["InvoiceManualInfo (Thông tin hóa đơn thủ công)"] {
}
TransactionEntryConfig["TransactionEntryConfig (Cấu hình bút toán)"] {
}
InputTypeGroup ||--o{ InputType : "InputTypeGroupId"
InputType ||--o{ InputArea : "InputTypeId"
InputType ||--o{ InputAreaField : "InputTypeId"
InputType ||--o{ InputBill : "InputTypeId"
InputType ||--o{ InputType : "ResultAllowcationInputTypeId"
InputArea ||--o{ InputAreaField : "InputAreaId"
InputField ||--o{ InputAreaField : "InputFieldId"
InputBill ||--o{ InputBill : "ParentInputBillFId"
CalcFeeProduct ||--o{ CalcFeeProductDoneRow : "CalcFeeProductId"
InvoiceClearing ||--o{ InvoiceClearingDetail : "InvoiceClearingId"
05. QcManagement (QcManagementDBContext)
📋 Flow chi tiết (16 bước)
Module QcManagement quản lý quy trình kiểm tra chất lượng (QC) cho hàng nhập hoặc sản xuất. Dữ liệu trải qua vòng đời từ tạo phiếu kiểm tra, khởi tạo tiêu chí theo sản phẩm, thực hiện kiểm tra từng tiêu chí, tính toán kết quả, đến xử lý sản phẩm không đạt thông qua Corrective Action Plan (CAP).
flowchart TD
n1(["1. InspectionSheet tạo phiếu kiểm tra (Phiếu kiểm tra)"])
n2[/"2. InspectionSheetItem nhân bản mặt hàng (Chi tiết mặt hàng kiểm tra)"/]
n3["3. QualityStandard truy vấn tiêu chuẩn (Tiêu chuẩn chất lượng)"]
n4["4. initQC khởi tạo, reset dữ liệu"]
n5[/"5. InspectionSheetItemCriteria tạo tiêu chí (Tiêu chí kiểm tra item)"/]
n6[/"6. InspectionSheetItemCriteriaAql thiết lập ngưỡng (AQL tiêu chí kiểm tra)"/]
n7[/"7. InspectionSheetItemTestRun kiểm tra lần 1 (Lần chạy kiểm tra item)"/]
n8[/"8. InspectionSheetItemCriteriaTestRunError ghi nhận lỗi (Lỗi kiểm tra)"/]
n9["9. RecalculateAllStatuses tính toán kết quả"]
n10["10. Aggregation tại mức sản phẩm"]
n11["11. Aggregation tại mức phiếu"]
n12["12. ManualUpdateStatus xử lý thủ công"]
n13{"13. InspectionSheetNextAction xác định hành động (Hành động tiếp theo) ↔Stock"}
n14[/"14. CorrectiveActionPlan tạo CAP (Phiếu hành động khắc phục)"/]
n15["15. CapErrorSimulation phân tích nguyên nhân"]
n16(["16. FinalCapDispositionAction hoàn thành CAP"])
n1 --> n2
n2 --> n3
n3 --> n4
n4 --> n5
n5 --> n6
n6 --> n7
n7 --> n8
n8 --> n9
n9 --> n10
n10 --> n11
n11 --> n12
n12 --> n13
n13 --> n14
n14 --> n15
n15 --> n16
classDef cross fill:#fef3c7,stroke:#d97706,color:#7c2d12;
classDef se fill:#dcfce7,stroke:#16a34a,color:#14532d;
class n1,n16 se;
class n13 cross;- 1. Người dùng tạo InspectionSheet (phiếu kiểm tra) với thông tin cơ bản: mã phiếu, ngày kiểm tra, loại kiểm tra QcFlowTypeId (PO nhập/công đoạn/thành phẩm/xuất bán), phương pháp lấy mẫu SamplingMethodId, kho liên quan (PurchaseOrderId hoặc ProductionOrderId hoặc StepId), bộ phận kiểm tra DepartmentId, bộ phận nhà máy FactoryDepartmentId, mức AQL (AqlLevelId), trạng thái QcProgressStateId=NotStarted.
- 2. Dữ liệu InspectionSheet tự động nhân bản từ tài liệu nguồn (PO, lệnh sản xuất, hoặc phản hồi khách hàng): tạo InspectionSheetItem cho mỗi mặt hàng với ProductId (nullable - khi không trong danh mục thì lưu ProductName), số lượng nguyên liệu PrimaryQuantity & PuQuantity, xác định đơn vị QC IsPrimaryUnitQc, tính lô QC QcLotQuantity, trạng thái ban đầu QcProgressStateId=NotStarted, IsPassed=false, PassedQuantity=0, FailedQuantity=0.
- 3. Truy vấn lịch sử tiêu chuẩn áp dụng: hệ thống tìm kiếm QualityStandard theo bộ lọc (ProductId/ProductCategoryId/ProductLineId/StepId + QcFlowTypeId) thông qua QualityStandardQcFlowType, QualityStandardAssignment. Lấy SamplingStandard được gán cho QualityStandard đó (đối với mỗi tiêu chí: áp dụng AQL rate + InspectionLevel).
- 4. Người dùng click khởi tạo QC cho item: gọi initQC → hệ thống xoá toàn bộ dữ liệu cũ (InspectionSheetItemCriteria + InspectionSheetItemTestRun + InspectionSheetItemCriteriaTestRun + InspectionSheetItemCriteriaAql + InspectionSheetItemCriteriaTestRunError), reset QcState (PassedCriteriasCount=0, FailedCriteriasCount=0, PassedQuantity=0, FailedQuantity=0, IsPassed=false, InspectionQcDecisionId=null, AcceptedQuantity=null).
- 5. Khởi tạo tiêu chí: tạo InspectionSheetItemCriteria cho mỗi QualityCriteria trong QualityStandard được chọn, gán InspectionLevelId từ QualityStandardCriteria, tính MaxSamplingQuantity dựa trên bảng SamplingStandardSampleSize (lot size × inspection level), gán NominalValue từ biểu thức QualityCriteria.NominalValueExpression.
- 6. Thiết lập ngưỡng AQL: tạo InspectionSheetItemCriteriaAql (một bản ghi cho mỗi mức lỗi: Critical/Major/Minor) với Aqlid, SamplingQuantity, Accept (ngưỡng chấp nhận lỗi), Reject (ngưỡng từ chối lỗi), IsPassed=false ban đầu.
- 7. Thực hiện kiểm tra lần 1 (test run): người dùng nhập dữ liệu kiểm tra một mẫu → tạo InspectionSheetItemTestRun (coordinator, lưu Quantity tổng lượng kiểm tra, PackageNo, PalletQuantity) + tạo InspectionSheetItemCriteriaTestRun cho mỗi tiêu chí (ghi Quantity kiểm tra, PassedQuantity, FailedQuantity).
- 8. Ghi nhận lỗi chi tiết: người dùng nhập danh sách lỗi → tạo InspectionSheetItemCriteriaTestRunError (gán InspectionErrorId từ danh sách mã lỗi được liên kết với tiêu chí qua QualityCriteria), mỗi lỗi được phân loại theo ErrorLevelTypeId (Critical/Major/Minor).
- 9. Tự động tính toán kết quả tiêu chí: sau mỗi lần thêm/sửa/xoá test run, hệ thống gọi RecalculateAllStatuses() → cộng dồn PassedQuantity & FailedQuantity từ tất cả InspectionSheetItemCriteriaTestRun, so sánh với ngưỡng InspectionSheetItemCriteriaAql (FailedQuantity > Reject → IsPassed=false, ≤ Accept → IsPassed=true).
- 10. Aggregation tại mức sản phẩm: từ tất cả InspectionSheetItemCriteria (các tiêu chí đã bắt đầu kiểm tra), tính PassedQuantity = Min(tiêu chí.PassedQuantity) [bottleneck: mẫu phải đạt TẤT CẢ], FailedQuantity = Max(tiêu chí.FailedQuantity) [worst case], cập nhật PassedCriteriasCount (số tiêu chí IsPassed=true), FailedCriteriasCount (số tiêu chí IsPassed=false), IsPassed = (FailedCriteriasCount == 0).
- 11. Aggregation tại mức phiếu: từ tất cả InspectionSheetItem, tính QcProgressState (NotStarted: tất cả chưa bắt đầu, InProgress: có tiêu chí đang/hoàn thành, Completed: tất cả hoàn thành), xác định InspectionResultId tổng hợp (Pass: tất cả item IsPassed=true, Fail: có item IsPassed=false, Conditional: có item ManuallyEnded).
- 12. Xử lý update thủ công: người dùng có thể ManualUpdateStatus để override kết quả item (do yếu tố ngoài tiêu chí) → gọi RecalcSheetAfterItemManualUpdate() chỉ tính toán lại QcProgressState mà KHÔNG recalculate từ test run, giữ nguyên quantities đã set thủ công.
- 13. Xác định hành động tiếp theo: dựa trên InspectionResultId + QcFlowTypeId → tạo InspectionSheetNextAction (PO: chấp nhận nhập kho hoặc từ chối trả vendor; FinishedGoods: tiến hành xuất bán hoặc loại bỏ; ProductionProcess: tiếp tục công đoạn hoặc rework).
- 14. Tạo Corrective Action Plan (CAP) khi phát hiện lỗi: hệ thống tạo CorrectiveActionPlan từ InspectionSheet (chỉ khi có linked ProductId - unlinked items bị bỏ qua), tự động tập hợp lỗi từ InspectionSheetItemCriteriaTestRunError vào CapErrorItem (gán InspectionErrorId, nhóm theo ProductId + ErrorId).
- 15. Phân tích nguyên nhân CAP: người dùng điền InitialCause, thực hiện Error Simulation (CapErrorSimulation), Why Analysis (CapCauseWhy), xác định Root Cause (CauseTraceability) và hành động khắc phục (CapDispositionAction).
- 16. Hoàn thành CAP: người dùng chọn FinalCapDispositionAction (hành động cuối cùng sau khi xác minh), lưu ConclusionNote, cập nhật QcCapSectionStatusId & QaCapSectionStatusId (trạng thái phê duyệt), sau đó phiếu kiểm tra có thể được censor (đánh dấu hoàn thành chính thức và không được phép sửa test run nữa).
erDiagram
Aql["Aql (Mức AQL)"] {
}
CapDispositionAction["CapDispositionAction (Hành động xử lý sản phẩm)"] {
}
CapErrorItem["CapErrorItem (Chi tiết lỗi CAP)"] {
}
CorrectiveActionPlan["CorrectiveActionPlan (Phiếu hành động khắc phục)"] {
}
CustomerFeedback["CustomerFeedback (Báo lỗi khách hàng)"] {
}
CustomerFeedbackDetail["CustomerFeedbackDetail (Chi tiết báo lỗi khách hàng)"] {
}
InspectionError["InspectionError (Mã lỗi kiểm tra)"] {
}
InspectionErrorType["InspectionErrorType (Phân loại lỗi)"] {
}
InspectionQcDecision["InspectionQcDecision (Quyết định kiểm tra)"] {
}
InspectionSheet["InspectionSheet (Phiếu kiểm tra)"] {
}
InspectionSheetConsumptionGroup["InspectionSheetConsumptionGroup (Nhóm vật tư)"] {
}
InspectionSheetItem["InspectionSheetItem (Chi tiết mặt hàng kiểm tra)"] {
}
InspectionSheetItemCriteria["InspectionSheetItemCriteria (Tiêu chí kiểm tra item)"] {
}
InspectionSheetItemCriteriaAql["InspectionSheetItemCriteriaAql (AQL tiêu chí kiểm tra)"] {
}
InspectionSheetItemCriteriaTestRun["InspectionSheetItemCriteriaTestRun (Lần test tiêu chí)"] {
}
InspectionSheetItemCriteriaTestRunError["InspectionSheetItemCriteriaTestRunError (Lỗi kiểm tra)"] {
}
InspectionSheetItemTestRun["InspectionSheetItemTestRun (Lần chạy kiểm tra item)"] {
}
InspectionSheetNextAction["InspectionSheetNextAction (Hành động tiếp theo)"] {
}
QualityCriteria["QualityCriteria (Tiêu chí kiểm tra)"] {
}
QualityStandard["QualityStandard (Tiêu chuẩn chất lượng)"] {
}
QualityStandardCriteria["QualityStandardCriteria (Tiêu chí gắn tiêu chuẩn)"] {
}
QualityStandardQcFlowType["QualityStandardQcFlowType (Luồng kiểm tra tiêu chuẩn)"] {
}
SamplingStandard["SamplingStandard (Quy tắc lấy mẫu)"] {
}
SamplingStandardSampleSize["SamplingStandardSampleSize (Kích thước lấy mẫu)"] {
}
InspectionSheet ||--o{ InspectionSheetItem : "InspectionSheetId"
InspectionSheet ||--o{ InspectionSheetConsumptionGroup : "InspectionSheetId"
InspectionSheet ||--o{ InspectionSheetNextAction : "InspectionSheetId"
InspectionSheet ||--o{ CorrectiveActionPlan : "InspectionSheetId"
InspectionSheetItem ||--o{ InspectionSheetItemCriteria : "InspectionSheetItemId"
InspectionSheetItem ||--o{ InspectionSheetItemTestRun : "InspectionSheetItemId"
QualityStandard ||--o{ InspectionSheetItem : "QualityStandardId"
SamplingStandard ||--o{ InspectionSheetItem : "SamplingStandardId"
QualityCriteria ||--o{ InspectionSheetItemCriteria : "QualityCriteriaId"
SamplingStandard ||--o{ QualityStandard : "SamplingStandardId"
QualityStandard ||--o{ QualityStandardCriteria : "QualityStandardId"
QualityCriteria ||--o{ QualityStandardCriteria : "QualityCriteriaId"
Aql ||--o{ QualityStandardCriteria : "CriticalAqlid"
Aql ||--o{ QualityStandardCriteria : "MajorAqlid"
Aql ||--o{ QualityStandardCriteria : "MinorAqlid"
SamplingStandard ||--o{ SamplingStandardSampleSize : "SamplingStandardId"
SamplingStandardSampleSize ||--o{ InspectionSheetItemCriteriaAql : "SamplingStandardSampleSizeId"
InspectionSheetItemCriteria ||--o{ InspectionSheetItemCriteriaAql : "InspectionSheetItemCriteriaId"
Aql ||--o{ InspectionSheetItemCriteriaAql : "Aqlid"
InspectionSheetItemCriteria ||--o{ InspectionSheetItemCriteriaTestRun : "InspectionSheetItemCriteriaId"
InspectionSheetItemTestRun ||--o{ InspectionSheetItemCriteriaTestRun : "InspectionSheetItemTestRunId"
InspectionSheetItemCriteriaTestRun ||--o{ InspectionSheetItemCriteriaTestRunError : "InspectionSheetItemCriteriaTestRunId"
InspectionErrorType ||--o{ InspectionError : "InspectionErrorTypeId"
InspectionError ||--o{ InspectionSheetItemCriteriaTestRunError : "InspectionErrorId"
CorrectiveActionPlan ||--o{ CapErrorItem : "CorrectiveActionPlanId"
InspectionError ||--o{ CapErrorItem : "InspectionErrorId"
CapDispositionAction ||--o{ CorrectiveActionPlan : "CapDispositionActionId"
CapDispositionAction ||--o{ CorrectiveActionPlan : "FinalCapDispositionActionId"
CustomerFeedback ||--o{ CustomerFeedbackDetail : "CustomerFeedbackId"
QualityStandard ||--o{ QualityStandardQcFlowType : "QualityStandardId"
InspectionQcDecision ||--o{ InspectionSheetItem : "InspectionQcDecisionId"
06. Organization (OrganizationDBContext)
📋 Flow chi tiết (16 bước)
Module Organization là hệ thống quản lý nhân sự tập trung, xử lý quản lý phòng ban, lương thưởng, công tác nhân viên, quản lý phép nghỉ, quản lý khách hàng và lịch tổ chức. Dữ liệu sinh ra từ cấu hình tổ chức cơ bản (Subsidiary, Department) chảy qua quá trình cấp phát nhân viên (EmployeeDepartmentMapping), xử lý chấm công thô từ thiết bị (TimeSheetRaw) để tạo bảng công chi tiết (TimeSheet → TimeSheetDetail → TimeSheetAggregate), song song đó quản lý phép nghỉ (Leave, LeaveConfig), cấu hình ca làm việc (ShiftConfiguration, ShiftSchedule), tính toán lương theo kỳ (SalaryPeriod, SalaryGroup, SalaryField), và cuối cùng kết nối với khách hàng (Customer, CustomerCate).
flowchart TD
n1(["1. Tạo cơ cấu tổ chức cơ sở"])
n2["2. Cấp phát nhân viên EmployeeDepartmentMapping (Ánh xạ nhân viên phòng ban)"]
n3["3. Cấu hình LeaveConfig và Overtime (Cấu hình chính sách phép)"]
n4["4. Cấu hình ShiftConfiguration (Cấu hình ca làm việc)"]
n5[/"5. Thu thập TimeSheetRaw từ thiết bị"/]
n6["6. Xử lý và kiểm chứng chấm công"]
n7[/"7. Tạo TimeSheet và TimeSheetDetail (Bảng công nhân viên; Chi tiết bảng công)"/]
n8{"8. Tính toán vắng mặt và Leave (Đơn xin nghỉ phép)"}
n9[/"9. Ghi nhận TimeSheetAggregate (Tổng hợp công nhân viên)"/]
n10["10. Ghi nhận vắng mặt và tăng ca"]
n11["11. Định cấu hình SalaryPeriod (Kỳ lương)"]
n12["12. Gắn SalaryGroup vào kỳ lương (Nhóm lương)"]
n13[/"13. Nhập SalaryPeriodAdditionBill"/]
n14["14. Tính toán giá trị lương nhân viên"]
n15{"15. Kiểm chứng dữ liệu kỳ lương ↔Accountancy"}
n16(["16. Phê chuẩn kỳ lương và đạo thải ↔Accountancy"])
n1 --> n2
n2 --> n3
n3 --> n4
n4 --> n5
n5 --> n6
n6 --> n7
n7 --> n8
n8 --> n9
n9 --> n10
n10 --> n11
n11 --> n12
n12 --> n13
n13 --> n14
n14 --> n15
n15 --> n16
classDef cross fill:#fef3c7,stroke:#d97706,color:#7c2d12;
classDef se fill:#dcfce7,stroke:#16a34a,color:#14532d;
class n1,n16 se;
class n15,n16 cross;- Bước 1: Tạo cơ cấu tổ chức cơ sở - Subsidiary (công ty con) được tạo với ParentSubsidiaryId để hình thành cây cơ cấu công ty; Department được gán vào Subsidiary cụ thể, xây dựng cây phòng ban qua ParentId (với denormalization: PathCodes, PathNames, ParentIdLevel1-10) cho truy vấn nhanh.
- Bước 2: Cấp phát nhân viên vào phòng ban - EmployeeDepartmentMapping liên kết UserId tới DepartmentId theo chu kỳ thời gian (EffectiveDate, ExpirationDate) để theo dõi quân số phòng ban (NumberOfPerson, WorkingHoursPerDay ở Department).
- Bước 3: Cấu hình chính sách phép và tăng ca - LeaveConfig được tạo với chính sách (MonthRate, MaxAyear, SeniorityMonthsStart) cộng AbsenceTypeSymbol định nghĩa loại vắng mặt; OvertimeConfiguration liên kết ShiftConfiguration để cấu hình tăng ca.
- Bước 4: Cấu hình ca làm việc - ShiftConfiguration được tạo định nghĩa loại ca (giờ làm, kiểu ca); ShiftSchedule gán ca cho các phòng ban từ FromDate tới ToDate; ShiftScheduleDetail chỉ định ca cho từng phòng ban cụ thể.
- Bước 5: Thu thập dữ liệu chấm công thô từ thiết bị - TimeSheetRaw lưu dữ liệu thô từ VTAS terminal (Date, Time, TerminalId) cho mỗi EmployeeId cùng TimeKeepingMethod.
- Bước 6: Xử lý và kiểm chứng chấm công - Dữ liệu từ TimeSheetRaw được xử lý, liên kết với ShiftSchedule để xác định ca làm việc hợp lệ; kiểm tra lỗi muộn/sớm qua MinsLate, MinsEarly; bước này chuẩn bị dữ liệu cho bảng công chi tiết.
- Bước 7: Tạo bảng công và chi tiết chấm công - TimeSheet được tạo cho một tháng cụ thể (Month, Year, BeginDate, EndDate); TimeSheetDetail ghi chi tiết từng ngày làm việc; TimeSheetDetailShift ghi chi tiết từng ca làm việc cùng các loại tính toán (Counted).
- Bước 8: Tính toán vắng mặt và phép nghỉ - Nhân viên gửi Leave (DateStart, DateEnd, DateStartIsHalf, DateEndIsHalf, TotalDays) với LeaveConfigId tương ứng; Leave được gán AbsenceTypeSymbolId; quy trình phê duyệt: CheckedByUserId (kiểm tra) → CensoredByUserId (phê chuẩn).
- Bước 9: Tính tổng hợp công nhân viên - TimeSheetAggregate được tạo gộp dữ liệu từ TimeSheetDetail (CountedWeekday, CountedWeekend, CountedHoliday, CountedWeekdayHour, CountedWeekendHour, CountedHolidayHour) cho mỗi EmployeeId trong TimeSheet.
- Bước 10: Ghi nhận vắng mặt và tăng ca vào tổng hợp - TimeSheetAggregateAbsence (liên kết AbsenceTypeSymbolId) và TimeSheetAggregateOvertime được tạo để ghi vào TimeSheetAggregate các ngày vắng mặt, tăng ca từ Leave và ShiftRequestLetter.
- Bước 11: Định cấu hình kỳ lương và nhóm lương - SalaryPeriod được tạo cho tháng cụ thể (Month, Year, FromDate, ToDate, SalaryPeriodCensorStatusId) với trạng thái chưa kiểm tra; SalaryGroup được tạo với EmployeeFilter (JSON tiêu chí) để chọn nhân viên tham gia; SalaryField định nghĩa các trường lương (lương cơ bản, phụ cấp, v.v.).
- Bước 12: Gắn nhóm lương vào kỳ lương - SalaryPeriodGroup liên kết SalaryPeriodId tới SalaryGroupId và SalaryFieldId để xác định những trường lương áp dụng cho nhóm nào trong kỳ nào; SalaryGroupField chỉ định trường lương nào thuộc nhóm lương nào.
- Bước 13: Nhập hóa đơn cộng thêm (khoản cộng/trừ) - SalaryPeriodAdditionBill được tạo với BillCode, Content, Date và SalaryPeriodAdditionTypeId để ghi nhận các khoản phụ cấp, thưởng, tiền phạt cho kỳ lương; SalaryPeriodAdditionBillEmployee liên kết tới nhân viên cụ thể.
- Bước 14: Tính toán giá trị lương từng nhân viên - SalaryPeriodAdditionBillEmployeeValue lưu giá trị cộng/trừ cụ thể cho mỗi trường lương (FieldId) của mỗi nhân viên, tính toán dựa trên SalaryPeriodAdditionField và TimeSheetAggregate (công đã làm, giá trị giờ làm).
- Bước 15: Kiểm chứng dữ liệu kỳ lương - SalaryPeriod được check (CheckedByUserId, CheckedDatetimeUtc) để kiểm tra tính toàn vẹn: tổng công, tổng lương, kiểm tra nhân viên đã rời khỏi (ExpirationDate) có cần xóa khỏi lương không; SalaryPeriodCensorStatusId chuyển trạng thái.
- Bước 16: Phê chuẩn kỳ lương - SalaryPeriod được Censor (CensorByUserId, CensorDatetimeUtc, SalaryPeriodCensorStatusId thành trạng thái phê chuẩn); lúc này dữ liệu lương được khóa và có thể xuất sang Accountancy module để tạo hạch toán, đạo thải hoặc liên kết với Customer để ghi nhận chi phí nhân công cho từng dự án/khách hàng.
erDiagram
AbsenceTypeSymbol["AbsenceTypeSymbol (Ký hiệu loại vắng mặt)"] {
}
BusinessInfo["BusinessInfo (Thông tin kinh doanh)"] {
}
Customer["Customer (Khách hàng)"] {
}
CustomerCate["CustomerCate (Danh mục khách hàng)"] {
}
Department["Department (Phòng ban)"] {
}
DepartmentCapacityBalance["DepartmentCapacityBalance (Cân đối năng lực phòng ban)"] {
}
EmployeeDepartmentMapping["EmployeeDepartmentMapping (Ánh xạ nhân viên phòng ban)"] {
}
HrArea["HrArea (Khu vực hồ sơ)"] {
}
HrAreaField["HrAreaField (Trường khu vực hồ sơ)"] {
}
HrBill["HrBill (Hóa đơn hồ sơ)"] {
}
HrType["HrType (Loại hồ sơ nhân sự)"] {
}
HrTypeGroup["HrTypeGroup (Nhóm loại hồ sơ)"] {
}
HrTypeView["HrTypeView (View hiển thị loại hồ sơ)"] {
}
Leave["Leave (Đơn xin nghỉ phép)"] {
}
LeaveConfig["LeaveConfig (Cấu hình chính sách phép)"] {
}
LeaveConfigRole["LeaveConfigRole (Vai trò cấu hình phép)"] {
}
LeaveConfigValidation["LeaveConfigValidation (Validate cấu hình phép)"] {
}
OvertimeConfiguration["OvertimeConfiguration (Cấu hình tăng ca)"] {
}
OvertimeConfigurationMapping["OvertimeConfigurationMapping (Ánh xạ level tăng ca)"] {
}
OvertimeConfigurationTimeFrame["OvertimeConfigurationTimeFrame (Khung giờ tăng ca)"] {
}
RefUser["RefUser (Tham chiếu người dùng)"] {
}
SalaryField["SalaryField (Trường lương)"] {
}
SalaryGroup["SalaryGroup (Nhóm lương)"] {
}
SalaryGroupField["SalaryGroupField (Trường nhóm lương)"] {
}
SalaryPeriod["SalaryPeriod (Kỳ lương)"] {
}
SalaryPeriodGroup["SalaryPeriodGroup (Kỳ lương nhóm)"] {
}
ShiftConfiguration["ShiftConfiguration (Cấu hình ca làm việc)"] {
}
ShiftRequestLetterAbsence["ShiftRequestLetterAbsence (Chi tiết vắng mặt trong đơn shift)"] {
}
ShiftRequestLetterOvertime["ShiftRequestLetterOvertime (Chi tiết tăng ca trong đơn shift)"] {
}
ShiftSchedule["ShiftSchedule (Lịch ca làm việc)"] {
}
ShiftScheduleConfiguration["ShiftScheduleConfiguration (Cấu hình lịch ca)"] {
}
ShiftScheduleDetail["ShiftScheduleDetail (Chi tiết lịch ca)"] {
}
Subsidiary["Subsidiary (Công ty con)"] {
}
TimeSheet["TimeSheet (Bảng công nhân viên)"] {
}
TimeSheetAggregate["TimeSheetAggregate (Tổng hợp công nhân viên)"] {
}
TimeSheetDepartment["TimeSheetDepartment (Phòng ban trong bảng công)"] {
}
TimeSheetDetail["TimeSheetDetail (Chi tiết bảng công)"] {
}
UserCustomerManager["UserCustomerManager (Quản lý khách hàng của user)"] {
}
HrTypeGroup ||--o{ HrType : "HrTypeGroupId"
Department ||--o{ Department : "ParentId"
Subsidiary ||--o{ Subsidiary : "ParentSubsidiaryId"
OvertimeConfiguration ||--o{ ShiftConfiguration : "OvertimeConfigurationId"
AbsenceTypeSymbol ||--o{ Leave : "AbsenceTypeSymbolId"
LeaveConfig ||--o{ Leave : "LeaveConfigId"
HrType ||--o{ HrArea : "HrTypeId"
HrType ||--o{ HrAreaField : "HrTypeId"
HrArea ||--o{ HrAreaField : "HrAreaId"
HrType ||--o{ HrBill : "HrTypeId"
HrType ||--o{ HrTypeView : "HrTypeId"
SalaryField ||--o{ SalaryGroupField : "SalaryFieldId"
SalaryGroup ||--o{ SalaryGroupField : "SalaryGroupId"
SalaryPeriod ||--o{ SalaryPeriodGroup : "SalaryPeriodId"
SalaryGroup ||--o{ SalaryPeriodGroup : "SalaryGroupId"
OvertimeConfiguration ||--o{ OvertimeConfigurationMapping : "OvertimeConfigurationId"
OvertimeConfiguration ||--o{ OvertimeConfigurationTimeFrame : "OvertimeConfigurationId"
AbsenceTypeSymbol ||--o{ ShiftRequestLetterAbsence : "AbsenceTypeSymbolId"
ShiftConfiguration ||--o{ ShiftRequestLetterAbsence : "ShiftConfigurationId"
ShiftConfiguration ||--o{ ShiftRequestLetterOvertime : "ShiftConfigurationId"
LeaveConfig ||--o{ LeaveConfigRole : "LeaveConfigId"
LeaveConfig ||--o{ LeaveConfigValidation : "LeaveConfigId"
CustomerCate ||--o{ Customer : "CustomerCateId"
Customer ||--o{ UserCustomerManager : "CustomerId"
Department ||--o{ DepartmentCapacityBalance : "DepartmentId"
Department ||--o{ EmployeeDepartmentMapping : "DepartmentId"
TimeSheet ||--o{ TimeSheetAggregate : "TimeSheetId"
TimeSheet ||--o{ TimeSheetDepartment : "TimeSheetId"
TimeSheet ||--o{ TimeSheetDetail : "TimeSheetId"
ShiftSchedule ||--o{ ShiftScheduleConfiguration : "ShiftScheduleId"
ShiftSchedule ||--o{ ShiftScheduleDetail : "ShiftScheduleId"
07. Master (MasterDBContext)
📋 Flow chi tiết (16 bước)
Module Master quản lý cơ sở hạ tầng toàn hệ thống bao gồm RBAC (Role-Based Access Control), quản lý dữ liệu tham chiếu động (Category), sinh mã tài liệu (CustomGenCode), cấu hình in ấn, và ghi nhật ký kiểm toán. Mọi module khác phụ thuộc vào Master để kiểm soát quyền hạn, sinh mã tài liệu, và truy vấn dữ liệu danh mục tham chiếu.
flowchart TD n1[/"1. User tạo với Role (Người dùng hệ thống; Vai trò người dùng)"/] n2["2. Role phân cấp vai trò (Vai trò người dùng)"] n3[/"3. Module đăng ký nhóm (Module ứng dụng)"/] n4[/"4. RolePermission liên kết (Quyền module vai trò)"/] n5["5. RoleDataPermission giới hạn (Quyền dữ liệu vai trò)"] n6[/"6. Category tạo cây (Danh mục dữ liệu)"/] n7["7. CategoryField định nghĩa schema (Trường danh mục)"] n8[/"8. CategoryView giao diện lọc (Giao diện danh mục)"/] n9["9. SQL hooks logic runtime"] n10[/"10. CategoryData nhập động ↔Stock+6"/] n11[/"11. CustomGenCode sinh mã (Cấu hình sinh mã tùy chỉnh)"/] n12["12. GenerateCode sinh tài liệu ↔Stock+6"] n13["13. ConfirmCode xác nhận mã ↔Stock+6"] n14[/"14. Module sinh mã tài liệu (Module ứng dụng) ↔Stock+6"/] n15["15. Menu từ endpoint mapping (Mục menu ứng dụng)"] n16(["16. UserActivityLog kiểm toán ↔Stock+6"]) n1 --> n2 n2 --> n3 n3 --> n4 n4 --> n5 n5 --> n6 n6 --> n7 n7 --> n8 n8 --> n9 n9 --> n10 n10 --> n11 n11 --> n12 n12 --> n13 n13 --> n14 n14 --> n15 n15 --> n16 classDef cross fill:#fef3c7,stroke:#d97706,color:#7c2d12; classDef se fill:#dcfce7,stroke:#16a34a,color:#14532d; class n16 se; class n10,n12,n13,n14,n16 cross;
- Bước 1: User được tạo trong bảng User (UserId, UserName, SubsidiaryId) và liên kết tới Role thông qua trường RoleId trong User.
- Bước 2: Role được thiết lập trong bảng Role (RoleId, RoleName, SubsidiaryId, ParentRoleId) hỗ trợ phân cấp vai trò; trường IsModulePermissionInherit xác định quyền có được kế thừa từ ParentRole hay không.
- Bước 3: Module được đăng ký trong bảng Module (ModuleId, ModuleName, ModuleGroupId) và nhóm thông qua ModuleGroup để tổ chức giao diện điều hướng.
- Bước 4: RolePermission liên kết Role → Module thông qua (RoleId, ModuleId) với bitmask Permission xác định quyền hạn (View, Create, Update, Delete); nếu IsModulePermissionInherit=true thì quyền được kế thừa từ Role cha.
- Bước 5: RoleDataPermission giới hạn quyền ở cấp dữ liệu thông qua (RoleId, ObjectTypeId, ObjectId); chẳng hạn Role có thể chỉ xem dữ liệu của một Subsidiary cụ thể hoặc Branch cụ thể.
- Bước 6: Category được tạo trong bảng Category (CategoryId, CategoryCode, Title, CategoryGroupId); hỗ trợ cấu trúc cây qua ParentKey và IsTreeView=true (ví dụ: loại nhân viên, loại ca làm việc).
- Bước 7: CategoryField định nghĩa schema động cho mỗi Category (FieldName, DataTypeId, FormTypeId, IsRequired, IsUnique); không cần migration DB khi thêm danh mục mới.
- Bước 8: CategoryView (liên kết từ Category) cung cấp các giao diện lọc tùy chỉnh; CategoryViewField chỉ định các trường nào xuất hiện trong từng view.
- Bước 9: SQL hooks được cấu hình trong Category (PreLoadAction, PostLoadAction, BeforeSaveAction, AfterSaveAction) cho phép logic nghiệp vụ runtime; JoinSqlRaw/SearchSqlRaw cho phép lọc phức tạp.
- Bước 10: Dữ liệu danh mục được nhập vào động bằng CategoryDataService thông qua API POST /api/Category/{code}/Data; mỗi dòng được lưu vào bảng tương ứng với CategoryCode.
- Bước 11: CustomGenCode được cấu hình trong bảng CustomGenCode (CustomGenCodeId, CodeFormat, BaseFormat, CodeLength, ResetDate) để sinh mã tài liệu; ví dụ: INV-{yyyy}-{###} sinh ra INV-2026-1001, INV-2026-1002.
- Bước 12: Khi yêu cầu sinh mã, GenerateCode API truy vấn CustomGenCode, tăng giá trị LastCode, tạo bản ghi trong CustomGenCodeValue với trạng thái pending; mã được trả về ứng dụng.
- Bước 13: ConfirmCode API xác nhận mã được sử dụng; cập nhật CustomGenCodeValue thành confirmed; nếu không xác nhận, mã bị rollback và có thể tái sử dụng.
- Bước 14: Module khác (Stock, Voucher, PurchaseOrder) gọi CustomGenCode API để sinh mã cho các tài liệu (phiếu kho, hoá đơn, đơn mua hàng) và lưu mã vào bảng tương ứng.
- Bước 15: Menu được xây dựng từ bảng Menu và đạo hàm từ ModuleApiEndpointMapping (liên kết Module → ApiEndpoint → Method); IModuleAccessService lọc Menu dựa trên RolePermission của User hiện tại.
- Bước 16: Mọi thay đổi dữ liệu (Create/Update/Delete) kích hoạt UserActivityLog entry (Module khác gọi IUserLogActionService); ghi UserId, ObjectTypeId, ObjectId, trạng thái trước/sau trong ObjectJson; lưu vào ActivityLogDB để tách biệt audit trail khỏi dữ liệu kinh tế.
erDiagram
Action["Action (Hành động)"] {
}
ApiEndpoint["ApiEndpoint (Điểm cuối API)"] {
}
Category["Category (Danh mục dữ liệu)"] {
}
CategoryField["CategoryField (Trường danh mục)"] {
}
CategoryGroup["CategoryGroup (Nhóm danh mục)"] {
}
CategoryView["CategoryView (Giao diện danh mục)"] {
}
CategoryViewField["CategoryViewField (Trường giao diện danh mục)"] {
}
Config["Config (Cấu hình hệ thống)"] {
}
CurrencyConvert["CurrencyConvert (Tỷ giá tiền tệ)"] {
}
CustomGenCode["CustomGenCode (Cấu hình sinh mã tùy chỉnh)"] {
}
CustomGenCodeValue["CustomGenCodeValue (Giá trị sinh mã tùy chỉnh)"] {
}
EmailConfiguration["EmailConfiguration (Cấu hình email)"] {
}
I18nLanguage["I18nLanguage (Dịch đa ngôn ngữ)"] {
}
MailTemplate["MailTemplate (Mẫu email)"] {
}
Menu["Menu (Mục menu ứng dụng)"] {
}
Method["Method (Phương thức HTTP)"] {
}
Module["Module (Module ứng dụng)"] {
}
ModuleApiEndpointMapping["ModuleApiEndpointMapping (Ánh xạ module-endpoint)"] {
}
ModuleGroup["ModuleGroup (Nhóm module)"] {
}
PrintConfigStandard["PrintConfigStandard (Cấu hình in chuẩn)"] {
}
Role["Role (Vai trò người dùng)"] {
}
RoleDataPermission["RoleDataPermission (Quyền dữ liệu vai trò)"] {
}
RolePermission["RolePermission (Quyền module vai trò)"] {
}
User["User (Người dùng hệ thống)"] {
}
Action ||--o{ ApiEndpoint : "ActionId"
Method ||--o{ ApiEndpoint : "MethodId"
Category ||--o{ CategoryField : "CategoryId"
CategoryGroup ||--o{ Category : "CategoryGroupId"
Category ||--o{ CategoryView : "CategoryId"
CategoryView ||--o{ CategoryViewField : "CategoryViewId"
ModuleGroup ||--o{ Module : "ModuleGroupId"
Module ||--o{ ModuleApiEndpointMapping : "ModuleId"
ApiEndpoint ||--o{ ModuleApiEndpointMapping : "ApiEndpointId"
Role ||--o{ RolePermission : "RoleId"
Module ||--o{ RolePermission : "ModuleId"
Role ||--o{ RoleDataPermission : "RoleId"
Role ||--o{ Role : "ParentRoleId"
CustomGenCode ||--o{ CustomGenCodeValue : "CustomGenCodeId"
08. ReportConfig (ReportConfigDBContext)
📋 Flow chi tiết (16 bước)
Module ReportConfig cung cấp hệ thống cấu hình bảng điều khiển và báo cáo linh hoạt, cho phép quản trị viên định nghĩa các mẫu báo cáo và dashboard mà không cần thay đổi code. Dữ liệu di chuyển từ cấu hình mẫu (ReportType, DashboardType) → định nghĩa view và trường (ReportTypeView, DashboardTypeView) → thực thi query (BodySql) → lưu trữ kết quả dữ liệu (ReportSavedData) → render giao diện cho người dùng.
flowchart TD n1(["1. ReportTypeGroup tổ chức báo cáo (Nhóm loại báo cáo)"]) n2["2. ReportType/DashboardType tạo mẫu (Loại báo cáo; Loại bảng điều khiển)"] n3["3. BodySql định nghĩa query"] n4["4. Columns JSON cấu hình trường"] n5["5. ReportTypeView biến thể view (Chế độ xem báo cáo)"] n6["6. ReportTypeViewField sắp xếp cột (Trường lọc chế độ xem)"] n7["7. JsProcessedChart cấu hình biểu đồ"] n8[/"8. Tải DashboardType/ReportType (Loại bảng điều khiển; Loại báo cáo)"/] n9["9. Áp dụng bộ lọc người dùng"] n10[/"10. Thực thi BodySql truy vấn ↔Stock+4"/] n11["11. Chuyển đổi kiểu dữ liệu"] n12["12. Áp dụng template Excel/XML"] n13["13. HtmlTemplate/BodyJs xử lý client"] n14[/"14. Render kết quả UI"/] n15[/"15. ReportSavedData lưu snapshot (Dữ liệu báo cáo lưu trữ)"/] n16(["16. ETL refresh cập nhật dữ liệu ↔Manufacturing+1"]) n1 --> n2 n2 --> n3 n3 --> n4 n4 --> n5 n5 --> n6 n6 --> n7 n7 --> n8 n8 --> n9 n9 --> n10 n10 --> n11 n11 --> n12 n12 --> n13 n13 --> n14 n14 --> n15 n15 --> n16 classDef cross fill:#fef3c7,stroke:#d97706,color:#7c2d12; classDef se fill:#dcfce7,stroke:#16a34a,color:#14532d; class n1,n16 se; class n10,n16 cross;
- Bước 1: Người dùng (DBA/Admin) tạo ReportTypeGroup hoặc DashboardTypeGroup để tổ chức các báo cáo/dashboard theo nhóm lôgic (ví dụ: Bán hàng, Kiểm chất, Quản lý kho).
- Bước 2: Tạo ReportType (với ReportTypeGroupId, ReportTypeName, Columns JSON schema) hoặc DashboardType (với DashboardTypeGroupId, ModuleTypeId, DashboardTypeName, DashboardLocationId: Home=1 hoặc Report=2) làm mẫu chính.
- Bước 3: Định nghĩa BodySql trong ReportType hoặc DashboardType chứa các truy vấn SQL tham số hóa (có điều kiện WHERE động, JOIN, GROUP BY) để lấy dữ liệu từ các bảng khác trong hệ thống (ví dụ: InspectionSheet từ QC, Stock từ Stock Module).
- Bước 4: Cấu hình Columns (JSON) trong ReportType/DashboardType để định nghĩa các trường dữ liệu: tên trường, kiểu dữ liệu (decimal, datetime, string), định dạng hiển thị (currency, date format).
- Bước 5: Tạo ReportTypeView hoặc DashboardTypeView đặt tên cho các biến thể view (ví dụ: Summary, Detail, Trend) và đánh dấu IsDefault=true cho view mặc định.
- Bước 6: Gán các trường cụ thể cho mỗi view bằng ReportTypeViewField hoặc DashboardTypeViewField với SortOrder để xác định thứ tự hiển thị cột (ví dụ: trường Location, OnHand, TotalValue).
- Bước 7: Cấu hình biểu đồ trong DashboardType thông qua JsProcessedChart, MenuContextChart, WizardChart để định nghĩa loại biểu đồ, trục X/Y, chuyên sâu phân tích.
- Bước 8: Khi người dùng cuối truy cập dashboard/báo cáo, hệ thống tải DashboardType hoặc ReportType có DashboardLocationId hoặc ReportPath tương ứng.
- Bước 9: Hệ thống áp dụng bộ lọc người dùng (ReportFilterDataModel: khoảng ngày, phòng ban, sản phẩm) vào BodySql dưới dạng tham số hóa để tránh SQL injection.
- Bước 10: Thực thi BodySql (có thể có HeadSql, FooterSql trong ReportType) trên cơ sở dữ liệu và nhận kết quả dưới dạng NonCamelCaseDictionary (schema linh hoạt, không cần migration).
- Bước 11: Áp dụng metadata từ Columns JSON để chuyển đổi kiểu dữ liệu, định dạng giá trị (ví dụ: Decimal(5,2) → định dạng tiền tệ), sắp xếp theo SortOrder từ ReportTypeViewField.
- Bước 12: Nếu là báo cáo in/xuất, lấy ReportExcelTemplateFile hoặc ReportXmlTemplateFile (được tạo từ ReportType via TemplateExcelId) để áp dụng layout mẫu.
- Bước 13: Áp dụng cấu hình HTML (HtmlTemplate từ ReportType) hoặc JavaScript (BodyJs, FooterJs, AfterLoadDataJsCode) để xử lý dữ liệu phía client, tính toán thêm, ẩn/hiện cột.
- Bước 14: Render kết quả dữ liệu vào UI với cấu hình layout từ DashboardTypeView hoặc ReportTypeView đã chọn, hiển thị biểu đồ theo JsProcessedChart/WizardChart.
- Bước 15: Nếu người dùng lưu báo cáo, tạo ReportSavedData (ReportTypeId, Title, Description, Data dạng JSON, Filter điều kiện) với CreatedByUserId, CreatedDatetimeUtc để lưu trữ snapshot.
- Bước 16: Theo dõi ETL refresh status qua JobProcessDataInfo (lưu LastExecutionDate), dùng để cập nhật dữ liệu báo cáo định kỳ từ các modules khác (StatisticReportData, Manufacturing, Accountancy) và đảm bảo dữ liệu dashboard luôn mới nhất.
erDiagram
DashboardType["DashboardType (Loại bảng điều khiển)"] {
}
DashboardTypeGroup["DashboardTypeGroup (Nhóm loại bảng điều khiển)"] {
}
DashboardTypeView["DashboardTypeView (Chế độ xem bảng điều khiển)"] {
}
DashboardTypeViewField["DashboardTypeViewField (Trường lọc chế độ xem bảng điều khiển)"] {
}
ReportExcelTemplateFile["ReportExcelTemplateFile (Tệp template Excel báo cáo)"] {
}
ReportSavedData["ReportSavedData (Dữ liệu báo cáo lưu trữ)"] {
}
ReportType["ReportType (Loại báo cáo)"] {
}
ReportTypeCustom["ReportTypeCustom (Báo cáo tùy chỉnh)"] {
}
ReportTypeGroup["ReportTypeGroup (Nhóm loại báo cáo)"] {
}
ReportTypeView["ReportTypeView (Chế độ xem báo cáo)"] {
}
ReportTypeViewField["ReportTypeViewField (Trường lọc chế độ xem)"] {
}
ReportTypeViewFieldValue["ReportTypeViewFieldValue (Giá trị trường chế độ xem báo cáo)"] {
}
ReportXmlTemplateFile["ReportXmlTemplateFile (Tệp template XML báo cáo)"] {
}
ReportTypeGroup ||--o{ ReportType : "ReportTypeGroupId"
ReportType ||--o{ ReportTypeView : "ReportTypeId"
ReportTypeView ||--o{ ReportTypeViewField : "ReportTypeViewId"
ReportType ||--o{ ReportSavedData : "ReportTypeId"
ReportType ||--o{ ReportExcelTemplateFile : "ReportTypeId"
ReportType ||--o{ ReportXmlTemplateFile : "ReportTypeId"
DashboardTypeGroup ||--o{ DashboardType : "DashboardTypeGroupId"
DashboardType ||--o{ DashboardTypeView : "DashboardTypeId"
DashboardTypeView ||--o{ DashboardTypeViewField : "DashboardTypeViewId"
09. StatisticReportData (StatisticReportDataDBContext)
📋 Flow chi tiết (22 bước)
Module thực hiện tính toán và lưu trữ dữ liệu lương hàng ngày cho nhân viên. Quy trình chính gồm: (1) lấy dữ liệu chấm công từ module Organization, (2) tính toán giá trị lương theo nhóm lương cho mỗi nhân viên mỗi ngày, (3) lưu dữ liệu vào bảng nóng (CurrentMonth) để truy vấn nhanh tháng hiện tại, (4) lưu trữ (archival) dữ liệu cũ sang bảng lạnh (Historical) khi tháng kết thúc.
flowchart TD
n1(["1. Kích hoạt công việc hàng ngày"])
n2["2. Lấy khóa phân tán JobProcessDataInfo (Thông tin quá trình công việc)"]
n3["3. Xác định điểm khôi phục SalaryDaily"]
n4[/"4. Truy vấn TimeSheet Organization (Bảng công nhân viên) ↔Organization"/]
n5["5. Tích hợp ISalaryEmployeeService ↔Organization"]
n6["6. Chuẩn bị công thức SalaryGroup (Nhóm lương) ↔Organization"]
n7["7. Lấy danh sách nhân viên ShiftSchedule (Lịch ca làm việc) ↔Organization"]
n8["8. Tính toán lương nhân viên EvalSalary ↔Organization"]
n9[/"9. Nhận danh sách giá trị lương SalaryField (Trường lương)"/]
n10[/"10. Xây dựng SalaryDailyEmployeeCurrentMonth (Nhân viên lương tháng hiện tại)"/]
n11[/"11. Xây dựng SalaryDailyEmployeeValueCurrentMonth (Giá trị lương tháng hiện tại)"/]
n12[/"12. BULK INSERT batch 5000 bản ghi"/]
n13[/"13. Cập nhật SalaryDailyLastDateZone"/]
n14{"14. Kiểm tra kết thúc tháng"}
n15[/"15. SELECT lưu trữ SalaryDailyEmployee (Nhân viên lương hàng ngày)"/]
n16[/"16. BULK INSERT SalaryDailyEmployee lạnh (Nhân viên lương hàng ngày)"/]
n17[/"17. Lưu trữ SalaryDailyEmployeeValue (Giá trị lương nhân viên hàng ngày)"/]
n18[/"18. DELETE dữ liệu CurrentMonth cũ"/]
n19[/"19. Cập nhật JobProcessDataInfo tháng mới (Thông tin quá trình công việc)"/]
n20[/"20. Truy vấn ReportConfig Dashboard ↔ReportConfig"/]
n21[/"21. Lưu ReportTempTable báo cáo (Bảng tạm thời báo cáo) ↔ReportConfig"/]
n22["22. Xóa cache DashboardJobData cũ (Dữ liệu công việc bảng điều khiển)"]
n1 --> n2
n2 --> n3
n3 --> n4
n4 --> n5
n5 --> n6
n6 --> n7
n7 --> n8
n8 --> n9
n9 --> n10
n10 --> n11
n11 --> n12
n12 --> n13
n13 --> n14
n14 --> n15
n15 --> n16
n16 --> n17
n17 --> n18
n18 --> n19
n19 --> n20
n20 --> n21
n21 --> n22
classDef cross fill:#fef3c7,stroke:#d97706,color:#7c2d12;
classDef se fill:#dcfce7,stroke:#16a34a,color:#14532d;
class n1 se;
class n4,n5,n6,n7,n8,n20,n21 cross;- Công việc được kích hoạt hàng ngày vào thời điểm cố định (23:00 UTC): hệ thống gọi EvalDaily(zoneDate) với ngày hiện tại.
- Lấy khóa phân tán từ DistributedLockProvider để đảm bảo chỉ một node xử lý: ghi IP địa chỉ vào JobProcessDataInfo.LocalIpAddress, ngăn chặn tính toán trùng lặp.
- Đọc JobProcessDataInfo.SalaryDailyLastDateZone để xác định điểm khôi phục: nếu chưa có, mặc định từ 60 ngày trước, tạo phạm vi [minDateZone, zoneDate] để tính toán lại.
- Truy vấn OrganizationDBContext.TimeSheet để lấy dữ liệu chấm công trong phạm vi ngày: kết nối với TimeSheetDepartment để xác định các bộ phận, quyết định dữ liệu nào cần xử lý.
- Tích hợp với ISalaryEmployeeService để sinh ra CalcTimeSheetTable: biến đổi dữ liệu chấm công thành bảng tính toán, gộp các chi tiết chấm công theo ngày và nhân viên.
- Lặp qua từng SalaryGroup: với mỗi nhóm lương từ OrganizationDBContext, chuẩn bị công thức tính toán lương cùng danh sách nhân viên thuộc nhóm đó.
- Với mỗi ngày trong phạm vi [minDateZone, zoneDate], lấy danh sách nhân viên được gán công việc: gọi IShiftScheduleService để xác định ai làm việc hôm đó, lọc từ dữ liệu SalaryGroup.
- Với mỗi nhân viên hôm đó, gọi ISalaryEmployeeService.EvalSalaryEmployeeByGroup(): tính toán ngày công (attendance), giờ tăng ca (overtime), thưởng (bonuses), khấu trừ (deductions).
- ISalaryEmployeeService trả về danh sách giá trị lương: mỗi phần tử chứa SalaryFieldId, SalaryFieldName, Value (decimal), IsConstant (cố định hay biến động).
- Xây dựng record cho SalaryDailyEmployeeCurrentMonth: ghi nhận (DateZone, EmployeeId, SubsidiaryId, SalaryPeriodId, SalaryGroupId, DepartmentId, WorkPositionId, IsPeriodCompleted, IsAssigned, ToUtcDate, ToZoneDate, LocalIpAddress).
- Xây dựng record cho SalaryDailyEmployeeValueCurrentMonth: với mỗi giá trị lương trả về, tạo bản ghi (DateZone, EmployeeId, SubsidiaryId, SalaryFieldId, SalaryFieldName, Value, IsConstant).
- Tích lũy các record vào batch (5.000 bản ghi mỗi batch): khi đạt ngưỡng, thực hiện BULK INSERT đến SalaryDailyEmployeeCurrentMonth và SalaryDailyEmployeeValueCurrentMonth.
- Cập nhật JobProcessDataInfo.SalaryDailyLastDateZone = zoneDate: ghi nhận điểm tiến trình xử lý để lần tiếp theo biết bắt đầu từ đâu.
- Kiểm tra xem tháng hiện tại có kết thúc chưa: so sánh ngày xử lý với ngày đầu tiên của tháng tiếp theo.
- Nếu tháng đã kết thúc, khởi động quy trình lưu trữ (Archival): SELECT dữ liệu từ SalaryDailyEmployeeCurrentMonth có DateZone < ngày đầu tháng tiếp theo.
- BULK INSERT những record được SELECT vào SalaryDailyEmployee (bảng lạnh/cold): giữ lại dữ liệu lịch sử toàn bộ.
- Thực hiện quy trình tương tự với SalaryDailyEmployeeValueCurrentMonth: SELECT, BULK INSERT vào SalaryDailyEmployeeValue, sau đó xóa khỏi bảng nóng.
- DELETE dữ liệu đã lưu trữ từ SalaryDailyEmployeeCurrentMonth và SalaryDailyEmployeeValueCurrentMonth: giải phóng không gian bộ nhớ, duy trì kích thước bảng nóng.
- Cập nhật JobProcessDataInfo.SalaryDailyLastDateZone lần nữa: khôi phục giá trị cho tháng mới để tính toán từ ngày đầu tháng tiếp theo.
- Dashboard/báo cáo (Report Config module) truy vấn dữ liệu: sử dụng SalaryDailyEmployeeCurrentMonth (hiện tại) hoặc SalaryDailyEmployee (lịch sử) thông qua DashboardJobData cache để render bảng điều khiển lương.
- Trường hợp báo cáo tạm thời: ReportTempTable lưu tên logic và vật lý của bảng tạm, cho phép ReportTypeId tham chiếu dữ liệu lương để sinh báo cáo động.
- Dữ liệu hết hạn cache trong DashboardJobData được xóa định kỳ (CleanupCache): giữ cache tươi mới, loại bỏ FilterSnapshot và DataJson cũ hơn 24 giờ để tiết kiệm dung lượng.
erDiagram
DashboardJobData["DashboardJobData (Dữ liệu công việc bảng điều khiển)"] {
}
JobProcessDataInfo["JobProcessDataInfo (Thông tin quá trình công việc)"] {
}
ReportTempTable["ReportTempTable (Bảng tạm thời báo cáo)"] {
}
SalaryDailyEmployee["SalaryDailyEmployee (Nhân viên lương hàng ngày)"] {
}
SalaryDailyEmployeeCurrentMonth["SalaryDailyEmployeeCurrentMonth (Nhân viên lương tháng hiện tại)"] {
}
SalaryDailyEmployeeValue["SalaryDailyEmployeeValue (Giá trị lương nhân viên hàng ngày)"] {
}
SalaryDailyEmployeeValueCurrentMonth["SalaryDailyEmployeeValueCurrentMonth (Giá trị lương tháng hiện tại)"] {
}
SalaryDailyEmployee ||--o{ SalaryDailyEmployeeValue : "FK*"
SalaryDailyEmployeeCurrentMonth ||--o{ SalaryDailyEmployeeValueCurrentMonth : "FK*"