Compile function Signature_Services(@Service, @Params)
/***********************************************************************************************************************

    This program is proprietary and is not to be used by or disclosed to others, nor is it to be copied without written
    permission from SRP Computer Solutions, Inc.

    Name        :   Signature_Services

    Description :   Handler program for all module related services.

    Notes       :   Service module to support environmental state issues. Environmental refers to the state of the
                    operating system, which includes version, client vs. server, and path to critical systems.

    Parameters  :
        Service         [in] -- Name of the service being requested
        Param1-10   [in/out] -- Additional request parameter holders
        Response       [out] -- Response to be sent back to the Controller (MCP) or requesting procedure

    Metadata    :

    History     :   (Date, Initials, Notes)
        11/07/19    djs     Original programmer.
        08/21/20    djs     Updated GetSigInfo to support multiple CLEAN_INSP scans. The code will return the
                            signature info for the most recently signed scan.
        07/13/21    djs     Removed caching code (Memory_Services) so that users can sign multiple stages in quick
                            succession without causing issues related to caching the signature profile.
        08/03/21    djs     Restored caching code because it was causing the application to perform very poorly during
                            certain operations (e.g. releasing material). Added a UseCaching variable to all services,
                            so that developers can choose to use caching or not.
    	06/13/24	djm		Add support for EPP signature profile.

***********************************************************************************************************************/
#pragma precomp SRP_PreCompiler

$insert POPUP_EQUATES
$INSERT LOGICAL
$Insert	WO_MAT_EQUATES
$insert SERVICE_SETUP
$insert RLIST_EQUATES
$insert WO_MAT_QA_EQUATES
$insert RDS_EQUATES
$insert COMPANY_EQUATES
$insert APP_INSERTS
$insert CLEAN_INSP_EQUATES
$insert NCR_EQUATES
$insert WM_OUT_EQUATES

Equ SS_PASS$ To 1
Equ SS_SIG$  To 2
Equ SS_DATA$ To 3
Equ SS_NA$   To 4

Equ COL$LOG_FILE        to 1
Equ COL$LOG_DTM         to 2
Equ COL$ACTION          to 3
Equ COL$WH_CD           to 4
Equ COL$LOC_CD          to 5
Equ COL$WO_NOS          to 6
Equ COL$CASS_NOS        to 7
Equ COL$USER_ID         to 8
Equ COL$TAGS            to 9
Equ COL$TOOL_ID         to 10

Declare function   obj_Prod_Spec, RDS_Services, Error_Services, Signature_Services, Memory_Services, obj_RDS_Test
Declare function   SRP_Array, Environment_Services, GetTickCount, QA_Services, Datetime, Supplement_Services
Declare function   Database_Services, obj_WO_Mat, Dialog_Box, MemberOf, Msg, Logging_Services
Declare subroutine Error_Services, Popup, Memory_Services, SRP_Stopwatch, Set_Status, Database_Services, obj_WO_Mat
Declare subroutine Obj_Notes, Signature_Services, Obj_WO_Mat_Log, ErrMsg, Obj_Rds, Lot_Event_Services, Mona_Services
Declare subroutine Logging_Services

PSNKey     = ServiceKeyID
ReactorKey = ServiceKeyID

IsProd = Environment_Services('IsProd')
If IsProd EQ True$ then
	MonaResource = 'GRP_OPENINSIGHT_MES_OP_FE_SIGNATURESERVICES'
end else
	MonaResource = 'GRP_OPENINSIGHT_MES_OP_FE_DEV_SIGNATURESERVICES'
end

GoToService

Return Response or ""

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Service Parameter Options
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Options CI_STAGES     = 'PREI,PREC,PRES,FWII,FWIC,FWIS,LWII,LWIC,LWIS,PSTI,PSTC,PSTS'
Options QA_STAGES     = 'LOAD,UNLOAD,QA'
Options SLOTS         = '1,2,3,4,5,6,F,L,A' ; // From SYSREPOSPOPUPS LSL2**TEST_SLOTS 

//-----------------------------------------------------------------------------
// SERVICES
//-----------------------------------------------------------------------------


Service FQAReady(WOMatKey, UseCaching)
    
    If UseCaching EQ '' then UseCaching = True$
    Response   = False$
    SigProfile = Signature_Services('GetSigProfile', WOMatKey, UseCaching)
    Stages     = SigProfile<1>
    Sigs       = SigProfile<2>
    SigDTMs    = SigProfile<3>
    NumStages  = DCount(Stages, @VM)
    If NumStages GT 1 then        
        Done = False$
        For StageIndex = 1 to (NumStages - 1)
            Stage = Stages<0, StageIndex>
            StageComp = Signature_Services('CheckSignature', WOMatKey, Stage)
            If StageComp EQ False$ then
                Done = True$
                ErrorMsg = 'Unable to sign FQA due to incomplete Signature Profile at ':Stage:' stage.'
                Error_Services('Add', ErrorMsg)
            end
        Until Done EQ True$
        Next StageIndex
    end
    
    If Error_Services('NoError') then Response = True$
    
end service


Service CheckQALabelStatus(WOMatKey)

    rec = Xlate('WO_MAT', WoMatKey,'', '')
    locRec = rec<WO_MAT_INV_LOCATION$>
    actionRec = rec<WO_MAT_INV_ACTION$>
    retVal = 0
    Locate 'LBLCHK' In actionRec Using @VM Setting locIdx Then
        Locate 'PKO' in locRec Using @VM Setting idx Then
            if idx GE 1 then
                If (rec<WO_MAT_INV_USER$, idx> NE '' AND rec<WO_MAT_INV_DTM$, idx> NE '') then
                    retVal = 1	
                end
            end
        end
    end
    
    Response = retVal
    
end service


Service GetSigProfile(WOMatKey, UseCaching, RDSNo)
    StartTick = GetTickCount()
    MetricName = 'GetSigProfile'
    
    If UseCaching EQ '' then UseCaching = True$
    
    SigProfile	= ''
    Signatures	= ''
    SigDTMs		= ''
    
    If RDSNo NE '' then
		WONo = Xlate('RDS', RDSNo, 'WO', 'X')
	end else
		WONo = Field(WOMatKey, '*', 1)
	end
    PSNo        = Xlate('WO_LOG', WONo, 'PS_NO', 'X')
    
    SigProfKey = PSNo:'*':WOMatKey:'*':RDSNo
    ReactorKey = PSNo :'*Reactor'
    
    If UseCaching and Memory_Services('IsValueCurrent', SigProfKey, 60, True$) then
        SigProfile = Memory_Services('GetValue', SigProfKey) 
    end else
        SigProfile  = obj_Prod_Spec('GetSigProfile', PSNo:@RM:'':@RM:WOMatKey:@RM:RDSNo)
        Memory_Services('SetValue', SigProfKey, SigProfile)
    end
    
    If UseCaching and Memory_Services('IsValueCurrent', ReactorKey, 60, True$) then
        ReactorType = Memory_Services('GetValue', ReactorKey)
    end else
        ReactorType = Xlate('PROD_SPEC', PSNo, 'REACTOR_TYPE', 'X')
        Memory_Services('SetValue', ReactorKey, ReactorType)
    end
    
    For each Stage in SigProfile using @VM setting LineNo
        SigInfo = Signature_Services('GetSigInfo', WOMatKey, Stage, UseCaching, RDSNo)
        Signatures<0, LineNo> = SigInfo<1>
        SigDTMs<0, LineNo>    = SigInfo<2>
    Next Stage
    
    SigDTMs = OCONV(SigDTMs,'DT4/^S')
    Response = SigProfile:@FM:Signatures:@FM:SigDTMs 
    
    EndTick = GetTickCount()
    Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)
end service


Service FinalSigComp(WOMatKey, UseCaching)
    
    If UseCaching EQ '' then UseCaching = True$
    Response    = False$	
    WONo        = Field(WOMatKey, '*', 1)
    ReactorType = Xlate('WO_LOG', WONo, 'REACTOR_TYPE', 'X')
    PSNo        = Xlate('WO_LOG', WONo, 'PS_NO', 'X')
    ReactorType = Xlate('PROD_SPEC', PSNo, 'REACTOR_TYPE', 'X')
    SigProfile = Signature_Services('GetSigProfile', WOMatKey, UseCaching)
    NumStages   = DCount(SigProfile, @VM)
    FinalStage  = SigProfile<0, NumStages>
    Response    = Signature_Services('CheckSignature', WOMatKey, FinalStage, UseCaching)
    
end service


Service FinalSigDTM(WOMatKey, UseCaching)
    
    If UseCaching EQ '' then UseCaching = True$
    Response    = False$	
    WONo        = Field(WOMatKey, '*', 1)
    ReactorType = Xlate('WO_LOG', WONo, 'REACTOR_TYPE', 'X')
    PSNo        = Xlate('WO_LOG', WONo, 'PS_NO', 'X')
    ReactorType = Xlate('PROD_SPEC', PSNo, 'REACTOR_TYPE', 'X')
    SigProfile = Signature_Services('GetSigProfile', WOMatKey, UseCaching)
    NumStages   = DCount(SigProfile, @VM)
    FinalStage  = SigProfile<0, NumStages>
    Response    = Signature_Services('GetSigInfo', WOMatKey, FinalStage, UseCaching)<2>
    
end service


Service CheckSigOrder(WOMatKey, CurrStage, UseCaching, RDSNo)
	
	ErrorMsg = ''
    StartTick = GetTickCount()
    MetricName = 'CheckSigOrder'
    
    If UseCaching EQ '' then UseCachine = True$
    Response    = False$
    If RDSNo NE '' then
		WONo = Xlate('RDS', RDSNo, 'WO', 'X')
	end else
		WONo = Field(WOMatKey, '*', 1)
	end
    ReactorType = Xlate('WO_LOG', WONo, 'REACTOR_TYPE', 'X')
    PSNo        = Xlate('WO_LOG', WONo, 'PS_NO', 'X')
    SigProfile  = obj_Prod_Spec('GetSigProfile', PSNo:@RM:'':@RM:WOMatKey:@RM:RDSNo)
    
    Locate CurrStage in SigProfile using @VM setting sPos then
        If sPos GT 1 then
            PrevStage = SigProfile<0, sPos-1>
        end else
            // There is no previous stage
            PrevStage = ''
        end
        
        // Check previous stage is complete
        If PrevStage NE '' then
            PrevStageSigInfo = Signature_Services('GetSigInfo', WOMatKey, PrevStage, UseCaching, RDSNo)
            PrevStageSig     = PrevStageSigInfo<1>
            PrevStageComp    = (PrevStageSig NE '')
        end else
            PrevStageSig  = ''
            PrevStageComp = True$
        end
        CurrStageComp = Signature_Services('CheckSignature', WOMatKey, CurrStage, UseCaching, RDSNo)
        
        Begin Case
            Case ( Not(MemberOf(@User4, 'BYPASS') ) and ( (CurrStage EQ 'QA') or (CurrStage EQ 'MO_QA') ) and (PrevStageSig EQ @User4) )
                ErrorMsg = 'FQA stage signature cannot match previous step ':PrevStage:' signature. ':WOMatKey:' (':Service:')'
            Case PrevStageComp EQ False$
                ErrorMsg = 'Previous step ':PrevStage:' is not signed. ':WOMatKey:' (':Service:')'
            Case CurrStageComp EQ True$
                // Allow user to re-sign. This may be necessary in certain cases such as when multiple surfscans
                // have taken place and one still needs to be signed.
                Response = True$
            Case Error_Services('HasError') EQ True$
                // Error message already in stack. 
                ErrorMsg = Error_Services('GetMessage')
            Case Otherwise$
                // Ok
                Response = True$                       
        End Case                              
    end else
        // Stage not prescribed in the PSN, so ignore the signature order.
        Response = True$
    end
    
    EndTick = GetTickCount()
    Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)
    
    If ErrorMsg NE '' then
    	Error_Services('Add', ErrorMsg)
    end
    
End Service


Service CheckSignature(WOMatKey, Stage, UseCaching, RDSNo)
    
    If UseCaching EQ '' then UseCaching = True$
    Response    = False$
    SigInfo     = Signature_Services('GetSigInfo', WOMatKey, Stage, UseCaching, RDSNo)
    If Error_Services('NoError') then
        Signature = SigInfo<1>
        SigDTM    = SigInfo<2>
        If Signature NE '' then Response = True$
    end
    
end service


