Skip to content

Release/1.2 - #35

Merged
ThienlocTran merged 43 commits into
mainfrom
release/1.2
Jul 2, 2026
Merged

ThienlocTran merged 43 commits into
mainfrom
release/1.2

Conversation

@ThienlocTran

@ThienlocTran ThienlocTran commented Jul 2, 2026 •

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features
    • Bổ sung màn hình phiếu nhập kho: tạo/sửa, gửi duyệt/hủy, kiểm hàng và hoàn tất; kèm lịch sử duyệt và luồng nhắc trạng thái.
    • Ra mắt màn hình tồn kho, lịch sử giao dịch kho (lọc/tìm kiếm/phân trang) và màn hình duyệt phiếu (phê duyệt/ từ chối).
    • Cải tiến giao diện tìm kiếm/bộ lọc, bảng dữ liệu và hiển thị nhãn trạng thái phong phú hơn.
  • Bug Fixes / Improvements
    • Nâng cấp kiểm soát quyền và điều hướng theo vai trò; đồng bộ chế độ chỉ xem.
    • Hỗ trợ trạng thái “đang xử lý” cho hộp thoại xác nhận và tối ưu trải nghiệm loading/empty/error.

ThienlocTran and others added 30 commits June 18, 2026 22:24
…receipt-list-ui

feat(inbound): add employee import receipt list UI
…orm-ui

feat(inbound): add import receipt form UI
…eference-dropdowns

feat(inbound): improve import receipt reference dropdown resilience
…etail-table-total

feat(inbound): show import receipt item totals and summary
…orm-actions

feat(inbound): integrate import receipt actions
…ceipt-rejection-reason

feat(inbound): show rejected import receipt reason
…ect modal)

- T95: pending-approval list view with status filter and pagination

- T96: approval detail modal (info + product table)

- T97: approve integration (two-level) with refresh

- T98: reject modal with required-reason validation

- add approval service functions in importReceiptService.js
…on-ui

T105: Create import receiving and inspection UI
…y-transaction-UI

Feature/t72 history inventory transaction UI
…eipt-approval-ui to resolve conflict in importReceiptService.js
…pproval-ui

