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

    Name        :   Material_Movement_Services

    Description :   Handler program for all material movement services.

    Notes       :   

    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)
        01/07/21	DPC     Original programmer - built to support PTI movement on form NDW_PTI_MAT_SCAN
        08/23/21	DPC		Added code to support new PTO form (NDW_PTO_MAT_SCAN)
		01/22/22	DPC		Added code to support GaN and EPP processes
		06/01/22	DPC		Removed GAN references
***********************************************************************************************************************/
#pragma precomp SRP_PreCompiler

$insert SERVICE_SETUP
$insert SCANS_EQUATES
$insert APP_INSERTS
$insert WO_MAT_EQUATES
$insert RDS_EQUATES
$insert NOTIFICATION_EQU
$insert EPI_PART_EQUATES
$Insert WO_LOG_EQUATES
$Insert MSG_EQUATES

Declare function    Scan_Services, Memory_Services, Database_Services, SRP_JSON, RTI_CreateGUID, Memberof, obj_WO_Mat
Declare function    Get_Property, RDS_Services, EpiPro_Services, DateTime, Signature_Services, Material_Movement_Services
Declare subroutine  Scan_Services, Memory_Services, Database_Services, SRP_JSON, Security_Services, Hold_Services
Declare subroutine  obj_WO_Mat_Log, obj_WO_Mat, Set_Status, SAP_Services, Obj_Notes, Print_SAP_Cass_Ship_Label

GoToService else
    Error_Services('Add', Service : ' is not a valid service request within the ' : ServiceModule : ' module.')
end

Return Response or ""

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Service Parameter Options
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Options SCAN_TYPES      = 'CASSETTE1', 'CASSETTE2', 'SUPPLIER'
Options SCAN_TYPES_PTO	= 'LABEL1', 'LABEL2'
//-----------------------------------------------------------------------------
// SERVICES
//-----------------------------------------------------------------------------

//----------------------------------------------------------------------------------------------------------------------
// ProcessFQAScanData
//
// Processes new scan data and updates the SCANS database row for the indicated Scan ID. This service should parse the
// scan data and identify its intended field and then evaluate if the scan data is valid. Note: just because the scan
// resource is not ready to be accepted, it does not mean this scan data is invalid. Each scan data will have to be
// evaluated on a case by case basis.
//----------------------------------------------------------------------------------------------------------------------
Service ProcessFQAScanData(ScanData, ScanType = SCAN_TYPES, Param1, Param2, Param3)

    If (ScanData NE '') then
        // Code 3of9 encodes the asterisk and underscore characters. These need to be decoded.
        //ScanData    = Scan_Services('DecodeScanData', ScanData)
        
        If Error_Services('NoError') then
            
            ColumnIndex = ''
            ColumnValue = ''
            
            // Identify the scan data based on the data identifier prefix (if any). Otherwise, assume this is a
            // type of cassette ID (i.e., RDS or WMO).
            
            Begin Case
                
                Case ScanType EQ 'SUPPLIER'
                    ValidLot = False$    
                    If ScanData[1, 2] EQ '2T' OR ScanData[1,2] EQ '1T' then ScanData[1, 2] = ''
                    ScanSubLot = ScanData
                    
                    CassetteID = Param1
                    If INDEX(CassetteID,'.',2) then
                        RDSType = 'EPP'
                    end else
                        RDSType = 'SIC'
                    end
                    
                    ValidLot = Rds_Services('IsValidSubLot', CassetteID, RDSType, ScanSubLot)
                    If ValidLot NE True$ then
                        // Add error to error stack
                        ErrorMessage = 'Invalid Supplier Lot: ':ScanData
                        Error_Services('Add', ErrorMessage)
                    end
                    
                    If Error_Services('NoError') then
                        Response = ScanSubLot
                    end 
                    
                Case ScanType EQ 'CASSETTE1'
                    
                    // Assume this is intended to be a Cassette ID scan (either WMO or RDS). Only if this is a
                    // non-existent carrier will the scan data be considered invalid.
                    // Strip '1T', 'I', and 'O' prefixes.
                    If ScanData[1, 2] EQ '1T' then
                        ScanData[1, 2] = ''
                    end else If ScanData[1,1] EQ 'I' OR ScanData[1,1] EQ 'O' then
                        ScanData[1,1] = ''
                    end
                    CassetteID = ScanData
                    
                    If INDEX(CassetteID,'.',2) then
                        RDSType  = 'EPP'
                        WOMatKey = Field(CassetteID, '.', 1):'*':Field(CassetteID, '.', 3)
                    end else
                        RDSType  = 'SIC'
                        WOMatKey = Xlate('RDS', CassetteID, 'WO_MAT_KEY', 'X')
                    end
                    
                    ValidCassetteID = False$  ; // Assume Cassette ID is not valid for now.
                    ValidCassetteID = Rds_Services('IsValidRDS', CassetteID, RDSType )
					
					If RDSType EQ 'EPP' then
						LotDesc = 'EPP Lot '
					end else
						LotDesc = 'RDS Number '
					end
        
					If ValidCassetteID EQ True$ then
                        If Param1 NE '' AND ScanData NE Param1 then ;*Cassette2 Scan
                            Error_Services('Add', LotDesc:'Mismatch: ':CassetteID: ' does not match ': Param1)
                        end
                        
                        // Check if FQA is signed
                        FQASigned   = ''
                        WorkOrdNo   = Field(WOMatKey, '*', 1)
                        ReactorType = XLATE('WO_LOG', WorkOrdNo, 'REACT_TYPE', 'X')
                        QAStage     = ''
                        
                        Begin Case
                            Case RDSType EQ 'EPP'
                                QAStage = 'MO_QA'
                            Case Otherwise$
                                QAStage = 'QA'
                        End Case

                        FQASigned = Signature_Services('CheckSignature', WOMatKey, QAStage)
                        
                        IF NOT(FQASigned) THEN
                            Error_Services('Add', 'FQA not signed for ':LotDesc:CassetteID: '!')
                        END
                        
                    end else
                    	Error_Services('Add', 'Invalid ':LotDesc:CassetteID)
                    end
                    
                    If Error_Services('NoError') then
                        Response = CassetteID
                    end
                    
                Case ScanType EQ 'CASSETTE2'
                    Response = Material_Movement_Services('ProcessPTIScanData', ScanData, 'CASSETTE1', Param1)
                    Response = ScanData
                    
            End Case
            
        end
    end else
        Error_Services('Add', 'ScanData argument was missing in the ' : Service : ' service.')
    end
    
