Báo cáo sai lệch — rsinoutward/get-all-transactions — vòng 2
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: B (chỉ đọc DB/Mongo, ghi cache Redis mốc tồn đầu kỳ) → người đọc spec + báo cáo + đoạn gắn cờ ở mục 3
- Số quy tắc: 17 · PASS 9 · DEVIATION có chủ đích 4 (R-01, R-02, R-15, R-16) · DEVIATION KHÔNG chủ đích 0 · UNVERIFIED 4 (R-06/R-07/R-11 SQL thật, R-17 replica; một phần R-04/R-09/R-12/R-13)
- Coverage thấp nhất: F-02 99,6% dòng / 97,7% nhánh
- Finding logic: CRITICAL 0 · MAJOR 0 · MINOR 1 (F-logic-1, vòng 1) · UNSURE 0 — không cần xác minh
- 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): 13
- Chạy bằng:
/mig-auto(spec dospec-reviewerkiểm 2 lần, chưa có người duyệt) · Số vòng: 2 / lần sửa 1/3
1. Điều kiện đạt
| Mã | Mục tiêu | Kết quả | Bằng chứng |
|---|---|---|---|
| G1 | mvnw verify |
✅ | verify/round-2/build.md (581 test) |
| G2 | Test khớp test-plan, mọi R-xx có test | ✅ | verify/round-2/tc-map.md (61/61), verify/round-2/oracle.md |
| G3 | Coverage mỗi luồng | ✅ | verify/round-2/coverage.md |
| G4 | 0 issue Sonar | CHỜ NGƯỜI | sonar-ide.md |
| G5 | 0 CRITICAL/MAJOR | ✅ | verify/round-2/review-logic.md (thu hẹp, 0 finding); vòng 1: 1 MINOR |
| G6 | Không lách cổng | ✅ | verify/round-2/gates.md; 49/49 file 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)
Coverage theo luồng
| Luồng | Tên | % dòng | % nhánh |
|---|---|---|---|
| F-01 | Kiểm đầu vào, dựng điều kiện, đếm + đọc trang | 99,7% | 98,4% |
| F-02 | Bổ sung dòng và tổng | 99,6% | 97,7% |
| F-03 | Liên thông dược (Mongo + Redis) | 100% | 100% |
2. Ma trận truy vết
| Quy tắc | Tầng | Code mới | Test | Golden | Kết luận |
|---|---|---|---|---|---|
| R-01 | C | RsInoutwardQueryController |
TC-01..03 | — | DEVIATION (có chủ đích, D-02 — body 400) |
| R-02 | C | RsInoutwardTransactionQueryService |
TC-04..06 | — | DEVIATION (có chủ đích, D-01) |
| R-03 | C | service + TransactionDateRange |
TC-07..11 | — | PASS |
| R-04 | C | service, RsInoutwardErrorCode, i18n |
TC-12..14 | — | PASS mã; text theo locale UNVERIFIED (D-14) |
| R-05 | C | service + ProductUnitLookupRelationalAdapter |
TC-15..21 | — | PASS |
| R-06 | C | RsInoutwardTransactionSqlBuilder |
TC-22..27 | GM-01 | UNVERIFIED (SQL chưa chạy MSSQL) |
| R-07 | C | TransactionDateRange, builder |
TC-28..32 | GM-01 | UNVERIFIED (WHERE AND, IN () thật) |
| R-08 | C | RsInoutwardTransactionRelationalRepository |
TC-33..36 | — | PASS |
| R-09 | C | repository + response DTO | TC-37..40, TC-58 | — | PASS; giờ date UNVERIFIED (D-07) |
| R-10 | C | service, adapter business type/customer | TC-41..43 | — | PASS |
| R-11 | C | builder | TC-44, TC-45 | GM-01 | UNVERIFIED (SQL thật) |
| R-12 | C | RsInoutwardPharmaEnricher, PharmaHistoryMongoAdapter |
TC-46..48 | — | PASS (mock Mongo) |
| R-13 | B | enricher, OpeningBalanceTimeRedisAdapter |
TC-49..53 | — | PASS (mock Redis) |
| R-14 | C | service, response DTO | TC-54..56 | — | PASS |
| R-15 | C | adapter bọc lỗi | TC-57, TC-58 | — | DEVIATION (có chủ đích, D-03: 417→500) |
| R-16 | C | — | TC-59 | — | DEVIATION (có chủ đích, D-10 — không kiểm gói) |
| R-17 | C | @Transactional(readOnly = true) |
TC-60 | — | UNVERIFIED (replica, D-08) |
3. Người cần xem
| # | Vị trí | Vì sao cần người xem |
|---|---|---|
| 1 | …/infrastructure/relational/rsinoutward/adapter/RsInoutwardTransactionSqlBuilder.java |
SQL native giữ nguyên lỗi cũ (WHERE AND khi không có ngày, IN () khi quý rỗng); chỉ kiểm bằng mock — cần chạy MSSQL thật (GM-01) |
| 2 | …/infrastructure/mongo/pharma/PharmaHistoryMongoAdapter.java |
Dựng MongoTemplate riêng trên DB ${app.dbname.pharma:EasyPharma}; ws1 chưa khai thuộc tính → dev cũ là EasyPosPharma — người khai app.dbname.pharma theo môi trường (D-09). F-logic-1 MINOR: lỗi map document (vd pharmacy_push_status: "abc") không ra EXCEPTION_ERROR |
| 3 | …/infrastructure/cache/OpeningBalanceTimeRedisAdapter.java |
SET không TTL — ngoại lệ ADR-0401 (D-12) |
| 4 | src/main/resources/i18n/messages_vi.properties, messages_en.properties |
Thêm 7 key, sửa text PRODUCT_NOT_FOUND (đổi text dùng chung của key đã có), TOKEN_EXPIRED giữ text dính dòng (D-13); trùng key với ws2 khi gộp nhánh |
| 5 | BusinessTypePort/CustomerPort + adapter |
Thêm method vào port dùng chung (không đổi method cũ) |
| 6 | Toàn bộ SQL/Mongo/Redis | UNVERIFIED trên hạ tầng thật (K-13) |
3b. Phân tích luồng sau chuyển
Chi tiết: verify/round-1/flow-analysis.md, verify/round-2/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 | Kiểm đầu vào, dựng điều kiện, đếm + đọc trang | bước 9-12 đối chiếu lại: 4 KHỚP; các bước khác giữ như vòng 1 (KHÁC có chủ đích D-01/D-02, CHỈ CÓ Ở CŨ D-10) | không | RsInoutwardTransactionRelationalRepository.java:85-94 ép (int) offset và PageImpl dựng lại tổng (K-09) |
| F-02 | Bổ sung dòng và tổng (trang có dòng) | bước 6-8 đối chiếu lại: 3 KHỚP / 0 KHÁC / 0 CHỈ CÓ Ở CŨ / 0 CHỈ CÓ Ở MỚI; bước 1-5 giữ như vòng 1 | không | RsInoutwardTransactionSqlBuilder.java:134-155 câu tổng JOIN không DISTINCT (D-06), lô viết chữ thường |
| F-03 | Liên thông dược (Mongo + Redis) | 8 KHỚP / 1 KHÁC (F-logic-1) / 0 CHỈ CÓ Ở CŨ / 0 CHỈ CÓ Ở MỚI | không | Redis SET không TTL bằng serializer cũ (ngoại lệ ADR-0401, D-12). app.dbname.pharma chưa khai trong yml nào của ws1 nên mọi môi trường dùng EasyPharma, còn dev cũ dùng EasyPosPharma (D-09). DB pharma phải cùng cluster với MongoClient của ws1. |
3c. Nghi vấn bug / sai ở code cũ (đã GIỮ nguyên khi chuyển)
B-01 Không chọn ngày, hoặc "từ ngày" sau "đến ngày" khác tháng → báo "Lỗi hệ thống"
- Mức nghi ngờ: TRUNG BÌNH — câu SQL rõ ràng bị ghép thiếu, nhưng màn hình có thể luôn gửi ngày.
- Ở đâu: code cũ
easyposbackend1:…/repository/impl/RsInoutWardRepositoryImpl.java:195-199,easyposmodel:…/util/BaseCommon.java:2321-2352→ code mới (giữ nguyên)RsInoutwardTransactionSqlBuilder - Code cũ đang làm gì: luôn viết chữ
WHERErồi mới thêm điều kiện ngày; không có ngày thì câu SQL thànhWHERE AND …— sai cú pháp. Khoảng ngày ngược tháng thì danh sách quý rỗng →IN ()— cũng sai cú pháp. - Ví dụ cụ thể:
?comId=10không có ngày → cũ 417 "Lỗi hệ thống", mới 500 "Lỗi hệ thống". Từ 10/05/2024 đến 30/04/2024 → lỗi tương tự. Lẽ ra: trả danh sách toàn bộ hoặc báo "ngày không hợp lệ". - Vì sao nghi là sai: lỗi cú pháp SQL không thể là ý định nghiệp vụ.
- Ảnh hưởng nếu đúng là bug: client gọi không kèm ngày nhận lỗi hệ thống.
- Đã xử lý khi chuyển: GIỮ. Test khoá:
TC-30,TC-31(RsInoutwardTransactionRelationalRepositoryTest). - Nếu muốn sửa: đổi builder chỉ thêm
ANDkhi đã có điều kiện trước, hoặc bắt buộc ngày bằng 400DATE_TIME_INVALID; đổi TC-30/31. - Liên quan: D-04, R-07
B-02 Tổng tiền/số lượng bị nhân lên khi lọc theo sản phẩm/kho/lô
- Mức nghi ngờ: TRUNG BÌNH — danh sách dùng DISTINCT còn câu tổng thì không, hai số trên cùng màn hình không khớp.
- Ở đâu: code cũ
RsInoutWardRepositoryImpl.java:326-338(tổng),:351-385(đếm SP) → mớiRsInoutwardTransactionSqlBuilder.java:134-155 - Code cũ đang làm gì: câu tổng nối với bảng chi tiết, mỗi dòng chi tiết khớp lại cộng thêm tiền và số lượng của cả phiếu. "Số sản phẩm" của phiếu lọc theo sản phẩm/lô nhưng không theo kho.
- Ví dụ cụ thể: phiếu NK001 1.000.000đ, 10 cái, có 2 dòng cùng SP A (2 lô). Lọc SP A → danh sách 1 phiếu nhưng tổng nhập 2.000.000đ, tổng SL 20. Lẽ ra 1.000.000đ / 10.
- Vì sao nghi là sai: tổng không khớp danh sách.
- Ảnh hưởng nếu đúng là bug: người xem tổng tiền khi lọc theo SP/kho/lô thấy số lớn hơn thực tế.
- Đã xử lý khi chuyển: GIỮ. Test khoá:
TC-42,TC-45. - Nếu muốn sửa: dùng
EXISTS/DISTINCTtrong câu tổng, đổi TC-45; monolith còn chạy song song sẽ ra số khác. - Liên quan: D-06, R-10, R-11
B-03 Từ khoá toàn dấu cách loại phiếu không có tên KH/NCC; %, _ không được hiểu nguyên văn
- Mức nghi ngờ: THẤP
- Ở đâu:
RsInoutWardRepositoryImpl.java:207-210,backend:util/Common.java:1315-1329→ mớiTransactionKeyword - Code cũ đang làm gì: từ khoá chỉ bị bỏ khi rỗng; chuẩn hoá xoá dấu cách nên
" "thành"%%"; ký tự đại diện của LIKE không được escape. - Ví dụ cụ thể:
keyword=" "→ phiếu không có tên KH/NCC biến mất;keyword="a_b"khớp "acb". - Đã xử lý khi chuyển: GIỮ. Test khoá:
TC-25,TC-26. - Nếu muốn sửa: bỏ keyword toàn trắng, escape
%_[. - Liên quan: D-05, R-06
B-04 Lọc theo kho không thấy phiếu chuyển vào kho đó
- Mức nghi ngờ: THẤP
- Ở đâu:
RsInoutWardRepositoryImpl.java:251-256,333-335→ mới builder - Code cũ đang làm gì: điều kiện kho chỉ xét kho xuất (
from_warehouse_id). - Ví dụ cụ thể: chuyển K1 → K2, lọc K2 → không thấy phiếu.
- Đã xử lý khi chuyển: GIỮ. Test khoá:
TC-24. - Liên quan: D-11, R-06
B-05 Text lỗi "phiên hết hạn" bị dính dòng trong file ngôn ngữ cũ
- Mức nghi ngờ: THẤP — rõ là lỗi file i18n.
- Ở đâu:
easyposmodel:src/main/resources/i18n_custom/messages_vi.properties:442→ mớimessages_vi.properties(keyTOKEN_EXPIRED) - Ví dụ cụ thể: token không có công ty → text "Phiên đăng nhập đã hết hạnOTP_NOT_NULL=Mã xác minh không được bỏ trống".
- Đã xử lý khi chuyển: GIỮ text cũ. Chưa có TC khoá text (TC-04 khoá mã). Ws2 (
batch/get-with-paging) đã dùng text sửa → khi gộp nhánh người chọn một. - Liên quan: D-13, R-02
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 | F-spec-1 lần 2 (CHƯA QUYẾT) + D-14 | Text thông báo theo locale của starter | Accept-Language: en → cũ tiếng Việt / mới có thể tiếng Anh |
Chốt chung cho mọi unit: ép vi (đổi cấu hình locale toàn service) hay theo starter |
| 2 | auto-changes #1 | Sửa test-plan TC-36 count 3 → 707 |
Không khác — test-plan tính sai số học (4294968003 − 2³² = 707) | Xác nhận sửa test-plan dù không phải trường hợp "spec sai" của §10 |
| 3 | auto-changes #2 | Sửa spec R-11, TC-45 AND → and chữ thường |
Không khác hành vi (giữ đúng chữ code cũ) | Xác nhận |
| 4 | D-01 | Chỉ giữ 2 kiểm token/công ty | token sau đổi mật khẩu → cũ 500 TOKEN_EXPIRED / mới trả danh sách |
Đồng ý bỏ kiểm user đầy đủ |
| 5 | D-02 | Body lỗi/response theo starter | thiếu comId → cũ 400 body rỗng / mới 400 body starter |
Đồng ý |
| 6 | D-03 | 417 → 500, giữ mã EXCEPTION_ERROR |
productIds=1,abc → 417 / 500 |
Đồng ý |
| 7 | D-07 | Giờ date = giờ tường DB (coi JVM cũ GMT+7) |
Nếu JVM prod cũ không phải GMT+7 → giờ hiển thị và cờ liên thông khác | Xác nhận múi giờ JVM prod cũ |
| 8 | D-08 | readOnly = true → replica |
Không khác (cũ cũng replica — suy luận) | Xác nhận |
| 9 | D-09 | Mongo pharma qua adapter, thuộc tính app.dbname.pharma (mặc định EasyPharma) |
Dev cũ EasyPosPharma |
Khai app.dbname.pharma trong yml từng môi trường |
| 10 | D-10 | Không kiểm gói dịch vụ | công ty hết hạn dùng thử → cũ 400 TRIAL_EXPIRED / mới trả danh sách |
Đồng ý (gateway kiểm?) |
| 11 | D-12 | Redis SET không TTL, serializer cũ | Không khác cũ; trái ADR-0401 | Duyệt ngoại lệ ADR-0401 |
| 12 | spec-review mục 5 (F-spec-2 lần 2) | Sửa text key PRODUCT_NOT_FOUND có sẵn ở ws1 theo text cũ |
Text cũ của ws1 "Không tìm thấy sản phẩm có id={0}" bị thay | Xác nhận không API nào khác dựa vào text cũ của key này |
| 13 | F-logic-1 (MINOR) | Chưa sửa | Document Mongo sai kiểu → cũ 417 EXCEPTION_ERROR / mới lỗi handler chung |
Có cần bọc thêm MappingException/ConversionFailedException |
4. Finding đã xác nhận
Không có finding CRITICAL/MAJOR ở cả hai vòng. MINOR (không qua xác minh, để người xem):
- F-logic-1 (vòng 1) —
PharmaHistoryMongoAdapterchỉ bọcDataAccessException; lỗi map/convert document đi ra handler chung — chi tiếtverify/round-1/review-logic.md.
5. Finding bị loại
| Finding | Lý do loại |
|---|---|
| — | — |
6. Xử lý của người
| Vấn đề | Cổng | Chỉ thị |
|---|---|---|
| Kiểm Sonar (SonarQube for IDE, Analyze VCS Changed Files trong ws1) | G4 |
7. So với vòng trước
| Vấn đề | Vòng trước | Vòng này |
|---|---|---|
| TC-45 (G2) | MỚI | ĐÃ HẾT |
| F-logic-1 (MINOR) | MỚI | CÒN (không sửa trong vòng lặp) |
8. Thay đổi trong repo đích
49 file (verify/round-2/changed-files.md): 33 file src/main/java (mới: controller/web mapper/DTO, RsInoutwardTransactionQueryService, RsInoutwardPharmaEnricher, command/model/result/repository/port mới, adapter relational/Mongo/Redis; sửa: RsInoutwardErrorCode, BusinessTypePort, CustomerPort + 2 adapter), 2 file i18n, 13 file test + fixture. Không có file ngoài src/main, src/test; không sửa pom.xml.
9. Đề xuất bổ sung kinh nghiệm
| Đề xuất | Đưa vào đâu |
|---|---|
Repo đích dùng auto-config Mongo/Redis: không khai thêm bean MongoTemplate/MongoOperations/RedisTemplate (tắt bean mặc định hoặc gây mơ hồ inject) — dựng template trong adapter |
lessons.md (1b) |
Monolith có hibernate.jdbc.time_zone: Asia/Ho_Chi_Minh (easyposbackend1:…/application.yml:139-140) và getMessageI18n cố định vi (BaseCommon.java:1926-1929); starter dịch theo Accept-Language |
legacy-facts.md |
| Kiểm số học expected khi test-plan có phép ép kiểu tràn (TC-36 ghi 3 thay vì 707) | lessons.md (1c) |
@ReadQuery cấp service (aspect @Order(1) bọc ngoài transaction) → đọc replica, ngược với K-24 |
lessons.md (bổ sung K-24) |