Service GetSigInfo(WOMatKey, Stage, UseCaching, RDSNo)

    If UseCaching EQ '' then UseCaching = True$
    Signature   = ''
    SigDTM      = ''
    If RDSNo NE '' then
		WONo = Xlate('RDS', RDSNo, 'WO', 'X')
	end else
		WONo = Field(WOMatKey, '*', 1)
	end
    PSNo        = Xlate('WO_LOG', WONo, 'PS_NO', 'X')
    
    SigProfKey = PSNo:'*':WOMatKey:'*':RDSNo
    ReactorKey = PSNo:'*Reactor'
    
    If UseCaching and Memory_Services('IsValueCurrent', SigProfKey, 60, True$) then
        SigProfile = Memory_Services('GetValue', SigProfKey) 
    end else
        SigProfile  = obj_Prod_Spec('GetSigProfile', PSNo:@RM:'':@RM:WOMatKey:@RM:RDSNo)
        Memory_Services('SetValue', SigProfKey, SigProfile)
    end
    
    If UseCaching and Memory_Services('IsValueCurrent', ReactorKey, 60, True$) then
        ReactorType = Memory_Services('GetValue', ReactorKey)
    end else
        ReactorType = Xlate('PROD_SPEC', PSNo, 'REACTOR_TYPE', 'X')
        Memory_Services('SetValue', ReactorKey, ReactorType)
    end
    
    Locate Stage in SigProfile using @VM setting sPos then
        
        Begin Case
            * * EpiPro * *
            Case ( (ReactorType EQ 'EPP') or (ReactorType EQ 'P') )
                CassNo   = Field(WOMatKey, '*', 2)
                WMOKey   = WONo:'*1*':CassNo
                PostCINo = Xlate('WM_OUT', WMOKey, 'EPO_CI_NO', 'X')
                Begin Case
                	
                    * * Verify wafer quantity stage (aka PRE) * *
                    
                    Case Stage EQ 'VER'                        
                        Signature = Xlate('RDS', RDSNo, 'PRE_EPI_SIG', 'X')                        
                        SigDate   = OConv(Xlate('RDS', RDSNo, 'PRE_EPI_SIG_DT', 'X'), 'D')
                        SigTime   = OConv(Xlate('RDS', RDSNo, 'PRE_EPI_SIG_TIME', 'X'), 'MTS')                       
                        SigDTM    = IConv(SigDate:' ':SigTime, 'DT')
                        
                        * * Pre Epi Cleaning and Inspection Stages * *
                        
                    Case Stage EQ 'PREC'
                        PreCINo     = Xlate('RDS', RDSNo, 'PRE_CI_NO', 'X')
                        StageRows   = Xlate('CLEAN_INSP', PreCINo, 'CLEAN_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', PreCINo, 'CLEAN_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', PreCINo, 'CLEAN_SIG_DTM', 'X')
                        GoSub GetMostRecentSig
                        
                    Case Stage EQ 'PREI'
                        PreCINo     = Xlate('RDS', RDSNo, 'PRE_CI_NO', 'X')
                        StageRows   = Xlate('CLEAN_INSP', PreCINo, 'INSP_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', PreCINo, 'INSP_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', PreCINo, 'INSP_SIG_DTM', 'X')
                        GoSub GetMostRecentSig
                        
                    Case Stage EQ 'PRES'
                        PreCINo     = Xlate('RDS', RDSNo, 'PRE_CI_NO', 'X')
                        StageRows   = Xlate('CLEAN_INSP', PreCINo, 'SCAN_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', PreCINo, 'SCAN_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', PreCINo, 'SCAN_SIG_DTM', 'X')
                        GoSub GetMostRecentSig
                        
                        * * Reactor Load * *
                        
                    Case Stage EQ 'LOAD'
                        Signature = Xlate('RDS', RDSNo, 'OPERATOR_IN', 'X')
                        SigDate   = OConv(Xlate('RDS', RDSNo, 'DATE_IN', 'X'), 'D')
                        SigTime   = OConv(Xlate('RDS', RDSNo, 'TIME_IN', 'X'), 'MTS')
                        SigDTM    = IConv(SigDate:' ':SigTime, 'DT')
                        
                        * * First Wafer Inspection and Surfscan Stages * *
                        
                    Case Stage EQ 'FWII'	
                        FWICINo     = Xlate('RDS', RDSNo, 'FWI_CI_NO', 'X')
                        StageRows   = Xlate('CLEAN_INSP', FWICINo, 'INSP_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', FWICINo, 'INSP_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', FWICINo, 'INSP_SIG_DTM', 'X')
                        GoSub GetMostRecentSig
                        
                    Case Stage EQ 'FWIS'
                        FWICINo     = Xlate('RDS', RDSNo, 'FWI_CI_NO', 'X')
                        StageRows   = Xlate('CLEAN_INSP', FWICINo, 'SCAN_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', FWICINo, 'SCAN_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', FWICINo, 'SCAN_SIG_DTM', 'X')
                        GoSub GetMostRecentSig
                        
                        * * Reactor Unload * *
                        
                    Case Stage EQ 'UNLOAD'
                        Signature  = Xlate('RDS', RDSNo, 'OPERATOR_OUT', 'X')
                        SigDate    = OConv(Xlate('RDS', RDSNo, 'DATE_OUT', 'X'), 'D')
                        SigTime    = OConv(Xlate('RDS', RDSNo, 'TIME_OUT', 'X'), 'MTS')
                        SigDTM     = IConv(SigDate:' ':SigTime, 'DT')
                        
                        * * Last Wafer Inspection and Surfscan Stages  * *
                        
                    Case Stage EQ 'LWII'
                        LWICINo     = Xlate('RDS', RDSNo, 'LWI_CI_NO', 'X')
                        StageRows   = Xlate('CLEAN_INSP', LWICINo, 'INSP_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', LWICINo, 'INSP_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', LWICINo, 'INSP_SIG_DTM', 'X')
                        GoSub GetMostRecentSig  
                        
                    Case Stage EQ 'LWIS'
                        LWICINo     = Xlate('RDS', RDSNo, 'LWI_CI_NO', 'X')
                        StageRows   = Xlate('CLEAN_INSP', LWICINo, 'SCAN_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', LWICINo, 'SCAN_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', LWICINo, 'SCAN_SIG_DTM', 'X')
                        GoSub GetMostRecentSig
                        
                    Case Stage EQ 'POST'
                        Signature = Xlate('RDS', RDSNo, 'POST_EPI_SIG', 'X')
                        SigDate   = OConv(Xlate('RDS', RDSNo, 'POST_EPI_SIG_DATE', 'X'), 'D')
                        SigTime   = OConv(Xlate('RDS', RDSNo, 'POST_EPI_SIG_TIME', 'X'), 'MTS')
                        SigDTM    = IConv(SigDate:' ':SigTime, 'DT')
                        
                        * * Post Epi Clean and Inspection Stages  * *
                        
                    Case Stage EQ 'PSTC'
                        PostCINo    = Xlate('RDS', RDSNo, 'POST_CI_NO', 'X')
                        StageRows   = Xlate('CLEAN_INSP', PostCINo, 'CLEAN_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', PostCINo, 'CLEAN_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', PostCINo, 'CLEAN_SIG_DTM', 'X')
                        GoSub GetMostRecentSig
                        
                    Case Stage EQ 'PSTI'
                        PostCINo    = Xlate('RDS', RDSNo, 'POST_CI_NO', 'X')
                        StageRows   = Xlate('CLEAN_INSP', PostCINo, 'INSP_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', PostCINo, 'INSP_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', PostCINo, 'INSP_SIG_DTM', 'X')
                        GoSub GetMostRecentSig
                        
                    Case Stage EQ 'PSTS'
                        PostCINo    = Xlate('RDS', RDSNo, 'POST_CI_NO', 'X')
                        StageRows   = Xlate('CLEAN_INSP', PostCINo, 'SCAN_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', PostCINo, 'SCAN_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', PostCINo, 'SCAN_SIG_DTM', 'X')
                        GoSub GetMostRecentSig                	
                	
                	
                    Case Stage EQ 'MO_PSTC'
                        StageRows = Xlate('CLEAN_INSP', PostCINo, 'CLEAN_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', PostCINo, 'CLEAN_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', PostCINo, 'CLEAN_SIG_DTM', 'X')
                        GoSub GetMostRecentSig
                        
                    Case Stage EQ 'MO_PSTI'
                        StageRows   = Xlate('CLEAN_INSP', PostCINo, 'INSP_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', PostCINo, 'INSP_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', PostCINo, 'INSP_SIG_DTM', 'X')
                        GoSub GetMostRecentSig
                        
                    Case Stage EQ 'MO_PSTS'
                        StageRows   = Xlate('CLEAN_INSP', PostCINo, 'SCAN_RECIPE', 'X')
                        Signatures  = Xlate('CLEAN_INSP', PostCINo, 'SCAN_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', PostCINo, 'SCAN_SIG_DTM', 'X')         
                        GoSub GetMostRecentSig
                        
                    Case Stage EQ 'MO_QA'                        
                        Signature = Xlate('WM_OUT', WMOKey, 'SUP_VER_SIG', 'X')
                        SigDTM    = Xlate('WM_OUT', WMOKey, 'SUP_VER_SIG_DTM', 'X')
                        
                End Case               
                
                
                * * GaN * *
            Case (ReactorType EQ 'GAN')
                
                If Stage EQ 'G_FQA' then
                    Signature = Xlate('WO_MAT', WOMatKey, 'CASS_FINAL_SIG', 'X')
                    SigDTM    = Xlate('WO_MAT', WOMatKey, 'CASS_FINAL_SIG_DTM', 'X')
                end
                
                * * Silicon (aka Non-EpiPro) * *
            Case Otherwise$
                RDSNo = Xlate('WO_MAT', WOMatKey, 'RDS_NO', 'X')
                Begin Case
                    
                    * * Verify wafer quantity stage (aka PRE) * *
                    
                    Case Stage EQ 'VER'                        
                        Signature = Xlate('RDS', RDSNo, 'PRE_EPI_SIG', 'X')                        
                        SigDate   = OConv(Xlate('RDS', RDSNo, 'PRE_EPI_SIG_DT', 'X'), 'D')
                        SigTime   = OConv(Xlate('RDS', RDSNo, 'PRE_EPI_SIG_TIME', 'X'), 'MTS')                       
                        SigDTM    = IConv(SigDate:' ':SigTime, 'DT')
                        
                        * * Pre Epi Cleaning and Inspection Stages * *
                        
                    Case Stage EQ 'PREC'
                        PreCINo     = Xlate('RDS', RDSNo, 'PRE_CI_NO', 'X')
                        StageRows   = Xlate('CLEAN_INSP', PreCINo, 'CLEAN_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', PreCINo, 'CLEAN_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', PreCINo, 'CLEAN_SIG_DTM', 'X')
                        GoSub GetMostRecentSig
                        
                    Case Stage EQ 'PREI'
                        PreCINo     = Xlate('RDS', RDSNo, 'PRE_CI_NO', 'X')
                        StageRows   = Xlate('CLEAN_INSP', PreCINo, 'INSP_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', PreCINo, 'INSP_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', PreCINo, 'INSP_SIG_DTM', 'X')
                        GoSub GetMostRecentSig
                        
                    Case Stage EQ 'PRES'
                        PreCINo     = Xlate('RDS', RDSNo, 'PRE_CI_NO', 'X')
                        StageRows   = Xlate('CLEAN_INSP', PreCINo, 'SCAN_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', PreCINo, 'SCAN_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', PreCINo, 'SCAN_SIG_DTM', 'X')
                        GoSub GetMostRecentSig
                        
                        * * Reactor Load * *
                        
                    Case Stage EQ 'LOAD'
                        Signature = Xlate('RDS', RDSNo, 'OPERATOR_IN', 'X')
                        SigDate   = OConv(Xlate('RDS', RDSNo, 'DATE_IN', 'X'), 'D')
                        SigTime   = OConv(Xlate('RDS', RDSNo, 'TIME_IN', 'X'), 'MTS')
                        SigDTM    = IConv(SigDate:' ':SigTime, 'DT')
                        
                        * * First Wafer Inspection and Surfscan Stages * *
                        
                    Case Stage EQ 'FWII'	
                        FWICINo     = Xlate('RDS', RDSNo, 'FWI_CI_NO', 'X')
                        StageRows   = Xlate('CLEAN_INSP', FWICINo, 'INSP_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', FWICINo, 'INSP_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', FWICINo, 'INSP_SIG_DTM', 'X')
                        GoSub GetMostRecentSig
                        
                    Case Stage EQ 'FWIS'
                        FWICINo     = Xlate('RDS', RDSNo, 'FWI_CI_NO', 'X')
                        StageRows   = Xlate('CLEAN_INSP', FWICINo, 'SCAN_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', FWICINo, 'SCAN_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', FWICINo, 'SCAN_SIG_DTM', 'X')
                        GoSub GetMostRecentSig
                        
                        * * Reactor Unload * *
                        
                    Case Stage EQ 'UNLOAD'
                        Signature  = Xlate('RDS', RDSNo, 'OPERATOR_OUT', 'X')
                        SigDate    = OConv(Xlate('RDS', RDSNo, 'DATE_OUT', 'X'), 'D')
                        SigTime    = OConv(Xlate('RDS', RDSNo, 'TIME_OUT', 'X'), 'MTS')
                        SigDTM     = IConv(SigDate:' ':SigTime, 'DT')
                        
                        * * Last Wafer Inspection and Surfscan Stages  * *
                        
                    Case Stage EQ 'LWII'
                        LWICINo     = Xlate('RDS', RDSNo, 'LWI_CI_NO', 'X')
                        StageRows   = Xlate('CLEAN_INSP', LWICINo, 'INSP_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', LWICINo, 'INSP_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', LWICINo, 'INSP_SIG_DTM', 'X')
                        GoSub GetMostRecentSig  
                        
                    Case Stage EQ 'LWIS'
                        LWICINo     = Xlate('RDS', RDSNo, 'LWI_CI_NO', 'X')
                        StageRows   = Xlate('CLEAN_INSP', LWICINo, 'SCAN_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', LWICINo, 'SCAN_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', LWICINo, 'SCAN_SIG_DTM', 'X')
                        GoSub GetMostRecentSig
                        
                    Case Stage EQ 'POST'
                        Signature = Xlate('RDS', RDSNo, 'POST_EPI_SIG', 'X')
                        SigDate   = OConv(Xlate('RDS', RDSNo, 'POST_EPI_SIG_DATE', 'X'), 'D')
                        SigTime   = OConv(Xlate('RDS', RDSNo, 'POST_EPI_SIG_TIME', 'X'), 'MTS')
                        SigDTM    = IConv(SigDate:' ':SigTime, 'DT')
                        
                        * * Post Epi Clean and Inspection Stages  * *
                        
                    Case Stage EQ 'PSTC'
                        PostCINo    = Xlate('RDS', RDSNo, 'POST_CI_NO', 'X')
                        StageRows   = Xlate('CLEAN_INSP', PostCINo, 'CLEAN_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', PostCINo, 'CLEAN_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', PostCINo, 'CLEAN_SIG_DTM', 'X')
                        GoSub GetMostRecentSig
                        
                    Case Stage EQ 'PSTI'
                        PostCINo    = Xlate('RDS', RDSNo, 'POST_CI_NO', 'X')
                        StageRows   = Xlate('CLEAN_INSP', PostCINo, 'INSP_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', PostCINo, 'INSP_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', PostCINo, 'INSP_SIG_DTM', 'X')
                        GoSub GetMostRecentSig
                        
                    Case Stage EQ 'PSTS'
                        PostCINo    = Xlate('RDS', RDSNo, 'POST_CI_NO', 'X')
                        StageRows   = Xlate('CLEAN_INSP', PostCINo, 'SCAN_TOOL', 'X')
                        Signatures  = Xlate('CLEAN_INSP', PostCINo, 'SCAN_SIG', 'X')
                        SigDTMs     = Xlate('CLEAN_INSP', PostCINo, 'SCAN_SIG_DTM', 'X')
                        GoSub GetMostRecentSig
                        
                        * * Final QA Stage * *
                        
                    Case Stage EQ 'QA'
                        Signature = Xlate('RDS', RDSNo, 'SUP_VER_SIG', 'X')
                        SigDate   = OConv(Xlate('RDS', RDSNo, 'SUP_VER_SIG_DATE', 'X'), 'D')
                        SigTime   = OConv(Xlate('RDS', RDSNo, 'SUP_VER_SIG_TIME', 'X'), 'MTS')
                        SigDTM    = IConv(SigDate:' ':SigTime, 'DT')
                        
                End Case
        End Case
        
    end else
        ErrorMsg = Stage:' stage not found in signature profile. ':WOMatKey:' (':Service:')'
        Error_Services('Add', ErrorMsg)
    end
    
    Response = Signature:@FM:SigDTM
    
end service