end service

//----------------------------------------------------------------------------------------------------------------------
// ProcessPTIScanData
//
// Processes new scan data and updates the SCANS database row for the indicated Scan ID. This service should parse the
// scan data and identify its intended field and then evaluate if the scan data is valid. Note: just because the scan
// resource is not ready to be accepted, it does not mean this scan data is invalid. Each scan data will have to be
// evaluated on a case by case basis.
//----------------------------------------------------------------------------------------------------------------------
Service ProcessPTIScanData(ScanData, ScanType = SCAN_TYPES, Param1, Param2, Param3)

    If (ScanData NE '') then
        // Code 3of9 encodes the asterisk and underscore characters. These need to be decoded.
        //ScanData    = Scan_Services('DecodeScanData', ScanData)
        
        If Error_Services('NoError') then
            
            ColumnIndex = ''
            ColumnValue = ''
            
            // Identify the scan data based on the data identifier prefix (if any). Otherwise, assume this is a
            // type of cassette ID (i.e., RDS or WMO).
            
            Begin Case
                
                Case ScanType EQ 'SUPPLIER'
                    ValidLot = False$    
                    If ScanData[1, 2] EQ '2T' OR ScanData[1,2] EQ '1T' then ScanData[1, 2] = ''
                    ScanSubLot = ScanData
                    
                    CassetteID = Param1
                    If INDEX(CassetteID,'.',2) then
                        RDSType = 'EPP'
                    end else
                        RDSType = 'SIC'
                    end
                    
                    ValidLot = Rds_Services('IsValidSubLot', CassetteID, RDSType, ScanSubLot)
                    If ValidLot NE True$ then
                        // Add error to error stack
                        ErrorMessage = 'Invalid Supplier Lot: ':ScanData
                        Error_Services('Add', ErrorMessage)
                    end
                    
                    If Error_Services('NoError') then
                        Response = ScanSubLot
                    end 
                    
                Case ScanType EQ 'CASSETTE1'
                    
                    // Assume this is intended to be a Cassette ID scan (either WMO or RDS). Only if this is a
                    // non-existent carrier will the scan data be considered invalid.
                    // Strip '1T', 'I', and 'O' prefixes.
                    If ScanData[1, 2] EQ '1T' then
                        ScanData[1, 2] = ''
                    end else If ScanData[1,1] EQ 'I' OR ScanData[1,1] EQ 'O' then
                        ScanData[1,1] = ''
                    end
                    CassetteID = ScanData
                    
                    If INDEX(CassetteID,'.',2) then
                        RDSType = 'EPP'
                    end else
                        RDSType = 'SIC'
                    end
                    
                    ValidCassetteID = False$  ; // Assume Cassette ID is not valid for now.
                    ValidCassetteID = Rds_Services('IsValidRDS', CassetteID, RDSType )
                    If ValidCassetteID EQ True$ then
                        If Param1 NE '' AND ScanData NE Param1 then ;*Cassette2 Scan
                            Error_Services('Add', 'RDS Mismatch: ':CassetteID: ' does not match ': Param1)
                        end
                    end else
                        Error_Services('Add', 'Invalid RDS Number: ':CassetteID)
                    end
                    
                    If Error_Services('NoError') then
                        Response = CassetteID
                    end
                    
                Case ScanType EQ 'CASSETTE2'
                    Response = Material_Movement_Services('ProcessPTIScanData', ScanData, 'CASSETTE1', Param1)
                    Response = ScanData
                    
            End Case
            
        end
    end else
        Error_Services('Add', 'ScanData argument was missing in the ' : Service : ' service.')
    end
    
