Spaces:
Sleeping
Sleeping
| import apiClient from '@/lib/api/axios-instance'; | |
| import { AxiosError } from 'axios'; | |
| import { | |
| AssetRequest, | |
| AssetRequestCreateRequest, | |
| AssetRequestApprovalRequest, | |
| AssetRequestRejectionRequest, | |
| AssetRequestAssignmentRequest, | |
| AssetRequestCommentRequest, | |
| AssetRequestWithAvailability, | |
| AvailabilityCheck, | |
| RequestFilters, | |
| RequestedAssetItem, | |
| validateAssetRequestApiResponse, | |
| isValidStatusTransition | |
| } from '@/types/asset-request'; | |
| import { assetAssignmentApi } from './assetAssignmentApi'; | |
| import { assetApi } from './assetApi'; | |
| // Helper to create a safe asset request object with default values for null/undefined fields | |
| const createSafeAssetRequest = (data: any): AssetRequest => { | |
| return { | |
| requestID: data.requestID || 0, | |
| employeeID: data.employeeID || 0, | |
| employeeName: data.employeeName || '', | |
| requestedAssets: data.requestedAssets || [], | |
| justification: data.justification || '', | |
| status: data.status || 'pending', | |
| submittedDate: data.submittedDate || new Date().toISOString(), | |
| reviewedDate: data.reviewedDate || null, | |
| reviewedBy: data.reviewedBy || null, | |
| reviewerName: data.reviewerName || null, | |
| adminComments: data.adminComments || null, | |
| assignedDate: data.assignedDate || null, | |
| assignedBy: data.assignedBy || null, | |
| assignerName: data.assignerName || null, | |
| acknowledgedDate: data.acknowledgedDate || null, | |
| acknowledgedBy: data.acknowledgedBy || null, | |
| acknowledgementComments: data.acknowledgementComments || null, | |
| }; | |
| }; | |
| // Helper to handle API errors with user-friendly messages | |
| const handleApiError = (error: unknown, operation: string): never => { | |
| console.error(`AssetRequestApi: Error during ${operation}:`, error); | |
| if (error instanceof AxiosError) { | |
| const status = error.response?.status; | |
| const message = error.response?.data?.message || error.message; | |
| switch (status) { | |
| case 400: | |
| throw new Error(`Invalid request: ${message}`); | |
| case 401: | |
| throw new Error('You are not authorized to perform this action. Please log in again.'); | |
| case 403: | |
| throw new Error('You do not have permission to perform this action.'); | |
| case 404: | |
| throw new Error('The requested asset request was not found.'); | |
| case 409: | |
| throw new Error('This request conflicts with existing data or cannot be processed in its current state.'); | |
| case 422: | |
| throw new Error(`Validation failed: ${message}`); | |
| case 500: | |
| throw new Error('Server error occurred. Please try again later.'); | |
| case 503: | |
| throw new Error('Service is temporarily unavailable. Please try again later.'); | |
| default: | |
| throw new Error(`${operation} failed: ${message}`); | |
| } | |
| } | |
| if (error instanceof Error) { | |
| throw new Error(`${operation} failed: ${error.message}`); | |
| } | |
| throw new Error(`${operation} failed: Unknown error occurred`); | |
| }; | |
| // Helper for retry logic | |
| const retryOperation = async <T>( | |
| operation: () => Promise<T>, | |
| maxRetries: number = 3, | |
| delay: number = 1000 | |
| ): Promise<T> => { | |
| let lastError: unknown; | |
| for (let attempt = 1; attempt <= maxRetries; attempt++) { | |
| try { | |
| return await operation(); | |
| } catch (error) { | |
| lastError = error; | |
| // Don't retry on client errors (4xx) except for 408 (timeout) and 429 (rate limit) | |
| if (error instanceof AxiosError) { | |
| const status = error.response?.status; | |
| if (status && status >= 400 && status < 500 && status !== 408 && status !== 429) { | |
| throw error; | |
| } | |
| } | |
| if (attempt === maxRetries) { | |
| throw lastError; | |
| } | |
| // Exponential backoff | |
| const waitTime = delay * Math.pow(2, attempt - 1); | |
| console.log(`AssetRequestApi: Retry attempt ${attempt}/${maxRetries} in ${waitTime}ms`); | |
| await new Promise(resolve => setTimeout(resolve, waitTime)); | |
| } | |
| } | |
| throw lastError; | |
| }; | |
| class AssetRequestApi { | |
| private baseUrl = `/api/AssetRequests`; | |
| /** | |
| * Create a new asset request | |
| * @param request - Asset request creation data | |
| * @returns Created asset request | |
| */ | |
| async create(request: AssetRequestCreateRequest): Promise<AssetRequest> { | |
| return retryOperation(async () => { | |
| try { | |
| console.log('AssetRequestApi: Creating asset request with data', request); | |
| const response = await apiClient.post(this.baseUrl, request); | |
| console.log('AssetRequestApi: Create response status', response.status); | |
| // Handle wrapped response format: { success, message, data } | |
| const responseData = response.data.data || response.data; | |
| try { | |
| return validateAssetRequestApiResponse(responseData); | |
| } catch (error) { | |
| console.warn('AssetRequestApi: Invalid response data from create, using safe fallback:', error); | |
| return createSafeAssetRequest(responseData); | |
| } | |
| } catch (error) { | |
| handleApiError(error, 'create asset request'); | |
| } | |
| }, 2); // Fewer retries for create operations | |
| } | |
| /** | |
| * Get asset request by ID | |
| * @param id - Request ID | |
| * @returns Asset request or null if not found | |
| */ | |
| async getById(id: number): Promise<AssetRequest | null> { | |
| return retryOperation(async () => { | |
| try { | |
| console.log(`AssetRequestApi: Fetching asset request ${id}`); | |
| const response = await apiClient.get(`${this.baseUrl}/${id}`); | |
| console.log(`AssetRequestApi: Response status for asset request ${id}`, response.status); | |
| if (response.data) { | |
| // Handle wrapped response format: { success, message, data } | |
| const responseData = response.data.data || response.data; | |
| try { | |
| return validateAssetRequestApiResponse(responseData); | |
| } catch (error) { | |
| console.warn(`AssetRequestApi: Invalid asset request data for ID ${id}, using safe fallback:`, error); | |
| return createSafeAssetRequest(responseData); | |
| } | |
| } | |
| return null; | |
| } catch (error) { | |
| if (error instanceof AxiosError && error.response?.status === 404) { | |
| return null; // Request not found is a valid case | |
| } | |
| handleApiError(error, `fetch asset request ${id}`); | |
| } | |
| }); | |
| } | |
| /** | |
| * Get asset requests by employee ID | |
| * @param employeeId - Employee ID | |
| * @returns List of asset requests for the employee | |
| */ | |
| async getByEmployeeId(employeeId: number): Promise<AssetRequest[]> { | |
| return retryOperation(async () => { | |
| try { | |
| console.log(`AssetRequestApi: Fetching asset requests for employee ${employeeId}`); | |
| const response = await apiClient.get(`${this.baseUrl}/employee/${employeeId}`); | |
| console.log(`AssetRequestApi: Response status for employee ${employeeId} requests`, response.status); | |
| if (!response.data) { | |
| console.warn(`AssetRequestApi: No asset requests found for employee ${employeeId}`); | |
| return []; | |
| } | |
| // Handle wrapped response format: { success, message, data } | |
| const dataArray = response.data.data || response.data; | |
| if (!Array.isArray(dataArray)) { | |
| console.warn(`AssetRequestApi: Response is not an array for employee ${employeeId}`, response.data); | |
| return []; | |
| } | |
| console.log(`AssetRequestApi: Found ${dataArray.length} asset requests for employee ${employeeId}`); | |
| // Map each item to ensure it has all required fields and validate | |
| return dataArray.map(item => { | |
| try { | |
| return validateAssetRequestApiResponse(item); | |
| } catch (error) { | |
| console.warn(`AssetRequestApi: Invalid asset request data for employee ${employeeId}, using safe fallback:`, error); | |
| return createSafeAssetRequest(item); | |
| } | |
| }); | |
| } catch (error) { | |
| if (error instanceof AxiosError && error.response?.status === 404) { | |
| return []; // No requests found is a valid case | |
| } | |
| handleApiError(error, `fetch asset requests for employee ${employeeId}`); | |
| } | |
| }); | |
| } | |
| /** | |
| * Get all asset requests with optional filtering | |
| * @param filters - Optional filters for status, employee, date range | |
| * @returns List of asset requests | |
| */ | |
| async getAll(filters?: RequestFilters): Promise<AssetRequest[]> { | |
| return retryOperation(async () => { | |
| try { | |
| console.log('AssetRequestApi: Fetching all asset requests with filters', filters); | |
| // Build query parameters | |
| const params: any = {}; | |
| if (filters?.status) params.status = filters.status; | |
| if (filters?.employeeId) params.employeeId = filters.employeeId; | |
| if (filters?.dateFrom) params.dateFrom = filters.dateFrom; | |
| if (filters?.dateTo) params.dateTo = filters.dateTo; | |
| const response = await apiClient.get(this.baseUrl, { params }); | |
| console.log('AssetRequestApi: Response status', response.status); | |
| if (!response.data) { | |
| console.warn('AssetRequestApi: No data in response'); | |
| return []; | |
| } | |
| // Handle wrapped response format: { success, message, data } | |
| const dataArray = response.data.data || response.data; | |
| if (!Array.isArray(dataArray)) { | |
| console.warn('AssetRequestApi: Response is not an array', response.data); | |
| return []; | |
| } | |
| console.log(`AssetRequestApi: Found ${dataArray.length} asset requests`); | |
| // Map each item to ensure it has all required fields and validate | |
| return dataArray.map(item => { | |
| try { | |
| return validateAssetRequestApiResponse(item); | |
| } catch (error) { | |
| console.warn('AssetRequestApi: Invalid asset request data, using safe fallback:', error); | |
| return createSafeAssetRequest(item); | |
| } | |
| }); | |
| } catch (error) { | |
| console.error('AssetRequestApi: Error fetching asset requests:', error); | |
| // Return empty array instead of throwing to prevent UI crashes | |
| return []; | |
| } | |
| }); | |
| } | |
| /** | |
| * Get request history for an employee | |
| * @param employeeId - Employee ID | |
| * @returns List of asset requests for the employee (alias for getByEmployeeId) | |
| */ | |
| async getRequestHistory(employeeId: number): Promise<AssetRequest[]> { | |
| return this.getByEmployeeId(employeeId); | |
| } | |
| /** | |
| * Get all pending asset requests with availability information | |
| * @returns List of pending requests with availability status | |
| */ | |
| async getPending(): Promise<AssetRequestWithAvailability[]> { | |
| return retryOperation(async () => { | |
| try { | |
| console.log('AssetRequestApi: Fetching pending asset requests with availability'); | |
| const response = await apiClient.get(`${this.baseUrl}/pending`); | |
| console.log('AssetRequestApi: Response status for pending requests', response.status); | |
| if (!response.data) { | |
| console.warn('AssetRequestApi: No pending requests found'); | |
| return []; | |
| } | |
| // Handle wrapped response format: { success, message, data } | |
| const dataArray = response.data.data || response.data; | |
| if (!Array.isArray(dataArray)) { | |
| console.warn('AssetRequestApi: Response is not an array', response.data); | |
| return []; | |
| } | |
| console.log(`AssetRequestApi: Found ${dataArray.length} pending asset requests`); | |
| // Map each item and ensure availability status is present | |
| return dataArray.map(item => { | |
| try { | |
| const baseRequest = validateAssetRequestApiResponse(item); | |
| return { | |
| ...baseRequest, | |
| availabilityStatus: item.availabilityStatus || [] | |
| } as AssetRequestWithAvailability; | |
| } catch (error) { | |
| console.warn('AssetRequestApi: Invalid pending request data, using safe fallback:', error); | |
| const safeRequest = createSafeAssetRequest(item); | |
| return { | |
| ...safeRequest, | |
| availabilityStatus: item.availabilityStatus || [] | |
| } as AssetRequestWithAvailability; | |
| } | |
| }); | |
| } catch (error) { | |
| console.error('AssetRequestApi: Error fetching pending requests:', error); | |
| // Return empty array instead of throwing to prevent UI crashes | |
| return []; | |
| } | |
| }); | |
| } | |
| /** | |
| * Approve an asset request | |
| * @param id - Request ID | |
| * @param data - Approval data with admin comments and reviewer ID | |
| * @returns Updated asset request | |
| */ | |
| async approve(id: number, data: AssetRequestApprovalRequest): Promise<AssetRequest> { | |
| return retryOperation(async () => { | |
| try { | |
| // First, get the current request to validate status transition | |
| const currentRequest = await this.getById(id); | |
| if (!currentRequest) { | |
| throw new Error(`Asset request with ID ${id} not found`); | |
| } | |
| // Validate status transition | |
| if (!isValidStatusTransition(currentRequest.status, 'approved')) { | |
| throw new Error( | |
| `Cannot approve request: Request is currently ${currentRequest.status}. ` + | |
| 'Only pending requests can be approved.' | |
| ); | |
| } | |
| console.log(`AssetRequestApi: Approving asset request ${id} with data`, data); | |
| const response = await apiClient.put(`${this.baseUrl}/${id}/approve`, data); | |
| console.log(`AssetRequestApi: Approve response status for request ${id}`, response.status); | |
| // Handle wrapped response format: { success, message, data } | |
| const responseData = response.data.data || response.data; | |
| try { | |
| return validateAssetRequestApiResponse(responseData); | |
| } catch (error) { | |
| console.warn(`AssetRequestApi: Invalid response data from approve for ID ${id}, using safe fallback:`, error); | |
| return createSafeAssetRequest(responseData); | |
| } | |
| } catch (error) { | |
| handleApiError(error, `approve asset request ${id}`); | |
| } | |
| }, 2); // Fewer retries for state-changing operations | |
| } | |
| /** | |
| * Reject an asset request | |
| * @param id - Request ID | |
| * @param data - Rejection data with admin comments and reviewer ID | |
| * @returns Updated asset request | |
| */ | |
| async reject(id: number, data: AssetRequestRejectionRequest): Promise<AssetRequest> { | |
| return retryOperation(async () => { | |
| try { | |
| // First, get the current request to validate status transition | |
| const currentRequest = await this.getById(id); | |
| if (!currentRequest) { | |
| throw new Error(`Asset request with ID ${id} not found`); | |
| } | |
| // Validate status transition | |
| if (!isValidStatusTransition(currentRequest.status, 'rejected')) { | |
| throw new Error( | |
| `Cannot reject request: Request is currently ${currentRequest.status}. ` + | |
| 'Only pending requests can be rejected.' | |
| ); | |
| } | |
| console.log(`AssetRequestApi: Rejecting asset request ${id} with data`, data); | |
| const response = await apiClient.put(`${this.baseUrl}/${id}/reject`, data); | |
| console.log(`AssetRequestApi: Reject response status for request ${id}`, response.status); | |
| // Handle wrapped response format: { success, message, data } | |
| const responseData = response.data.data || response.data; | |
| try { | |
| return validateAssetRequestApiResponse(responseData); | |
| } catch (error) { | |
| console.warn(`AssetRequestApi: Invalid response data from reject for ID ${id}, using safe fallback:`, error); | |
| return createSafeAssetRequest(responseData); | |
| } | |
| } catch (error) { | |
| handleApiError(error, `reject asset request ${id}`); | |
| } | |
| }, 2); // Fewer retries for state-changing operations | |
| } | |
| /** | |
| * Assign assets to an approved request | |
| * @param id - Request ID | |
| * @param data - Assignment data with asset details and assigner ID | |
| * @returns Updated asset request | |
| */ | |
| async assign(id: number, data: AssetRequestAssignmentRequest): Promise<AssetRequest> { | |
| return retryOperation(async () => { | |
| const createdAssignments: number[] = []; // Track created assignments for rollback | |
| try { | |
| // First, get the current request to validate status transition | |
| const currentRequest = await this.getById(id); | |
| if (!currentRequest) { | |
| throw new Error(`Asset request with ID ${id} not found`); | |
| } | |
| // Validate status transition | |
| if (!isValidStatusTransition(currentRequest.status, 'assigned')) { | |
| throw new Error( | |
| `Cannot assign assets: Request is currently ${currentRequest.status}. ` + | |
| 'Only approved requests can have assets assigned.' | |
| ); | |
| } | |
| // Validate that we have assets to assign | |
| if (!data.assignedAssets || data.assignedAssets.length === 0) { | |
| throw new Error('At least one asset must be assigned'); | |
| } | |
| // Check availability for all requested assets before proceeding | |
| const availabilityCheck = await this.checkAvailability(currentRequest.requestedAssets); | |
| if (!availabilityCheck.canFulfill) { | |
| const shortfalls = availabilityCheck.details | |
| .filter(d => d.shortfall > 0) | |
| .map(d => `${d.assetType}: need ${d.requested}, only ${d.available} available`) | |
| .join('; '); | |
| throw new Error(`Insufficient inventory to fulfill request: ${shortfalls}`); | |
| } | |
| // Validate that assigned assets match requested quantities | |
| const assignedByType = new Map<string, number>(); | |
| data.assignedAssets.forEach(asset => { | |
| const count = assignedByType.get(asset.assetType) || 0; | |
| assignedByType.set(asset.assetType, count + 1); | |
| }); | |
| for (const requestedItem of currentRequest.requestedAssets) { | |
| const assignedCount = assignedByType.get(requestedItem.assetType) || 0; | |
| if (assignedCount !== requestedItem.quantity) { | |
| throw new Error( | |
| `Asset quantity mismatch for ${requestedItem.assetType}: ` + | |
| `requested ${requestedItem.quantity}, assigning ${assignedCount}` | |
| ); | |
| } | |
| } | |
| // Validate that all assets are currently available (not assigned) | |
| const allAssignments = await assetAssignmentApi.getAll(); | |
| const activeAssignments = allAssignments.filter(a => !a.returnedDate); | |
| const assignedAssetIds = new Set(activeAssignments.map(a => a.assetID)); | |
| for (const assignedAsset of data.assignedAssets) { | |
| if (assignedAssetIds.has(assignedAsset.assetID)) { | |
| throw new Error( | |
| `Asset ${assignedAsset.assetCode} is already assigned to another employee. ` + | |
| 'Please select a different asset.' | |
| ); | |
| } | |
| } | |
| console.log(`AssetRequestApi: Assigning assets to request ${id} with data`, data); | |
| console.log(`AssetRequestApi: Validation passed - proceeding with ${data.assignedAssets.length} assignments`); | |
| // Create asset assignments for each assigned asset | |
| for (const assignedAsset of data.assignedAssets) { | |
| try { | |
| const assignmentData = { | |
| assetID: assignedAsset.assetID, | |
| employeeID: currentRequest.employeeID, | |
| employeeName: currentRequest.employeeName, | |
| assetCode: assignedAsset.assetCode, | |
| assignedDate: new Date().toISOString(), | |
| returnedDate: null, | |
| remarks: `Assigned via asset request #${id}: ${currentRequest.justification.substring(0, 100)}` | |
| }; | |
| console.log(`AssetRequestApi: Creating assignment for asset ${assignedAsset.assetID}`, assignmentData); | |
| const assignment = await assetAssignmentApi.create(assignmentData); | |
| createdAssignments.push(assignment.assignmentID); | |
| console.log(`AssetRequestApi: Successfully created assignment ${assignment.assignmentID} for asset ${assignedAsset.assetID}`); | |
| } catch (error) { | |
| console.error(`AssetRequestApi: Failed to create assignment for asset ${assignedAsset.assetID}:`, error); | |
| throw new Error(`Failed to assign asset ${assignedAsset.assetCode}: ${error instanceof Error ? error.message : 'Unknown error'}`); | |
| } | |
| } | |
| console.log(`AssetRequestApi: Successfully created ${data.assignedAssets.length} asset assignments`); | |
| // Update the request status to assigned | |
| try { | |
| const response = await apiClient.put(`${this.baseUrl}/${id}/assign`, data); | |
| console.log(`AssetRequestApi: Assign response status for request ${id}`, response.status); | |
| // Log audit trail | |
| console.log(`AssetRequestApi: Audit log - Request ${id} assigned by user ${data.assignedBy} at ${new Date().toISOString()}`); | |
| console.log(`AssetRequestApi: Audit log - Assets assigned: ${data.assignedAssets.map(a => a.assetCode).join(', ')}`); | |
| console.log(`AssetRequestApi: Audit log - Employee: ${currentRequest.employeeName} (ID: ${currentRequest.employeeID})`); | |
| // Handle wrapped response format: { success, message, data } | |
| const responseData = response.data.data || response.data; | |
| try { | |
| return validateAssetRequestApiResponse(responseData); | |
| } catch (error) { | |
| console.warn(`AssetRequestApi: Invalid response data from assign for ID ${id}, using safe fallback:`, error); | |
| return createSafeAssetRequest(responseData); | |
| } | |
| } catch (error) { | |
| // If updating the request status fails, rollback all created assignments | |
| console.error(`AssetRequestApi: Failed to update request status, rolling back ${createdAssignments.length} assignments`); | |
| await this.rollbackAssignments(createdAssignments); | |
| throw error; | |
| } | |
| } catch (error) { | |
| // If any error occurs, rollback all created assignments | |
| if (createdAssignments.length > 0) { | |
| console.error(`AssetRequestApi: Error during assignment, rolling back ${createdAssignments.length} assignments`); | |
| await this.rollbackAssignments(createdAssignments); | |
| } | |
| handleApiError(error, `assign assets to request ${id}`); | |
| } | |
| }, 1); // No retries for assignment operations to avoid duplicate assignments | |
| } | |
| /** | |
| * Add admin comments to a request without changing status | |
| * @param id - Request ID | |
| * @param data - Comment data with admin comments and commenter ID | |
| * @returns Updated asset request | |
| */ | |
| async addComment(id: number, data: AssetRequestCommentRequest): Promise<AssetRequest> { | |
| return retryOperation(async () => { | |
| try { | |
| console.log(`AssetRequestApi: Adding comment to asset request ${id} with data`, data); | |
| const response = await apiClient.put(`${this.baseUrl}/${id}/comment`, data); | |
| console.log(`AssetRequestApi: Add comment response status for request ${id}`, response.status); | |
| // Handle wrapped response format: { success, message, data } | |
| const responseData = response.data.data || response.data; | |
| try { | |
| return validateAssetRequestApiResponse(responseData); | |
| } catch (error) { | |
| console.warn(`AssetRequestApi: Invalid response data from addComment for ID ${id}, using safe fallback:`, error); | |
| return createSafeAssetRequest(responseData); | |
| } | |
| } catch (error) { | |
| handleApiError(error, `add comment to asset request ${id}`); | |
| } | |
| }, 2); // Allow retries for comment operations | |
| } | |
| /** | |
| * Rollback created assignments in case of failure | |
| * @param assignmentIds - Array of assignment IDs to delete | |
| */ | |
| private async rollbackAssignments(assignmentIds: number[]): Promise<void> { | |
| console.log(`AssetRequestApi: Starting rollback for ${assignmentIds.length} assignments`); | |
| for (const assignmentId of assignmentIds) { | |
| try { | |
| await assetAssignmentApi.delete(assignmentId); | |
| console.log(`AssetRequestApi: Rolled back assignment ${assignmentId}`); | |
| } catch (error) { | |
| console.error(`AssetRequestApi: Failed to rollback assignment ${assignmentId}:`, error); | |
| // Continue with other rollbacks even if one fails | |
| } | |
| } | |
| console.log(`AssetRequestApi: Rollback completed`); | |
| } | |
| /** | |
| * Check availability of requested assets | |
| * @param requestedAssets - List of requested asset items | |
| * @returns Availability check result with details for each asset type | |
| */ | |
| async checkAvailability(requestedAssets: RequestedAssetItem[]): Promise<AvailabilityCheck> { | |
| return retryOperation(async () => { | |
| try { | |
| console.log('AssetRequestApi: Checking availability for requested assets', requestedAssets); | |
| // Get all assets to calculate availability | |
| const allAssets = await assetApi.getAll(); | |
| // Get all active assignments to calculate what's currently assigned | |
| const allAssignments = await assetAssignmentApi.getAll(); | |
| const activeAssignments = allAssignments.filter(assignment => !assignment.returnedDate); | |
| // Calculate availability for each requested asset type | |
| const details = requestedAssets.map(requestedItem => { | |
| // Count total assets of this type | |
| const totalAssets = allAssets.filter(asset => asset.assetType === requestedItem.assetType).length; | |
| // Count how many are currently assigned | |
| const assignedCount = activeAssignments.filter(assignment => { | |
| const asset = allAssets.find(a => a.assetID === assignment.assetID); | |
| return asset && asset.assetType === requestedItem.assetType; | |
| }).length; | |
| // Calculate available | |
| const available = totalAssets - assignedCount; | |
| const shortfall = Math.max(0, requestedItem.quantity - available); | |
| return { | |
| assetType: requestedItem.assetType, | |
| requested: requestedItem.quantity, | |
| available, | |
| shortfall | |
| }; | |
| }); | |
| // Check if all requested items can be fulfilled | |
| const canFulfill = details.every(detail => detail.shortfall === 0); | |
| console.log('AssetRequestApi: Availability check result', { canFulfill, details }); | |
| return { | |
| canFulfill, | |
| details | |
| }; | |
| } catch (error) { | |
| console.error('AssetRequestApi: Error checking availability:', error); | |
| // Return a conservative result on error | |
| return { | |
| canFulfill: false, | |
| details: requestedAssets.map(item => ({ | |
| assetType: item.assetType, | |
| requested: item.quantity, | |
| available: 0, | |
| shortfall: item.quantity | |
| })) | |
| }; | |
| } | |
| }); | |
| } | |
| } | |
| export const assetRequestApi = new AssetRequestApi(); | |