//----------------------------------------------------------------------------------------------------------------------
// GetStageSummary
//
// Input: 
//      WOMatKey - [Required]
//      Stage    - [Required]
//
// Output:
//      Response<1> - True$ if stage passes all requirements, False$ otherwise.
//      Response<2> - True$ if stage has been signed, False$ otherwise.
//      Response<3> - True$ if stage requires data and has data, False$ otherwise.
//      Response<4> - True$ if stage not defined in the PSN, False$ otherwise.
//          
//----------------------------------------------------------------------------------------------------------------------
Service GetStageSummary(WOMatKey, Stage)
    
    StageSummary  = ''
    Error         = ''
    RDSNo         = Xlate('WO_MAT', WOMatKey, 'RDS_NO', 'X')
    SigProf       = Signature_Services('GetSigProfile', WOMatKey, False$)
    SigProfStages = SigProf<1>
    SigProfSigs   = SigProf<2>
    If (RowExists('WO_MAT', WOMatKey) and (Stage NE '') ) then
        Begin Case
            Case Stage EQ 'VER'
                // Ensure "Verify Wafers" quantity is not null
                // Ensure VER signature is not null
                If RDSNo NE '' then
                    VerSig                 = Xlate('RDS', RDSNo, 'PRE_EPI_SIG', 'X')
                    VerifyQty              = Xlate('RDS', RDSNo, 'VERIFY_QTY', 'X')
                    StageSummary<SS_PASS$> = ( (VerSig NE '') and (VerifyQty NE '') )
                    StageSummary<SS_SIG$>  = (VerSig NE '')
                    StageSummary<SS_DATA$> = (VerifyQty NE '')
                    Locate Stage in SigProfStages using @VM setting vPos then
                        StageSummary<SS_NA$> = False$
                    end else
                        StageSummary<SS_NA$> = True$
                    end
                end else
                    Error = 'Error in ':Service:' service. Null RDSNo for WO_MAT record ':WOMatKey:'!'
                end
            Case Stage EQ 'SUPP'
                // Verify RDS supplement information has been acknowledged
                If RDSNo NE '' then
                    SupplFlag              = Xlate('RDS', RDSNo, 'SUPPLEMENT', 'X')
                    SupplAckReq            = Xlate('RDS', RDSNo, 'SUPPL_ACK_REQ', 'X')
                    SupplSig               = Xlate('RDS', RDSNo, 'SUPPL_SIG', 'X')
                    StageSummary<SS_PASS$> = (SupplFlag EQ True$) and (SupplAckReq EQ False$)
                    StageSummary<SS_SIG$>  = (SupplSig NE '')
                    StageSummary<SS_DATA$> = (SupplFlag EQ True$)
                    StageSummary<SS_NA$>   = (SupplFlag NE True$) ; // Stage only applicable if supplement flag is true
                end else
                    Error = 'Error in ':Service:' service. Null RDSNo for WO_MAT record ':WOMatKey:'!'
                end
            Case Stage EQ 'LOAD'
                // Verify "Wafers In" quantity is not null
                // Ensure LOAD signature is not null
                If RDSNo NE '' then
                    LoadSig                = Xlate('RDS', RDSNo, 'OPERATOR_IN', 'X')
                    WafersIn               = Xlate('RDS', RDSNo, 'WAFERS_IN', 'X')
                    StageSummary<SS_PASS$> = ( (LoadSig NE '') and (WafersIn NE '') )
                    StageSummary<SS_SIG$>  = (LoadSig NE '')
                    StageSummary<SS_DATA$> = (WafersIn NE '')
                    Locate Stage in SigProfStages using @VM setting vPos then
                        StageSummary<SS_NA$> = False$
                    end else
                        StageSummary<SS_NA$> = True$
                    end
                end else
                    Error = 'Error in ':Service:' service. Null RDSNo for WO_MAT record ':WOMatKey:'!'
                end
            Case Stage EQ 'UNLOAD'
                // Ensure UNLOAD signature is not null
                If RDSNo NE '' then
                    UnloadSig              = Xlate('RDS', RDSNo, 'OPERATOR_OUT', 'X')
                    StageSummary<SS_PASS$> = (UnloadSig NE '')
                    StageSummary<SS_SIG$>  = (UnloadSig NE '')
                    StageSummary<SS_DATA$> = True$ ; // No data for this stage entered by user
                    Locate Stage in SigProfStages using @VM setting vPos then
                        StageSummary<SS_NA$> = False$
                    end else
                        StageSummary<SS_NA$> = True$
                    end
                end else
                    Error = 'Error in ':Service:' service. Null RDSNo for WO_MAT record ':WOMatKey:'!'
                end
            Case Stage EQ 'POST'
                // Ensure POST signature is not null
                If RDSNo NE '' then
                    PostEpiSig             = Xlate('RDS', RDSNo, 'POST_EPI_SIG', 'X')
                    StageSummary<SS_PASS$> = (PostEpiSig NE '')
                    StageSummary<SS_SIG$>  = (PostEpiSig NE '')
                    StageSummary<SS_DATA$> = True$  ; // No data for this stage entered by user
                    StageSummary<SS_NA$>   = False$ ; // This stage is essentially never required by the PSN, but is required by some code.
                end else
                    Error = 'Error in ':Service:' service. Null RDSNo for WO_MAT record ':WOMatKey:'!'
                end
            Case Stage EQ 'QA'
                // Verify QA signature is not null
                If RDSNo NE '' then
                    FQASig                 = Xlate('RDS', RDSNo, 'SUP_VER_SIG', 'X')
                    StageSummary<SS_PASS$> = (FQASig NE '')
                    StageSummary<SS_SIG$>  = (FQASig NE '')
                    StageSummary<SS_DATA$> = True$ ; //Signature_Services('QASigReady', RDSNo)
                    Locate Stage in SigProfStages using @VM setting vPos then
                        StageSummary<SS_NA$> = False$
                    end else
                        StageSummary<SS_NA$> = True$
                    end
                end else
                    Error = 'Error in ':Service:' service. Null RDSNo for WO_MAT record ':WOMatKey:'!'
                end
            Case Stage EQ 'RDS_TEST'
                // Gather RDS metrology information
                StageComplete = False$
                If RDSNo NE '' then
                    MetNo = Xlate('RDS', RDSNo, 'MET_KEYS', 'X')
                    If MetNo NE '' then
                        MetRec                 = Database_Services('ReadDataRow', 'RDS_TEST', MetNo)
                        TWSigned               = obj_RDS_Test('TWSignedOff', MetNo:@RM:MetRec)
                        TestComp               = obj_RDS_Test('TestComplete', MetNo:@RM:MetRec)
                        OutOfSpec              = obj_RDS_Test('OutOfSpec', MetNo:@RM:MetRec)
                        StageSummary<SS_PASS$> = (TWSigned and TestComp and Not(OutOfSpec))
                        StageSummary<SS_SIG$>  = TestComp
                        StageSummary<SS_DATA$> = TestComp
                        StageSummary<SS_NA$>   = False$ ; // I think RDS metrology is always required.
                    end
                end
            Case Stage EQ 'PREI'
                Locate Stage in SigProfStages using @VM setting vPos then
                    StageSummary<SS_NA$> = False$
                end else
                    StageSummary<SS_NA$> = True$
                end
                StageRx    = StageSummary<SS_NA$>
                CINo = Xlate('RDS', RDSNo, 'PRE_CI_NO', 'X')
                If CINo NE '' then
                    CITool     = Xlate('CLEAN_INSP', CINo, 'INSP_TOOL', 'X')
                    CISig      = Xlate('CLEAN_INSP', CINo, 'INSP_SIG', 'X')
                    Begin Case
                        Case ( (CITool EQ '') and (StageRx EQ True$) )
                            // Required data is missing
                            StageSummary<SS_DATA$> = False$
                            StageSummary<SS_SIG$>  = (CISig NE '')
                            StageSummary<SS_PASS$> = False$
                        Case ( (CITool NE '') and (CISig EQ '') )
                            // Data is present, but signature missing
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = False$
                            StageSummary<SS_PASS$> = False$
                        Case Otherwise$
                            // Full pass
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = True$
                            StageSummary<SS_PASS$> = True$
                    End Case
                end else
                    If StageRx then
                        // Data is missing
                        StageSummary<SS_DATA$> = False$
                        StageSummary<SS_SIG$>  = False$
                        StageSummary<SS_PASS$> = False$
                    end else
                        // Stage not applicable
                        StageSummary<SS_DATA$> = ''
                        StageSummary<SS_SIG$>  = ''
                        StageSummary<SS_PASS$> = ''
                    end
                end
            Case Stage EQ 'PREC'
                Locate Stage in SigProfStages using @VM setting vPos then
                    StageSummary<SS_NA$> = False$
                end else
                    StageSummary<SS_NA$> = True$
                end
                StageRx    = StageSummary<SS_NA$>
                CINo = Xlate('RDS', RDSNo, 'PRE_CI_NO', 'X')
                If (CINo NE '') then
                    CITool     = Xlate('CLEAN_INSP', CINo, 'CLEAN_TOOL', 'X')
                    CISig      = Xlate('CLEAN_INSP', CINo, 'CLEAN_SIG', 'X')
                    Begin Case
                        Case ( (CITool EQ '') and (StageRx EQ True$) )
                            // Required data is missing
                            StageSummary<SS_DATA$> = False$
                            StageSummary<SS_SIG$>  = (CISig NE '')
                            StageSummary<SS_PASS$> = False$
                        Case ( (CITool NE '') and (CISig EQ '') )
                            // Data is present, but signature missing
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = False$
                            StageSummary<SS_PASS$> = False$
                        Case Otherwise$
                            // Full pass
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = True$
                            StageSummary<SS_PASS$> = True$
                    End Case
                end else
                    If StageRx then
                        // Data is missing
                        StageSummary<SS_DATA$> = False$
                        StageSummary<SS_SIG$>  = False$
                        StageSummary<SS_PASS$> = False$
                    end else
                        // Stage not applicable
                        StageSummary<SS_DATA$> = ''
                        StageSummary<SS_SIG$>  = ''
                        StageSummary<SS_PASS$> = ''
                    end
                end
            Case Stage EQ 'PRES'
                Locate Stage in SigProfStages using @VM setting vPos then
                    StageSummary<SS_NA$> = False$
                end else
                    StageSummary<SS_NA$> = True$
                end
                CINo = Xlate('RDS', RDSNo, 'PRE_CI_NO', 'X')
                StageRx    = StageSummary<SS_NA$>
                If (CINo NE '') then
                    CITool     = Xlate('CLEAN_INSP', CINo, 'SPEC_SURFSCAN_RECIPE', 'X')
                    CISig      = Xlate('CLEAN_INSP', CINo, 'SCAN_SIG', 'X')
                    Begin Case
                        Case ( (CITool EQ '') and (StageRx EQ True$) )
                            // Required data is missing
                            StageSummary<SS_DATA$> = False$
                            StageSummary<SS_SIG$>  = (CISig NE '')
                            StageSummary<SS_PASS$> = False$
                        Case ( (CITool NE '') and (CISig EQ '') )
                            // Data is present, but signature missing
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = False$
                            StageSummary<SS_PASS$> = False$
                        Case Otherwise$
                            // Full pass
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = True$
                            StageSummary<SS_PASS$> = True$
                    End Case
                end else
                    If StageRx then
                        // Data is missing
                        StageSummary<SS_DATA$> = False$
                        StageSummary<SS_SIG$>  = False$
                        StageSummary<SS_PASS$> = False$
                    end else
                        // Stage not applicable
                        StageSummary<SS_DATA$> = ''
                        StageSummary<SS_SIG$>  = ''
                        StageSummary<SS_PASS$> = ''
                    end
                end
            Case Stage EQ 'FWII'
                Locate Stage in SigProfStages using @VM setting vPos then
                    StageSummary<SS_NA$> = False$
                end else
                    StageSummary<SS_NA$> = True$
                end
                StageRx    = StageSummary<SS_NA$>
                CINo = Xlate('RDS', RDSNo, 'FWI_CI_NO', 'X')
                If CINo NE '' then
                    CITool     = Xlate('CLEAN_INSP', CINo, 'INSP_TOOL', 'X')
                    CISig      = Xlate('CLEAN_INSP', CINo, 'INSP_SIG', 'X')
                    Begin Case
                        Case ( (CITool EQ '') and (StageRx EQ True$) )
                            // Required data is missing
                            StageSummary<SS_DATA$> = False$
                            StageSummary<SS_SIG$>  = (CISig NE '')
                            StageSummary<SS_PASS$> = False$
                        Case ( (CITool NE '') and (CISig EQ '') )
                            // Data is present, but signature missing
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = False$
                            StageSummary<SS_PASS$> = False$
                        Case Otherwise$
                            // Full pass
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = True$
                            StageSummary<SS_PASS$> = True$
                    End Case
                end else
                    If StageRx then
                        // Data is missing
                        StageSummary<SS_DATA$> = False$
                        StageSummary<SS_SIG$>  = False$
                        StageSummary<SS_PASS$> = False$
                    end else
                        // Stage not applicable
                        StageSummary<SS_DATA$> = ''
                        StageSummary<SS_SIG$>  = ''
                        StageSummary<SS_PASS$> = ''
                    end
                end
            Case Stage EQ 'FWIC'
                Locate Stage in SigProfStages using @VM setting vPos then
                    StageSummary<SS_NA$> = False$
                end else
                    StageSummary<SS_NA$> = True$
                end
                StageRx    = StageSummary<SS_NA$>
                CINo = Xlate('RDS', RDSNo, 'FWI_CI_NO', 'X')
                If (CINo NE '') then
                    CITool     = Xlate('CLEAN_INSP', CINo, 'CLEAN_TOOL', 'X')
                    CISig      = Xlate('CLEAN_INSP', CINo, 'CLEAN_SIG', 'X')
                    Begin Case
                        Case ( (CITool EQ '') and (StageRx EQ True$) )
                            // Required data is missing
                            StageSummary<SS_DATA$> = False$
                            StageSummary<SS_SIG$>  = (CISig NE '')
                            StageSummary<SS_PASS$> = False$
                        Case ( (CITool NE '') and (CISig EQ '') )
                            // Data is present, but signature missing
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = False$
                            StageSummary<SS_PASS$> = False$
                        Case Otherwise$
                            // Full pass
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = True$
                            StageSummary<SS_PASS$> = True$
                    End Case
                end else
                    If StageRx then
                        // Data is missing
                        StageSummary<SS_DATA$> = False$
                        StageSummary<SS_SIG$>  = False$
                        StageSummary<SS_PASS$> = False$
                    end else
                        // Stage not applicable
                        StageSummary<SS_DATA$> = ''
                        StageSummary<SS_SIG$>  = ''
                        StageSummary<SS_PASS$> = ''
                    end
                end
            Case Stage EQ 'FWIS'
                Locate Stage in SigProfStages using @VM setting vPos then
                    StageSummary<SS_NA$> = False$
                end else
                    StageSummary<SS_NA$> = True$
                end
                CINo = Xlate('RDS', RDSNo, 'FWI_CI_NO', 'X')
                StageRx    = StageSummary<SS_NA$>
                If (CINo NE '') then
                    CITool     = Xlate('CLEAN_INSP', CINo, 'SPEC_SURFSCAN_RECIPE', 'X')
                    CISig      = Xlate('CLEAN_INSP', CINo, 'SCAN_SIG', 'X')
                    Begin Case
                        Case ( (CITool EQ '') and (StageRx EQ True$) )
                            // Required data is missing
                            StageSummary<SS_DATA$> = False$
                            StageSummary<SS_SIG$>  = (CISig NE '')
                            StageSummary<SS_PASS$> = False$
                        Case ( (CITool NE '') and (CISig EQ '') )
                            // Data is present, but signature missing
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = False$
                            StageSummary<SS_PASS$> = False$
                        Case Otherwise$
                            // Full pass
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = True$
                            StageSummary<SS_PASS$> = True$
                    End Case
                end else
                    If StageRx then
                        // Data is missing
                        StageSummary<SS_DATA$> = False$
                        StageSummary<SS_SIG$>  = False$
                        StageSummary<SS_PASS$> = False$
                    end else
                        // Stage not applicable
                        StageSummary<SS_DATA$> = ''
                        StageSummary<SS_SIG$>  = ''
                        StageSummary<SS_PASS$> = ''
                    end
                end
            Case Stage EQ 'LOAD_QA_MET'
                // Gather load QA metrology information
                NumTests      = 0
                TestSigs      = ''
                TestOOSs      = ''
                TestResults   = ''
                Error         = ''
                NotApplicable = True$
                StageComplete = False$
                DataComp      = False$
                SigsComp      = False$
                WOMatKey      = Xlate('RDS', RDSNo, 'WO_MAT_KEY', 'X')
                If RowExists('WO_MAT', WOMatKey) then
                    WOMatQARec   = Database_Services('ReadDataRow', 'WO_MAT_QA', WOMatKey)
                    QAMetStages  = WOMatQARec<WO_MAT_QA_STAGE$>
                    QAMetSigs    = WOMatQARec<WO_MAT_QA_SIG$>
                    QAMetOOSs    = WOMatQARec<WO_MAT_QA_OUT_OF_SPEC$>
                    QAMetResults = WOMatQARec<WO_MAT_QA_RESULT$>
                    For each QAMetStage in QAMetStages using @VM setting vPos
                        If QAMetStage EQ 'FWI' then
                            NumTests             += 1
                            NotApplicable         = False$
                            TestSig               = QAMetSigs<0, vPos>
                            TestOOS               = QAMetOOSs<0, vPos>
                            TestResult            = QAMetResults<0, vPos>
                            TestSigs<0, vPos>     = (TestSig NE '')
                            TestOOSs<0, vPos>     = TestOOS
                            TestResults<0, vPos>  = (TestResult NE '')
                        end
                    Next QAMetStage
                    DataComp = ( ( NumTests EQ Sum(TestResults) ) and (NumTests GT 0) )
                    SigsComp = ( ( NumTests EQ Sum(TestSigs) ) and (NumTests GT 0) )
                end
                StageSummary<SS_DATA$> = DataComp
                StageSummary<SS_SIG$>  = SigsComp
                StageSummary<SS_PASS$> = (DataComp and SigsComp)
                StageSummary<SS_NA$>   = NotApplicable
            Case Stage EQ 'LWII'
                Locate Stage in SigProfStages using @VM setting vPos then
                    StageSummary<SS_NA$> = False$
                end else
                    StageSummary<SS_NA$> = True$
                end
                StageRx    = StageSummary<SS_NA$>
                CINo = Xlate('RDS', RDSNo, 'LWI_CI_NO', 'X')
                If CINo NE '' then
                    CITool     = Xlate('CLEAN_INSP', CINo, 'INSP_TOOL', 'X')
                    CISig      = Xlate('CLEAN_INSP', CINo, 'INSP_SIG', 'X')
                    Begin Case
                        Case ( (CITool EQ '') and (StageRx EQ True$) )
                            // Required data is missing
                            StageSummary<SS_DATA$> = False$
                            StageSummary<SS_SIG$>  = (CISig NE '')
                            StageSummary<SS_PASS$> = False$
                        Case ( (CITool NE '') and (CISig EQ '') )
                            // Data is present, but signature missing
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = False$
                            StageSummary<SS_PASS$> = False$
                        Case Otherwise$
                            // Full pass
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = True$
                            StageSummary<SS_PASS$> = True$
                    End Case
                end else
                    If StageRx then
                        // Data is missing
                        StageSummary<SS_DATA$> = False$
                        StageSummary<SS_SIG$>  = False$
                        StageSummary<SS_PASS$> = False$
                    end else
                        // Stage not applicable
                        StageSummary<SS_DATA$> = ''
                        StageSummary<SS_SIG$>  = ''
                        StageSummary<SS_PASS$> = ''
                    end
                end
            Case Stage EQ 'LWIC'
                Locate Stage in SigProfStages using @VM setting vPos then
                    StageSummary<SS_NA$> = False$
                end else
                    StageSummary<SS_NA$> = True$
                end
                StageRx    = StageSummary<SS_NA$>
                CINo = Xlate('RDS', RDSNo, 'LWI_CI_NO', 'X')
                If (CINo NE '') then
                    CITool     = Xlate('CLEAN_INSP', CINo, 'CLEAN_TOOL', 'X')
                    CISig      = Xlate('CLEAN_INSP', CINo, 'CLEAN_SIG', 'X')
                    Begin Case
                        Case ( (CITool EQ '') and (StageRx EQ True$) )
                            // Required data is missing
                            StageSummary<SS_DATA$> = False$
                            StageSummary<SS_SIG$>  = (CISig NE '')
                            StageSummary<SS_PASS$> = False$
                        Case ( (CITool NE '') and (CISig EQ '') )
                            // Data is present, but signature missing
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = False$
                            StageSummary<SS_PASS$> = False$
                        Case Otherwise$
                            // Full pass
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = True$
                            StageSummary<SS_PASS$> = True$
                    End Case
                end else
                    If StageRx then
                        // Data is missing
                        StageSummary<SS_DATA$> = False$
                        StageSummary<SS_SIG$>  = False$
                        StageSummary<SS_PASS$> = False$
                    end else
                        // Stage not applicable
                        StageSummary<SS_DATA$> = ''
                        StageSummary<SS_SIG$>  = ''
                        StageSummary<SS_PASS$> = ''
                    end
                end
            Case Stage EQ 'LWIS'
                Locate Stage in SigProfStages using @VM setting vPos then
                    StageSummary<SS_NA$> = False$
                end else
                    StageSummary<SS_NA$> = True$
                end
                CINo = Xlate('RDS', RDSNo, 'LWI_CI_NO', 'X')
                StageRx    = StageSummary<SS_NA$>
                If (CINo NE '') then
                    CITool     = Xlate('CLEAN_INSP', CINo, 'SPEC_SURFSCAN_RECIPE', 'X')
                    CISig      = Xlate('CLEAN_INSP', CINo, 'SCAN_SIG', 'X')
                    Begin Case
                        Case ( (CITool EQ '') and (StageRx EQ True$) )
                            // Required data is missing
                            StageSummary<SS_DATA$> = False$
                            StageSummary<SS_SIG$>  = (CISig NE '')
                            StageSummary<SS_PASS$> = False$
                        Case ( (CITool NE '') and (CISig EQ '') )
                            // Data is present, but signature missing
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = False$
                            StageSummary<SS_PASS$> = False$
                        Case Otherwise$
                            // Full pass
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = True$
                            StageSummary<SS_PASS$> = True$
                    End Case
                end else
                    If StageRx then
                        // Data is missing
                        StageSummary<SS_DATA$> = False$
                        StageSummary<SS_SIG$>  = False$
                        StageSummary<SS_PASS$> = False$
                    end else
                        // Stage not applicable
                        StageSummary<SS_DATA$> = ''
                        StageSummary<SS_SIG$>  = ''
                        StageSummary<SS_PASS$> = ''
                    end
                end
            Case Stage EQ 'ROTR'
                
                CINo = Xlate('RDS', RDSNo, 'LWI_CI_NO', 'X')
                If (CINo NE '') then
                    CITool           = Xlate('CLEAN_INSP', CINo, 'SPEC_SURFSCAN_RECIPE', 'X')
                    CISig            = Xlate('CLEAN_INSP', CINo, 'SCAN_SIG', 'X')
                    ROTRAction       = Xlate('CLEAN_INSP', CINo, 'RDS_ROTR_ACTION', 'X')
                    ROTRActionReason = Xlate('CLEAN_INSP', CINo, 'ROTR_ACTION_REASON', 'X')
                    Begin Case
                        Case ( (ROTRAction EQ 'F' ) and (CISig NE '') )
                            // ROTR Acceptance required
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = False$
                            StageSummary<SS_PASS$> = False$
                            StageSummary<SS_NA$>   = False$
                        Case ( (ROTRAction EQ 'F') and (CISig EQ '') and (ROTRActionReason EQ 'SurfScan signature is missing.') )
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = False$
                            StageSummary<SS_PASS$> = False$
                            StageSummary<SS_NA$>   = False$
                        Case ( (ROTRAction EQ 'F') and (CISig EQ '') )
                            StageSummary<SS_DATA$> = False$
                            StageSummary<SS_SIG$>  = False$
                            StageSummary<SS_PASS$> = False$
                            StageSummary<SS_NA$>   = False$
                        Case Otherwise$
                            // Pass
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = True$
                            StageSummary<SS_PASS$> = True$
                            StageSummary<SS_NA$>   = False$
                    End Case
                end else
                    If StageRx then
                        // Data is missing
                        StageSummary<SS_DATA$> = False$
                        StageSummary<SS_SIG$>  = False$
                        StageSummary<SS_PASS$> = False$
                    end else
                        // Stage not applicable
                        StageSummary<SS_DATA$> = ''
                        StageSummary<SS_SIG$>  = ''
                        StageSummary<SS_PASS$> = ''
                    end
                end
            Case Stage EQ 'UNLOAD_QA_MET'
                // Gather unload QA metrology information
                NumTests      = 0
                TestSigs      = ''
                TestOOSs      = ''
                TestResults   = ''
                Error         = ''
                NotApplicable = True$
                StageComplete = False$
                DataComp      = False$
                SigsComp      = False$
                WOMatKey      = Xlate('RDS', RDSNo, 'WO_MAT_KEY', 'X')
                If RowExists('WO_MAT', WOMatKey) then
                    WOMatQARec   = Database_Services('ReadDataRow', 'WO_MAT_QA', WOMatKey)
                    QAMetStages  = WOMatQARec<WO_MAT_QA_STAGE$>
                    QAMetSigs    = WOMatQARec<WO_MAT_QA_SIG$>
                    QAMetOOSs    = WOMatQARec<WO_MAT_QA_OUT_OF_SPEC$>
                    QAMetResults = WOMatQARec<WO_MAT_QA_RESULT$>
                    For each QAMetStage in QAMetStages using @VM setting vPos
                        If QAMetStage EQ 'UNLOAD' then
                            NumTests             += 1
                            NotApplicable         = False$
                            TestSig               = QAMetSigs<0, vPos>
                            TestOOS               = QAMetOOSs<0, vPos>
                            TestResult            = QAMetResults<0, vPos>
                            TestSigs<0, vPos>     = (TestSig NE '')
                            TestOOSs<0, vPos>     = TestOOS
                            TestResults<0, vPos>  = (TestResult NE '')
                        end
                    Next QAMetStage
                    DataComp = ( ( NumTests EQ Sum(TestResults) ) and (NumTests GT 0) )
                    SigsComp = ( ( NumTests EQ Sum(TestSigs) ) and (NumTests GT 0) )
                end
                StageSummary<SS_DATA$> = DataComp
                StageSummary<SS_SIG$>  = SigsComp
                StageSummary<SS_PASS$> = (DataComp and SigsComp)
                StageSummary<SS_NA$>   = NotApplicable
            Case Stage EQ 'PSTI'
                Locate Stage in SigProfStages using @VM setting vPos then
                    StageSummary<SS_NA$> = False$
                end else
                    StageSummary<SS_NA$> = True$
                end
                StageRx    = StageSummary<SS_NA$>
                CINo = Xlate('RDS', RDSNo, 'POST_CI_NO', 'X')
                If CINo NE '' then
                    CITool     = Xlate('CLEAN_INSP', CINo, 'INSP_TOOL', 'X')
                    CISig      = Xlate('CLEAN_INSP', CINo, 'INSP_SIG', 'X')
                    Begin Case
                        Case ( (CITool EQ '') and (StageRx EQ True$) )
                            // Required data is missing
                            StageSummary<SS_DATA$> = False$
                            StageSummary<SS_SIG$>  = (CISig NE '')
                            StageSummary<SS_PASS$> = False$
                        Case ( (CITool NE '') and (CISig EQ '') )
                            // Data is present, but signature missing
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = False$
                            StageSummary<SS_PASS$> = False$
                        Case Otherwise$
                            // Full pass
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = True$
                            StageSummary<SS_PASS$> = True$
                    End Case
                end else
                    If StageRx then
                        // Data is missing
                        StageSummary<SS_DATA$> = False$
                        StageSummary<SS_SIG$>  = False$
                        StageSummary<SS_PASS$> = False$
                    end else
                        // Stage not applicable
                        StageSummary<SS_DATA$> = ''
                        StageSummary<SS_SIG$>  = ''
                        StageSummary<SS_PASS$> = ''
                    end
                end
            Case Stage EQ 'PSTC'
                Locate Stage in SigProfStages using @VM setting vPos then
                    StageSummary<SS_NA$> = False$
                end else
                    StageSummary<SS_NA$> = True$
                end
                StageRx    = StageSummary<SS_NA$>
                CINo = Xlate('RDS', RDSNo, 'POST_CI_NO', 'X')
                If (CINo NE '') then
                    CITool     = Xlate('CLEAN_INSP', CINo, 'CLEAN_TOOL', 'X')
                    CISig      = Xlate('CLEAN_INSP', CINo, 'CLEAN_SIG', 'X')
                    Begin Case
                        Case ( (CITool EQ '') and (StageRx EQ True$) )
                            // Required data is missing
                            StageSummary<SS_DATA$> = False$
                            StageSummary<SS_SIG$>  = (CISig NE '')
                            StageSummary<SS_PASS$> = False$
                        Case ( (CITool NE '') and (CISig EQ '') )
                            // Data is present, but signature missing
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = False$
                            StageSummary<SS_PASS$> = False$
                        Case Otherwise$
                            // Full pass
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = True$
                            StageSummary<SS_PASS$> = True$
                    End Case
                end else
                    If StageRx then
                        // Data is missing
                        StageSummary<SS_DATA$> = False$
                        StageSummary<SS_SIG$>  = False$
                        StageSummary<SS_PASS$> = False$
                    end else
                        // Stage not applicable
                        StageSummary<SS_DATA$> = ''
                        StageSummary<SS_SIG$>  = ''
                        StageSummary<SS_PASS$> = ''
                    end
                end
            Case Stage EQ 'PSTS'
                Locate Stage in SigProfStages using @VM setting vPos then
                    StageSummary<SS_NA$> = False$
                end else
                    StageSummary<SS_NA$> = True$
                end
                CINo = Xlate('RDS', RDSNo, 'POST_CI_NO', 'X')
                StageRx    = StageSummary<SS_NA$>
                If (CINo NE '') then
                    CITool     = Xlate('CLEAN_INSP', CINo, 'SPEC_SURFSCAN_RECIPE', 'X')
                    CISig      = Xlate('CLEAN_INSP', CINo, 'SCAN_SIG', 'X')
                    Begin Case
                        Case ( (CITool EQ '') and (StageRx EQ True$) )
                            // Required data is missing
                            StageSummary<SS_DATA$> = False$
                            StageSummary<SS_SIG$>  = (CISig NE '')
                            StageSummary<SS_PASS$> = False$
                        Case ( (CITool NE '') and (CISig EQ '') )
                            // Data is present, but signature missing
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = False$
                            StageSummary<SS_PASS$> = False$
                        Case Otherwise$
                            // Full pass
                            StageSummary<SS_DATA$> = True$
                            StageSummary<SS_SIG$>  = True$
                            StageSummary<SS_PASS$> = True$
                    End Case
                end else
                    If StageRx then
                        // Data is missing
                        StageSummary<SS_DATA$> = False$
                        StageSummary<SS_SIG$>  = False$
                        StageSummary<SS_PASS$> = False$
                    end else
                        // Stage not applicable
                        StageSummary<SS_DATA$> = ''
                        StageSummary<SS_SIG$>  = ''
                        StageSummary<SS_PASS$> = ''
                    end
                end
            Case Stage EQ 'POST_QA_MET'
                // Gather post-epi QA metrology information
                NumTests      = 0
                TestSigs      = ''
                TestOOSs      = ''
                TestResults   = ''
                Error         = ''
                NotApplicable = True$
                StageComplete = False$
                DataComp      = False$
                SigsComp      = False$
                If RowExists('WO_MAT', WOMatKey) then
                    WOMatQARec   = Database_Services('ReadDataRow', 'WO_MAT_QA', WOMatKey)
                    QAMetStages  = WOMatQARec<WO_MAT_QA_STAGE$>
                    QAMetSigs    = WOMatQARec<WO_MAT_QA_SIG$>
                    QAMetOOSs    = WOMatQARec<WO_MAT_QA_OUT_OF_SPEC$>
                    QAMetResults = WOMatQARec<WO_MAT_QA_RESULT$>
                    For each QAMetStage in QAMetStages using @VM setting vPos
                        If QAMetStage EQ 'QA' then
                            NumTests             += 1
                            NotApplicable         = False$
                            TestSig               = QAMetSigs<0, vPos>
                            TestOOS               = QAMetOOSs<0, vPos>
                            TestResult            = QAMetResults<0, vPos>
                            TestSigs<0, vPos>     = (TestSig NE '')
                            TestOOSs<0, vPos>     = TestOOS
                            TestResults<0, vPos>  = (TestResult NE '')
                        end
                    Next QAMetStage
                    DataComp = ( ( NumTests EQ Sum(TestResults) ) and (NumTests GT 0) )
                    SigsComp = ( ( NumTests EQ Sum(TestSigs) ) and (NumTests GT 0) )
                end
                StageSummary<SS_DATA$> = DataComp
                StageSummary<SS_SIG$>  = SigsComp
                StageSummary<SS_PASS$> = (DataComp and SigsComp)
                StageSummary<SS_NA$>   = NotApplicable
            Case Stage EQ 'LBLCHK'
                // Verify LBLCHK inv action exists in WO_MAT material log.
                NotApplicable = False$
                InvActions    = Xlate('WO_MAT', WOMatKey, 'INV_ACTION', 'X')
                Locate Stage in InvActions using @VM setting vPos then
                    DataComp = True$
                    SigsComp = True$
                end else
                    DataComp = False$
                    SigsComp = False$
                end
                StageSummary<SS_DATA$> = DataComp
                StageSummary<SS_SIG$>  = SigsComp
                StageSummary<SS_PASS$> = (DataComp and SigsComp)
                StageSummary<SS_NA$>   = NotApplicable
            Case Stage EQ 'NCR'
                // Gather NCR information
                NotApplicable = True$
                DataComp      = False$
                SigsComp      = False$
                NumSigs       = 0
                NumData       = 0
                NCRKeys       = Xlate('WO_MAT', WOMatKey, 'NCR_KEYS', 'X')
                NCRSigs       = Xlate('WO_MAT', WOMatKey, 'NCR_FINAL_SIG', 'X')
                If NCRKeys NE '' then
                    NotApplicable = False$
                    NumKeys       = DCount(NCRKeys, @VM)
                    For each NCRNo in NCRKeys using @VM setting vPos
                        Sig    = Xlate('NCR', NCRNo, 'AUTH_REJ_SIG', 'X')
                        If Sig NE '' then NumSigs += 1
                        Error     = False$
                        ReqFields = 'SHIFT,DEPT,DEPT_RESP,LOSS_CODE,LOSS_COMMENTS,CONTAIN_ACTIONS,LOSS_STAGE,LOSS_BY'
                        For each ReqField in ReqFields using ',' setting cPos
                            Val = Xlate('NCR', NCRNo, ReqField, 'X')
                            If Val EQ '' then Error = True$
                        Until Error
                        Next ReqField
                        If Error EQ False$ then NumData += 1
                    Next NCRNo
                    SigsComp  = (NumKeys EQ NumSigs)
                    DataComp  = (NumKeys EQ NumData)
                end
                StageSummary<SS_DATA$> = DataComp
                StageSummary<SS_SIG$>  = SigsComp
                StageSummary<SS_PASS$> = (DataComp and SigsComp)
                StageSummary<SS_NA$>   = NotApplicable
            Case Stage EQ 'SAP_BATCH_ID'
                // Verify SAP Batch ID is not null
                SAPBatchNo             = Xlate('WO_MAT', WOMatKey, 'SAP_BATCH_NO', 'X')
                DataComp               = (SapBatchNo NE '')
                SigsComp               = DataComp
                StageSummary<SS_DATA$> = DataComp
                StageSummary<SS_SIG$>  = SigsComp
                StageSummary<SS_PASS$> = (DataComp and SigsComp)
                StageSummary<SS_NA$>   = False$
            Case Otherwise$
                // Check stage signature to determine if it is complete.
                StageComplete = True$
                SigInfo       = Signature_Services('GetSigInfo', WOMatKey, Stage, False$)
                If Error_Services('NoError') then
                    Sig           = SigInfo<1>
                    StageComplete = (Sig NE '')
                end else
                    // Stage may not apply to this cassette
                    ErrorMessage  = Error_Services('GetMessage')
                    NotApplicable = (IndexC(ErrorMessage, 'signature profile', 1) GT 0)
                    If NotApplicable EQ True$ then StageComplete = 2
                end
                If StageComplete EQ 0 then Error = Stage:' not signed.'
        End Case
    end
    
    Response = StageSummary
    
