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)

	ErrorMsg = ''
    If (ScanData NE '') 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
					ErrorMsg = 'Invalid Supplier Lot: ':ScanData
				end
				
				If ErrorMsg EQ '' then Response = ScanSubLot
				
			Case ScanType EQ 'CASSETTE1'
				
				DelimCount = DCount(ScanData, '|')
				Begin Case
					Case ( (ScanData[1, 2] EQ '1T') or (ScanData[1, 1] EQ 'O') )
						ErrorMsg = '1D Lot Label Scanned. 2D Lot Label Required.'
					Case (DelimCount NE 8)
						ErrorMsg = 'Invalid Lot Label Scan.'
					Case Otherwise$
						CassetteID = Field(ScanData, '|', 3)
						SeqNo      = Field(ScanData, '|', 8)                 		
				End Case
				
				If ErrorMsg EQ '' then 
				
					If CassetteID[1, 2] EQ '1T' then
						CassetteID[1, 2] = ''
					end else If CassetteID[1,1] EQ 'I' OR CassetteID[1,1] EQ 'O' then
						CassetteID[1,1] = ''
					end
					
					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 = Rds_Services('IsValidRDS', CassetteID, RDSType )
					
					If ValidCassetteID EQ True$ then
						If Param1 NE '' AND ScanData NE Param1 then ;*Cassette2 Scan
							ErrorMsg = 'Lot Label Mismatch: ':CassetteID: ' does not match ': Param1
						end
						
						If ErrorMsg EQ '' then 
							// 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 ErrorMsg = 'FQA not signed for ':CassetteID:'!'
						end
						
					end else
						ErrorMsg = 'Invalid Lot ID ':CassetteID
					end
					
					If ErrorMsg EQ '' then
						Response = ''
						Response<1> = CassetteID
						Response<2> = SeqNo
					end
				end
				
			Case ScanType EQ 'CASSETTE2'
				// 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.
				
				ValidCassetteID = False$  ; // Assume Cassette ID is not valid for now.
				Cassette1ID     = Param1
				Seq1No          = Param2
				DelimCount      = DCount(ScanData, '|')
				Begin Case
					Case ( (ScanData[1, 2] EQ '1T') or (ScanData[1, 1] EQ 'O') )
						ErrorMsg = '1D Lot Label Scanned. 2D Lot Label Required.'
					Case (DelimCount NE 8)
						ErrorMsg = 'Invalid Lot Label Scan.'
					Case Otherwise$
						CassetteID = Field(ScanData, '|', 3)
						Seq2No     = Field(ScanData, '|', 8)                 		
				End Case
				
				If ErrorMsg EQ '' then 
					
					If CassetteID[1, 2] EQ '1T' then
						CassetteID[1, 2] = ''
					end else If CassetteID[1,1] EQ 'I' OR CassetteID[1,1] EQ 'O' then
						CassetteID[1,1] = ''
					end
					
					If INDEX(CassetteID,'.',2) then
						RDSType = 'EPP'
					end else
						RDSType = 'SIC'
					end
					
					ValidCassetteID = Rds_Services('IsValidRDS', CassetteID, RDSType )
					If ValidCassetteID EQ True$ then
						If Cassette1ID NE '' AND (CassetteID NE Cassette1ID) then ;*Cassette2 Scan
							ErrorMsg = 'Lot Label Mismatch: ':CassetteID: ' does not match ': Cassette1ID
						end
						If Seq1No EQ Seq2No then
							ErrorMsg = 'FQA Label verification failed due':CRLF$:'to operator double-scanning barcode.'
						end
					end else
						ErrorMsg = 'Invalid RDS Number: ':CassetteID
					end
					
					If ErrorMsg EQ '' then Response = CassetteID
				end
				
		End Case
    end else
        ErrorMsg = 'ScanData argument was missing in the ' : Service : ' service.'
    end
    
    If ErrorMsg NE '' then Error_Services('Add', ErrorMsg)
    
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)

	ErrorMsg = ''
    If (ScanData NE '') 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 ErrorMsg = 'Invalid Supplier Lot: ':ScanData
				If ErrorMsg EQ '' then Response = ScanSubLot
				
			Case ScanType EQ 'CASSETTE1'
				debug
				// 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.
				ValidCassetteID = False$  ; // Assume Cassette ID is not valid for now.
				DelimCount      = DCount(ScanData, '|')
				Begin Case
					Case (ScanData[1, 1] EQ 'I')
						! Temporary exception while we exhaust current WMI inventory on KIT racks that
						! do not have a 2D barcode.
						CassetteID = ScanData
						SeqNo      = 'SEQ1'
					Case ( (ScanData[1, 2] EQ '1T') or (ScanData[1, 1] EQ 'O') )
						ErrorMsg   = '1D Lot Label Scanned. 2D Lot Label Required.'
					Case (DelimCount NE 8)
						ErrorMsg = 'Invalid Lot Label Scan.'
					Case Otherwise$
						CassetteID = Field(ScanData, '|', 3)
						SeqNo      = Field(ScanData, '|', 8)                 		
				End Case

				If ErrorMsg EQ '' then 
					If CassetteID[1, 2] EQ '1T' then
						CassetteID[1, 2] = ''
					end else If CassetteID[1,1] EQ 'I' OR CassetteID[1,1] EQ 'O' then
						CassetteID[1,1] = ''
					end
					
					If INDEX(CassetteID,'.',2) then
						RDSType = 'EPP'
					end else
						RDSType = 'SIC'
					end
					
					ValidCassetteID = Rds_Services('IsValidRDS', CassetteID, RDSType)
					If ValidCassetteID EQ True$ then
						If Param1 NE '' AND (ScanData NE Param1) then ;*Cassette2 Scan
							ErrorMsg = 'Lot Label Mismatch: ':CassetteID: ' does not match ': Param1
						end
					end else
						ErrorMsg = 'Invalid Cassette Number: ':CassetteID
					end
					
					If ErrorMsg EQ '' then
						Response = ''
						Response<1> = CassetteID
						Response<2> = SeqNo
					end
				end
				
			Case ScanType EQ 'CASSETTE2'
				// 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.
				
				ValidCassetteID = False$  ; // Assume Cassette ID is not valid for now.
				Cassette1ID     = Param1
				Seq1No          = Param2

				DelimCount = DCount(ScanData, '|')
				Begin Case
					Case (ScanData[1, 1] EQ 'I')
						! Temporary exception while we exhaust current WMI inventory on KIT racks that
						! do not have a 2D barcode.
						CassetteID = ScanData
						Seq2No     = 'SEQ2'
					Case ( (ScanData[1, 2] EQ '1T') or (ScanData[1, 1] EQ 'O') )
						ErrorMsg = '1D Lot Label Scanned. 2D Lot Label Required.'
					Case (DelimCount NE 8)
						ErrorMsg = 'Invalid Lot Label Scan.'
					Case Otherwise$
						CassetteID = Field(ScanData, '|', 3)
						Seq2No     = Field(ScanData, '|', 8)                 		
				End Case
				
				If ErrorMsg EQ '' then 
				
					If CassetteID[1, 2] EQ '1T' then
						CassetteID[1, 2] = ''
					end else If CassetteID[1,1] EQ 'I' OR CassetteID[1,1] EQ 'O' then
						CassetteID[1,1] = ''
					end
					
					If INDEX(CassetteID,'.',2) then
						RDSType = 'EPP'
					end else
						RDSType = 'SIC'
					end
					
					ValidCassetteID = Rds_Services('IsValidRDS', CassetteID, RDSType)
					If ValidCassetteID EQ True$ then
						If Cassette1ID NE '' AND (CassetteID NE Cassette1ID) then ;*Cassette2 Scan
							ErrorMsg = 'Lot Label Mismatch: ':CassetteID: ' does not match ': Cassette1ID
						end
						If Seq1No EQ Seq2No then
							ErrorMsg = 'PTI Label verification failed due':CRLF$:'to operator double-scanning barcode.'
						end
					end else
						ErrorMsg = 'Invalid Cassette Number: ':CassetteID
					end
					
					If ErrorMsg EQ '' then Response = CassetteID
				end
				
		End Case
            
    end else
        ErrorMsg = 'ScanData argument was missing in the ' : Service : ' service.'
    end
    
    If ErrorMsg NE '' then Error_Services('Add', ErrorMsg)
    
