Báo cáo sai lệch — warehouse/delete — vòng 1
0. Kết luận nhanh
- Điều kiện đạt:
G1 ✅ G2 ✅ G3 ✅ G4 — G5 ✅ G6 ✅→ CHỜ KIỂM SONAR (máy đạt hết, chờ người kiểm G4) - Trạng thái: SẴN SÀNG CHO NGƯỜI XEM
- Tầng rủi ro: A (R-02 phân quyền; các quy tắc còn lại B/C) → người cần đọc: spec + báo cáo + toàn bộ diff
- Số quy tắc: 10 · PASS 4 · DEVIATION có chủ đích 3 · DEVIATION KHÔNG chủ đích 0 · UNVERIFIED 3
- Coverage thấp nhất: F-01 97,3% dòng / 93,8% nhánh
- Finding logic: CRITICAL 0 · MAJOR 0 · MINOR 0 · UNSURE 0
- Nghi vấn bug code cũ (mục 3c): 5 (CAO 0) · Quyết định tự chốt cần xác nhận (mục 3d): 11
- Chạy bằng:
/mig-auto(spec dospec-reviewerkiểm, chưa có người duyệt) · Số vòng: 1 verify / lần sửa 0/3
1. Điều kiện đạt
| Mã | Mục tiêu | Kết quả | Bằng chứng |
|---|---|---|---|
| G1 | Kiến trúc ADR (mvnw verify: build, test, ArchUnit, Checkstyle/PMD/SpotBugs) |
✅ (602 test, 0 lỗi) | verify/round-1/build.md |
| G2 | Test khớp test-plan, mọi R-xx có test | ✅ (26/26 TC, 10/10 R) | verify/round-1/oracle.md |
| G3 | Coverage mỗi luồng (dòng ≥ 80%, nhánh ≥ 70%) | ✅ | verify/round-1/coverage.md |
| G4 | 0 issue Sonar (người kiểm bằng SonarQube for IDE) | CHỜ NGƯỜI | sonar-ide.md |
| G5 | 0 finding logic CRITICAL/MAJOR đã xác nhận | ✅ (không có finding) | verify/round-1/review-logic.md, verification.md |
| G6 | Không lách cổng, không ghi ngoài phạm vi | ✅ | verify/round-1/gates.md |
Không chặn nhưng cần người: Integration test CHƯA CHẠY · Golden master CHƯA CHẠY (GM-01, chưa cấu hình §4)
Coverage theo luồng
| Luồng | Tên | % dòng | % nhánh |
|---|---|---|---|
| F-01 | Xoá kho | 97,3% (71/73) | 93,8% (30/32) |
2. Ma trận truy vết
| Quy tắc | Tầng | Code mới | Test | Golden | Kết luận |
|---|---|---|---|---|---|
| R-01 | C | WarehouseController.deleteWarehouse |
TC-01, TC-02 | — | PASS (body 400 thiếu tham số khác — D-02) |
| R-02 | A | CurrentCompanyService gọi ở WarehouseManagementService.deleteWarehouse |
TC-03, TC-04 | — | DEVIATION (có chủ đích, D-01) |
| R-03 | C | WarehouseManagementService.deleteWarehouse |
TC-05 | — | PASS |
| R-04 | B | Warehouse.validateDeletable |
TC-06, TC-07, TC-08 | — | PASS |
| R-05 | B | WarehouseDeletePolicy, InventoryRelationalRepository.findOnHandsByWarehouse |
TC-09..TC-14, TC-21..TC-23 | GM-01 | UNVERIFIED (SQL thật + kiểu cột MSSQL — D-13, K-13) |
| R-06 | B | WarehouseUsageChecker, BillRelationalAdapter.existsByWarehouse, RsInoutwardRelationalRepository.existsByFromWarehouse |
TC-15..TC-17, TC-24, TC-25 | GM-01 | UNVERIFIED (SQL thật — K-13); chạy tuần tự theo D-07 |
| R-07 | B | WarehouseRelationalRepository.deleteById |
TC-18, TC-20 | GM-01 | UNVERIFIED (SQL thật, khoá ngoại — D-09) |
| R-08 | C | WarehouseController.deleteWarehouse |
TC-01 | — | PASS |
| R-09 | B | WarehouseManagementService.deleteWarehouse (@Transactional) |
TC-19 | — | DEVIATION (có chủ đích, D-11) |
| R-10 | B | (không chuyển) | TC-26 | — | DEVIATION (có chủ đích, D-12) — kiểm gói thật UNVERIFIED |
3. Người cần xem
| # | Vị trí | Vì sao cần người xem |
|---|---|---|
| 1 | Toàn bộ verify/round-1/changes.diff |
Tầng A (R-02 phân quyền) — đọc toàn bộ diff |
| 2 | infrastructure/relational/inventory/adapter/InventoryRelationalRepository.java (findOnHandsByWarehouse) |
Mô phỏng kiểm kiểu Tuple Hibernate 5 bằng isInstance; kiểu thật từng cột MSSQL chưa chạy thử — cột trả Byte/Long/Character sẽ ra 500 (D-13, UNVERIFIED) |
| 3 | WarehouseRelationalRepository.deleteById, BillRelationalAdapter.existsByWarehouse, RsInoutwardRelationalRepository.existsByFromWarehouse |
Native SQL chỉ test bằng mock (K-13) — chạy GM-01 trên MSSQL |
| 4 | application/warehouse/WarehouseUsageChecker.java |
Lệch cấu trúc nhỏ so với spec mục 5 (bean phụ trợ để constructor không quá 7 tham số); hành vi/thứ tự không đổi |
| 5 | Constructor InventoryRelationalRepository, RsInoutwardRelationalRepository thêm EntityManager; WarehouseManagementService thêm WarehouseUsageChecker |
Chạm class dùng chung của context khác (chỉ thêm method/tham số, không đổi method cũ) |
| 6 | G4 | Người chạy SonarQube for IDE |
3b. Phân tích luồng sau chuyển
Chi tiết: verify/round-1/flow-analysis.md
| Luồng | Tên | Số bước KHỚP / KHÁC / CHỈ CÓ Ở CŨ / CHỈ CÓ Ở MỚI | Lệch luồng đã duyệt (spec mục 6) | Điểm dev cần chú ý |
|---|---|---|---|---|
| F-01 | Xoá kho | 6 KHỚP / 5 KHÁC có chủ đích (D-01, D-03/D-13, D-07/D-11, D-12) / 0 / 0 | Cấu trúc: kiểm tồn/chứng từ qua WarehouseUsageChecker; thứ tự và mã lỗi đúng |
kiểm kiểu cột (D-13); native DELETE không kiểm số dòng — không đổi sang JPA remove; || đoản mạch hoá đơn → phiếu kho; một transaction trên DB chính, không khoá |
3c. Nghi vấn bug / sai ở code cũ (đã GIỮ nguyên khi chuyển)
B-01 Xoá kho để lại dữ liệu tồn của kho đó (dòng tồn "mồ côi")
- Mức nghi ngờ: TRUNG BÌNH — code cũ có sẵn lệnh xoá tồn nhưng bị comment, không rõ cố ý hay quên.
- Ở đâu: code cũ
backend:service/impl/WarehouseServiceImpl.java:80-82→ code mới (giữ nguyên)WarehouseManagementService.deleteWarehouse(chỉdeleteById) - Code cũ đang làm gì: chỉ xoá dòng kho; các dòng tồn kho (
inventory) đã về 0 của kho đó vẫn ở lại trong DB, trỏ tới một kho không còn tồn tại. - Ví dụ cụ thể: kho 6 có 3 sản phẩm đã bán hết (tồn 0), chưa có chứng từ → xoá kho 6 thành công; 3 dòng
inventory.warehouse_id = 6còn lại. Nếu đúng nghiệp vụ thì lẽ ra 3 dòng này bị xoá cùng. - Vì sao nghi là sai: dòng
inventoryRepository.deleteAll(inventoryList)đã viết sẵn rồi bị comment. - Ảnh hưởng nếu đúng là bug: báo cáo/tra cứu tồn theo sản phẩm không join
warehousecó thể đếm cả kho đã xoá (tồn 0 nên số lượng không đổi, nhưng danh sách kho có thể lẫn id lạ). - Đã xử lý khi chuyển: GIỮ nguyên hành vi cũ. Test khoá:
TC-18(WarehouseManagementServiceTest$DeleteEndpoint#deletesOnlyWarehouseRow). - Nếu muốn sửa: thêm xoá
inventorycủa kho trongdeleteWarehouse(và quyết địnhbatches_detail), đổi TC-18; cần dọn dữ liệu mồ côi cũ. - Liên quan: D-08, R-07
B-02 Kho chỉ từng nhận hàng chuyển đến thì vẫn xoá được dù đã có phiếu chuyển kho
- Mức nghi ngờ: TRUNG BÌNH — "đã phát sinh chứng từ" thường gồm cả phiếu nhận.
- Ở đâu: code cũ
base:repositories/BaseWarehouseRepository.java:58-69→ code mới (giữ nguyên)RsInoutwardRelationalRepository.existsByFromWarehouse - Code cũ đang làm gì: chỉ kiểm phiếu kho mà kho là kho đi (
from_warehouse_id); phiếu chuyển kho chỉ có một dòng chi tiết ghi cả kho đi lẫn kho nhận (base:services/impl/BaseWarehouseServiceImpl.java:1320-1321), nên kho nhận không bị tính. - Ví dụ cụ thể: chuyển 10 cái từ kho 5 sang kho 9; sau đó sửa phiếu chuyển về 0 (tồn kho 9 = 0), kho 9 không có hoá đơn → xoá kho 9 thành công; phiếu chuyển kho vẫn trỏ tới kho 9 đã xoá. Lẽ ra phải báo "Kho hàng đã phát sinh chứng từ".
- Vì sao nghi là sai: chứng từ có kho làm kho nhận cũng là chứng từ của kho.
- Ảnh hưởng nếu đúng là bug: hiếm (kho nhận thường còn tồn hoặc đã bán/chuyển đi — các đường này đều bị chặn); khi xảy ra, màn xem phiếu chuyển kho hiển thị kho không tồn tại.
- Đã xử lý khi chuyển: GIỮ nguyên. Test khoá:
TC-25(RsInoutwardRelationalRepositoryTest#existsByFromWarehouse, assert không cóto_warehouse_id). - Nếu muốn sửa: thêm
OR rsd.to_warehouse_id = :warehouseIdvào câu SQL, đổi TC-25. - Liên quan: D-06, R-06
B-03 Kiểm tồn dùng tồn tổng của dòng tồn, không dùng tồn theo lô; dòng tồn trống thì lỗi hệ thống
- Mức nghi ngờ: THẤP — câu SQL cũ có tính sẵn tồn theo lô nhưng code không dùng; với dữ liệu khớp nhau thì kết quả như nhau.
- Ở đâu: code cũ
backend:service/impl/WarehouseServiceImpl.java:51-74→ code mới (giữ nguyên)InventoryRelationalRepository.findOnHandsByWarehouse+WarehouseDeletePolicy.validateOnHands - Code cũ đang làm gì: lấy
inventory.on_handcủa từng dòng; dòng nào > 0 thì chặn; dòng nào tồn trống (null) gặp trước thì lỗi hệ thống. - Ví dụ cụ thể: kho 6, sản phẩm theo lô có
inventory.on_hand = 0nhưng lô L1 trongbatches_detailcòn 5 (dữ liệu lệch) → vẫn xoá được. Kho 7 có một dòngon_hand = NULL→ lỗi hệ thống (cũ 417, mới 500), không xoá được. - Vì sao nghi là sai: SQL tính cột
onHandtheo lô nhưng code đọc cộton_hand. - Ảnh hưởng nếu đúng là bug: chỉ khi dữ liệu tồn tổng và tồn lô lệch nhau, hoặc tồn null.
- Đã xử lý khi chuyển: GIỮ nguyên. Test khoá:
TC-12,TC-13(WarehouseDeletePolicyTest),TC-21(InventoryRelationalRepositoryTest). - Nếu muốn sửa: đọc biểu thức tồn theo lô, coi null là 0; đổi TC-12, TC-21.
- Liên quan: D-05, R-05
B-04 Xoá kho không tồn tại (bấm xoá hai lần) trả lỗi máy chủ 500
- Mức nghi ngờ: THẤP — có thể cố ý, nhưng "không tìm thấy" thường không phải lỗi máy chủ.
- Ở đâu: code cũ
backend:service/impl/WarehouseServiceImpl.java:39-41→ code mớiWarehouseManagementService.deleteWarehouse - Code cũ đang làm gì: không tìm thấy kho → lỗi 500 "Kho hàng không tồn tại".
- Ví dụ cụ thể: người A xoá kho 8, người B (màn hình cũ) bấm xoá kho 8 → 500. Lẽ ra 404 hoặc coi như đã xoá.
- Vì sao nghi là sai: lỗi do dữ liệu người gửi, không phải lỗi hệ thống; có thể làm giám sát lỗi 5xx báo động giả.
- Ảnh hưởng nếu đúng là bug: người dùng thấy "lỗi hệ thống"; thao tác lặp hay gặp.
- Đã xử lý khi chuyển: GIỮ nguyên. Test khoá:
TC-05(…$DeleteEndpoint#rejectsMissingWarehouse). - Nếu muốn sửa:
NotFoundException(404), đổi TC-05; frontend kiểm cách hiển thị. - Liên quan: D-04, R-03
B-05 Xoá kho thành công nhưng thông báo "Cập nhật thành công!"
- Mức nghi ngờ: THẤP — chỉ là văn bản thông báo.
- Ở đâu: code cũ
backend:service/impl/WarehouseServiceImpl.java:83→ code mớiWarehouseController.deleteWarehouse - Code cũ đang làm gì: trả mã
SUCCESS_UPDATE. - Ví dụ cụ thể: xoá kho 6 → "Cập nhật thành công!". Lẽ ra "Xoá thành công".
- Vì sao nghi là sai: thông báo không khớp thao tác.
- Ảnh hưởng nếu đúng là bug: chỉ hiển thị (nếu frontend dùng
reason). - Đã xử lý khi chuyển: GIỮ nguyên. Test khoá:
TC-01. - Nếu muốn sửa: đổi mã reason, đổi TC-01; kiểm frontend.
- Liên quan: D-10, R-08
3d. Quyết định AI tự chốt và thay đổi tự động (cần người xác nhận)
| # | Nguồn | AI đã làm gì | Khác code cũ ở điểm nào (input → cũ / mới) | Người cần xác nhận gì |
|---|---|---|---|---|
| 1 | D-01 (LỖI KỸ THUẬT) | Kiểm comId là công ty của token |
user công ty 10 xoá comId=20&id=7 → cũ 200 xoá được / mới 400 COMPANY_NOT_OF_CURRENT_USER |
Đồng ý chặn xoá kho công ty khác |
| 2 | D-02 (ADR BẮT KHÁC) | Body response/lỗi theo starter | kho WH1 → cũ reason="Kho mặc định không được xoá" / mới reason="DELETE_WAREHOUSE_DEFAULT_INVALID"; thiếu id → cũ 400 body rỗng / mới 400 body starter |
Frontend đọc message[0].code, không đọc reason |
| 3 | D-03 (ADR BẮT KHÁC) | Lỗi hệ thống 417 → 500, giữ mã EXCEPTION_ERROR |
on_hand null / code null → cũ 417 / mới 500 |
Frontend không phân biệt 417/500 |
| 4 | D-07 (LỖI KỸ THUẬT) | Kiểm hoá đơn rồi phiếu kho tuần tự | kho có hoá đơn, câu phiếu kho trả trước → cũ có thể 200 (race) / mới luôn 400; câu phiếu kho lỗi trả trước → cũ 417 / mới 400 | Đồng ý sửa race |
| 5 | D-09 (MƠ HỒ) | Xoá như cũ, lỗi ràng buộc → 500 | có FK inventory.warehouse_id → cũ 417 / mới 500; không FK → 200 |
Cho biết DB thật có khoá ngoại tới warehouse.id không (chạy GM-01) |
| 6 | D-11 (ADR BẮT KHÁC) | Một transaction trên DB chính | replica trễ → cũ có thể bỏ sót hoá đơn mới / mới thấy | Đồng ý |
| 7 | D-12 (PHỤ THUỘC NGOÀI) | Không chuyển kiểm gói dịch vụ | công ty hết hạn gói → cũ 400 ACCOUNT_EXPIRED / mới xoá được |
Quyết tầng đảm nhận kiểm gói (chung với warehouse/create D-16, warehouse/update D-12) |
| 8 | D-13 (MƠ HỒ) | Đọc 10 cột, kiểm kiểu mọi dòng trước khi kiểm tồn | dòng có cột sai kiểu → cũ 417 (Hibernate 5) / mới 500, nhưng tập kiểu được chấp nhận có thể khác giữa Hibernate 5/6 | Chạy GM-01 trên MSSQL thật để xác nhận kho bình thường không bị 500 |
| 9 | spec-review | Spec qua 1 lần review (0 CRITICAL), các R-xx đã sửa theo MAJOR: R-05 (F-spec-1, đọc hết dòng trước khi kiểm), R-07 (F-spec-2, native DELETE không kiểm số dòng) | — | Đọc kỹ R-05, R-07 trong spec.md |
| 10 | Thiết kế (impl) | Thêm WarehouseUsageChecker giữa service và 3 nguồn đọc (constructor ≤ 7 tham số) |
không đổi hành vi | Đồng ý cấu trúc này |
| 11 | Thiết kế (impl) | InventoryErrorCode.EXCEPTION_ERROR mới (cùng giá trị) |
không đổi mã trả về | — |
4. Finding đã xác nhận
Không có.
5. Finding bị loại
| Finding | Lý do loại |
|---|---|
| — | — |
6. Xử lý của người
| Vấn đề | Cổng | Chỉ thị |
|---|---|---|
| G4 — chưa kiểm Sonar | G4 |
7. So với vòng trước
| Vấn đề | Vòng trước | Vòng này |
|---|---|---|
| (vòng đầu) | — | — |
8. Thay đổi trong repo đích
26 file (verify/round-1/changed-files.md): 17 main Java + 2 i18n + 7 test, đều trong src/main, src/test. Mới: WarehouseUsageChecker, WarehouseDeletePolicy, WarehouseDeletePolicyTest, InventoryRelationalRepositoryTest. Chạm class dùng chung của context khác: InventoryRepository/InventoryRelationalRepository/InventoryQueryService/InventoryErrorCode, RsInoutwardRepository/RsInoutwardRelationalRepository/RsInoutwardQueryService, BillPort/BillRelationalAdapter — chỉ thêm, không đổi method cũ. Không có file ngoài phạm vi.
9. Đề xuất bổ sung kinh nghiệm (/mig-learn gom vào proposal để người duyệt)
| Đề xuất | Đưa vào đâu |
|---|---|
Code cũ không có transaction bao ngoài mà xoá bằng SimpleJpaRepository.delete → mới phải xoá bằng native DELETE không kiểm số dòng; JPA remove trên entity đã nạp trong cùng transaction ra StaleStateException khi bị xoá đồng thời (F-spec-2) |
lessons.md 1a/1b |
Tuple.get("snake_case") của Hibernate 5 tra alias không phân biệt hoa thường nhưng on_hand ≠ onHand: kiểm cột thật được đọc; và bước collect dựng đủ mọi dòng trước khi kiểm → lỗi đọc thắng lỗi nghiệp vụ (F-spec-1) |
lessons.md 1a |
CompletableFuture.anyOf + isDone() ? a.join() : b.join() để lấy "kết quả còn lại" là race; gặp thì ghi D-xx LỖI KỸ THUẬT |
lessons.md 1a |
Constructor @Service tối đa 7 tham số (Checkstyle ParameterNumber cho constructor) — thêm use case vào service có sẵn 6 dependency thì tách bean phụ trợ ngay từ thiết kế spec mục 5 |
target-cheatsheet mục 4 |
Trong Bash tool, heredoc/node -e làm mất \\ trong chuỗi Java regex — sửa chuỗi có \\ bằng Edit |
lessons.md 1c |