end service

//to do before release of ProcessPTOScanData
//1. prerequisite - waiting on newly printed (on/after 10/18/21) labels to make their way through warehouse, though not critical
//2. showstopper - need barcode scanners to be able to scan 2D barcodes - currently only 1 such scanner (purchased for shipping but not deployed, given to Cheryl) 
//3. fix prod print routine to use SRP GetPrinter methods similar to what the Test_Print_SAP_Cass_Ship_Label does
//----------------------------------------------------------------------------------------------------------------------
// ProcessPTOScanData
//
// This service should parse the scan data and identify its intended field and then evaluate if the scan data is valid.
// For PTO scan, we will first look at the 2D barcode from the 4x2 (RDS or cassette label).  After parsing, we will take 
// the 3rd data point and try to validate that is in an RDS and that that RDS record is in the proper state to do the PTO
// transaction.  If so, it will automatically print the 4x4 (what OI refers to as Shipping label), from which the operator
// will scan the 2D barcode.  Parsing that barcode, we will attempt to bring back the RDS again and validate that it is the
// same RDS we got from the first label.  If so, then the 'Save' button is enabled and the operator is allowed to process the
// transaction.
//----------------------------------------------------------------------------------------------------------------------
Service ProcessPTOScanData(ScanData, ScanType = SCAN_TYPES_PTO, Param1, Param2)

    Location = 'PTO Mat'	
    If (ScanData NE '') then
        
        If Error_Services('NoError') then
            
            ColumnIndex = ''
            ColumnValue = ''
            Cassette1	= ''
            Cassette2	= ''
            
            Begin Case
                
                Case ScanType EQ 'LABEL1'
                    //determine whether regular NEPP or EPP label scan
                    //NEPP should be data matrix scan and have 7 parts, RDS_No is the 3rd
                    //EPP is 1D scan and will have the WMO number (e.g. 170369*1*48)
                    // This should be a multi-part 2D datamatrix scan, so there should be a long string separated by pipe symbols.
                    // Validate the 1. it is multi-part string and 2. that the RDS number from the string is a) valid RDS number 
                    // and b) is currently is the proper state to do PTO transaction.
                    // Strip any standardized prefixes (e.g. '1T', '2T', etc.) as necessary
                    
                    // data from Label1 should have 8 parts (no matter type - Si or EPP), we're looking for the 3rd
                    cnt = DCount(ScanData, '|')
                    if cnt NE 8 then
                        Cassette1 = 0
                        Error_Services('Add', 'Invalid Lot Label Scan.')
                        return
                    end else
                        //RDS should be 3rd position
                        Cassette1 = Field(ScanData, '|', 3)
                    end
                    
                    //strip the prefix encoding
                    If Cassette1[1, 2] EQ '1T' OR Cassette1[1, 2] EQ '2T' then
                        Cassette1[1, 2] = ''
                    end else If Cassette1[1,1] EQ 'I' OR ScanData[1,1] EQ 'O' then
                        Cassette1[1,1] = ''
                    end
                    
                    WOMatKey = ''
                    RdsRec = ''
                    WONo = ''
                    CassNo = ''

                    If INDEX(Cassette1,'.',2) then
                        RDSType = 'EPP'
                        WMOKey = Cassette1
						Convert '.' to '*' in WMOKey
                        If RowExists('WM_OUT', WMOKey) then
							WONo        = Field(WMOKey, '*', 1, 1)
							CassNo      = Field(WMOKey, '*', 3, 1)
							WOMatKey    = WONo:'*':CassNo
						end
                    end else
                        RDSType = 'SIC'
                        WOMatKey = XLATE('RDS', Cassette1, 'WO_MAT_KEY', 'X')
						RdsRec = Database_Services('ReadDataRow','RDS', Cassette1)
						WONo = RdsRec<RDS_WO$>
						CassNo = RdsRec<RDS_CASS_NO$>
                    end

                    ValidCassette1 = False$  ; // Assume Cassette ID is not valid for now.
                    testCass1  = Cassette1

                    Convert '*' to '.' in testCass1
                    If Rds_Services('IsValidRDS', Cassette1, RDSType) then
                        If Rds_Services('GetHoldStatus', Cassette1, RDSType) EQ False$ then
                            If Rds_Services('IsPackaged', Cassette1, RDSType) EQ True$ then
                                ValidCassette1 = True$
                            end else
                                Error_Services('Add', RDSType: ' RDS ':testCass1: ' has not completed packaging.')
                                return                    			
                            end
                        end else
                            Error_Services('Add', RDSType: ' RDS ':testCass1: ' is currently on hold.')
                            return                    			
                        end
                    end else
                        Error_Services('Add', RDSType: ' RDS ' : testCass1 : ' is an invalid RDS number.')
                        return                    			
                    end
					
                    LastPTO = obj_WO_Mat('OutofPTO',WOMatKey)
					StepNo  = 1
                    IF LastPTO THEN
                        MsgHead	= 'Cassette previously scanned through PTO'
                        MsgText  = 'Scanned at ':LastPTO<2>:' by ':LastPTO<1>:CRLF$
                        MsgText	:= 'Are you sure you wish to rescan and reprint the shipping label?'

                        OK = Msg(@WINDOW, '','YESNO','',MsgHead:@FM:MsgText)

                        IF NOT(OK) THEN
                            Error_Services('Add', 'Scan Cancelled')
                            RETURN
                        END else
                            Print_SAP_Cass_Ship_Label(WONo,StepNo,CassNo,Cassette1, RDSType)
                        end
                    end else
                    	Print_SAP_Cass_Ship_Label(WONo,StepNo,CassNo,Cassette1, RDSType)
                    END
                    
                    If ValidCassette1 EQ True$ then
                        If Param1 NE '' AND ScanData NE Param1 then ;*Cassette2 Scan
                            Error_Services('Add', 'RDS Mismatch: ':Cassette1: ' does not match ': Param1)
                            return                    			
                        end
                    end else
                        Error_Services('Add', 'Invalid RDS Number: ':Cassette1)
                        return                    			
                    end
                    
                    If Error_Services('NoError') then
                        Response = Cassette1
                    end
                    
                Case ScanType EQ 'LABEL2'
                    cnt = DCount(ScanData, ';')
                    if cnt EQ 9 then 
                        //Tower is customer and RDS should be 2nd position
                        Cassette2 = Field(ScanData, ';', 2)
                    end else if cnt EQ 10 then
                        //all other customers and RDS should be 3rd position
                        Cassette2 = Field(ScanData, ';', 3)
                    end else
                        Error_Services('Add', 'Invalid Shipping Label Scan.')
                        return
                    end
                    
                    //strip the prefix encoding
                    If Cassette2[1, 2] EQ '1T' OR Cassette2[1, 2] EQ '2T' then
                        Cassette2[1, 2] = ''
                    end else If Cassette2[1,1] EQ 'I' OR ScanData[1,1] EQ 'O' then
                        Cassette2[1,1] = ''
                    end
                    testCass1 = Param3
                    testCass2 = Cassette2
                    convert '*' to '.' in testCass1
                    convert '*' to '.' in testCass2
                    
                    If testCass1 NE testCass2 then
                        ErrorMsg	= 'Scan Mismatch - RDS values do not match. Both cassettes placed on hold - Supervisor, Lead, or Engineering disposition required.':CRLF$ |
									: 'RDS Label #1: ':testCass1:CRLF$ | 
									: 'RDS Label #2: ':testCass2:CRLF$ 
																						   
                        ScanMismatch = True$
                        gosub ToggleLotHold
                        Error_Services('Add', ErrorMsg)
                        return                    			
                    end
                    
                    If Error_Services('NoError') then
                        Response = Cassette2
                    end
                    
            End Case
            
        end
    end else
        Error_Services('Add', 'ScanData argument was missing in the ' : Service : ' service.')
    end
    
