Báo cáo chuyển đổi warehouse-service

rsinoutward · rsinoutward/html-preview · api

GET/api/page/rs-inoutward/html-preview/{id}

Vòng
1
Cổng G1–G6
PENDING_SONAR
Bước
sonar-check
Nhánh đích
feat/thongpd/migrate-get-rs

Phân tích luồng sau chuyển — rsinoutward/html-preview — vòng 1

logic-reviewer viết dựa trên code đã implement (không phải thiết kế trong spec), đối chiếu với code cũ. Viết tắt: S = easyposbackend1:src/main/java/vn/softdreams/easypos/service/impl/RsInoutWardManagementServiceImpl.java; đường dẫn mới tính từ src/main/java/com/softdreams/easypos/warehouse/.

F-01 Kiểm, đọc phiếu, mẫu, separator, trả kết quả

  • Điểm vào: cũ easyposbackend1:src/main/java/vn/softdreams/easypos/web/rest/RsInoutWardManagementResource.java:144-153 → mới presentation/web/rsinoutward/RsInoutwardQueryController.java:115-130
  • Quy tắc: R-01, R-02, R-03, R-04, R-05, R-06, R-13, R-14

Sơ đồ luồng mới (theo code thực tế)

sequenceDiagram
    participant C as Client
    participant Ctl as RsInoutwardQueryController.getHtmlPreview
    participant S as RsInoutwardHtmlPreviewQueryService.getHtmlPreview (readOnly tx)
    participant U as CurrentUserPort
    participant D as RsInoutwardDetailRepository
    participant P as PrintTemplateRelationalAdapter
    participant K as CompanyConfigRelationalAdapter
    participant UP as UserProfileRelationalAdapter
    C->>Ctl: GET /api/page/rs-inoutward/html-preview/{id}
    Ctl->>S: getHtmlPreview(id) (try: RuntimeException → SystemException EXCEPTION_ERROR)
    S->>U: findCompanyId() (rỗng → COMPANY_NOT_EXISTS_CODE)
    S->>D: findHeader(id, comId) (rỗng → RS_INOUT_WARD_ID_NOT_FOUND)
    S->>D: findLines(id, comId)
    S->>P: findSelected(comId, 9) — SELECT TOP 1 content, page_size (rỗng → PRINT_TEMPLATE_NULL)
    S->>K: findSeparatorRawValue(comId) (≥2 dòng / value NULL → EXCEPTION_ERROR)
    S->>S: PrintSeparatorParser.parse | PrintSeparator.defaults()
    S->>U: findUserId()
    S->>UP: findFullName(userId) (không có → "")
    S->>S: RsInoutwardPrintDataBuilder.build (F-02)
    S->>S: PrintTemplateRenderer.render (F-03)
    S-->>Ctl: RsInoutwardHtmlPreviewResult(reviewContent, pageSize)
    Ctl-->>C: 200 BaseResponseDTO.ok(RsInoutwardPrintTemplateResponse, SUCCESS_LOAD_DATA)

Đối chiếu từng bước

# Luồng cũ (repo:path:dòng) Luồng mới (path:dòng) Kết luận
1 RsInoutWardManagementResource.java:144-146 GET, @Timed, không kiểm quyền RsInoutwardQueryController.java:115-117 KHỚP (body lỗi type mismatch theo starter — D-02)
2 RsInoutWardManagementResource.java:147-152 bọc mọi Exception → 417 EXCEPTION_ERROR RsInoutwardQueryController.java:119-123 bọc RuntimeException → 500 EXCEPTION_ERROR KHÁC có chủ đích (D-03, D-04)
3 S:8131 getUserWithAuthorities() (user status=1, comId, fullName) RsInoutwardHtmlPreviewQueryService.java:71-72 chỉ kiểm comId KHÁC có chủ đích (D-01)
4 S:8133-8136 getOneById null → lỗi RsInoutwardHtmlPreviewQueryService.java:73-74 KHỚP
5 S:8137-8140 getDetailByRsInoutWardId, rỗng → detail null RsInoutwardHtmlPreviewQueryService.java:75 (list rỗng → không dòng) KHỚP
6 S:8142-8146; PrintTemplateRepository.java:43-44 select top 1, không ORDER BY PrintTemplateRelationalAdapter.java:25-29, 39-51 KHỚP (D-07)
7 S:8148-8154 config separator, mặc định ./,; JSON hỏng/null/value NULL/≥2 dòng → lỗi CompanyConfigRelationalAdapter.java:66-95; PrintSeparatorParser.java:30-41 KHỚP
8 fullName lấy từ user nạp ở bước 3 RsInoutwardHtmlPreviewQueryService.java:81 đọc ep_user.full_name theo id KHÁC có chủ đích (D-05)
9 S:8156-8162 buildPrintData, updateContentForRs RsInoutwardHtmlPreviewQueryService.java:82-84 KHỚP (chi tiết F-02, F-03)
10 S:8164-8168 {reviewContent, pageSize}, SUCCESS_LOAD_DATA RsInoutwardHtmlPreviewQueryService.java:85; RsInoutwardQueryController.java:124-129; RsInoutwardPrintTemplateResponse @JsonInclude(ALWAYS) KHỚP (text theo starter — D-02, D-11)
11 S:8127-8129 kiểm gói, @ReadQuery không kiểm gói; @Transactional(readOnly = true) KHÁC có chủ đích (D-09, D-10)