end service


Service GetCassSummary(WOMatKey)
    
    CassSummary = ''
    If RowExists('WO_MAT', WOMatKey) then
        // Collect signature and metrology pass/fail data.
        Stages    = Database_Services('ReadDataRow', 'APP_INFO', 'SILICON_STAGE_LIST')
        For each Stage in Stages using @FM setting fPos
            StageSummary         = Signature_Services('GetStageSummary', WOMatKey, Stage)
            CassSummary<fPos, 1> = Stage
            CassSummary<fPos, 2> = StageSummary<SS_PASS$>
            CassSummary<fPos, 3> = StageSummary<SS_SIG$>
            CassSummary<fPos, 4> = StageSummary<SS_DATA$>
            CassSummary<fPos, 5> = StageSummary<SS_NA$>
        Next Stage
    end
    Response = CassSummary
    
end service


Service GetStageIDs()
    
    Response = Database_Services('ReadDataRow', 'APP_INFO', 'SILICON_STAGE_LIST')
    
end service


Service PostEpiSigReady(RDSNo)
    
    ErrCode         = ''
    ErrorMessage    = ''
    RDSRec          = Database_Services('ReadDataRow', 'RDS', RDSNo)
    WONo            = RDSRec<RDS_WO$>
    CassNo          = RDSRec<RDS_CASS_NO$>
    Reactor         = RDSRec<RDS_REACTOR$>
    WOStep          = 1
    WOMatKey        = WONo:'*':CassNo
    ReprocessedMat  = XLATE('WO_MAT', WOMatKey, 'REPROCESSED_MAT', 'X')
    WOMatCurrStatus = obj_WO_Mat('CurrStatus', WOMatKey)
    If (WOMatCurrStatus NE 'HOLD') then
        Set_Status(0)
        obj_WO_Mat('MQAComp', WOMatKey:@RM:WOStep:@RM:'UNLOAD')
        If Not(Get_Status(ErrCode)) then
            Response = True$
        end else
            ErrorMessage = 'Error in ':Service:' service. Error code: ':ErrCode
        end
    end else
        ErrorMessage  = 'Cassette is on hold and may not be signed off.'
    end
    If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
    