end service


Service SaveRecord(CassetteID, Warehouse, Location, OperatorID)

    *    Write success record in Material Log
    
    RDSKey = CassetteID
    WMOKey = CassetteID
    Convert '.' to '*' in WMOKey
    
    Begin Case
        Case RowExists('RDS', RDSKey) EQ True$            
            // RDS number
            WOMatKey = Xlate('RDS', RDSKey, 'WO', 'X')
            WOMatKey = WoMatKey:'*':Xlate('RDS', RDSKey, 'CASS_NO', 'X')            
            
        Case RowExists('WM_OUT', WMOKey) EQ True$           
            // WM_OUT key
            WOMatKey = Xlate('WM_OUT', WMOKey, 'WO_MAT_KEY', 'X')
            
        Case RowExists('WO_MAT', CassetteID) EQ True$
            // WO_MAT key
            WOMatKey = CassetteID
            
        Case Otherwise$
            ErrorMessage = 'Invalid cassette ID ':CassetteID
            Error_Services('Add', ErrorMessage)
            
    End Case
    
    If Error_Services('NoError') then
        
        LogFile = 'WO_MAT'               
        Action  = 'PLACE'                
        WhCd    = Warehouse
        LocCd   = Location
        UserID  = OperatorID
        Tag     = CassetteID
        ToolID  = ''
        errCode = ''
        WONo    = Field(WOMatKey, '*', 1, 1)
        CassNo  = Field(WOMatKey, '*', 2, 1)       
        InvDTM  = OCONV(Date(),'D4/'):' ':OCONV(Time(),'MTS')
        
        WOMLParms  = LogFile:@RM
        WOMLParms := InvDTM:@RM
        WOMLParms := Action:@RM
        WOMLParms := WhCd:@RM
        WOMLParms := LocCd:@RM
        WOMLParms := WONo:@RM
        WOMLParms := CassNo:@RM
        WOMLParms := UserID:@RM
        WOMLParms := Tag:@RM
        WOMLParms := ToolID 
        
        Set_Status(0)

        errCode = ''
        obj_WO_Mat_Log('Create',WOMLParms)
        
