@@ -452,136 +452,6 @@ const bmMaterialsController = function (BuildingMaterial) {
452452 }
453453 } ;
454454
455- const bmGetMaterialStockOutRisk = async function ( req , res ) {
456- try {
457- const { projectIds } = req . query || { } ;
458- const query = { } ;
459-
460- if ( projectIds && projectIds !== 'all' && typeof projectIds === 'string' ) {
461- const projectIdArray = projectIds
462- . split ( ',' )
463- . map ( ( id ) => id . trim ( ) )
464- . filter ( ( id ) => id . length > 0 ) ;
465-
466- if ( projectIdArray . length > 0 ) {
467- const validProjectIds = projectIdArray
468- . filter ( ( id ) => mongoose . Types . ObjectId . isValid ( id ) )
469- . map ( ( id ) => mongoose . Types . ObjectId ( id ) ) ;
470-
471- if ( validProjectIds . length > 0 ) {
472- query . project = { $in : validProjectIds } ;
473- } else {
474- return res . status ( 400 ) . json ( {
475- error : 'Invalid project IDs provided' ,
476- details : 'All provided project IDs are invalid' ,
477- } ) ;
478- }
479- }
480- }
481-
482- const materials = await BuildingMaterial . find ( query )
483- . populate ( 'project' , '_id name' )
484- . populate ( 'itemType' , '_id name unit' )
485- . lean ( )
486- . exec ( ) ;
487-
488- const now = new Date ( ) ;
489- const thirtyDaysAgo = new Date ( now ) ;
490- thirtyDaysAgo . setDate ( thirtyDaysAgo . getDate ( ) - 30 ) ;
491- const daysInPeriod = 30 ;
492- const SENTINEL_NO_USAGE_DATA = 999 ;
493-
494- const stockOutRiskData = [ ] ;
495-
496- for ( const material of materials ) {
497- if (
498- ! material ||
499- typeof material . stockAvailable !== 'number' ||
500- material . stockAvailable <= 0 ||
501- ! material . project ||
502- ! material . itemType ||
503- ! material . project . _id ||
504- ! material . itemType . _id
505- ) {
506- continue ;
507- }
508-
509- const updateRecords = Array . isArray ( material . updateRecord ) ? material . updateRecord : [ ] ;
510- let totalUsage = 0 ;
511- const usageByDate = { } ;
512-
513- for ( const record of updateRecords ) {
514- if ( ! record || ! record . date ) continue ;
515-
516- const recordDate = new Date ( record . date ) ;
517- if ( Number . isNaN ( recordDate . getTime ( ) ) ) continue ;
518- if ( recordDate < thirtyDaysAgo || recordDate > now ) continue ;
519-
520- const dateKey = recordDate . toISOString ( ) . split ( 'T' ) [ 0 ] ;
521- const quantityUsed = parseFloat ( record . quantityUsed ) || 0 ;
522-
523- if ( quantityUsed > 0 ) {
524- if ( ! usageByDate [ dateKey ] ) {
525- usageByDate [ dateKey ] = 0 ;
526- }
527- usageByDate [ dateKey ] += quantityUsed ;
528- totalUsage += quantityUsed ;
529- }
530- }
531-
532- let averageDailyUsage = 0 ;
533-
534- if ( totalUsage > 0 ) {
535- averageDailyUsage = totalUsage / daysInPeriod ;
536- } else if ( material . stockUsed > 0 ) {
537- averageDailyUsage = parseFloat ( material . stockUsed ) / daysInPeriod ;
538- }
539-
540- let daysUntilStockOut = 0 ;
541- if ( averageDailyUsage > 0 ) {
542- daysUntilStockOut = Math . floor ( material . stockAvailable / averageDailyUsage ) ;
543- } else {
544- daysUntilStockOut = SENTINEL_NO_USAGE_DATA ;
545- }
546-
547- if ( daysUntilStockOut >= 0 && daysUntilStockOut < SENTINEL_NO_USAGE_DATA ) {
548- stockOutRiskData . push ( {
549- materialName : material . itemType . name || 'Unknown Material' ,
550- materialId : material . itemType . _id . toString ( ) ,
551- projectId : material . project . _id . toString ( ) ,
552- projectName : material . project . name || 'Unknown Project' ,
553- stockAvailable : parseFloat ( material . stockAvailable . toFixed ( 2 ) ) ,
554- averageDailyUsage : parseFloat ( averageDailyUsage . toFixed ( 2 ) ) ,
555- daysUntilStockOut : Math . max ( 0 , daysUntilStockOut ) ,
556- unit : material . itemType . unit || '' ,
557- } ) ;
558- }
559- }
560-
561- stockOutRiskData . sort ( ( a , b ) => {
562- const daysA = Number ( a . daysUntilStockOut ) || 0 ;
563- const daysB = Number ( b . daysUntilStockOut ) || 0 ;
564- return daysA - daysB ;
565- } ) ;
566-
567- res . status ( 200 ) . json ( stockOutRiskData ) ;
568- } catch ( err ) {
569- let statusCode = 500 ;
570- let errorMessage = 'Internal Server Error' ;
571-
572- if ( err . name === 'CastError' || err . name === 'ValidationError' ) {
573- statusCode = 400 ;
574- errorMessage = 'Invalid request parameters' ;
575- } else if ( err . name === 'MongoError' || err . name === 'MongoServerError' ) {
576- statusCode = 503 ;
577- errorMessage = 'Database error' ;
578- }
579-
580- res . status ( statusCode ) . json ( {
581- error : errorMessage ,
582- } ) ;
583- }
584- } ;
585455 // eslint-disable-next-line max-lines-per-function
586456 /**
587457 * Compute default start date if startDateInput is not provided.
0 commit comments