end service


Service SignPostEpiStage(RDSNo, User, WMOKey)
    
    ErrorMessage = ''
    Begin Case
        Case (User EQ '')
            ErrorMessage = 'Error in ':Service:' service. Null User passed into service.'
        Case ( (RDSNo NE '') and (WMOKey NE '') )
            ErrorMessage = 'Error in ':Service:' service. RDSNo and WMOKey cannot both be passed in.'
        Case ( (RDSNo NE '') and (WMOKey EQ '') )
            // Sign RDS post-epi signature
            RDSRec    = Database_Services('ReadDataRow', 'RDS', RDSNo)
            WONo      = RDSRec<RDS_WO$>
            CassNo    = RDSRec<RDS_CASS_NO$>
            WOMatKey  = WONo:'*':CassNo
            MakeupBox = Xlate('RDS', RDSNo, 'MAKEUP_BOX', 'X')
            
            RawSigDt      = Date()
            RawSigTm      = Time()
            SigDt         = OConv(Date(), 'D2/')
            SigTm         = Time()
            SigTmPlusOne  = OConv(SigTm + 1, 'MTS')
            SigTmPlusTwo  = OConv(SigTm + 2, 'MTS')
            SigTmPlusFive = OConv(SigTm + 5, 'MTS')
            SigTmPlusTen  = OConv(SigTm + 10, 'MTS')
            SigTm         = OConv(SigTm,'MTS')
            SigBy         = User
            WOStep        = 1
            EventParms    = ''
            EventCnt      = 1
                              
            ToolID = ''
            WHCd   = 'CR'
            LocCD  = 'QA'
            Tag    = ''

            EventParms<COL$LOG_FILE> = 'WO_MAT'
            EventParms<COL$LOG_DTM>  = SigDt:' ':SigTm
            EventParms<COL$ACTION>   = WOStep:'POST'
            EventParms<COL$WH_CD>    = 'CR'
            EventParms<COL$LOC_CD>   = 'POST'
            EventParms<COL$WO_NOS>   = WONo
            EventParms<COL$CASS_NOS> = CassNo
            EventParms<COL$USER_ID>  = User
            EventParms<COL$TAGS>     = ''
            EventParms<COL$TOOL_ID>  = ''
            
            CONVERT @FM TO @RM IN EventParms
            
            obj_WO_Mat_Log('Create', EventParms)        
            
            IF Get_Status(ErrCode) THEN
                ErrorMessage = 'Error in ':Service:' service. Error code: ':ErrCode
            END
            
            If ErrorMessage EQ '' then
                IF MakeupBox EQ True$ THEN
                    
                    EventParms<COL$LOG_FILE> = 'WO_MAT'
                    EventParms<COL$LOG_DTM>  = SigDt:' ':SigTmPlusTwo
                    EventParms<COL$ACTION>   = 'RTU'
                    EventParms<COL$WH_CD>    = 'CR'
                    EventParms<COL$LOC_CD>   = 'MU'
                    EventParms<COL$WO_NOS>   = WONo
                    EventParms<COL$CASS_NOS> = CassNo
                    EventParms<COL$USER_ID>  = User
                    EventParms<COL$TAGS>     = ''
                    EventParms<COL$TOOL_ID>  = ''
                    
                    CONVERT @FM TO @RM IN EventParms
                    
                    obj_WO_Mat_Log('Create', EventParms)        ;* * * * * INV EVENT LOG * * * * *
                    
                    IF Get_Status(ErrCode) THEN
                        ErrorMessage = 'Error in ':Service:' service. Error code: ':ErrCode
                    END
                END

                If ErrorMessage EQ '' then
                    RDSRec<RDS_POST_EPI_SIG$>      = User
                    RDSRec<RDS_POST_EPI_SIG_DATE$> = IConv(SigDt, 'D')
                    RDSRec<RDS_POST_EPI_SIG_TIME$> = IConv(SigTm, 'MT') + 1
                    Database_Services('WriteDataRow', 'RDS', RDSNo, RDSRec, True$, False$, False$)
                    If Error_Services('HasError') then
                        ErrorMessage = Error_Services('GetMessage')
                    end
                end
            end            
            
        Case ( (RDSNo EQ '') and (WMOKey NE '') )
            // Sign WM_OUT post-epi signature
            WMORec = Database_Services('ReadDataRow', 'WM_OUT', WMOKey)
            If Error_Services('NoError') then
                WMORec<WM_OUT_POST_EPI_SIG$>     = User
                WMORec<WM_OUT_POST_EPI_SIG_DTM$> = Datetime()
                Database_Services('WriteDataRow', 'WM_OUT', WMOKey, WMORec, True$, False$, True$)
            end
    End Case
    
    If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
    
end service


Service QASigReady(RDSNo)
    
    ErrCode      = ''
    ErrorMessage = ''
    RDSRec       = Database_Services('ReadDataRow', 'RDS', RDSNo)
    WONo         = RDSRec<RDS_WO$>
    CassNo       = RDSRec<RDS_CASS_NO$>
    Reactor      = RDSRec<RDS_REACTOR$>
    WOStep       = 1
    WOMatKey     = WONo:'*':CassNo
    ReactorType  = Xlate('RDS', RDSNo, 'REACTOR_TYPE', 'X')
    
    
    ******************************************
    * Verify if the Post-EPI has been signed *
    ******************************************
    PostEpiSig = Xlate('RDS', RDSNo, 'POST_EPI_SIG', 'X')
    IF (PostEpiSig = '') THEN
        ErrorMessage = 'Process Error':@SVM:'Technician Signature Required before Final QA Verification.'
    END
    
    If ErrorMessage EQ '' then
        IF Not(MemberOf(@USER4,'BYPASS')) THEN        
            IF (PostEpiSig EQ @USER4) THEN
                ErrorMessage  = 'User ':QUOTE(PostEpiSig):' has signed this RDS for Post-Epi.'
                ErrorMessage := 'Another qualified user must sign FQA verification'
            END
        END
    end
    
    ***************************************
    * Verify Metrology has been completed *
    ***************************************
    If ErrorMessage EQ '' then
        Set_Status(0)
        obj_WO_Mat('MQAComp',WONo:'*':CassNo:@RM:WOStep:@RM:'QA')
        ErrCode = ''
        IF Get_Status(ErrCode) THEN
            ErrorMessage = 'Error in ':Service:' service. Error code: ':ErrCode
        END
    end
    
    ***********************************************
    * Verify RDS_TEST record has measurement data *
    ***********************************************
    If ErrorMessage EQ '' then
        ThickAvg = Xlate('RDS', RDSNo, 'TTHICK_AVG_ALL', 'X')
        If ThickAvg EQ '' then
            ErrorMessage = 'RDS metrology measurement data is missing. FQA cannot be signed.'
        end
        ResAvg = Xlate('RDS', RDSNo, 'TRES_AVG_ALL', 'X')
        If ResAvg EQ '' then
            ErrorMessage = 'RDS Resistivity measurement data is missing. FQA cannot be signed.'
            Recipients   = XLATE('SEC_GROUPS', 'OI_ADMIN', 'USER', 'X')
            SentFrom     = 'OI Admin'
            Subject      = 'Missing Resistivity Avg data'
            Message      = RDSNo: ' failed FQA due to missing resistivity average data.'
            AttachWindow = ''
            AttachKey    = ''   
            SendToGroup  = ''   
            Parms        = Recipients:@RM:SentFrom:@RM:Subject:@RM:Message:@RM:AttachWindow:@RM:AttachKey:@RM:SendToGroup
            obj_Notes('Create',Parms) 
        end
    end
    
    ************************************************
    * Verify Signatures Profile has been fulfilled *
    ************************************************
    If ErrorMessage EQ '' then
        Signature_Services('CheckSigOrder', WOMatKey, 'QA')
        If Error_Services('HasError') then            
            ErrorMessage = Error_Services('GetMessage')
        end
    end
    
    If ErrorMessage EQ '' then
        Signature_Services('FQAReady', WOMatKey)
        If Error_Services('HasError') then
            ErrorMessage = Error_Services('GetMessage')
        end   
    end
    
    WafersOut = Xlate('RDS', RDSNo, 'WFRS_OUT', 'X')
    **********************************************
    * Verify the FlatFinder information          *
    **********************************************
    If ErrorMessage EQ '' then
        EpiPartNo            = Xlate('RDS', RDSNo, 'EPI_PART_NO', 'X')
        WaferSize            = Xlate('EPI_PART', EpiPartNo, 'SUB_WAFER_SIZE', 'X')
        WaferSizeInch        = Field(WaferSize, ' ', 3, 1)
        CustNo               = Xlate('RDS', RDSNo, 'CUST_NO', 'X')
        CompanyRow           = Xlate('COMPANY', CustNo, '', 'X')
        WaferFlatSizeInches  = CompanyRow<COMPANY_WAFER_FLAT_WAFER_SIZE_INCH$>
        WaferFlatLengthMins  = Oconv(CompanyRow<COMPANY_WAFER_FLAT_LENGTH_MIN$>, 'MD1')
        WaferFlatLengthMaxes = Oconv(CompanyRow<COMPANY_WAFER_FLAT_LENGTH_MAX$>, 'MD1')
        Locate 6 in WaferFlatSizeInches using @VM setting vPos then
            WaferFlatLengthMin  = WaferFlatLengthMins<0, vPos>
            WaferFlatLengthMax  = WaferFlatLengthMaxes<0, vPos>
        end else
            WaferFlatLengthMin  = ''
            WaferFlatLengthMax  = ''
        end
        WafersOut = Xlate('RDS', RDSNo, 'WFRS_OUT', 'X')
        
        // Get FlatFinder Read Value
        *        FlatFinderWafersQty   = RDSRec<RDS_FLATFINDER_WAFER_CNT$>
        FlatFinderWaferLength = RDSRec<RDS_FLATFINDER_FLAT_LENGTH$>
        
        
        If (WaferSizeInch EQ 6) then
            ****************************************
            * FlatFinder - Wafers Quantity Section *
            ****************************************
            *        If (FlatFinderWafersQty NE '') then
            *            If (FlatFinderWafersQty NE WafersOut) then
            *                ErrMsg('Unable to sign FQA because Flat Finder and Wafers Out quantities do not match.')
            *                RETURN 0
            *            end
            *        end else
            *            ErrMsg('Unable to sign FQA because the Flat Finder quantity is missing.')
            *            RETURN 0
            *        end
            ************************************
            * FlatFinder - Flat Length Section *
            ************************************
            If (WaferFlatLengthMin NE '') AND (WaferFlatLengthMax NE '') then
                If (FlatFinderWaferLength NE '') then
                    If (FlatFinderWaferLength GE WaferFlatLengthMin) AND (FlatFinderWaferLength LE WaferFlatLengthMax) then
                    end else
                        ErrMsg('Unable to sign FQA because Flat Finder wafer lengths are out of bounds.')
                        RETURN 0
                    end
                end else
                    ErrMsg('Unable to sign FQA because the Flat Finder wafer length is missing.')
                    RETURN 0
                end
            end
        end
        
        Begin Case
            
            Case WaferSizeInch = '6'
                
                WaferFlatSizeInches     = CompanyRow<COMPANY_WAFER_FLAT_WAFER_SIZE_INCH$>
                WaferFlatLengthMins     = Oconv(CompanyRow<COMPANY_WAFER_FLAT_LENGTH_MIN$>, 'MD1')
                WaferFlatLengthMaxes    = Oconv(CompanyRow<COMPANY_WAFER_FLAT_LENGTH_MAX$>, 'MD1')
                
                Locate WaferSizeInch in WaferFlatSizeInches using @VM setting vPos then
                    WaferFlatLengthMin  = WaferFlatLengthMins<0, vPos>
                    WaferFlatLengthMax  = WaferFlatLengthMaxes<0, vPos>
                end else
                    WaferFlatLengthMin  = ''
                    WaferFlatLengthMax  = ''
                end
                
                ***************************************
                * FlatFinder - Wafers Quatity Section *
                ***************************************
                *                If (FlatFinderWafersQty NE '') then
                *                    If (FlatFinderWafersQty NE WafersOut) then
                *                        ErrorMessage = 'Unable to sign FQA because Flat Finder and Wafers Out quantities do not match.'
                *                    end
                *                end else
                *                    ErrorMessage = 'Unable to sign FQA because the Flat Finder quantity is missing.'
                *                end
                ************************************
                * FlatFinder - Flat Length Section *
                ************************************
                If (WaferFlatLengthMin NE '') AND (WaferFlatLengthMax NE '') then
                    If (FlatFinderWaferLength NE '') then
                        If (FlatFinderWaferLength GE WaferFlatLengthMin) AND (FlatFinderWaferLength LE WaferFlatLengthMax) then
                        end else
                            ErrorMessage = 'Unable to sign FQA because Flat Finder wafer lengths are out of bounds.'
                        end
                    end else
                        ErrorMessage = 'Unable to sign FQA because the Flat Finder wafer length is missing.'
                    end
                end
                
            Case WaferSizeInch = '8'
                
                // Get NotchFinder Read Value
                *                NotchFinderWafersQty     = FlatFinderWafersQty
                
                ***************************************
                * NotchFinder - Wafers Quatity Section *
                ***************************************
                *            If (NotchFinderWafersQty NE '') then
                *                If (NotchFinderWafersQty NE WafersOut) then
                *                    ErrorMsg = 'Unable to sign FQA because Notch Finder and Wafers Out quantities do not match.'
                *                    Error_Services('Add', ErrorMsg)
                *                    return
                *                end
                *            end else
                *                ErrorMsg = 'Unable to sign FQA because the Notch Finder quantity is missing.'
                *                Error_Services('Add', ErrorMsg)
                *                return
                *            end
                
            Case Otherwise$
        End Case
    end
    
    
    *************************
    * Verify Wafer Quantity *
    *************************
    If ErrorMessage EQ '' then
        CassSchedWafers = Xlate('RDS', RDSNo, 'WAFERS_SCHEDULED', 'X')
        
        If CassSchedWafers NE WafersOut then
            
            Message     = 'Unable to sign FQA because the Scheduled' : CRLF$ : 'Qty does not equal the '
            Message     := 'Wafers Out Qty.' : CRLF$ : 'Lead or supervisor must override.'
            LeadMessage = 'Scheduled Wafers Quantity does not equal the Wafers Out Quantity.'
            
            If NOT( MemberOf(@USER4, 'LEAD') OR MemberOf(@USER4, 'SUPERVISOR') ) then
                Response = Msg(@Window, '', 'POST_EPI_WAFER_OUT', '', Message)
            end else
                Response = Msg(@Window, '', 'POST_EPI_WAFER_OUT', '', LeadMessage)
            end
            
            Begin Case
                Case Response EQ 1
                    Response = True$        ; // User Clicked Override
                Case Response EQ 2
                    Response = False$       ; // User Clicked Cancel
                Case Response EQ char(27)
                    Response = False$       ; // User Pressed Escape Key
            End Case            
            
            If Response EQ True$ then
                Response    = Dialog_Box('NDW_VERIFY_USER', @WINDOW, @USER4 : @FM : 'LEAD' : @VM : 'SUPERVISOR')
                Valid       = Response<1>
                Username    = Response<2>
                If Valid then
                    RDSRec<RDS_WAFER_OUT_SIG$>  = Username
                    RDSRec<RDS_WAFER_OUT_DATE$> = Date()
                    RDSRec<RDS_WAFER_OUT_TIME$> = Time()
                    Database_Services('WriteDataRow', 'RDS', RDSNo, RDSRec)
                    If Error_Services('HasError') then
                        ErrorMessage = Error_Services('GetMessage')
                    end
                end else
                    ErrorMessage  = 'Unable to sign FQA because the Scheduled' : CRLF$ : 'Qty does not equal the '
                    ErrorMessage := 'Wafers Out Qty.' : CRLF$ : 'Lead or supervisor must override.'
                end
            end else
                ErrorMessage  = 'Unable to sign FQA because the Scheduled' : CRLF$ : 'Qty does not equal the '
                ErrorMessage := 'Wafers Out Qty.' : CRLF$ : 'Lead or supervisor must override.'
            end
            
        end
    end
    
    *********************************
    * Verify NCR total >= USL Fails *
    *********************************
    If ErrorMessage EQ '' then
        ROTREnabled = Xlate('REACTOR', Reactor, 'ENABLE_ROTR', 'X')
        If (ROTREnabled EQ True$) then
            LWICIKey          = Xlate('RDS', RDSNo, 'LWI_CI_NO', 'X')
            NumUSLFailValues  = Xlate('CLEAN_INSP', LWICIKey, 'NUM_FAILED_WAFERS', 'X')
            NumUSLFailCount   = COUNT(NumUSLFailValues,@VM) + (NumUSLFailValues NE '')
            NumUSLFail        = NumUSLFailValues<1,NumUSLFailCount>
            If NumUSLFail EQ '' then NumUSLFail = 0
            TotalNCR    = Xlate('RDS', RDSNo, 'TOT_REJ', 'X')
            If TotalNCR EQ '' then TotalNCR = 0
            
            If NumUSLFail GT TotalNCR then
                Message     = 'Unable to sign FQA because the total quantity of NCR wafers':CRLF$:'is less than the '|
                : 'number of wafers above the USL threshold.' : CRLF$ : 'A lead or supervisor must override.'
                LeadMessage = 'Total quantity of NCR wafers is less than the number of wafers above the USL threshold.'
                
                If NOT( MemberOf(@USER4, 'LEAD') OR MemberOf(@USER4, 'SUPERVISOR') ) then
                    Response = Msg(@Window, '', 'POST_EPI_WAFER_OUT', '', Message)
                end else
                    Response = Msg(@Window, '', 'POST_EPI_WAFER_OUT', '', LeadMessage)
                end
                
                Begin Case
                    Case Response EQ 1
                        Response = True$        ; // User Clicked Override
                    Case Response EQ 2
                        Response = False$       ; // User Clicked Cancel
                    Case Response EQ char(27)
                        Response = False$       ; // User Pressed Escape Key
                End Case            
                
                If Response EQ True$ then
                    Response    = Dialog_Box('NDW_VERIFY_USER', @WINDOW, @USER4:@FM:'LEAD':@VM:'SUPERVISOR':@VM:'ENGINEER')
                    Valid       = Response<1>
                    If NOT(Valid) then
                        ErrorMessage = Message
                    end
                end else
                    ErrorMessage = Message
                end
            end
        end
    end
    
    ********************************************
    * Verify NCR wafer qty >= Failed wafer qty *
    ********************************************
    If ErrorMessage EQ '' then
        NCRReq = QA_Services('GetNCRRequired', WOMatKey)
        If NCRReq then
            Message     = 'Unable to sign FQA because the total quantity of NCR wafers':CRLF$:'is less than the '|
            : 'number of wafers that failed surfscan.' : CRLF$ : 'A lead or supervisor must override.'
            LeadMessage = 'Total quantity of NCR wafers is less than the number of wafers that failed surfscan.'
            
            If NOT( MemberOf(@USER4, 'LEAD') OR MemberOf(@USER4, 'SUPERVISOR') ) then
                Response = Msg(@Window, '', 'POST_EPI_WAFER_OUT', '', Message)
            end else
                Response = Msg(@Window, '', 'POST_EPI_WAFER_OUT', '', LeadMessage)
            end
            
            Begin Case
                Case Response EQ 1
                    Response = True$        ; // User Clicked Override
                Case Response EQ 2
                    Response = False$       ; // User Clicked Cancel
                Case Response EQ char(27)
                    Response = False$       ; // User Pressed Escape Key
            End Case            
            
            If Response EQ True$ then
                Response    = Dialog_Box('NDW_VERIFY_USER', @WINDOW, @USER4:@FM:'LEAD':@VM:'SUPERVISOR':@VM:'ENGINEER')
                Valid       = Response<1>
                If NOT(Valid) then
                    ErrorMessage = Message
                end
            end else
                ErrorMessage = Message
            end
        end
    end
    
    *************************
    * Verify if Shift exist *
    *************************
    If ErrorMessage EQ '' then
        Shift        = RDSRec<RDS_SHIFT$>
        IF Shift EQ '' THEN
            ErrorMessage = 'Error in ':Service:' servce. Shift is required before signing.'
        END
    end
    
    ****************************
    * Verify if lot is on Hold *
    ****************************
    If ErrorMessage EQ '' then
        WOMatCurrStatus = obj_WO_Mat('CurrStatus', WOMatKey)
        IF WOMatCurrStatus EQ 'HOLD' THEN
            ErrorMessage = 'Error in ':Service:' service. Cassette is on Hold and may not be signed off.'
        END
    end
    
    **********************
    * Check ROTR Failure *
    **********************
    If ErrorMessage EQ '' then
        ROTRAction = Xlate('RDS', RDSNo, 'ROTR_ACTION', 'X')
        If (ROTRAction NE 'P') AND (ROTRAction NE 'A') AND (ROTRAction NE '') then
            ErrorMessage = 'Error in ':Service:' service. ROTR does not meet all requirements.'
        end
    end
    
    ***************************************
    * Verify Metrology has been completed *
    ***************************************
    If ErrorMessage EQ '' then
        QAMetTests   = Xlate('RDS', RDSNo, 'MET_TEST_QA', 'X')
        If QAMetTests NE '' then
            QAMetMins = Xlate('RDS', RDSNo, 'MET_MIN_QA', 'X')
            QAMetMaxes   = Xlate('RDS', RDSNo, 'MET_MAX_QA', 'X')
            QAMetResults = Xlate('RDS', RDSNo, 'MET_RESULT_QA', 'X')
            QAMetSigs    = Xlate('RDS', RDSNo, 'MET_SIG_QA', 'X')
            For each QAMetTest in QAMetTests using @VM setting vPos
                QAMetMin    = QAMetMins<0, vPos>
                QAMetMax    = QAMetMaxes<0, vPos>
                QAMetResult = QAMetResults<0, vPos>
                QAMetSig    = QAMetSigs<0, vPos>
                If QAMetTest NE '' then
                    Begin Case
                        Case ( (QAMetMin EQ '') and (QAMetMax EQ '') )
                            Null
                        Case (QAMetResult EQ '')
                            ErrorMessage = 'Error in ':Service:' service. Required QA Metrology results have not been entered.'
                        Case ( (QAMetResult LT QAMetMin) or (QAMetResult GT QAMetMax) )
                            ErrorMessage = 'Error in ':Service:' service. One or more QA Metrology results is out of specification.'
                        Case ( (QAMetResult NE '') and (QAMetSig EQ '') )
                            ErrorMessage = 'Error in ':Service:' service. One or more QA Metrology results are not signed off.'
                    End Case
                end
            Next QAMetTest
        end
    end
    
    ******************************************
    * Verify Unload Stage QA Metrology Tests *
    ******************************************
    If ErrorMessage EQ '' then
		WOMatQAKey = WOMatKey
		WOMatQARec = Database_Services('ReadDataRow', 'WO_MAT_QA', WOMatQAKey)
		OutOfSpec  = WOMatQARec<WO_MAT_QA_OUT_OF_SPEC$>
		OutOfSpec  = Sum(OutOfSpec)
		If OutOfSpec GT 0 then
			FailReasons  = WOMatQARec<WO_MAT_QA_FAIL_REASON$>
			ErrorMessage = 'Error in ':Service:' service. '
			For each FailReason in FailReasons using @VM
				ErrorMessage := FailReason:' '
			Next FailReason
		end
    end
    
    ************************************************************************
    * Prompt user to validate the Process Specification Stage Instructions *
    ************************************************************************
    If ErrorMessage EQ '' then
        PSNo    = RDSRec<RDS_PROD_SPEC_ID$>
        VerInst = Xlate('PRS_STAGE', PSNo:'*QA', 'INST', 'X')
        VerInst = Trim(VerInst)
        IF LEN(VerInst) > 5 THEN
            Yes = Dialog_Box( 'RDS_VER', @WINDOW, VerInst )
            IF NOT(Yes) THEN
                ErrorMessage = 'Error in ':Service:'. PRS_STAGE instructions must be accepted.'
            END
        END
    end
    If ErrorMessage NE '' then
        Response = False$
        Error_Services('Add', ErrorMessage)
    end else
        Response = True$
    end
    