*        aiParms	= 'WO_MAT':@RM:WONo:@RM:CassNo:@RM:WhCd:'*':LocCd:@RM:Action:@RM:InvDTM:@RM:UserID:@RM:Tag:@RM:ToolID
*        obj_WO_Mat('AddInvTrans', aiParms)
        
									   

        IF Get_Status(errCode) THEN
            swap @SVM with CRLF$ in errCode
            ErrorMsg = 'Errors calling obj_WO_Mat_Log("Create"). Error code: ':errCode:', Len(errCode)=':Len(errCode):'.  FI has been notified.'
            Error_Services('Add', ErrorMsg)
            if LEN(errCode) > 5 then
                Gosub SendErrorNotification
            end
        end
        
    end
    
end service


SendErrorNotification:
    
    Recipients		= XLATE('NOTIFICATION','FI_SUPPORT',NOTIFICATION_USER_ID$,'X')
    SentFrom		= 'MATERIAL_MOVEMENT_SERVICES'
    Subject			= 'ERROR CALLING OBJ_WO_MAT '
    Message			= 'Error occured while attempting to write WO_MAT_LOG at ':Location:' Scan':CRLF$:ErrorMsg
    AttachKey		= WoMatKey
    AttachWindow	= ''   
    SendToGroup		= ''   
    
    Parms = Recipients:@RM:SentFrom:@RM:Subject:@RM:Message:@RM:AttachWindow:@RM:AttachKey:@RM:SendToGroup
    obj_Notes('Create',Parms)
    
