Báo cáo sai lệch — inventory/cogs-calculate — 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 (tiền giá vốn, ghi
cogs_history, task log làm đổi số tiền phiếu/hoá đơn) → người cần đọc: spec + báo cáo + toàn bộ diff
- Số quy tắc: 18 · PASS 9 (R-04, R-06…R-11, R-13, R-14) · DEVIATION có chủ đích 7 (R-01, R-02, R-03, R-12, R-15, R-17, R-18) · DEVIATION KHÔNG chủ đích 0 · UNVERIFIED 2 (R-05 phía tiêu thụ, R-16)
- Coverage thấp nhất: F-01 99.2% dòng / 88.9% nhánh
- Finding logic: CRITICAL 0 · MAJOR 0 · MINOR 1 · UNSURE 0
- Nghi vấn bug code cũ (mục 3c): 5 (CAO 2) · Quyết định tự chốt cần xác nhận (mục 3d): 9
- Chạy bằng:
/mig-auto lượt 20261105-1351 (spec qua 2 lần spec-reviewer, chưa có người duyệt) · Số vòng: 1 verify / lần sửa 0/3
- Phạm vi API còn nghi vấn (❓) —
warehouse-api-scope-final.md mục 1.2: "Cần quyết định có thuộc kho không".
1. Điều kiện đạt
| Mã |
Mục tiêu |
Kết quả |
Bằng chứng |
| G1 |
mvnw verify |
✅ |
verify/round-1/build.md (677 test, SpotBugs 0) |
| G2 |
Test khớp test-plan |
✅ |
verify/round-1/oracle.md (48/48 TC) |
| G3 |
Coverage mỗi luồng |
✅ |
verify/round-1/coverage.md |
| G4 |
Sonar |
CHỜ NGƯỜI |
sonar-ide.md |
| G5 |
0 CRITICAL/MAJOR |
✅ |
verify/round-1/review-logic.md (không cần xác minh) |
| G6 |
Không lách cổng |
✅ |
verify/round-1/gates.md; 38 file đều có trong impl-notes.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, GM-02 — tầng A, nên chạy trước khi commit)
Coverage theo luồng
| Luồng |
Tên |
% dòng |
% nhánh |
| F-01 |
Nhận yêu cầu, chọn nhánh, gửi task log tính sau |
99.2% (236/238) |
88.9% (48/54) |
| F-02 |
Tính giá vốn đồng bộ, gửi cập nhật, ghi lịch sử |
99.1% (333/336) |
89.4% (93/104) |
2. Ma trận truy vết
| Quy tắc |
Tầng |
Code mới |
Test |
Golden |
Kết luận |
| R-01 |
A |
CogsController, CogsCalculationRequest |
TC-01, 02 |
— |
DEVIATION (D-02: body 400) |
| R-02 |
A |
CogsCalculationService.java:54 |
TC-03, 04 |
— |
DEVIATION (D-01) |
| R-03 |
A |
CogsCalculationService.java:55-58, CogsPeriod |
TC-05, 06 |
— |
DEVIATION (D-02: key reason null bị bỏ) |
| R-04 |
A |
CogsCalculationService.java:60-64; SQL ppu chính |
TC-07..11 |
GM-02 |
PASS |
| R-05 |
A |
CogsCalculationService.java:65-73 |
TC-08, 12, 13, 14 |
GM-02 |
PASS phía gửi; phía tiêu thụ UNVERIFIED (D-07) |
| R-06 |
A |
CogsComputationService.java:68-78 |
TC-15, 16 |
GM-01 |
PASS |
| R-07 |
A |
CogsRelationalRepository SQL |
TC-17, 18, 19 |
GM-01 |
PASS (H2; MSSQL UNVERIFIED) |
| R-08 |
A |
CogsItem.aggregate |
TC-20, 21 |
GM-01 |
PASS |
| R-09 |
A |
CogsItem.calculate |
TC-22 |
GM-01 |
PASS |
| R-10 |
A |
CogsComputationService.java:116-184 |
TC-23..28, 46, 47 |
GM-01 |
PASS (giữ D-04) |
| R-11 |
A |
CogsComputationService.java:80-82 |
TC-29 |
— |
PASS |
| R-12 |
A |
CogsComputationService.saveHistories, INSERT |
TC-30..33 |
GM-01 |
DEVIATION (D-09 giờ VN) |
| R-13 |
A |
CogsController.java:46-53 |
TC-34, 35 |
— |
PASS (định dạng D-02) |
| R-14 |
A |
thứ tự gửi trước, ghi sau |
TC-36 |
— |
PASS (giữ D-05) |
| R-15 |
A |
CogsTaskLogMongoRabbitAdapter |
TC-37..40 |
GM-01, 02 |
DEVIATION (D-07 adapter mới; tương thích easyposasync UNVERIFIED) |
| R-16 |
A |
— |
— |
— |
DEVIATION (D-08) — UNVERIFIED |
| R-17 |
A |
SystemException(EXCEPTION_ERROR) |
TC-41, 42 |
— |
DEVIATION (D-03: 417 → 500) |
| R-18 |
A |
SendQueueConfigRelationalAdapter |
TC-43, 44, 45 |
— |
DEVIATION (D-10: không Redis, không EasyInvoice) |
3. Người cần xem
| # |
Vị trí |
Vì sao cần người xem |
| 1 |
Toàn bộ diff (tầng A) — 29 file main, 7 file test |
verify/round-1/changes.diff |
| 2 |
infrastructure/messaging/CogsTaskLogMongoRabbitAdapter.java |
Định dạng document task_log_reference + message RabbitMQ phải khớp easyposasync; chưa chạy với Mongo/RabbitMQ thật. Repo đích chưa có cấu hình spring.rabbitmq.* — cần bổ sung trước khi chạy |
| 3 |
Consumer easyposasync |
@RabbitListener của CogsCompletionListener/CogsUpdateAsyncListener đang bị comment trong source → xác nhận bản prod có bật không (nếu tắt: nhánh > 100 không bao giờ tính, phiếu/hoá đơn không được cập nhật — ở cả cũ và mới) |
| 4 |
infrastructure/relational/config/adapter/SendQueueConfigRelationalAdapter.java:43-46 (F-logic-1, MINOR) |
config.business_type NULL → NullPointerException thay vì SystemException(EXCEPTION_ERROR) |
| 5 |
CogsTaskLogMongoRabbitAdapter.java:105-112 (flow-analysis) |
Chỉ nuốt AmqpException; cũ nuốt mọi Exception khi gửi queue |
| 6 |
CogsRelationalRepository.java SQL |
Native SQL chỉ chạy H2 — chạy GM-01 trên MSSQL (K-13) |
| 7 |
Phạm vi API (❓) |
Chốt API này có thuộc warehouse-service không |
| 8 |
G4 |
Sonar chưa kiểm |
3b. Phân tích luồng sau chuyển
Chi tiết: verify/round-1/flow-analysis.md
| Luồng |
Tên |
KHỚP / KHÁC / CHỈ CŨ / CHỈ MỚI |
Lệch luồng đã duyệt |
Điểm dev cần chú ý |
| F-01 |
Nhận yêu cầu, chọn nhánh, gửi task log |
7 / 4 (D-01, D-02, D-03, D-10) / 1 (D-08) / 0 |
Không |
Gửi Mongo/Rabbit trong transaction SQL; chỉ nuốt AmqpException; định dạng document; lỗi lô k không thu hồi |
| F-02 |
Tính đồng bộ, gửi cập nhật, ghi lịch sử |
12 / 2 (D-09, D-07/D-10) / 0 / 0 |
Không |
Không đổi HashSet/HashMap (D-04); wardDeltas dùng chung; ghi giờ VN; SQL chỉ chạy H2 |
3c. Nghi vấn bug / sai ở code cũ (đã GIỮ nguyên khi chuyển)
B-01 Giá vốn tính dở khi ppu cha và ppu con rơi vào hai lô khác nhau
- Mức nghi ngờ: CAO — số tiền phiếu xuất/hoá đơn lệch với giá vốn đã lưu cùng lần tính; xảy ra khi tập cha + con > 100 ppu.
- Ở đâu: code cũ
easyposbase:src/main/java/sds/ep/baseservice/services/impl/BaseCogsHistoryServiceImpl.java:83-106,142-168 → mới (giữ nguyên) application/cogs/CogsComputationService.java:73-146
- Code cũ đang làm gì: chia các ppu (cha và con) thành từng lô 100. Sau mỗi lô, tính giá vốn với số liệu đã có tới lô đó và gửi ngay lệnh cập nhật đơn giá phiếu xuất/hoá đơn. Dòng của ppu con ở lô sau làm đổi giá vốn của cha, nhưng các lệnh đã gửi ở lô trước không được tính lại.
- Ví dụ cụ thể: ppu cha A đầu kỳ 10 cái × 100đ, xuất 5 cái trong kỳ; có 150 ppu con, con thứ 150 nhập 10 cái × 160đ (rơi lô 2). Lô 1: giá vốn A = 100 → phiếu xuất của A cập nhật 500đ. Lô 2: giá vốn A = 130 → lịch sử giá vốn lưu 130, API trả 130, nhưng phiếu xuất vẫn 500đ (lẽ ra 650đ). Thêm: dòng hoá đơn của con ở lô trước khi cha chưa có số liệu thì không bao giờ được cập nhật.
- Vì sao nghi là sai: cùng một lần tính mà giá vốn ghi vào lịch sử khác giá vốn dùng cho phiếu; ppu nào rơi lô nào phụ thuộc thứ tự băm, không theo nghiệp vụ.
- Ảnh hưởng nếu đúng là bug: sản phẩm có nhiều đơn vị quy đổi/biến thể (> 100 ppu trong họ) sẽ có giá trị xuất kho và giá vốn hoá đơn sai; chỉ xảy ra nếu consumer
easyposasync đang bật.
- Đã xử lý khi chuyển: GIỮ nguyên. Test khoá:
TC-46 (CogsComputationServiceTest#partialCogsAcrossBatches), TC-47 (#billOfChildBeforeParentItemDropped).
- Nếu muốn sửa: tính giá vốn cho mọi lô xong mới tạo lệnh cập nhật (
CogsComputationService.calculate), đổi TC-46/47; monolith còn chạy song song sẽ cho số khác.
- Liên quan: D-04, R-06, R-10
B-02 API lỗi nhưng lệnh cập nhật phiếu/hoá đơn đã gửi vẫn được thực hiện
- Mức nghi ngờ: CAO — dữ liệu tiền lệch khi có lỗi giữa chừng.
- Ở đâu:
easyposbase:…/BaseTaskLogServiceImpl.java:121-130,373-388, BaseCogsHistoryServiceImpl.java:104-121 → mới CogsComputationService.java + CogsTaskLogMongoRabbitAdapter.java:91-113
- Code cũ đang làm gì: ghi task log vào Mongo và gửi RabbitMQ ngay khi tạo (không chờ lưu DB xong), lịch sử giá vốn ghi cuối cùng.
- Ví dụ cụ thể: 20 ppu, đã gửi lệnh cập nhật phiếu xuất, tới bước ghi
cogs_history thì mất kết nối DB → API báo lỗi, lịch sử không có; nhưng easyposasync (nếu bật) vẫn đổi đơn giá phiếu xuất theo giá vốn mới.
- Vì sao nghi là sai: người dùng thấy lỗi nhưng dữ liệu đã đổi một phần.
- Ảnh hưởng nếu đúng là bug: hiếm (chỉ khi lỗi hạ tầng giữa chừng), nhưng khó phát hiện.
- Đã xử lý khi chuyển: GIỮ. Test khoá:
TC-36 (#publishBeforeSave), TC-45 (#failureBeforeSave).
- Nếu muốn sửa: gửi task log sau commit (
@TransactionalEventListener(AFTER_COMMIT) như task log quyết toán của repo đích).
- Liên quan: D-05, R-14
B-03 Phiếu đã xoá mềm vẫn được tính vào giá vốn
- Mức nghi ngờ: TRUNG BÌNH — ý nghĩa
status = 0 chỉ suy từ tên method delete…ForMedicine.
- Ở đâu:
easyposbase:…/repositories/BaseCogsHistoryRepository.java:22-66 → mới CogsRelationalRepository.java:59-85
- Code cũ đang làm gì: đọc mọi dòng phiếu/hoá đơn, không lọc trạng thái.
- Ví dụ cụ thể: nhập 10 × 100đ (phiếu đã xoá) + nhập 10 × 200đ → giá vốn 150 (lẽ ra 200).
- Đã xử lý khi chuyển: GIỮ. Test khoá:
TC-17 (CogsRelationalRepositoryTest#detailRows, dòng G).
- Nếu muốn sửa: thêm
rio.status/trạng thái hoá đơn vào SQL, đổi TC-17/18.
- Liên quan: D-11
B-04 Lịch sử giá vốn bị trùng khi ppu có nhiều lô trong một kho
- Mức nghi ngờ: TRUNG BÌNH — chắc chắn sinh trùng; chưa biết báo cáo nào đọc
cogs_history có cộng dồn không.
- Ở đâu:
easyposbase:…/repositories/BaseInventoryRepository.java:272-277 → mới CogsRelationalRepository.java:87-95
- Ví dụ cụ thể: ppu 501 có 2 lô ở kho 1 → 2 dòng
cogs_history(kho 1) giống nhau, data 2 phần tử giống nhau, count = 1.
- Đã xử lý khi chuyển: GIỮ. Test khoá:
TC-30, TC-32.
- Nếu muốn sửa:
DISTINCT theo (ppu_id, warehouse_id).
- Liên quan: D-12
B-05 Không kiểm dữ liệu vào; báo status: true cả khi thất bại
- Mức nghi ngờ: THẤP — có thể client đọc
message[0].code.
- Ở đâu:
easyposbase:…/BaseWarehouseServiceImpl.java:1891-1927 → mới CogsCalculationService.java:55-64
- Ví dụ cụ thể: thiếu ngày → 200
{"message":[{"code":"FAIL"}],"status":true}; fromDate sau toDate vẫn chạy và lưu lịch sử from > to.
- Đã xử lý khi chuyển: GIỮ. Test khoá:
TC-05, TC-07, TC-48.
- Liên quan: D-06
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ũ (input → cũ / mới) |
Người cần xác nhận gì |
| 1 |
D-07 (PHỤ THUỘC NGOÀI) |
Port + adapter Mongo task_log_reference + RabbitMQ (Jackson2JsonMessageConverter), giữ định dạng cũ |
— (định dạng như cũ, chưa chạy thật) |
Bổ sung spring.rabbitmq.*; xác nhận consumer easyposasync prod có bật; chạy GM-02 với easyposasync |
| 2 |
D-10 (PHỤ THUỘC NGOÀI) |
Kiểm config.business_type + company.service, không Redis, không EasyInvoice |
Redis không có key + công ty không cấu hình EasyInvoice → cũ 417 / mới 200 và ghi dữ liệu; Redis có nhưng DB thiếu → cũ chạy / mới 500 |
Redis CONFIG_COM_ID có luôn được nạp không; có cần kiểm EasyInvoice |
| 3 |
D-01 (LỖI KỸ THUẬT) |
Kiểm công ty token |
comId khác → cũ tính hộ / mới 400 |
Đồng ý chặn |
| 4 |
D-02 (ADR BẮT KHÁC) |
BaseResponseDTO, giữ status true, mã, reason, count |
key null (data, count, reason) bị bỏ; body hỏng 400 INVALID_REQUEST |
Client chịu được thiếu key |
| 5 |
D-03 (ADR BẮT KHÁC) |
SystemException(EXCEPTION_ERROR) |
417 → 500 |
— |
| 6 |
D-08 (PHỤ THUỘC NGOÀI) |
Không kiểm gói |
hết gói → cũ 400 / mới tính |
Tầng nào kiểm gói |
| 7 |
D-09 (MƠ HỒ) |
Ghi cogs_history và norm_date task log theo giờ VN |
JVM cũ ở UTC → khác ngày/giờ |
Múi giờ JVM prod cũ |
| 8 |
spec-review |
Spec qua 2 lần review; các R-xx đã sửa theo CRITICAL/MAJOR: R-05, R-10, R-14, R-15, R-18 |
— |
Đọc kỹ các quy tắc này trong spec.md |
| 9 |
Phạm vi ❓ |
Chuyển vì người yêu cầu đích danh |
— |
Chốt API có thuộc warehouse-service |
4. Finding đã xác nhận
Không có CRITICAL/MAJOR. MINOR: F-logic-1 (mục 3 dòng 4) — chi tiết verify/round-1/review-logic.md.
5. Finding bị loại
| Finding |
Lý do loại |
| (không có) |
|
6. Xử lý của người
| Vấn đề |
Cổng |
Chỉ thị |
| G4 chờ người kiểm Sonar |
G4 |
|
F-logic-1 (MINOR) — muốn sửa thì ghi SỬA |
— |
|
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
38 file (verify/round-1/changed-files.md), đều trong src/main, src/test: context mới domain/cogs, application/cogs, port CogsTaskLogPort, SendQueueConfigPort, adapter infrastructure/relational/cogs, infrastructure/relational/config, infrastructure/messaging/CogsTaskLog*, presentation/web/inventory/CogsController + DTO; sửa InventoryWebMapper, i18n vi/en.
9. Đề xuất bổ sung kinh nghiệm
| Đề xuất |
Đưa vào đâu |
Task log Mongo task_log_reference của monolith luôn qua getConfigSendQueue (Redis → config + company + EasyInvoice) trước khi ghi — lỗi ở đó làm 417 |
legacy-facts.md |
RabbitTemplate monolith dùng Jackson2JsonMessageConverter (body JSON "id", __TypeId__) — adapter mới phải tự dựng message |
legacy-facts.md |
Listener easyposasync cho COGS đang tắt trong source — kiểm @RabbitListener bị comment trước khi mô tả "bên tiêu thụ" |
lessons.md 1a |
H2 MODE=MSSQLServer không nhận AUTO_INCREMENT (dùng IDENTITY); cột value cần NON_KEYWORDS=VALUE |
target-cheatsheet.md mục 5 |
Constructor tối đa 7 tham số (Checkstyle); chuỗi cứng trong method bị chặn ngoài infrastructure/relational|nonrelational |
target-cheatsheet.md mục 4 |
| Shell Bash của máy không nhận heredoc/chuỗi dài có ký tự đặc biệt — ghi script bằng công cụ Write |
lessons.md Phần 2 |