end service


Service SignQAStage(RDSNo, User)
    
    ErrorMessage = ''
    If RDSNo NE '' then
        RDSRec    = Database_Services('ReadDataRow', 'RDS', RDSNo)
        WONo      = RDSRec<RDS_WO$>
        CassNo    = RDSRec<RDS_CASS_NO$>
        WOMatKey  = WONo:'*':CassNo
        MakeupBox = Xlate('RDS', RDSNo, 'MAKEUP_BOX', 'X')
        
        RawSigDt      = Date()
        RawSigTm      = Time()
        SigDt         = OConv(Date(), 'D2/')
        SigTm         = Time()
        SigTmPlusOne  = OConv(SigTm + 1, 'MTS')
        SigTmPlusTwo  = OConv(SigTm + 2, 'MTS')
        SigTmPlusFive = OConv(SigTm + 5, 'MTS')
        SigTmPlusTen  = OConv(SigTm + 10, 'MTS')
        SigTm         = OConv(SigTm,'MTS')
        SigBy         = User
        WOStep        = 1
        EventParms    = ''
        EventCnt      = 1
        
        ToolID = ''
        WHCd   = 'CR'
        LocCD  = 'QA'
        Tag    = ''
        
        EventParms<COL$LOG_FILE> = 'WO_MAT'
        EventParms<COL$LOG_DTM>  = SigDt:' ':SigTm
        EventParms<COL$ACTION>   = WOStep:'QA'
        EventParms<COL$WH_CD>    = 'CR'
        EventParms<COL$LOC_CD>   = 'QA'
        EventParms<COL$WO_NOS>   = WONo
        EventParms<COL$CASS_NOS> = CassNo
        EventParms<COL$USER_ID>  = User
        EventParms<COL$TAGS>     = ''
        EventParms<COL$TOOL_ID>  = ''
        
        CONVERT @FM TO @RM IN EventParms
        
        obj_WO_Mat_Log('Create', EventParms)        
        
        IF Get_Status(ErrCode) THEN
            ErrorMessage = 'Error in ':Service:' service. Error code: ':ErrCode
        END
        
        If ErrorMessage EQ '' then
            IF MakeupBox EQ True$ THEN
                
                EventParms<COL$LOG_FILE> = 'WO_MAT'
                EventParms<COL$LOG_DTM>  = SigDt:' ':SigTmPlusTwo
                EventParms<COL$ACTION>   = 'RTU'
                EventParms<COL$WH_CD>    = 'CR'
                EventParms<COL$LOC_CD>   = 'MU'
                EventParms<COL$WO_NOS>   = WONo
                EventParms<COL$CASS_NOS> = CassNo
                EventParms<COL$USER_ID>  = User
                EventParms<COL$TAGS>     = ''
                EventParms<COL$TOOL_ID>  = ''
                
                CONVERT @FM TO @RM IN EventParms
                
                obj_WO_Mat_Log('Create', EventParms)        ;* * * * * INV EVENT LOG * * * * *
                
                IF Get_Status(ErrCode) THEN
                    ErrorMessage = 'Error in ':Service:' service. Error code: ':ErrCode
                END
            END
            
            If ErrorMessage EQ '' then
                RDSRec<RDS_SUP_VER_SIG$>      = User
                RDSRec<RDS_SUP_VER_SIG_DATE$> = IConv(SigDt, 'D')
                RDSRec<RDS_SUP_VER_SIG_TIME$> = IConv(SigTm, 'MT') + 1
                Database_Services('WriteDataRow', 'RDS', RDSNo, RDSRec, True$, False$, False$)
                If Error_Services('HasError') then
                    ErrorMessage = Error_Services('GetMessage')
                end
            end
        end
    end else
        ErrorMessage = 'Error in ':Service:' service. Null RDSNo passed into service.'
    end
    If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
    
end service


Service CleanInspSigReady(RDSNo, Stage)
    
    ErrorMessage = ''
    If ( (RDSNo NE '') and (Stage NE '') ) then
        WONo     = Xlate('RDS', RDSNo, 'WO', 'X')
        CassNo   = Xlate('RDS', RDSNo, 'CASS_NO', 'X')
        WOMatKey = WONo:'*':CassNo
        Signature_Services('CheckSigOrder', WOMatKey, Stage)
        If Error_Services('HasError') then ErrorMessage = Error_Services('GetMessage')
        Begin Case
            Case ( (Stage EQ 'PREI') or (Stage EQ 'PREC') or (Stage EQ 'PRES') )
                CINo = Xlate('RDS', RDSNo, 'PRE_CI_NO', 'X')
            Case ( (Stage EQ 'FWII') or (Stage EQ 'FWIC') or (Stage EQ 'FWIS') ) 
                CINo = Xlate('RDS', RDSNo, 'FWI_CI_NO', 'X')
            Case ( (Stage EQ 'LWII') or (Stage EQ 'LWIC') or (Stage EQ 'LWIS') )
                CINo = Xlate('RDS', RDSNo, 'LWI_CI_NO', 'X')
            Case ( (Stage EQ 'PSTI') or (Stage EQ 'PSTC') or (Stage EQ 'PSTS') )
                CINo = Xlate('RDS', RDSNo, 'POST_CI_NO', 'X')
            Case Otherwise$
                ErrorMessage = 'Error in ':Service:' service. Invalid stage ID passed into service.'
        End Case
        If ErrorMessage EQ '' then
            If RowExists('CLEAN_INSP', CINo) then
                CIRow = Database_Services('ReadDataRow', 'CLEAN_INSP', CINo)
                If Error_Services('HasError') then
                    ErrorMessage = Error_Services('GetMessage')
                end
            end else
                ErrorMessage = 'Error in ':Service:' service. No ':Stage:' record on file for RDS ':RDSNo:'!'
            end
        end
        Begin Case
            Case ErrorMessage NE ''
                Null
            Case ( (Stage EQ 'PREI') or (Stage EQ 'FWII') or (Stage EQ 'LWII') or (Stage EQ 'PSTI') )
                // Verify each inspection row has data.
                // Front side data
                InspTools           = CIRow<CLEAN_INSP_INSP_TOOL$>
                InspLPDs            = CIRow<CLEAN_INSP_INSP_LPD$>
                InspScratches       = CIRow<CLEAN_INSP_INSP_SCRATCHES$>
                InspScratchLengths  = CIRow<CLEAN_INSP_INSP_SCRATCH_LEN$>
                InspPits            = CIRow<CLEAN_INSP_INSP_PITS$>
                InspMounds          = CIRow<CLEAN_INSP_INSP_MOUNDS$>
                InspStackFaults     = CIRow<CLEAN_INSP_INSP_STACK_FAULTS$>
                InspSpikes          = CIRow<CLEAN_INSP_INSP_SPIKES$>
                InspSpots           = CIRow<CLEAN_INSP_INSP_SPOTS$>
                InspFOVs            = CIRow<CLEAN_INSP_INSP_FOV$>
                InspBLDefects       = CIRow<CLEAN_INSP_INSP_BL_DEFECTS$>
                InspSigs            = CIRow<CLEAN_INSP_INSP_SIG$>
                InspSigDTMs         = CIRow<CLEAN_INSP_INSP_SIG_DTM$>
                // Front side specs
                SpecLPD             = CIRow<CLEAN_INSP_SPEC_LPD$>
                SpecScratches       = CIRow<CLEAN_INSP_SPEC_SCRATCHES$>
                SpecScratchLen      = CIRow<CLEAN_INSP_SPEC_SCRATCH_LEN$>
                SpecPits            = CIRow<CLEAN_INSP_SPEC_PITS$>
                SpecMounds          = CIRow<CLEAN_INSP_SPEC_MOUNDS$>
                SpecStackFaults     = CIRow<CLEAN_INSP_SPEC_STACK_FAULTS$>
                SpecSpikes          = CIRow<CLEAN_INSP_SPEC_SPIKES$>
                SpecSpots           = CIRow<CLEAN_INSP_SPEC_SPOTS$>
                SpecFOV             = CIRow<CLEAN_INSP_SPEC_FOV$>
                SpecBLDefects       = CIRow<CLEAN_INSP_SPEC_BL_DEFECTS$>
                // Backside data
                InspBsideScratches  = CIRow<CLEAN_INSP_INSP_BSIDE_SCRATCHES$>
                InspBsideScratchLen = CIRow<CLEAN_INSP_INSP_BSIDE_SCRATCH_LEN$>
                InspBsideNodules    = CIRow<CLEAN_INSP_INSP_BSIDE_NODULES$>
                InspBsideSpikes     = CIRow<CLEAN_INSP_INSP_BSIDE_SPIKES$>
                // Backside specs
                SpecBsideScratches  = CIRow<CLEAN_INSP_SPEC_BSIDE_SCRATCHES$>
                SpecBsideScratchLen = CIRow<CLEAN_INSP_SPEC_BSIDE_SCRATCH_LEN$>
                SpecBsideNodules    = CIRow<CLEAN_INSP_SPEC_BSIDE_NODULES$>
                SpecBsideSpikes     = CIRow<CLEAN_INSP_SPEC_BSIDE_SPIKES$>
                // Verify front side and back side data
                NumRows             = DCount(InspTools, @VM)
                If NumRows GT 0 then
                    For RowIndex = 1 to NumRows
                        RowLPD             = InspLPDs<0, RowIndex>
                        RowScratches       = InspScratches<0, RowIndex>
                        RowScratchLen      = InspScratchLengths<0, RowIndex>
                        RowPits            = InspPits<0, RowIndex>
                        RowMounds          = InspMounds<0, RowIndex>
                        RowStackFaults     = InspStackFaults<0, RowIndex>
                        RowSpikes          = InspSpikes<0, RowIndex>
                        RowSpots           = InspSpots<0, RowIndex>
                        RowFOV             = InspFOVs<0, RowIndex>
                        RowBLDefects       = InspBLDefects<0, RowIndex>
                        RowBsideScratches  = InspBsideScratches<0, RowIndex>
                        RowBsideScratchLen = InspBsideScratchLen<0, RowIndex>
                        RowBsideNodules    = InspBsideNodules<0, RowIndex>
                        RowBsideSpikes     = InspBsideSpikes<0, RowIndex>
                        If ( (SpecLPD NE '') and (RowLPD EQ '') ) then
                            ErrorMessage = 'Error in ':Service:' service. Inspection LPD data is missing.'
                        end
                        If ( (ErrorMessage EQ '') and (SpecScratches NE '') and (RowScratches EQ '') ) then
                            ErrorMessage = 'Error in ':Service:' service. Inspection scratches data is missing.'
                        end
                        If ( ( ErrorMessage EQ '') and  (SpecScratchLen NE '') and (RowScratchLen EQ '' ) ) then
                            ErrorMessage = 'Error in ':Service:' service. Inspection scratches data is missing.'
                        end
                        If ( (ErrorMessage EQ '') and (SpecPits NE '') and (RowPits EQ '') ) then
                            ErrorMessage = 'Error in ':Service:' service. Inspection pits data is missing.'
                        end
                        If ( (ErrorMessage EQ '') and (SpecMounds NE '') and (RowMounds EQ '') ) then
                            ErrorMessage = 'Error in ':Service:' service. Inspection mounds data is missing.'
                        end
                        If ( (ErrorMessage EQ '') and (SpecStackFaults NE '') and (RowStackFaults EQ '') ) then
                            ErrorMessage = 'Error in ':Service:' service. Inspection stack faults data is missing.'
                        end
                        If ( (ErrorMessage EQ '') and (SpecSpikes NE '') and (RowSpikes EQ '') ) then
                            ErrorMessage = 'Error in ':Service:' service. Inspection spikes data is missing.'
                        end
                        If ( (ErrorMessage EQ '') and (SpecSpots NE '') and (RowSpots EQ '' ) ) then
                            ErrorMessage = 'Error in ':Service:' service. Inspection spots data is missing.'
                        end
                        If ( (ErrorMessage EQ '') and (SpecFOV NE '') and (RowFOV EQ '') ) then
                            ErrorMessage = 'Error in ':Service:' service. Inspection FOV data is missing.'
                        end
                        If ( (ErrorMessage EQ '') and (SpecBLDefects NE '') and (RowBLDefects EQ '') ) then
                            ErrorMessage = 'Error in ':Service:' service. Inspection BL defects data is missing.'
                        end
                        If ( (ErrorMessage EQ '') and (SpecBsideScratches NE '') and (RowBsideScratches EQ '') ) then
                            ErrorMessage = 'Error in ':Service:' service. Inspection back side scratches data is missing.'
                        end
                        If ( (ErrorMessage EQ '') and (SpecBsideScratchLen NE '') and (RowBsideScratchLen EQ '') ) then
                            ErrorMessage = 'Error in ':Service:' service. Inspection back side scratch length data is missing.'
                        end
                        If ( (ErrorMessage EQ '') and (SpecBsideNodules NE '') and (RowBsideNodules EQ '') ) then
                            ErrorMessage = 'Error in ':Service:' service. Inspection back side nodules data is missing.'
                        end
                        If ( (ErrorMessage EQ '') and (SpecBsideSpikes NE '') and (RowBsideSpikes EQ '') ) then
                            ErrorMessage = 'Error in ':Service:' service. Inspection back side spikes data is missing.'
                        end
                    Until ErrorMessage NE ''
                    Next RowIndex
                end
                
            Case ( (Stage EQ 'PREC') or (Stage EQ 'FWIC') or (Stage EQ 'LWIC') or (Stage EQ 'PSTC') )
                // Verify each cleans row has a tool.
                CleanTools = CIRow<CLEAN_INSP_CLEAN_TOOL$>
                NumRows    = DCount(CleanTools, @VM)
                If NumRows GT 0 then
                    For RowIndex = 1 to NumRows
                        RowTool   = CleanTools<0, RowIndex>
                        If RowTool EQ '' then
                            ErrorMessage = 'Error in ':Service:' service. Clean tool ID is missing.'
                        end
                    Until ErrorMessage NE ''
                    Next RowIndex
                end
            Case ( (Stage EQ 'PRES') or (Stage EQ 'FWIS') or (Stage EQ 'LWIS') or (Stage EQ 'PSTS') )
                // Verify surfscan row has a tool.
                ScanTools = CIRow<CLEAN_INSP_SCAN_TOOL$>
                NumRows   = DCount(ScanTools, @VM)
                If NumRows GT 0 then
                    For RowIndex = 1 to NumRows
                        RowTool = ScanTools<0, RowIndex>
                        If RowTool EQ '' then
                            ErrorMessage = 'Error in ':Service:' service. Scan tool ID is missing.'
                        end
                    Until ErrorMessage NE ''
                    Next RowIndex
                end
        End Case
    end
    
    If ErrorMessage EQ '' then
        Response = True$
    end else
        Error_Services('Add', ErrorMessage)
        Response = False$
    end
    
end service


Service SignCleanInsp(RDSNo, Stage=CI_STAGES, User)
    
    ErrorMessage = ''
    If ( (RDSNo NE '') and (Stage NE '') and (User NE '') ) then
        SigDTM   = Datetime()
        WONo     = Xlate('RDS', RDSNo, 'WO', 'X')
        CassNo   = Xlate('RDS', RDSNo, 'CASS_NO', 'X')
        WOMatKey = WONo:'*':CassNo
        Begin Case
            Case ( (Stage EQ 'PREI') or (Stage EQ 'PREC') or (Stage EQ 'PRES') )
                CINo = Xlate('RDS', RDSNo, 'PRE_CI_NO', 'X')
            Case ( (Stage EQ 'FWII') or (Stage EQ 'FWIC') or (Stage EQ 'FWIS') ) 
                CINo = Xlate('RDS', RDSNo, 'FWI_CI_NO', 'X')
            Case ( (Stage EQ 'LWII') or (Stage EQ 'LWIC') or (Stage EQ 'LWIS') )
                CINo = Xlate('RDS', RDSNo, 'LWI_CI_NO', 'X')
            Case ( (Stage EQ 'PSTI') or (Stage EQ 'PSTC') or (Stage EQ 'PSTS') )
                CINo = Xlate('RDS', RDSNo, 'POST_CI_NO', 'X')
            Case Otherwise$
                ErrorMessage = 'Error in ':Service:' service. Invalid stage ID passed into service.'
        End Case
        If ErrorMessage EQ '' then
            If RowExists('CLEAN_INSP', CINo) then
                CIRow = Database_Services('ReadDataRow', 'CLEAN_INSP', CINo)
                If Error_Services('HasError') then
                    ErrorMessage = Error_Services('GetMessage')
                end else
                    OrigCIRow = CIRow
                end
            end else
                ErrorMessage = 'Error in ':Service:' service. No ':Stage:' record on file for RDS ':RDSNo:'!'
            end
        end
        Begin Case
            Case ErrorMessage NE ''
                Null
            Case ( (Stage EQ 'PREI') or (Stage EQ 'FWII') or (Stage EQ 'LWII') or (Stage EQ 'PSTI') )
                InspTools   = CIRow<CLEAN_INSP_INSP_TOOL$>
                InspSigs    = CIRow<CLEAN_INSP_INSP_SIG$>
                InspSigDTMs = CIRow<CLEAN_INSP_INSP_SIG_DTM$>
                NumRows     = DCount(InspTools, @VM)
                If NumRows GT 0 then
                    For RowIndex = 1 to NumRows
                        // Sign each row if no signature present
                        If InspSigs<0, RowIndex> EQ '' then
                            InspSigs<0, RowIndex>    = User
                            InspSigDTMs<0, RowIndex> = SigDTM
                        end
                    Until ErrorMessage NE ''
                    Next RowIndex
                end
                CIRow<CLEAN_INSP_INSP_SIG$>     = InspSigs
                CIRow<CLEAN_INSP_INSP_SIG_DTM$> = InspSigDTMs
            Case ( (Stage EQ 'PREC') or (Stage EQ 'FWIC') or (Stage EQ 'LWIC') or (Stage EQ 'PSTC') )
                CleanTools   = CIRow<CLEAN_INSP_CLEAN_TOOL$>
                CleanSigs    = CIRow<CLEAN_INSP_CLEAN_SIG$>
                CleanSigDTMs = CIRow<CLEAN_INSP_CLEAN_SIG_DTM$>
                NumRows      = DCount(CleanTools, @VM)
                If NumRows GT 0 then
                    For RowIndex = 1 to NumRows
                        // Sign each row if no signature present
                        If CleanSigs<0, RowIndex> EQ '' then
                            CleanSigs<0, RowIndex>    = User
                            CleanSigDTMs<0, RowIndex> = SigDTM
                        end
                    Until ErrorMessage NE ''
                    Next RowIndex
                end
                CIRow<CLEAN_INSP_CLEAN_SIG$>     = CleanSigs
                CIRow<CLEAN_INSP_CLEAN_SIG_DTM$> = CleanSigDTMs
            Case ( (Stage EQ 'PRES') or (Stage EQ 'FWIS') or (Stage EQ 'LWIS') or (Stage EQ 'PSTS') )
                ScanTools   = CIRow<CLEAN_INSP_SCAN_TOOL$>
                SurfSigs    = CIRow<CLEAN_INSP_SCAN_SIG$>
                SurfSigDTMs = CIRow<CLEAN_INSP_SCAN_SIG_DTM$>
                NumRows     = DCount(ScanTools, @VM)
                If NumRows GT 0 then
                    For RowIndex = 1 to NumRows
                        // Sign each row if no signature present
                        If SurfSigs<0, RowIndex> EQ '' then
                            SurfSigs<0, RowIndex>    = User
                            SurfSigDTMs<0, RowIndex> = SigDTM
                        end
                    Until ErrorMessage NE ''
                    Next RowIndex
                end
                CIRow<CLEAN_INSP_SCAN_SIG$>     = SurfSigs
                CIRow<CLEAN_INSP_SCAN_SIG_DTM$> = SurfSigDTMs
        End Case
    end else
        ErrorMessage = 'Error in ':Service:' service. Null RDSNo, user, or stage passed in.'
    end
    
    If ErrorMessage EQ '' then
        If CIRow NE OrigCIRow then Database_Services('WriteDataRow', 'CLEAN_INSP', CINo, CIRow)
        If Error_Services('NoError') then
            Response = True$
        end else
            ErrorMessage = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
            Error_Services('Add', ErrorMessage)
            Response     = False$
        end
    end else
        Error_Services('Add', ErrorMessage)
        Response = False$
    end
    
end service


Service QAMetSigReady(RDSNo, QAStage=QA_STAGES, Slot=SLOTS, WOMatQAKey)
    
    ErrorMessage = ''
    If ( (RDSNo NE '' or WOMatQAKey NE '') and (QAStage NE '') and (Slot NE '') ) then
        If RDSNo NE '' then
            WOMatKey = Xlate('RDS', RDSNo, 'WO_MAT_KEY', 'X')
        end else
            WOMatKey = WOMatQAKey
        end
        If WOMatKey NE '' then
            If RowExists('WO_MAT_QA', WOMatKey) then
                WOMatQARec = Database_Services('ReadDataRow', 'WO_MAT_QA', WOMatKey)
                If Error_Services('HasError') then
                    ErrorMessage = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
                end else
                    WOMatQAStages = WOMatQARec<WO_MAT_QA_STAGE$>
                end
            end else
                ErrorMessage = 'Error in ':Service:' service. No WO_MAT_QA record on file for RDS ':RDSNo:'!'
            end
        end else
            ErrorMessage = 'Error in ':Service:' service. Null WO_MAT_KEY returned for RDS ':RDSNo:'!'
        end
        Begin Case
            Case ErrorMessage NE ''
                Null
            Case ( (QAStage EQ 'LOAD') or (QAStage EQ 'QA') or (QAStage EQ 'MO_QA') )
                
                Done         = False$
                WOMatRec     = Database_Services('ReadDataRow', 'WO_MAT', WOMatKey)
                ReactorType  = Xlate('WO_MAT', WOMatKey, 'REACTOR_TYPE', 'X')
                TestSlots    = WOMatQARec<WO_MAT_QA_SLOT$>
                
                // Check for QA Stage Supplements
                UnacknowledgedSupp = Supplement_Services('UnacknowledgedSupplementCheck', 'WO_MAT', WOMatKey, 'QA')
                If UnacknowledgedSupp NE FALSE$ then
                    ErrorMessage = 'The QA stage supplements must be acknowledged before the load operation can be signed.'
                end

                If ReactorType EQ 'EPP' then
                    WMOKey       = WOMatRec<WO_MAT_WMO_KEY$>
                    WMORec       = Database_Services('ReadDataRow', 'WM_OUT', WMOKey)
                    MUWfrIDs     = WOMatRec<WO_MAT_EPOS_REP_WAFER_ID$>
                    MUWfrResults = WMORec<WM_OUT_MU_WAFER_THK_RESULT$>
                end else
                    MUWfrIDs     = WOMatRec<WO_MAT_SLOT_REP_WAFER_ID$>
                    MUWfrResults = WOMatRec<WO_MAT_MU_WAFER_THK_RESULT$>
                end
                
                For each WOMatQAStage in WOMatQAStages using @VM setting vPos
                    TestSlot = TestSlots<0, vPos>
                    If ( (WOMatQAStage EQ QAStage) and (Slot EQ TestSlot) ) then
                        Done      = True$
                        // Compare Met Result to Met Min and Met Max
                        TestProp  = WOMatQARec<WO_MAT_QA_PROP$, vPos>
                        MetResult = WOMatQARec<WO_MAT_QA_RESULT$, vPos>
                        MetMin    = WOMatQARec<WO_MAT_QA_MIN$, vPos>
                        MetMax    = WOMatQARec<WO_MAT_QA_MAX$, vPos>
                        Begin Case
                            Case MetResult EQ ''
                                ErrorMessage = 'Error in ':Service:' service. Metrology result for stage ':QAStage:' slot ':Slot:' is missing.'
                            Case ( (MetResult LT MetMin) or (MetResult GT MetMax) )
                                ErrorMessage = 'Error in ':Service:' service. Metrology result is out of spec for stage ':QAStage:' slot ':Slot:'.'
                        End Case
                        // Compare Std Dev Result to Std Dev Max
                        StdDevResult = WOMatQARec<WO_MAT_QA_STD_RESULT$, vPos>
                        StdDevMax    = WOMatQARec<WO_MAT_QA_STD_MAX$, vPos>
                        Begin Case
                            Case StdDevResult EQ ''
                                // Std Dev data is not always supplied.
                                Null
                            Case StdDevResult GT StdDevMax
                                ErrorMessage = 'Error in ':Service:' service. StdDev exceeds spec for stage ':QAStage:'.'
                        End Case
                        
                        If TestProp EQ 'THICK' then
                            // Ensure makeup wafer thickness values are in spec and that at least one wafer
                            // from each makeup box has a thickness result.
                            
                            // Build a list of unique makeup boxes
                            MUBoxes      = ''
                            MUThickTests = ''
                            If MUWfrIDs NE '' then
                                For each MUWfrID in MUWfrIDs using @VM setting vPos
                                    If MUWfrID NE '' then
                                        MUBox = Field(MUWfrID, '.', 1, 2)
                                        Locate MUBox in MUBoxes using @VM setting MUIndex else
                                            MUBoxes<0, -1> = MUBox
                                        end
                                    end
                                Next MUWfrID
                                
                                // Verify makeup wafers are also within spec
                                For each MUWfrID in MuWfrIDs using @VM setting vPos
                                    If MUWfrID NE '' then
                                        MUBox = Field(MUWfrID, '.', 1, 2)
                                        MUWfrResult = MUWfrResults<0, vPos>
                                        Locate MUBox in MUBoxes using @VM setting MUIndex then
                                            MUThickTests<0, MUIndex> = True$
                                        end
                                        If MUWfrResult NE '' then
                                            If ( (MUWfrResult LT MetMin) or (MUWfrResult GT MetMax) ) then
                                                ErrorMessage = 'Error in ':Service:' service. Metrology result is out of spec for stage ':QAStage:' makeup wafer ID ':MUWfrID:'.'
                                            end
                                        end
                                    end
                                Next MUWfrID
                            end
                            If MUBoxes NE '' then
                                // Verify each MU Box has at least one thickness result
                                For each MUBox in MUBoxes using @VM setting MUBoxIndex
                                    If MUThickTests<0, MUBoxIndex> NE True$ then
                                        ErrorMessage = 'Error in ':Service:' service. At least one metrology result is required for stage ':QAStage:' makeup box ID ':MUBox:'.'
                                    end
                                Next MUBox
                            end
                        end
                    end
                Until ( (ErrorMessage NE '') or (Done EQ True$) )
                Next WOMatQAStage
            Case QAStage EQ 'UNLOAD'
                // Leverage fail reason column calculated by WO_MAT_QA_ACTIONS
                For each WOMatQAStage in WOMatQAStages using @VM setting vPos
                    If WOMatQAStage EQ QAStage then
                        FailReason = WOMatQARec<WO_MAT_QA_FAIL_REASON$, vPos>
                        If ( (FailReason NE '') |
                        and  (FailReason NE 'THICK_ONLY UNLOAD product measurement test has not been signed.') |
                        and  (FailReason NE 'CRES UNLOAD product measurement test has not been signed.') |
                        and  (FailReason NE 'UNLOAD LW_RHO product measurement test has not been signed.' ) ) then
                            ErrorMessage = 'Error in ':Service:' service. ':FailReason
                        end
                    end
                Until ErrorMessage NE ''
                Next WOMatQAStage
        End Case
    end else
        ErrorMessage = 'Error in ':Service:' service. Null RDSNo or stage passed in.'
    end
    If ErrorMessage NE '' then
        Error_Services('Add', ErrorMessage)
        Response = False$
    end else
        Response = True$
    end
    