return

ToggleLotHold:

	If ScanMismatch EQ True$ then

		// Write fail packaging record in material log for first cassette ID  
		LogFile = 'WO_MAT'               
		LogDTM  = DateTime()    
		Action  = 'PLACE'                
		WhCd    = '1K'
		LocCd   = 'PTO'
		UserID  = @User4
		Tags    = 'RDS Mismatch (':Cassette2:')'
		ToolID  = ''
		OperatorID = @User4
		
		If Cassette1 EQ '' then
			Cassette1 = Param1
		end
		
		// Check if first cassette ID is a valid RDS or WM_OUT key
		Convert '.' to '*' in Cassette1                           
		ValidCass = False$
		Begin Case
			Case ( RowExists('RDS', Cassette1) EQ True$ )
				WONo         = Xlate('RDS', Cassette1, 'WO', 'X')
				CassNo       = Xlate('RDS', Cassette1, 'CASS_NO', 'X')
				HoldEntity   = 'RDS'                             
			Case ( RowExists('WM_OUT', Cassette1) EQ True$ )
				WONo         = Field(Cassette1, '*', 1, 1)
				CassNo       = Field(Cassette1, '*', 3, 1)
				HoldEntity   = 'WM_OUT'
			Case ( RowExists('WO_MAT', Cassette1) EQ True$ )
				WONo         = Field(Cassette1, '*', 1, 1)
				CassNo       = Field(Cassette1, '*', 2, 1)
				HoldEntity   = 'WO_MAT'                    
			Case Otherwise$
				Null
				// To do: Throw Error                               
		End Case
		Convert '*' to '.' in Cassette1

		HoldEntityID = Cassette1 

		// Manually add work order material log entry
		WOMatKey      = WONo:'*':CassNo
		WOMatRec      = Database_Services('ReadDataRow', 'WO_MAT', WOMatKey)
		NumTimestamps = Dcount(WOMatRec<WO_MAT_INV_WH$>, @VM)
		NewEntryPos   = NumTimestamps + 1
		WOMatRec = INSERT(WOMatRec, WO_MAT_INV_WH$,         NewEntryPos, 0, WhCd)
		WOMatRec = INSERT(WOMatRec, WO_MAT_INV_LOCATION$,   NewEntryPos, 0, LocCd)
		WOMatRec = INSERT(WOMatRec, WO_MAT_INV_ACTION$,     NewEntryPos, 0, Action)
		WOMatRec = INSERT(WOMatRec, WO_MAT_INV_DTM$,        NewEntryPos, 0, LogDTM)
		WOMatRec = INSERT(WOMatRec, WO_MAT_INV_USER$,       NewEntryPos, 0, UserID)
		WOMatRec = INSERT(WOMatRec, WO_MAT_INV_TAG$,        NewEntryPos, 0, Tags)
		WOMatRec = INSERT(WOMatRec, WO_MAT_INV_TOOL_ID$,    NewEntryPos, 0, ToolID)
		Database_Services('WriteDataRow', 'WO_MAT', WOMatKey, WOMatRec, True$, False$, True$)

		// Place first cassette on hold
		WOMatKey    = WONo:'*':CassNo
		CtrlEntID   = False$		;* Control checked/unchecked
		OriginFlag	= 'PTO'         ;* Flag to indicate a hold initiated from the packagaing form
		Hold_Services('ToggleHold', WOMatKey, HoldEntity, HoldEntityID, CtrlEntID, OriginFlag, '', OperatorID)

		// Check if second cassette ID is a valid RDS or WM_OUT key
		Convert '.' to '*' in Cassette2                           
		ValidCass = False$
		Begin Case
			Case ( RowExists('RDS', Cassette2) EQ True$ )
				ValidCass  = True$
				WONo         = Xlate('RDS', Cassette2, 'WO', 'X')
				CassNo       = Xlate('RDS', Cassette2, 'CASS_NO', 'X')
				HoldEntity   = 'RDS'                              
			Case ( RowExists('WM_OUT', Cassette2) EQ True$ )
				ValidCass  = True$
				WONo         = Field(Cassette2, '*', 1, 1)
				CassNo       = Field(Cassette2, '*', 3, 1)
				HoldEntity   = 'WM_OUT'
			Case ( RowExists('WO_MAT', Cassette2) EQ True$ )
				ValidCass  = True$				
				WONo         = Field(Cassette2, '*', 1, 1)
				CassNo       = Field(Cassette2, '*', 2, 1)
				HoldEntity   = 'WO_MAT'                           
			Case Otherwise$
				Null
				
		End Case
		
		// Write fail packaging record in material log for second cassette ID
		If ValidCass EQ True$ then
			LogFile = 'WO_MAT'               
			LogDTM  = DateTime()    
			Action  = 'PLACE'                
			WhCd    = '1K'
			LocCd   = 'PTO'
			UserID  = @User4
			Tags    = 'RDS Mismatch (':Cassette1:')'
			ToolID  = ''
			HoldEntityID = Cassette2 
			
			// Manually add work order material log entry
								  
			WOMatRec      = Database_Services('ReadDataRow', 'WO_MAT', WOMatKey)
			NumTimestamps = Dcount(WOMatRec<WO_MAT_INV_WH$>, @VM)
			NewEntryPos   = NumTimestamps + 1
			WOMatRec = INSERT(WOMatRec, WO_MAT_INV_WH$,         NewEntryPos, 0, WhCd)
			WOMatRec = INSERT(WOMatRec, WO_MAT_INV_LOCATION$,   NewEntryPos, 0, LocCd)
			WOMatRec = INSERT(WOMatRec, WO_MAT_INV_ACTION$,     NewEntryPos, 0, Action)
			WOMatRec = INSERT(WOMatRec, WO_MAT_INV_DTM$,        NewEntryPos, 0, LogDTM)
			WOMatRec = INSERT(WOMatRec, WO_MAT_INV_USER$,       NewEntryPos, 0, UserID)
			WOMatRec = INSERT(WOMatRec, WO_MAT_INV_TAG$,        NewEntryPos, 0, Tags)
			WOMatRec = INSERT(WOMatRec, WO_MAT_INV_TOOL_ID$,    NewEntryPos, 0, ToolID)
			Database_Services('WriteDataRow', 'WO_MAT', WOMatKey, WOMatRec, True$, False$, True$)
			
			//pause before putting second cassette on hold

			// Place second cassette on hold

			WOMatKey   = WONo:'*':CassNo
			CtrlEntID  = False$		;* Control checked/unchecked
			OriginFlag = 'PTO'         ;* Flag to indicate a hold initiated from the packaging form
			Parms      = WOMatKey:@RM:HoldEntity:@RM:HoldEntityID:@RM:CtrlEntID:@RM:OriginFlag:@RM:OperatorID
			Hold_Services('ToggleHold', WOMatKey, HoldEntity, HoldEntityID, CtrlEntID, OriginFlag, '', OperatorID)
		end

		gosub SendPTOMismatchNotification
		errCode = ''
		IF Get_Status(errCode) THEN Error_Services('Add', 'Error code ':errCode:' in ':Service:' service.')
		Error_Services('Add', 'Cassette scans did not match! Both cassettes placed on hold - Supervisor, Lead, or Engineering disposition required.')

	end

return

SendPTOMismatchNotification:
    
    // Send scan mismatch notification
	Recipients		= XLATE('NOTIFICATION','PTO_MISMATCH',NOTIFICATION_USER_ID$,'X')
	SentFrom		= 'MATERIAL_MOVEMENT_SERVICES'
	Subject			= 'PTO Mat Scan Verification Mismatch'
	Message			= 'RDS label verification failed at PTO MatScan. Both cassettes placed on hold - Supervisor, Lead, or Engineering disposition required.':CRLF$ |
	: 'RDS Label #1: ':Cassette1:CRLF$ | 
	: 'RDS Label #2: ':Cassette2:CRLF$ |
	: 'Operator: ':@User4
	AttachWindow	= ''
	AttachKey		= ''
	SendToGroup		= ''   

	Parms = Recipients:@RM:SentFrom:@RM:Subject:@RM:Message:@RM:AttachWindow:@RM:AttachKey:@RM:SendToGroup
	obj_Notes('Create',Parms)
    
return