Dữ liệu và side effect

Bảng / topic / cache Cũ (đọc/ghi, điều kiện) Mới (đọc/ghi, điều kiện) Ghi chú
rs_inoutward + join, rs_inoutward_detail + join đọc, id + comId đọc qua RsInoutwardDetailRepository (unit by-id), id + comId
print_template đọc select top 1 *, com_id, type_template=9, is_selected=1 đọc SELECT TOP 1 content, page_size, cùng điều kiện chỉ lấy 2 cột dùng tới
config đọc entity company_id, code='separator' đọc native value, cùng điều kiện
ep_user / cache Redis user đọc theo username, status=1 (qua cache) đọc full_name theo id (không cache, không lọc status) D-05
Ghi không không chỉ đọc

Transaction, khoá, idempotency, tenant

  • Ranh giới transaction: cũ @Transactional(readOnly = true) + @ReadQuery ở S:8127-8130 → mới @Transactional(readOnly = true) ở RsInoutwardHtmlPreviewQueryService.java:69; controller bọc lỗi ngoài proxy nên lỗi mở/commit cũng ra EXCEPTION_ERROR.
  • Khoá / cập nhật nguyên tử: không có (chỉ đọc).
  • Idempotency, thời điểm phát event: không có event.
  • Lọc comId: header, dòng, print_template, config đều lọc theo comId trong token; ep_user lọc theo id user trong token (cũ theo username — không theo công ty ở cả hai).

Lệch so với luồng mới đã duyệt (spec mục 6)

không — thêm bước UserProfilePort/PrintSeparatorParser nằm trong "comId, phiếu + dòng, mẫu, separator, tên NV" của sơ đồ spec.

Dev cần chú ý khi review

Vị trí Vì sao
RsInoutwardQueryController.java:119-123 Bọc mọi lỗi thành EXCEPTION_ERROR — mã riêng (RS_INOUT_WARD_ID_NOT_FOUND, PRINT_TEMPLATE_NULL) cố ý không ra client (D-04).
PrintTemplateRelationalAdapter.java:52-60 content đọc bằng native query rồi toString(): nếu cột nvarchar(max)/ntext được Hibernate trả về dạng Clob thì toString() sai — chỉ test bằng mock, UNVERIFIED trên MSSQL thật (K-13).
CompanyConfigRelationalAdapter.java:58-95 Tách findRawValues dùng chung với findOverStockRawValue (file của unit trước) — hành vi over_stock giữ nguyên; chạy lại test của unit đó.
UserProfileRelationalAdapter.java:21-23 Truy vấn ep_user theo id, không lọc status — khác cũ có chủ đích (D-05).

F-02 Dựng dữ liệu in

  • Điểm vào: cũ S:7775 buildPrintData → mới application/rsinoutward/RsInoutwardPrintDataBuilder.java:117 build
  • Quy tắc: R-07, R-08

Sơ đồ luồng mới (theo code thực tế)

sequenceDiagram
    participant S as RsInoutwardHtmlPreviewQueryService
    participant B as RsInoutwardPrintDataBuilder
    participant L as LegacyAmount
    participant W as VietnameseNumberWords
    S->>B: new(employeeName, printTime=now(clock, Asia/Ho_Chi_Minh)).build(header, rows)
    B->>B: 50 key = null; gán đầu phiếu (orElse như Optional.ofNullable)
    B->>B: putParty (type 1 NCC, type 2 KH ?: NCC, khác "")
    B->>L: scaleOrZero(amount / discountAmount / quantity)
    B->>B: putLines: sort ổn định theo position (>1 dòng), bỏ dòng rỗng
    B->>L: scaleOrZero, vatAmount cho từng dòng
    B->>W: toWords(total) (IOOBE → "")
    B-->>S: Map tên thuộc tính → giá trị

Đối chiếu từng bước

# Luồng cũ (repo:path:dòng) Luồng mới (path:dòng) Kết luận
1 S:7778-7790 companyName/address/documentType/billCode/description/employeeName/paymentMethod/area RsInoutwardPrintDataBuilder.java:119-130 KHỚP (type 3 → "PHIẾU XUẤT KHO" — D-06)
2 S:7792-7793 createdDate = now Asia/Ho_Chi_Minh RsInoutwardHtmlPreviewQueryService.java:82; RsInoutwardPrintDataBuilder.java:131 KHỚP
3 S:7795-7799 phone/email/bank công ty, paymentDate = rs.getDate() (ZonedDateTime múi JVM) RsInoutwardPrintDataBuilder.java:132-136 (LocalDateTime.atZone(Asia/Ho_Chi_Minh)) KHÁC có chủ đích (D-10) — giờ tường như nhau khi JVM cũ GMT+7
4 S:7801-7855 thông tin đối tác theo type, rồi ?: mặc định ("Chưa có địa chỉ", "Không có") RsInoutwardPrintDataBuilder.java:137, 146-159 (PARTY_FIELDS) KHỚP
5 S:7857-7860 amount/discountAmount/totalQuantity qua getter (0.0 / setScale(6) có thể ArithmeticException), taxCode RsInoutwardPrintDataBuilder.java:138-141; LegacyAmount.scaleOrZero KHỚP
6 S:7865-7867 sort theo position khi > 1 dòng RsInoutwardPrintDataBuilder.java:163-166 KHỚP
7 S:7872-7878 bỏ dòng tên+mã rỗng, SL=0, giá=0 RsInoutwardPrintDataBuilder.java:183-188 KHỚP
8 S:7880-7896 quantity/unitPrice max 0 scale 6 HALF_UP; lineAmount; discount âm → 0; VAT; lineTotal RsInoutwardPrintDataBuilder.java:190-201 KHỚP
9 S:7898-7910 dòng in 9 thuộc tính, stt chỉ đếm dòng giữ lại, cộng tổng RsInoutwardPrintDataBuilder.java:170-174, 203-213 KHỚP
10 S:7914-7923 billProducts, totalAmount = totalPreTax = Σ, numberToWords (Throwable → "") RsInoutwardPrintDataBuilder.java:176-179, 222-228; VietnameseNumberWords.java:36-56 KHỚP

Dữ liệu và side effect

Bảng / topic / cache Cũ (đọc/ghi, điều kiện) Mới (đọc/ghi, điều kiện) Ghi chú
(không) tính trong bộ nhớ tính trong bộ nhớ

Transaction, khoá, idempotency, tenant

  • Ranh giới transaction: trong transaction readOnly của F-01 ở cả hai.
  • Khoá / cập nhật nguyên tử: không.
  • Idempotency, thời điểm phát event: không.
  • Lọc comId: không truy vấn.

Lệch so với luồng mới đã duyệt (spec mục 6)

không

Dev cần chú ý khi review

Vị trí Vì sao
RsInoutwardPrintDataBuilder.java:190-201 Thứ tự scale/âm → 0/VAT/lineTotal quyết định tiền in; BigDecimal.ZERO (scale 0) cho số âm giữ đúng như cũ.
RsInoutwardPrintDataBuilder.java:136 Ngày phiếu đổi từ LocalDateTime sang ZonedDateTime Asia/Ho_Chi_Minh — phụ thuộc D-10 (múi JVM cũ).
RsInoutwardPrintDataBuilder.java:222-228 Chỉ bắt IndexOutOfBoundsException (cũ Throwable) — đủ cho mọi lỗi của thuật toán hiện tại; sửa VietnameseNumberWords sau này phải giữ điều này.

F-03 Thay biến mẫu

  • Điểm vào: cũ S:7961 updateContentForRs → mới domain/rsinoutward/model/PrintTemplateRenderer.java:45 render
  • Quy tắc: R-09, R-10, R-11, R-12

Sơ đồ luồng mới (theo code thực tế)