end service


Service SignQAMet(RDSNo, QAStage=QA_STAGES, User, Slot=SLOTS, WOMatQAKey)
    
    ErrorMessage = ''
    If ( (RDSNo NE '' or WOMatQAKey NE '') and (QAStage NE '') ) then
        If RDSNo NE '' then
            WOMatKey = Xlate('RDS', RDSNo, 'WO_MAT_KEY', 'X')
        end else
            WOMatKey = WOMatQAKey
        end
        If WOMatKey NE '' then
            If RowExists('WO_MAT_QA', WOMatKey) then
                WOMatQARec = Database_Services('ReadDataRow', 'WO_MAT_QA', WOMatKey)
                If Error_Services('HasError') then
                    ErrorMessage = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
                end else
                    WOMatQAStages = WOMatQARec<WO_MAT_QA_STAGE$>
                    WOMatQASlots  = WOMatQARec<WO_MAT_QA_SLOT$>
                end
            end else
                ErrorMessage = 'Error in ':Service:' service. No WO_MAT_QA record on file for RDS ':RDSNo:'!'
            end
        end else
            ErrorMessage = 'Error in ':Service:' service. Null WO_MAT_KEY returned for RDS ':RDSNo:'!'
        end
        If ErrorMessage EQ '' then
            SigDTM = Datetime()
            For each WOMatQAStage in WOMatQAStages using @VM setting vPos
                TestSlot = WOMatQASlots<0, vPos>
                If ( (WOMatQAStage EQ QAStage) and (TestSlot EQ Slot) ) then
                    // Sign each row if no signature present.
                    If WOMatQARec<WO_MAT_QA_SIG$, vPos> EQ '' then
                        WOMatQARec<WO_MAT_QA_SIG$, vPos>     = User
                        WOMatQARec<WO_MAT_QA_SIG_DTM$, vPos> = SigDTM
                    end
                end
            Next WOMatQAStage
            Database_Services('WriteDataRow', 'WO_MAT_QA', WOMatKey, WOMatQARec)
            If Error_Services('HasError') then
                ErrorMessage = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
            end
        end
    end else
        ErrorMessage = 'Error in ':Service:' service. Null RDSNo or stage passed into service.'
    end
    If ErrorMessage NE '' then
        Error_Services('Add', ErrorMessage)
        Response = False$
    end else
        Response = True$
    end
    
end service


Service ROTRSigReady(RDSNo)
    
    ErrorMessage = ''
    If RDSNo NE '' then
        LWICINo = Xlate('RDS', RDSNo, 'LWI_CI_NO', 'X')
        If LWICINo NE '' then
            LWICIRec = Database_Services('ReadDataRow', 'CLEAN_INSP', LWICINo)
            If Error_Services('NoError') then
                ROTRAction = LWICIRec<CLEAN_INSP_ROTR_ACTION$>
                If ROTRAction NE 'F' then
                    ErrorMessage = 'Error in ':Service:' service. ROTR does not require acceptance.'
                end
            end else
                ErrorMessage = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
            end
        end else
            ErrorMessage = 'Error in ':Service:' service. No LWI CLEAN_INSP record on file for RDS ':RDSNo:'!'
        end
    end else
        ErrorMessage = 'Error in ':Service:' service. Null RDSNo passed into service.'
    end
    If ErrorMessage NE '' then
        Error_Services('Add', ErrorMessage)
        Response = False$
    end else
        Response = True$
    end
    
end service


Service SignROTR(RDSNo, User, ROTRReason)
    
    ErrorMessage = ''
    If ( (RDSNo NE '') and (User NE '') and (ROTRReason NE '') ) then
        LWICINo = Xlate('RDS', RDSNo, 'LWI_CI_NO', 'X')
        If LWICINo NE '' then
            LWICIRec = Database_Services('ReadDataRow', 'CLEAN_INSP', LWICINo)
            If Error_Services('NoError') then
                ROTRSig = LWICIRec<CLEAN_INSP_SIGN_ROTR_SIGNATURE$>
                If ROTRSig EQ '' then
                    // Only sign ROTR Acceptance if no signature present.
                    LWICIRec<CLEAN_INSP_SIGN_ROTR_SIGNATURE$> = User
                    LWICIRec<CLEAN_INSP_SIGN_ROTR_DTM$>       = Datetime()
                    LWICIRec<CLEAN_INSP_SIGN_ROTR_REASON$>    = ROTRReason
                    LWICIRec<CLEAN_INSP_ROTR_ACTION$>         = 'A' ; // Accepted
                    Database_Services('WriteDataRow', 'CLEAN_INSP', LWICINo, LWICIRec)
                    If Error_Services('HasError') then
                        ErrorMessage = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
                    end
                end
            end else
                ErrorMessage = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
            end
        end else
            ErrorMessage = 'Error in ':Service:' service. No LWI CLEAN_INSP record on file for RDS ':RDSNo:'!'
        end
    end else
        ErrorMessage = 'Error in ':Service:' service. Null RDSNo passed into service.'
    end
    If ErrorMessage NE '' then
        Error_Services('Add', ErrorMessage)
        Response = False$
    end else
        Response = True$
    end
    
end service


Service NCRSigReady(RDSNo)
    
    ErrorMessage = ''
    If RDSNo NE '' then
        WOMatKey = Xlate('RDS', RDSNo, 'WO_MAT_KEY', 'X')
        If WOMatKey NE '' then
            NCRKeys  = Xlate('WO_MAT', WOMatKey, 'NCR_KEYS', 'X')
            If NCRKeys NE '' then
                For each NCRKey in NCRKeys using @VM setting vPos
                    Error     = False$
                    ReqFields = 'SHIFT,DEPT,DEPT_RESP,LOSS_CODE,LOSS_COMMENTS,CONTAIN_ACTIONS,LOSS_STAGE,LOSS_BY'
                    For each ReqField in ReqFields using ',' setting cPos
                        Val = Xlate('NCR', NCRKey, ReqField, 'X')
                        If Val EQ '' then
                            ErrorMessage = 'Error in ':Service:' service. No NCRs on file for RDS ':RDSNo:'!'
                        end
                    Until ErrorMessage NE ''
                    Next ReqField
                Until ErrorMessage NE ''
                Next NCRKey
            end else
                ErrorMessage = 'Error in ':Service:' service. No NCRs on file for RDS ':RDSNo:'!'
            end
        end else
            ErrorMessage = 'Error in ':Service:' service. No WO_MAT_KEY on file for RDS ':RDSNo:'!'
        end
    end else
        ErrorMessage = 'Error in ':Service:' service. Null RDSNo passed into service.'
    end
    If ErrorMessage NE '' then
        Error_Services('Add', ErrorMessage)
        Response = False$
    end else
        Response = True$
    end
    
end service


Service SignNCRs(RDSNo, User)
    
    ErrorMessage = ''
    If RDSNo NE '' then
        WOMatKey = Xlate('RDS', RDSNo, 'WO_MAT_KEY', 'X')
        If WOMatKey NE '' then
            NCRKeys  = Xlate('WO_MAT', WOMatKey, 'NCR_KEYS', 'X')
            If NCRKeys NE '' then
                SigDTM   = Datetime()
                WOMatRec = Database_Services('ReadDataRow', 'WO_MAT', WOMatKey)
                If Error_Services('NoError') then
                    For each NCRKey in NCRKeys using @VM setting vPos
                        // Sign each NCR
                        NCRRec = Database_Services('ReadDataRow', 'NCR', NCRKey)
                        If Error_Services('NoError') then
                            NCRRec<NCR_AUTH_SHIP_SIG$>     = User
                            NCRRec<NCR_AUTH_REJ_SIG$>      = User
                            NCRRec<NCR_AUTH_SHIP_SIG_DTM$> = SigDTM
                            NCRRec<NCR_STATUS$>            = 'C'
                            Database_Services('WriteDataRow', 'NCR', NCRKey, NCRRec)
                            If Error_Services('NoError') then
                                // Sign WO_MAT NCR sig column
                                Locate NCRKey in WOMatRec<WO_MAT_NCR_KEYS$> using @VM setting Pos then
                                    CurrSig = Trim(WOMatRec<WO_MAT_NCR_FINAL_SIG$, Pos>)                
                                    If CurrSig EQ '' then
                                        WOMatRec<WO_MAT_NCR_FINAL_SIG$, Pos>     = User
                                        WOMatRec<WO_MAT_NCR_FINAL_SIG_DTM$, Pos> = SigDTM
                                        Database_Services('WriteDataRow', 'WO_MAT', WOMatKey, WOMatRec)
                                        If Error_Services('HasError') then
                                            ErrorMessage = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
                                        end
                                    end               
                                end else
                                    ErrorMessage = 'Error in ':Service:' service. Failed to locate NCR key ':NCRKey:' in WO_MAT record ':WOMatKey:'!'
                                end
                            end else
                                ErrorMessage = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
                            end
                        end else
                            ErrorMessage = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
                        end
                    Until ErrorMessage NE ''
                    Next NCRKey
                end else
                    ErrorMessage = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
                end
            end else
                ErrorMessage = 'Error in ':Service:' service. No NCRs on file for RDS ':RDSNo:'!'
            end
        end else
            ErrorMessage = 'Error in ':Service:' service. No WO_MAT_KEY on file for RDS ':RDSNo:'!'
        end
    end else
        ErrorMessage = 'Error in ':Service:' service. Null RDSNo passed into service.'
    end
    If ErrorMessage NE '' then
        Error_Services('Add', ErrorMessage)
        Response = False$
    end else
        Response = True$
    end
    
end service


Service SignSupplement(RDSNo, User)
    
    ErrorMessage = ''
    If RDSNo NE '' then
        RDSRec = Database_Services('ReadDataRow', 'RDS', RDSNo)
        If Error_Services('NoError') then
            RDSRec<RDS_SUPPL_SIG$>      = User
            RDSRec<RDS_SUPPL_SIG_DATE$> = Date()
            RDSRec<RDS_SUPPL_SIG_TIME$> = Time()
            RDSRec<RDS_SUPPL_ACK$>      = True$
            Database_Services('WriteDataRow', 'RDS', RDSNo, RDSRec, True$, False$, True$)
            If Error_Services('HasError') then
                ErrorMessage = Error_Services('GetMessage')
            end
        end else
            ErrorMessage = Error_Services('GetMessage')
        end
    end else
        ErrorMessage = 'Error in ':Service:' service. Null RDSNo passed into service.'
    end
    If ErrorMessage NE '' then
        Error_Services('Add', ErrorMessage)
        Response = False$
    end else
        Response = True$
    end
    
end service


Service UnsignFQAStage(RDSNo)
    
    Machine        = Environment_Services('GetServer')
    Headers        = 'Logging DTM' : @FM : 'Machine' : @FM : 'Log'
    ColumnWidths   = 20 : @FM : 15 : @FM : 300
    LogPath        = Environment_Services('GetApplicationRootPath') : '\LogFiles\Lot'
    makeDirSuccess = Utility("MAKEDIR", LogPath)
    LogFileName    = 'R':RDSNo:'.csv'
    objLog         = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, ' ', Headers, ColumnWidths, False$, False$)   		
    
    IsEPP = Rds_Services('IsEpiPro', RDSNo)
    If IsEpp EQ False$ then
        Database_Services('ActivateRecord', 'RDS', RDSNo)
		If Error_Services('NoError') then
			Valid       = ''    
			CurRdsNo    = RDSNo
			WONo        = {WO}
			CassNo      = {CASS_NO}
			WOStep      = {WO_STEP}
            WOMatKey = WONo:'*':CassNo
            WOMatRec = XLATE('WO_MAT',WOMatKey,'','X')
            // Unsign WO_MAT
            InvWH = WOMatRec<WO_MAT_INV_WH$>
            InvLoc = WOMatRec<WO_MAT_INV_LOCATION$>
            InvAction = WOMatRec<WO_MAT_INV_ACTION$>
            InvDTM = WOMatRec<WO_MAT_INV_DTM$>
            InvUser = WOMatRec<WO_MAT_INV_USER$>
            // Get List of keys to delete
            InvCount = Dcount(InvWH, @VM)
            DeleteList = ''
            For I = 1 to InvCount
                ThisLoc = InvAction<1,I>
                If ThisLoc EQ '1QA' or ThisLoc EQ 'POST' then
                    DeleteList<-1> = I
                end
            Next I
            // Reverse array to work backwards through deletions
            DeleteList = SRP_Array('Reverse', DeleteList, @FM)
            If DeleteList NE '' then
                For Each Item in DeleteList Using @FM
                    WOMatRec = Delete(WOMatRec, WO_MAT_INV_WH$, Item, 0)
                    WOMatRec = Delete(WOMatRec, WO_MAT_INV_LOCATION$, Item, 0)
                    WOMatRec = Delete(WOMatRec, WO_MAT_INV_ACTION$, Item, 0)
                    WOMatRec = Delete(WOMatRec, WO_MAT_INV_DTM$, Item, 0)
                    WOMatRec = Delete(WOMatRec, WO_MAT_INV_USER$, Item, 0)
                Next Item
                Database_Services('WriteDataRow', 'WO_MAT', WOMatKey, WOMatRec, True$, False$, False$)
            end
            
            // Unsign RDS
            {SUP_VER_SIG}      = ''
			{SUP_VER_SIG_DATE} = ''
			{SUP_VER_SIG_TIME} = ''
			Database_Services('WriteDataRow', 'RDS', RDSNo, @Record, True$, False$, False$)
            LogData    = ''
            LogData<1> = Oconv(Date(), 'D4/') : ' ' : Oconv(Time(), 'MTS') ; // Logging DTM
            LogData<2> = Machine
            LogData<3> = 'FQA stage unsigned for RDS ':RDSNo:' by user ':@User4
            Logging_Services('AppendLog', objLog, LogData, @RM, @FM, False$)			
        end else
            Error_Services('Add', 'Error accessing RDS.')
        end
    end else
        Error_Services('Add', 'This service is currently only available for non-EpiPro lots.')
    end
    
end service


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Internal GoSubs
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

GetMostRecentSig:
    
    NumRows = DCount(StageRows, @VM)
    SignatureFound = False$
    For RowIndex = NumRows to 1 Step -1
        Signature  = Signatures<0, RowIndex>                        
        SigDTM     = SigDTMs<0, RowIndex>
        If Signature NE '' then SignatureFound = True$
    Until SignatureFound EQ True$
    Next RowIndex
    
return

