From 11e1cd28923e0082e38b5d7008d1275ebd0e3f17 Mon Sep 17 00:00:00 2001 From: Tonfa137206 Date: Sun, 28 Jun 2026 15:26:01 +0700 Subject: [PATCH] feat(T95-T98): import receipt approval UI (list, detail, approve, reject 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 --- src/services/importReceiptService.js | 53 ++++ src/views/ApprovalsView.vue | 413 ++++++++++++++++++++++++++- 2 files changed, 463 insertions(+), 3 deletions(-) diff --git a/src/services/importReceiptService.js b/src/services/importReceiptService.js index e9301fe..08fbf3c 100644 --- a/src/services/importReceiptService.js +++ b/src/services/importReceiptService.js @@ -92,6 +92,59 @@ export async function getDetail(receiptId) { } } +// ===== Luồng duyệt phiếu nhập (T91-T94) ===== + +export async function getPendingApprovals({ page = 0, size = 10, status = '' } = {}) { + try { + const { data } = await importReceiptClient.get('/api/import-receipts/pending-approval', { + headers: getAuthorizationHeader(), + params: { + page, + size, + status: status || undefined, + }, + }) + return data + } catch (error) { + throw normalizeImportReceiptError(error, 'Không thể tải danh sách phiếu chờ duyệt.') + } +} + +export async function getApprovalDetail(receiptId) { + try { + const { data } = await importReceiptClient.get(`/api/import-receipts/${receiptId}/approval-detail`, { + headers: getAuthorizationHeader(), + }) + return data + } catch (error) { + throw normalizeImportReceiptError(error, 'Không thể tải chi tiết phiếu chờ duyệt.') + } +} + +export async function approveImportReceipt(receiptId) { + try { + const { data } = await importReceiptClient.put(`/api/import-receipts/${receiptId}/approve`, null, { + headers: getAuthorizationHeader(), + }) + return data + } catch (error) { + throw normalizeImportReceiptError(error, 'Không thể duyệt phiếu nhập.') + } +} + +export async function rejectImportReceipt(receiptId, reason) { + try { + const { data } = await importReceiptClient.put( + `/api/import-receipts/${receiptId}/reject`, + { reason }, + { headers: getAuthorizationHeader() }, + ) + return data + } catch (error) { + throw normalizeImportReceiptError(error, 'Không thể từ chối phiếu nhập.') + } +} + export async function getWarehouses() { try { const { data } = await importReceiptClient.get('/api/warehouses', { diff --git a/src/views/ApprovalsView.vue b/src/views/ApprovalsView.vue index df8f23f..00026ec 100644 --- a/src/views/ApprovalsView.vue +++ b/src/views/ApprovalsView.vue @@ -1,9 +1,416 @@ + +