sequenceDiagram
    participant S as RsInoutwardHtmlPreviewQueryService
    participant R as PrintTemplateRenderer
    participant V as PrintTemplateVariables
    participant T as PrintTableRenderer
    participant L as PrintTableLookup
    participant F as LegacyNumberFormat
    S->>R: render(content, data, separator)
    R->>V: defaults() (121 mục, parse 1 lần)
    loop mục không isTable
        R->>F: formatNumber (BigDecimal) / formatDate / String.valueOf
        R->>R: replaceOutsideRow = replaceAll("(?s)(?!<tr.*?)"+quote(var), value)
    end
    R->>T: render(html, data)
    loop billProducts, toppings, surcharges
        T->>L: findRowTemplate(probe, html), tableList(data, table)
        alt không phải List
            T->>T: xoá dòng mẫu nếu có
        else không có dòng mẫu
            T->>T: joinWithoutRowTemplate (", ")
        else danh sách rỗng
            T->>T: xoá dòng mẫu
        else
            T->>T: expandRows (stt, quantity strip, #,##0)
        end
    end
    R-->>S: reviewContent

Đối chiếu từng bước

# Luồng cũ (repo:path:dòng) Luồng mới (path:dòng) Kết luận
1 S:8160 parse CONTENT_PARAM_DEFAULT (Constants.java:55-62, dòng 61 là comment) PrintTemplateVariables.java:26-202 KHỚP (121 mục, cùng thứ tự và cờ)
2 S:7969 html/configs/data null → trả html PrintTemplateRenderer.java:46-48 KHỚP
3 S:7971-8011 giá trị biến thường: null → "", BigDecimal → currencyFormatterNumber, ngày in/thanh toán theo biến, còn lại String.valueOf PrintTemplateRenderer.java:51-56, 69-94; LegacyNumberFormat.java:26-32 KHỚP
4 S:8013 replaceAll không quote chuỗi thay thế PrintTemplateRenderer.java:64-66 KHỚP (giữ lỗi $/\ — D-08)
5 S:8016-8024 tên bảng theo thứ tự gặp, rỗng → "rows" PrintTableRenderer.java:36-47 KHỚP
6 S:8027-8035 biến dò đầu tiên khác rỗng PrintTableRenderer.java:68-79 KHỚP
7 S:8037; S:7947-7959 getContent dòng mẫu PrintTableLookup.java:21-35 KHỚP
8 S:8039-8049 danh sách + fallback billProducts↔rows; không phải List → xoá dòng mẫu PrintTableLookup.java:44-53; PrintTableRenderer.java:54-57 KHỚP
9 S:8053-8096 nhân dòng: no → stt, quantity strip, BigDecimal #,##0, null → "" ; replaceAll(quote(var), val); rỗng → xoá PrintTableRenderer.java:58-64, 81-111 KHỚP
10 S:8097-8120 không có dòng mẫu: nối ", ", bỏ null, replaceAll như biến thường PrintTableRenderer.java:113-141 KHỚP

Dữ liệu và side effect

Bảng / topic / cache Cũ (đọc/ghi, điều kiện) Mới (đọc/ghi, điều kiện) Ghi chú
(không) xử lý chuỗi trong bộ nhớ xử lý chuỗi trong bộ nhớ dữ liệu in là Map thay reflection

Transaction, khoá, idempotency, tenant

  • Ranh giới transaction: trong transaction readOnly của F-01 ở cả hai.
  • Khoá / cập nhật nguyên tử: không.
  • Idempotency, thời điểm phát event: không.
  • Lọc comId: không truy vấn.

Lệch so với luồng mới đã duyệt (spec mục 6)

không

Dev cần chú ý khi review

Vị trí Vì sao
PrintTemplateRenderer.java:64-66, PrintTableRenderer.java:89 Cố ý không dùng Matcher.quoteReplacement (D-08): giá trị chứa $n làm lỗi cả bản in — đừng "sửa" khi refactor.
LegacyNumberFormat.java:27 Locale.ROOT thay locale mặc định JVM của cũ; chỉ khác nếu JVM cũ chạy locale có dấu trừ/chữ số không phải ASCII — xác nhận bằng golden master.
PrintTemplateVariables.java:26-202 Chép tay 121 mục từ Constants.java:55-62; mọi sửa đổi phải giữ thứ tự (quyết định thứ tự bảng và biến dò).
PrintTableLookup.java:44-53 Map thay reflection: tên thuộc tính không có key trả null — đúng khi map dựng đủ 50/9 key như RsInoutwardPrintFields.

Nguồn của trang này

  • Phân tích luồng migrations/rsinoutward/html-preview/verify/round-1/flow-analysis.md vòng 1 · sha256 fb6d9f55a3da

v1 dựng 2026-11-09 14:46 · base commit đích 661efe9b8c