end service


//----------------------------------------------------------------------------------------------------------------------
// 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)

	ErrorMsg = ''
    Location = 'PTO Mat'	
    If (ScanData NE '') 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
					ErrorMsg = 'Invalid Lot Label Scan.'
				end else
					Cassette1 = Field(ScanData, '|', 3)
				end
				
				If ErrorMsg EQ '' then 
					// 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
								ErrorMsg = RDSType: ' RDS ':testCass1: ' has not completed packaging.'
							end
						end else
							ErrorMsg = RDSType: ' RDS ':testCass1: ' is currently on hold.'
						end
					end else
						ErrorMsg = RDSType: ' RDS ' : testCass1 : ' is an invalid RDS number.'
					end
					
					If ErrorMsg EQ '' then 
						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
								ErrorMsg = 'Scan Cancelled'
							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 ErrorMsg EQ '' then 
							If ValidCassette1 EQ True$ then
								If Param1 NE '' AND ScanData NE Param1 then ;*Cassette2 Scan
									ErrorMsg = 'RDS Mismatch: ':Cassette1: ' does not match ': Param1
								end
							end else
								ErrorMsg = 'Invalid RDS Number: ':Cassette1
							end
							
							If ErrorMsg EQ '' then Response = Cassette1
						end
					end
				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
					ErrorMsg = 'Invalid Shipping Label Scan.'
				end
				
				If ErrorMsg EQ '' then 
				
					// 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
					end
					
					If ErrorMsg EQ '' then Response = Cassette2
				end
		End Case
		
    end else
        ErrorMsg = 'ScanData argument was missing in the ' : Service : ' service.'
    end
    
    If ErrorMsg NE '' then Error_Services('Add', ErrorMsg)
    
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)
        
        If Get_Status(errCode) then
            Swap @SVM with CRLF$ in errCode
            ErrorMsg = 'Errors calling obj_WO_Mat_Log("Create"). Error code: ':errCode
            Error_Services('Add', ErrorMsg)
            Gosub SendErrorNotification
        end
    end
    
