pmtool / src /utils /inventoryManagement.ts
devarshia5's picture
Upload 487 files
d97b8f9 verified
Raw
History Blame Contribute Delete
11.6 kB
import { Asset } from '@/types/asset-management';
import { AssetAssignment } from '@/types/asset-management';
import { RequestedAssetItem, AssetAvailabilityStatus } from '@/types/asset-request';
/**
* Calculate available quantity for a specific asset type
* @param assetType - The type of asset to check
* @param allAssets - All assets in the system
* @param activeAssignments - All active (not returned) assignments
* @returns Available quantity
*/
export function calculateAvailableQuantity(
assetType: string,
allAssets: Asset[],
activeAssignments: AssetAssignment[]
): number {
// Count total assets of this type
const totalAssets = allAssets.filter(asset => asset.assetType === assetType).length;
// Count how many are currently assigned (active assignments only)
const assignedCount = activeAssignments.filter(assignment => {
const asset = allAssets.find(a => a.assetID === assignment.assetID);
return asset && asset.assetType === assetType;
}).length;
// Calculate available
const available = totalAssets - assignedCount;
return Math.max(0, available); // Ensure non-negative
}
/**
* Calculate availability status for multiple requested asset items
* @param requestedAssets - List of requested asset items
* @param allAssets - All assets in the system
* @param activeAssignments - All active (not returned) assignments
* @returns Array of availability status for each requested asset type
*/
export function calculateAvailabilityStatus(
requestedAssets: RequestedAssetItem[],
allAssets: Asset[],
activeAssignments: AssetAssignment[]
): AssetAvailabilityStatus[] {
return requestedAssets.map(requestedItem => {
const available = calculateAvailableQuantity(
requestedItem.assetType,
allAssets,
activeAssignments
);
return {
assetType: requestedItem.assetType,
requested: requestedItem.quantity,
available,
canFulfill: available >= requestedItem.quantity
};
});
}
/**
* Check if all requested assets can be fulfilled
* @param requestedAssets - List of requested asset items
* @param allAssets - All assets in the system
* @param activeAssignments - All active (not returned) assignments
* @returns True if all requested assets can be fulfilled
*/
export function canFulfillRequest(
requestedAssets: RequestedAssetItem[],
allAssets: Asset[],
activeAssignments: AssetAssignment[]
): boolean {
const availabilityStatus = calculateAvailabilityStatus(
requestedAssets,
allAssets,
activeAssignments
);
return availabilityStatus.every(status => status.canFulfill);
}
/**
* Get available assets of a specific type (not currently assigned)
* @param assetType - The type of asset to filter
* @param allAssets - All assets in the system
* @param activeAssignments - All active (not returned) assignments
* @returns Array of available assets
*/
export function getAvailableAssetsByType(
assetType: string,
allAssets: Asset[],
activeAssignments: AssetAssignment[]
): Asset[] {
// Get all assets of this type
const assetsOfType = allAssets.filter(asset => asset.assetType === assetType);
// Get IDs of assets that are currently assigned
const assignedAssetIds = new Set(
activeAssignments.map(assignment => assignment.assetID)
);
// Filter out assigned assets
return assetsOfType.filter(asset => !assignedAssetIds.has(asset.assetID));
}
/**
* Check if inventory is low for a specific asset type
* @param assetType - The type of asset to check
* @param allAssets - All assets in the system
* @param activeAssignments - All active (not returned) assignments
* @param threshold - Threshold percentage (default 20%)
* @returns True if inventory is below threshold
*/
export function isLowStock(
assetType: string,
allAssets: Asset[],
activeAssignments: AssetAssignment[],
threshold: number = 0.2
): boolean {
const totalAssets = allAssets.filter(asset => asset.assetType === assetType).length;
// If no assets exist, it's definitely low stock
if (totalAssets === 0) return true;
const available = calculateAvailableQuantity(assetType, allAssets, activeAssignments);
const availabilityRatio = available / totalAssets;
return availabilityRatio <= threshold;
}
/**
* Get inventory summary for all asset types
* @param allAssets - All assets in the system
* @param activeAssignments - All active (not returned) assignments
* @returns Map of asset type to inventory summary
*/
export function getInventorySummary(
allAssets: Asset[],
activeAssignments: AssetAssignment[]
): Map<string, { total: number; assigned: number; available: number; lowStock: boolean }> {
const summary = new Map<string, { total: number; assigned: number; available: number; lowStock: boolean }>();
// Get unique asset types
const assetTypes = new Set(allAssets.map(asset => asset.assetType));
assetTypes.forEach(assetType => {
const total = allAssets.filter(asset => asset.assetType === assetType).length;
const available = calculateAvailableQuantity(assetType, allAssets, activeAssignments);
const assigned = total - available;
const lowStock = isLowStock(assetType, allAssets, activeAssignments);
summary.set(assetType, {
total,
assigned,
available,
lowStock
});
});
return summary;
}
/**
* Validate that requested assets can be assigned without over-assignment
* @param requestedAssets - List of requested asset items
* @param allAssets - All assets in the system
* @param activeAssignments - All active (not returned) assignments
* @returns Validation result with error messages if any
*/
export function validateAssignmentAvailability(
requestedAssets: RequestedAssetItem[],
allAssets: Asset[],
activeAssignments: AssetAssignment[]
): { valid: boolean; errors: string[] } {
const errors: string[] = [];
requestedAssets.forEach(requestedItem => {
const available = calculateAvailableQuantity(
requestedItem.assetType,
allAssets,
activeAssignments
);
if (available < requestedItem.quantity) {
errors.push(
`Insufficient ${requestedItem.assetType}: requested ${requestedItem.quantity}, only ${available} available`
);
}
});
return {
valid: errors.length === 0,
errors
};
}
/**
* Audit log entry for inventory changes
*/
export interface InventoryAuditLog {
timestamp: string;
action: 'assignment' | 'return' | 'adjustment';
assetType: string;
assetId: number;
assetCode: string;
employeeId: number;
employeeName: string;
performedBy: number;
previousState: 'available' | 'assigned';
newState: 'available' | 'assigned';
requestId?: number;
remarks?: string;
}
/**
* Create an audit log entry for an inventory change
* @param params - Audit log parameters
* @returns Audit log entry
*/
export function createInventoryAuditLog(params: {
action: 'assignment' | 'return' | 'adjustment';
assetType: string;
assetId: number;
assetCode: string;
employeeId: number;
employeeName: string;
performedBy: number;
previousState: 'available' | 'assigned';
newState: 'available' | 'assigned';
requestId?: number;
remarks?: string;
}): InventoryAuditLog {
return {
timestamp: new Date().toISOString(),
...params
};
}
/**
* Log inventory change to console (can be extended to send to backend)
* @param log - Audit log entry
*/
export function logInventoryChange(log: InventoryAuditLog): void {
console.log('[INVENTORY AUDIT]', {
timestamp: log.timestamp,
action: log.action,
asset: `${log.assetType} (${log.assetCode})`,
employee: `${log.employeeName} (ID: ${log.employeeId})`,
stateChange: `${log.previousState}${log.newState}`,
requestId: log.requestId,
performedBy: log.performedBy,
remarks: log.remarks
});
}
/**
* Validate assignment request against current inventory
* @param assignedAssets - Assets being assigned
* @param allAssets - All assets in the system
* @param activeAssignments - All active assignments
* @returns Validation result with detailed errors
*/
export function validateAssignmentRequest(
assignedAssets: Array<{ assetID: number; assetCode: string; assetType: string }>,
allAssets: Asset[],
activeAssignments: AssetAssignment[]
): { valid: boolean; errors: string[]; warnings: string[] } {
const errors: string[] = [];
const warnings: string[] = [];
// Get currently assigned asset IDs
const assignedAssetIds = new Set(activeAssignments.map(a => a.assetID));
// Validate each asset
assignedAssets.forEach(assignedAsset => {
// Check if asset exists
const asset = allAssets.find(a => a.assetID === assignedAsset.assetID);
if (!asset) {
errors.push(`Asset ${assignedAsset.assetCode} (ID: ${assignedAsset.assetID}) does not exist`);
return;
}
// Check if asset is already assigned
if (assignedAssetIds.has(assignedAsset.assetID)) {
errors.push(`Asset ${assignedAsset.assetCode} is already assigned to another employee`);
return;
}
// Check asset condition
if (asset.assetCondition === 'Damaged' || asset.assetCondition === 'Under Repair') {
warnings.push(
`Asset ${assignedAsset.assetCode} is in ${asset.assetCondition} condition. ` +
'Consider selecting a different asset.'
);
}
// Verify asset type matches
if (asset.assetType !== assignedAsset.assetType) {
errors.push(
`Asset type mismatch for ${assignedAsset.assetCode}: ` +
`expected ${assignedAsset.assetType}, found ${asset.assetType}`
);
}
});
return {
valid: errors.length === 0,
errors,
warnings
};
}
/**
* Calculate inventory impact of an assignment
* @param assignedAssets - Assets being assigned
* @param allAssets - All assets in the system
* @param activeAssignments - All active assignments before the new assignment
* @returns Inventory impact summary
*/
export function calculateInventoryImpact(
assignedAssets: Array<{ assetType: string }>,
allAssets: Asset[],
activeAssignments: AssetAssignment[]
): Map<string, { before: number; after: number; percentageUsed: number }> {
const impact = new Map<string, { before: number; after: number; percentageUsed: number }>();
// Group assigned assets by type
const assignmentsByType = new Map<string, number>();
assignedAssets.forEach(asset => {
const count = assignmentsByType.get(asset.assetType) || 0;
assignmentsByType.set(asset.assetType, count + 1);
});
// Calculate impact for each asset type
assignmentsByType.forEach((assignedCount, assetType) => {
const totalAssets = allAssets.filter(a => a.assetType === assetType).length;
const beforeAvailable = calculateAvailableQuantity(assetType, allAssets, activeAssignments);
const afterAvailable = beforeAvailable - assignedCount;
const percentageUsed = totalAssets > 0 ? ((totalAssets - afterAvailable) / totalAssets) * 100 : 0;
impact.set(assetType, {
before: beforeAvailable,
after: afterAvailable,
percentageUsed: Math.round(percentageUsed)
});
});
return impact;
}