@@ -852,48 +852,95 @@ export const updateCWUProposal = tryDb<
852852 ) ;
853853} ) ;
854854
855+ /**
856+ * Internal function that updates a CWU proposal status.
857+ */
858+ async function _updateCWUProposalStatusInternal (
859+ trx : Transaction ,
860+ proposalId : Id ,
861+ status : CWUProposalStatus ,
862+ note : string ,
863+ session : AuthenticatedSession
864+ ) : Promise < CWUProposal > {
865+ const now = new Date ( ) ;
866+
867+ const [ result ] = await trx < RawCWUProposalHistoryRecord & { proposal : Id } > (
868+ "cwuProposalStatuses"
869+ ) . insert (
870+ {
871+ id : generateUuid ( ) ,
872+ proposal : proposalId ,
873+ createdAt : now ,
874+ createdBy : session . user . id ,
875+ status,
876+ note
877+ } ,
878+ "*"
879+ ) ;
880+
881+ // Update updatedAt/By stamp on proposal root record
882+ await trx ( "cwuProposals" ) . where ( { id : proposalId } ) . update ( {
883+ updatedAt : now ,
884+ updatedBy : session . user . id
885+ } ) ;
886+
887+ if ( ! result ) {
888+ throw new Error ( "unable to update proposal" ) ;
889+ }
890+
891+ const dbResult = await readOneCWUProposal ( trx , result . proposal , session ) ;
892+ if ( isInvalid ( dbResult ) || ! dbResult . value ) {
893+ throw new Error ( "unable to update proposal" ) ;
894+ }
895+
896+ return dbResult . value ;
897+ }
898+
855899export const updateCWUProposalStatus = tryDb <
856900 [ Id , CWUProposalStatus , string , AuthenticatedSession ] ,
857901 CWUProposal
858902> ( async ( connection , proposalId , status , note , session ) => {
859- const now = new Date ( ) ;
860903 return valid (
861904 await connection . transaction ( async ( trx ) => {
862- const [ result ] = await connection <
863- RawCWUProposalHistoryRecord & { proposal : Id }
864- > ( "cwuProposalStatuses" )
865- . transacting ( trx )
866- . insert (
867- {
868- id : generateUuid ( ) ,
869- proposal : proposalId ,
870- createdAt : now ,
871- createdBy : session . user . id ,
872- status,
873- note
874- } ,
875- "*"
876- ) ;
877-
878- // Update updatedAt/By stamp on proposal root record
879- await connection ( "cwuProposals" )
880- . transacting ( trx )
881- . where ( { id : proposalId } )
882- . update ( {
883- updatedAt : now ,
884- updatedBy : session . user . id
885- } ) ;
905+ return await _updateCWUProposalStatusInternal (
906+ trx ,
907+ proposalId ,
908+ status ,
909+ note ,
910+ session
911+ ) ;
912+ } )
913+ ) ;
914+ } ) ;
886915
887- if ( ! result ) {
888- throw new Error ( "unable to update proposal" ) ;
889- }
916+ /**
917+ * Disqualifies a CWU proposal and updates the opportunity processing status in a single transaction.
918+ * This ensures both operations succeed or fail together.
919+ */
920+ export const disqualifyCWUProposalAndUpdateOpportunity = tryDb <
921+ [ Id , string , AuthenticatedSession ] ,
922+ CWUProposal
923+ > ( async ( connection , proposalId , disqualificationReason , session ) => {
924+ return valid (
925+ await connection . transaction ( async ( trx ) => {
926+ // Update proposal status to disqualified
927+ const updatedProposal = await _updateCWUProposalStatusInternal (
928+ trx ,
929+ proposalId ,
930+ CWUProposalStatus . Disqualified ,
931+ disqualificationReason ,
932+ session
933+ ) ;
890934
891- const dbResult = await readOneCWUProposal ( trx , result . proposal , session ) ;
892- if ( isInvalid ( dbResult ) || ! dbResult . value ) {
893- throw new Error ( "unable to update proposal" ) ;
894- }
935+ // Check if opportunity should be moved to "Processing" status after disqualification
936+ const opportunityId = updatedProposal . opportunity . id ;
937+ await checkAndUpdateCWUOpportunityProcessingStatus (
938+ trx ,
939+ opportunityId ,
940+ session
941+ ) ;
895942
896- return dbResult . value ;
943+ return updatedProposal ;
897944 } )
898945 ) ;
899946} ) ;
@@ -970,11 +1017,66 @@ export const updateCWUProposalScore = tryDb<
9701017 throw new Error ( "unable to update proposal" ) ;
9711018 }
9721019
1020+ // updateCWUProposalScore was invoked -
1021+ // this proposal is now fully evaluated, check if we need to change the opportunity "Processing" status
1022+ const opportunityId = dbResult . value . opportunity . id ;
1023+ await checkAndUpdateCWUOpportunityProcessingStatus (
1024+ trx ,
1025+ opportunityId ,
1026+ session
1027+ ) ;
1028+
9731029 return dbResult . value ;
9741030 } )
9751031 ) ;
9761032} ) ;
9771033
1034+ /**
1035+ * Checks if all proposals for a CWU opportunity are evaluated and
1036+ * updates the opportunity status accordingly.
1037+ */
1038+ export async function checkAndUpdateCWUOpportunityProcessingStatus (
1039+ connection : Connection ,
1040+ opportunityId : Id ,
1041+ session : AuthenticatedSession
1042+ ) : Promise < void > {
1043+ // Get all active proposals
1044+ const activeProposals = await generateCWUProposalQuery ( connection )
1045+ . where ( { "proposals.opportunity" : opportunityId } )
1046+ . whereNotIn ( "statuses.status" , [
1047+ CWUProposalStatus . Withdrawn ,
1048+ CWUProposalStatus . Disqualified ,
1049+ CWUProposalStatus . Draft
1050+ ] ) ;
1051+
1052+ // Get current opportunity status
1053+ const currentOpportunity = await generateCWUOpportunityQuery ( connection )
1054+ . where ( { "opp.id" : opportunityId } )
1055+ . select ( "stat.status" )
1056+ . first ( ) ;
1057+
1058+ const currentStatus = currentOpportunity . status ;
1059+ const totalProposalsCount = activeProposals . length ;
1060+ const evaluatedCount = activeProposals . filter (
1061+ ( p ) => p . status === CWUProposalStatus . Evaluated
1062+ ) . length ;
1063+
1064+ // All proposals are evaluated and opportunity is in EVALUATION, change to PROCESSING
1065+ if (
1066+ totalProposalsCount > 0 &&
1067+ evaluatedCount === totalProposalsCount &&
1068+ currentStatus === CWUOpportunityStatus . Evaluation
1069+ ) {
1070+ await updateCWUOpportunityStatus (
1071+ connection ,
1072+ opportunityId ,
1073+ CWUOpportunityStatus . Processing ,
1074+ "Automatically moved to Processing as all proposals have been evaluated." ,
1075+ session
1076+ ) ;
1077+ }
1078+ }
1079+
9781080export const awardCWUProposal = tryDb <
9791081 [ Id , string , AuthenticatedSession ] ,
9801082 CWUProposal
0 commit comments