end service


SendErrorNotification:
    
    Recipients   = ''
    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  = 'FI_SUPPORT'   
    
    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 
		      
        WOMLParms  = LogFile:@RM
        WOMLParms := LogDTM:@RM
        WOMLParms := Action:@RM
        WOMLParms := WhCd:@RM
        WOMLParms := LocCd:@RM
        WOMLParms := WONo:@RM
        WOMLParms := CassNo:@RM
        WOMLParms := UserID:@RM
        WOMLParms := Tags:@RM
        WOMLParms := ToolID 
        Set_Status(0)
        errCode = ''
        obj_WO_Mat_Log('Create',WOMLParms)
        If Get_Status(errCode) then
            Swap @SVM with CRLF$ in errCode
            ErrorMsg = 'Error calling obj_WO_Mat_Log("Create"). Error code: ':errCode
            Gosub SendErrorNotification
        end        

		// 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 
			
			WOMLParms  = LogFile:@RM
			WOMLParms := LogDTM:@RM
			WOMLParms := Action:@RM
			WOMLParms := WhCd:@RM
			WOMLParms := LocCd:@RM
			WOMLParms := WONo:@RM
			WOMLParms := CassNo:@RM
			WOMLParms := UserID:@RM
			WOMLParms := Tags:@RM
			WOMLParms := ToolID 
			Set_Status(0)
			errCode    = ''
			obj_WO_Mat_Log('Create',WOMLParms)
			If Get_Status(errCode) then
				Swap @SVM with CRLF$ in errCode
				ErrorMsg = 'Error calling obj_WO_Mat_Log("Create"). Error code: ':errCode
				Gosub SendErrorNotification
			end  			
			
			// 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