feat(T95-T98): import receipt approval UI (list, detail, approve, rej…
@coderabbitai

coderabbitai Bot commented Jul 2, 2026 •

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 63b7a7da-fdbb-4736-b39f-da7594fe19f3

📥 Commits

Reviewing files that changed from the base of the PR and between aa2957a and a74a683.

📒 Files selected for processing (13)
  • src/components/AppSidebar.vue
  • src/components/AppTopbar.vue
  • src/components/ImportInspectionPanel.vue
  • src/components/SearchFilterBar.vue
  • src/router/index.js
  • src/services/authService.js
  • src/services/permissionService.js
  • src/stores/auth.js
  • src/views/StockDocumentCreateView.vue
  • src/views/StockDocumentDetailView.vue
  • src/views/StockDocumentsView.vue
  • src/views/WarehousesView.vue
  • src/views/employees/EmployeeListView.vue

📝 Walkthrough

Walkthrough

PR này thêm luồng phiếu nhập kho end-to-end, màn hình tra cứu tồn kho/giao dịch kho, cập nhật phân quyền theo role, và mở rộng một số component/style dùng chung để hỗ trợ các màn hình mới.

Changes

Phân quyền và điều hướng

Layer / File(s) Summary
Chuẩn hoá role và state auth
src/services/authService.js, src/stores/auth.js
Chuẩn hoá role từ nhiều nguồn user/role và đổi store auth sang giữ currentUser làm nguồn chính.
Permission service và router
src/services/permissionService.js, src/router/index.js
Thêm các hàm kiểm tra quyền theo nhóm tác vụ và đổi router guard sang trả về hướng điều hướng trực tiếp.
Sidebar, topbar và view quản trị
src/components/AppSidebar.vue, src/components/AppTopbar.vue, src/views/...
Sidebar/topbar và các view quản trị chuyển sang dùng quyền mới để ẩn/hiện UI, chặn thao tác và hiển thị dialog xác nhận.

Luồng phiếu nhập kho

Layer / File(s) Summary
Import receipt service
src/services/importReceiptService.js
Thêm API cho phiếu nhập, duyệt, kiểm hàng, lịch sử và chuẩn hoá dữ liệu, lỗi.
Tạo và sửa phiếu nhập
src/views/StockDocumentCreateView.vue, src/assets/styles/import-receipt-form.css
Màn hình tạo/sửa phiếu nhập có tải dữ liệu, validate, lưu nháp, gửi duyệt và huỷ phiếu.
Danh sách và chi tiết phiếu nhập
src/views/StockDocumentsView.vue, src/views/StockDocumentDetailView.vue, src/components/ImportReceiptHistoryModal.vue
Danh sách phiếu nhập, modal lịch sử duyệt, và màn hình chi tiết nhập kho được nối vào luồng API mới.
Màn hình duyệt phiếu nhập
src/views/ApprovalsView.vue
Danh sách chờ duyệt, modal chi tiết, từ chối, xác nhận và lịch sử.
Kiểm hàng và chênh lệch
src/components/ImportInspectionPanel.vue
Xác nhận hàng về, kiểm thực tế, biên bản chênh lệch và hoàn tất nhập kho.

Tồn kho, giao dịch kho và UI dùng chung

Layer / File(s) Summary
Tồn kho và giao dịch kho
src/services/inventoryService.js, src/views/InventoryView.vue, src/views/InventoryTransactionsView.vue, src/components/SearchFilterBar.vue
Thêm service tồn kho và hai view tra cứu tồn kho/giao dịch với lọc, tìm kiếm và phân trang.
Thành phần dùng chung
src/components/ConfirmDialog.vue, src/components/DataTable.vue, src/components/StatusBadge.vue, src/components/EmptyState.vue
ConfirmDialog, DataTable, SearchFilterBar, StatusBadge và EmptyState được mở rộng để phục vụ các màn hình mới.
Style và file cấu hình
src/style.css, src/assets/styles/import-receipt-form.css, src/constants/warehouseOptions.js, .gitignore
Style toàn cục, stylesheet phiếu nhập, và các file cấu hình nhỏ được cập nhật.

Estimated code review effort: 4 (Complex) | ~75 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive Tiêu đề quá chung chung và không mô tả nội dung thay đổi chính của PR. Đổi sang tiêu đề ngắn, cụ thể hơn như "Thêm luồng phiếu nhập và tồn kho" hoặc tương tự để nêu rõ mục tiêu chính.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch release/1.2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ThienlocTran

ThienlocTran commented Jul 2, 2026 •

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 2, 2026 •

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 22

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/ImportInspectionPanel.vue`:
- Around line 34-38: The permission check in ImportInspectionPanel.vue is
hard-coded in canProcessReceipt using authStore.currentRole, which can drift
from the shared authorization rules. Move this logic out of the component and
use the centralized permission/service state already used by the
router/sidebar/permission layer, then have the component consume that shared
source for UX-only gating while keeping backend authorization unchanged.
- Around line 209-219: Bắt buộc kiểm tra dữ liệu giải trình trước khi lưu biên
bản trong handleSaveDiscrepancyReport: nếu reason/action còn rỗng thì chặn lưu
và hiển thị lỗi thay vì chỉ dựa vào hasDiscrepancy. Hãy bổ sung validation ngay
trước khi gọi createDiscrepancyReport/buildDiscrepancyPayload để đảm bảo payload
luôn có lý do và hướng xử lý hợp lệ, đồng thời giữ trạng thái “Hoàn Tất Nhập
Kho” bị khóa cho đến khi biên bản được lưu với dữ liệu đầy đủ.
- Around line 94-127: The loadData request guard only protects the success path,
so stale requests can still overwrite error or loading state in the
catch/finally blocks. Update loadData in ImportInspectionPanel.vue to check the
currentRequestId again inside catch and finally before setting error.value or
loading.value, and keep all state mutations for receipt/inspectItems/discrepancy
fields tied to the latest request only.

In `@src/components/SearchFilterBar.vue`:
- Around line 2-23: SearchFilterBar is applying fallthrough attrs twice, once to
the root wrapper and again to the input via attrs. Update the SearchFilterBar
component to disable inherited attrs with defineOptions({ inheritAttrs: false })
so useAttrs() only feeds the <input>, keeping id, data-testid, and aria-* on the
correct element.

In `@src/router/index.js`:
- Around line 61-72: Clarify that resolveRouteAccess in src/router/index.js is
only a frontend navigation guard for UX and does not provide real authorization.
Update the route-guard logic or surrounding comment near
resolveRouteAccess/getCurrentRoleCode to explicitly state that backend API
endpoints must still enforce role checks, since users can bypass the router and
call APIs directly.

In `@src/services/importReceiptService.js`:
- Around line 248-254: The getProducts() response handling in
importReceiptService should normalize the API payload before reading content,
since data may be null/empty or returned as a direct array. Update the logic
around importReceiptClient.get and the data.content access so it safely handles
missing bodies and supports both { content: [...] } and array responses,
returning a valid product list instead of an empty dropdown.
- Around line 6-11: Add a timeout to the axios client created in
importReceiptClient so stuck requests do not leave awaiters loading
indefinitely. Update the axios.create configuration in importReceiptService to
include an explicit timeout value alongside API_BASE_URL and the existing
Content-Type header, keeping the change scoped to the importReceiptClient setup.

In `@src/services/inventoryService.js`:
- Around line 6-11: The inventoryClient created in inventoryService should
include a shared timeout so requests fail fast instead of leaving the inventory
screen stuck loading; update the axios.create configuration for inventoryClient
to add a reasonable timeout value alongside baseURL and headers, and ensure the
existing request flow still routes failures through normalizeInventoryError.

In `@src/views/ApprovalsView.vue`:
- Around line 149-171: Prevent duplicate approve requests by adding an in-flight
guard to confirmApprove and wiring a loading state into ConfirmDialog. Use the
existing actionState/actionMessage flow in ApprovalsView.vue so the approve
action is disabled or ignored while approveImportReceipt is running, and make
sure confirmApprove() exits early if an approval is already pending.
- Around line 67-87: `fetchPendingApprovals` đang có nguy cơ để response cũ ghi
đè state mới khi người dùng đổi filter/trang nhanh. Hãy thêm cơ chế chặn request
lỗi thời trong `ApprovalsView.vue`—ví dụ dùng request sequence/id hoặc
`AbortController` gắn với lần gọi hiện tại—và chỉ cập nhật `receipts`,
`totalPages`, `totalElements`, `errorMessage` khi response thuộc request mới
nhất. Giữ logic này trong `fetchPendingApprovals` và các điểm gọi liên quan để
đảm bảo chỉ dữ liệu mới nhất được render.

In `@src/views/InventoryTransactionsView.vue`:
- Around line 62-65: `onMounted` vẫn gọi `fetchTransactions()` ngay cả khi
`loadDropdowns()` đã bị 401 và redirect sang `/login`, nên cần chặn request tiếp
theo sau khi auth fail. Hãy sửa luồng trong `InventoryTransactionsView` để
`loadDropdowns()` trả về trạng thái thành công/thất bại hoặc ném lỗi, rồi chỉ
gọi `fetchTransactions()` khi `loadDropdowns` hoàn tất bình thường; nếu có
redirect/401 thì thoát sớm và không chạy thêm request nào.
- Around line 82-95: Add date-range validation in fetchTransactions before
calling getInventoryTransactions so the view does not send an invalid request
when from is after to. Use the existing filters.from and filters.to handling in
InventoryTransactionsView.vue to detect the invalid range, set
errorMessage.value with a clear validation message, and return early without
toggling the API call. Keep the loading/error state behavior consistent with the
rest of the view and ensure the same validation is applied wherever the filter
form is submitted or refreshed.

In `@src/views/InventoryView.vue`:
- Around line 64-67: `onMounted` trong `InventoryView.vue` vẫn gọi
`fetchInventory()` dù `loadDropdowns()` đã gặp 401 và redirect `/login`, gây
request tiếp theo không còn token. Hãy cập nhật luồng khởi tạo trong `onMounted`
(và nếu cần, `loadDropdowns`/`fetchInventory`) để sau khi `loadDropdowns()` báo
lỗi auth hoặc redirect thì dừng ngay, không chạy `fetchInventory()`. Dùng một
cờ/return sớm dựa trên kết quả của `loadDropdowns()` để chặn request thứ hai và
tránh flash loading/lỗi.
- Line 224: The product status cell in InventoryView’s productStatus slot
currently treats every non-HOAT_DONG value as “Ngừng hoạt động”, which masks
null or new backend statuses. Update the StatusBadge mapping logic to handle
only known status values explicitly and render a neutral/fallback label for
unknown or missing values, so unrecognized backend data is not misrepresented.
Use the productStatus template and StatusBadge binding as the location to adjust
the enum-to-label mapping.

In `@src/views/ProductsView.vue`:
- Around line 348-352: The EmptyState in ProductsView.vue currently shows a
description that suggests adding a new product even when canManage is false,
which conflicts with the read-only UI state. Update the description used by the
EmptyState rendering so it varies by permission, keeping the “thêm sản phẩm mới”
suggestion only for users who can manage and showing a permission-appropriate
message otherwise. Use the existing canManage condition in the ProductsView
component to locate and adjust the EmptyState props.

In `@src/views/StockDocumentCreateView.vue`:
- Around line 85-87: The save/send/cancel button guards in
StockDocumentCreateView need to include role-based checks, not just receipt
status. Update the computed flags canSubmit, canCancel, and canSave to also
verify the current user role matches the allowed roles for this screen, ideally
by using the existing permissionService helper instead of hard-coding role logic
in the component. Keep the UI guard in this view aligned with the
ADMIN/MANAGER/EMPLOYEE rules, while leaving backend authorization unchanged.
- Around line 356-359: Timer điều hướng trong StockDocumentCreateView chưa được
dọn khi rời trang, nên callback setTimeout vẫn có thể chạy và gọi
router.push('/stock-in') sau khi component đã unmount. Hãy lưu lại timeout id ở
các đoạn dùng setTimeout liên quan đến isCreateMode trong
StockDocumentCreateView.vue, và clearTimeout nó trong cleanup của component
(onUnmounted hoặc beforeRouteLeave nếu phù hợp); đồng thời áp dụng cùng cách dọn
cho các timer/listener/watcher tương tự ở các đoạn được chỉ ra.
- Around line 82-84: The hasValidItems computed in StockDocumentCreateView
currently allows an empty string or NaN unitPrice to pass validation, so tighten
the item checks to reject blank/invalid prices before submit. Update the
validation logic around items.value.every to explicitly verify item.unitPrice is
a non-empty, finite number (not ''/NaN) alongside productId and quantity, and
ensure the same form validation rule is applied wherever the payload is prepared
or submitted in this view.

In `@src/views/StockDocumentDetailView.vue`:
- Around line 19-20: The ImportInspectionPanel component is being mounted even
when the id prop is empty (default value is ''), causing unnecessary API calls
with invalid parameters. Add a guard condition to prevent mounting the
ImportInspectionPanel until id has a valid value. Modify the template condition
to check that both type === 'in' AND id is not empty before rendering the
ImportInspectionPanel component. This ensures the panel only mounts when the
receiptId prop has meaningful data.

In `@src/views/StockDocumentsView.vue`:
- Around line 244-260: The permission helpers in StockDocumentsView
(canEditImportReceipt, canSubmitImportReceipt, canCancelImportReceipt) currently
allow any non-MANAGER role when the status matches, so tighten them to an
explicit allowlist for valid roles only (for example ADMIN and EMPLOYEE). Update
the role checks in these functions so empty or invalid currentRole.value does
not return true, while keeping the existing status-based behavior for allowed
roles; leave submitLabel unchanged.
- Around line 84-107: Prevent stale receipt responses from overwriting newer
filter/page results in fetchReceipts. Add request-order protection in the
StockDocumentsView.vue async flow so only the latest invocation can update
receipts, totalPages, and totalElements after listReceipts resolves. Use a
unique guard tied to fetchReceipts (such as an incrementing request token or
cancellation check) and ignore any older response before mutating state; keep
the current loading/error handling and router.replace logic intact.

In `@src/views/WarehousesView.vue`:
- Around line 325-329: The EmptyState in WarehousesView should vary its
description based on the user’s canManage permission, since the current “thêm
kho mới” hint is shown even in read-only mode. Update the EmptyState block under
the v-else-if branch to choose a different description when canManage is false,
and keep the existing add-new suggestion only for users who can manage
warehouses.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8552809a-2a5c-4671-bd63-1b80e4633dff

📥 Commits

Reviewing files that changed from the base of the PR and between 5cec86b and aa2957a.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (25)
  • .gitignore
  • src/assets/styles/import-receipt-form.css
  • src/components/AppSidebar.vue
  • src/components/ConfirmDialog.vue
  • src/components/DataTable.vue
  • src/components/EmptyState.vue
  • src/components/ImportInspectionPanel.vue
  • src/components/ImportReceiptHistoryModal.vue
  • src/components/SearchFilterBar.vue
  • src/components/StatusBadge.vue
  • src/constants/warehouseOptions.js
  • src/router/index.js
  • src/services/importReceiptService.js
  • src/services/inventoryService.js
  • src/services/permissionService.js
  • src/style.css
  • src/views/ApprovalsView.vue
  • src/views/InventoryTransactionsView.vue
  • src/views/InventoryView.vue
  • src/views/ProductsView.vue
  • src/views/StockDocumentCreateView.vue
  • src/views/StockDocumentDetailView.vue
  • src/views/StockDocumentsView.vue
  • src/views/WarehousesView.vue
  • src/views/categories/CategoryListView.vue

Comment thread src/components/ImportInspectionPanel.vue
Comment thread src/components/ImportInspectionPanel.vue
Comment thread src/components/ImportInspectionPanel.vue
Comment thread src/components/SearchFilterBar.vue
Comment thread src/router/index.js
Comment thread src/views/StockDocumentCreateView.vue Outdated
Comment thread src/views/StockDocumentDetailView.vue Outdated
Comment thread src/views/StockDocumentsView.vue
Comment thread src/views/StockDocumentsView.vue
Comment thread src/views/WarehousesView.vue
@ThienlocTran
ThienlocTran merged commit c99a015 into main Jul 2, 2026
1 check was pending
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants