Compile function Packaging_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        :   Packaging_Services

    Description :   Handler program for all Scan 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)
        10/11/19    djs     Original programmer.
        10/08/21    djs     Modified CompletePackaging service to use obj_WO_Mat_Log('Create') instead of
                            obj_WO_Mat('AddInvTrans') to avoid record lock conflicts with the obj_WO_Mat('Post')
                            service running on the DB server.
        10/25/21	DPC		modified mismatch scan to keep WO_MAT log location at PKO instead of PACK

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

$insert SERVICE_SETUP
$insert SCANS_EQUATES
$insert APP_INSERTS
$insert WO_MAT_EQUATES
$insert NOTIFICATION_EQU
$insert PACKAGING_EQUATES
$insert LOT_OPERATION_EQUATES
$Insert LOT_EQUATES

Declare function    Scan_Services, Memory_Services, Database_Services, SRP_JSON, RTI_CreateGUID, Memberof
Declare function    Get_Property, RDS_Services, EpiPro_Services, DateTime, Signature_Services, Packaging_Services
Declare function    Lot_Services, Environment_Services, Logging_Services
Declare subroutine  Scan_Services, Memory_Services, Database_Services, SRP_JSON, Security_Services, obj_Notes
Declare subroutine  obj_WO_Mat_Log, obj_WO_Mat, Set_Status, SAP_Services, Rds_Services, Wm_Out_Services, Hold_Services
Declare subroutine  Lot_Event_Services, Lot_Services, Packaging_Services, Logging_Services

LogPath     = Environment_Services('GetApplicationRootPath') : '\LogFiles\Packaging'
LogDate     = Oconv(Date(), 'D4/')
LogTime     = Oconv(Time(), 'MTS')
LoggingDTM   = LogDate : ' ' : LogTime   ; // Logging DTM
LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' Packaging Log.csv'
Headers     = 'Logging DTM' : @FM : 'Message'
objLog      = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, Comma$, Headers, '', False$, False$)

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        = 'EMPLOYEE', 'CASSETTE_1', 'CASSETTE_2', 'POLY', 'TRILAM', 'PASSWORD'

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

Service CompletePackaging(CassetteID, OperatorID, BaggerIdentifier)
   
    *    1.	Write success packaging record in Material Log
    *    2.	Write success PTO record in Material Log
    *    3.	Send SAP Cassette Complete transaction (up to now, this has been done at FQA)
    *	 4. Create comment in CassetteID record
    
    RDSKey        = CassetteID
    WMOKey        = CassetteID
    LotId = ''
    Convert '.' to '*' in WMOKey
    CommentEntity = ''
    Begin Case
        Case RowExists('RDS', RDSKey) EQ True$            
            // RDS number
            WOMatKey      = Xlate('RDS', RDSKey, 'WO', 'X')
            WOMatKey      = WoMatKey:'*':Xlate('RDS', RDSKey, 'CASS_NO', 'X')
            CommentEntity = 'RDS'
            LotId = Lot_Services('GetLotIdByLegacyLotIdAndType', RDSKey, 'RDS')
            
        Case RowExists('WM_OUT', WMOKey) EQ True$           
            // WM_OUT key
            WOMatKey      = Xlate('WM_OUT', WMOKey, 'WO_MAT_KEY', 'X')
            CommentEntity = 'WM_OUT'
            LotId = Lot_Services('GetLotIdByLegacyLotIdAndType', WMOKey, 'WM_OUT')
            
        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
        
        Begin Case
        	Case BaggerIdentifier EQ 'L'
        		BaggerIdentifier = 'LeftBagger'
        	Case BaggerIdentifier EQ 'R'
        		BaggerIdentifier = 'RightBagger'
        	Case Otherwise$
        		BaggerIdentifier = ''
        End Case
        
        WONo    = Field(WOMatKey, '*', 1, 1)
        CassNo  = Field(WOMatKey, '*', 2, 1)
        
        InvDTM		= OCONV(Date(),'D4/'):' ':OCONV(Time(),'MTS')	        
        Set_Status(0)
        
        LogDate = OCONV( Date(), 'D2/' )
        LogTime = OCONV( Time(), 'MTS' )  
        LogFile = 'WO_MAT'               
        LogDTM  = LogDate:' ':LogTime    
        Action  = 'PACK'                
        WhCd    = 'CR'
        LocCd   = 'PACK'
        UserID  = OperatorID
        Tag     = 'Packaging complete'
        ToolID  = BaggerIdentifier

        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 := Tag:@RM
        WOMLParms := ToolID 
        
        obj_WO_Mat_Log('Create',WOMLParms)
        errCode = ''
        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)
            if LEN(errCode) > 5 then
                Gosub SendErrorNotification
            end
            Error_Services('Add', ErrorMsg)
        end else
            // Add CassComp transaction to SAP queue
            SAPBatchNo = Xlate('WO_MAT', WOMatKey, 'SAP_BATCH_NO', 'X')
            If SAPBatchNo EQ '' then SAP_Services('AddCassCompTransaction', WOMatKey)
            
            //Handle NG lot functions.
            If LotId NE '' then
                NewPackRecId = Packaging_Services('CreatePackagingRecord', LotId, UserID, ToolID)
                CurrOperationId = Lot_Services('GetLotCurrOperationId', LotId)
                If CurrOperationId NE '' then
                    Packaging_Services('AddPackToLotOperation', CurrOperationId, NewPackRecId, UserID)
                    If Error_Services('NoError') then
                        Lot_Services('MoveOutLot', LotId, UserId)
                    end
                end
                
                If Error_Services('HasError') then
                    //Remove any error messages as a result of NG Lot functions. At the moment we don't want them interrupting production when they occur.
                    Error_Services('Clear')
                end
            end
            
        end
    end
    
end service


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


Service ProcessScanData(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 'EMPLOYEE'
                    If ScanData[1, 2] EQ '1H' then                        
                        // Valid Employee ID scan.
                        UserID       = ScanData[3, 999]
                        EmployeeAuthorized  = Memberof(UserID, 'PACKAGING')
                        If EmployeeAuthorized EQ False$ then                       
                            Error_Services('Add', 'User ':UserID:' is not certified to perform packaging operation.')
                        end
                        Response = UserID
                    end else
                        Error_Services('Add', 'Invalid employee ID scan.')
                    end
                    
                Case ScanType EQ 'SUPPLIER'
                    If ScanData[1, 2] EQ '2T' then
                        
                        // Valid Supplier Lot scan.
                        ScanSupplierID = ScanData[3, 999]
                        FirstCassID    = Param1
                        SecondCassID   = Param2
                        OperatorID     = Param3
                        RDSKey         = FirstCassID
                        WMOKey         = FirstCassID
                        LotMismatch    = False$
                        ExpectedLotNo  = ''
                        Convert '.' to '*' in WMOKey
                        
                        Begin Case
                            
                            Case RowExists('RDS', RDSKey) EQ True$
                                // RDS number
                                RDSLotNo = Xlate('RDS', RDSKey, 'LOT_NUM', 'X')                                
                                If (ScanSupplierID NE RDSLotNo) then
                                    LotMismatch   = True$
                                    WOMatKey      = Xlate('RDS', RDSKey, 'WO_MAT_KEY', 'X')
                                    HoldEntity    = 'RDS'
                                    HoldEntityID  = RDSKey
                                    ExpectedLotNo = RDSLotNo                        
                                end
                                
                            Case RowExists('WM_OUT', WMOKey) EQ True$                                
                                // WM_OUT key
                                WONo        = Field(WMOKey, '*', 1, 1)
                                CassNo      = Field(WMOKey, '*', 3, 1)
                                WOMatKey    = WONo:'*':CassNo
                                WOMatLotNo  = Xlate('WO_MAT', WOMatKey, 'LOT_NO', 'X')
                                If (ScanSupplierID NE WOMatLotNo) then
                                    LotMismatch   = True$
                                    HoldEntity    = 'WM_OUT'
                                    HoldEntityID  = WMOKey
                                    ExpectedLotNo = WOMatLotNo                              
                                end
                                
                            Case Otherwise$
                                ErrorMessage = 'Invalid cassette ID ':FirstCassID
                                Error_Services('Add', ErrorMessage)
                                
                        End Case
                        
                        If LotMismatch EQ True$ then
                            
                            // Place cassette on hold
                            CtrlEntID   = False$	;* Control checked/unchecked
                            OriginFlag	= 'P'       ;* Flag to indicate a hold initiated from the packagaing form
                            Parms       = WOMatKey:@RM:HoldEntity:@RM:HoldEntityID:@RM:CtrlEntID:@RM:OriginFlag:@RM:OperatorID
                            Hold_Services('ToggleHold', WOMatKey, HoldEntity, HoldEntityID, CtrlEntID, OriginFlag, '', OperatorID)
                            
                            // Write fail packaging record in material log for first cassette ID
                            LogDate = OCONV( Date(), 'D2/' )
                            LogTime = OCONV( Time(), 'MTS' )  
                            LogFile = 'WO_MAT'               
                            LogDTM  = LogDate:' ':LogTime    
                            Action  = 'PACK'                
                            WhCd    = 'CR'
                            LocCd   = 'PKO'
                            UserID  = OperatorID
                            Tags    = 'Supp Lot Mismatch (':ScanSupplierID:')'
                            ToolID  = ''
                            
                            If ( RDS_Services('IsEpiPro', FirstCassID) EQ True$) then
                                WONo    = Field(FirstCassID, '.', 1, 1)
                                CassNo  = Field(FirstCassID, '.', 3, 1)
                            end else
                                WONo   = Xlate('RDS', FirstCassID, 'WO', 'X')
                                CassNo = Xlate('RDS', FirstCassID, 'CASS_NO', 'X')
                            end
                            
                            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 
                            
                            obj_WO_Mat_Log('Create',WOMLParms)
                            errCode = ''                   
                            IF Get_Status(errCode) THEN
                                ErrorMsg = 'Error calling obj_WO_Mat_Log("Create"). Error code: ':errCode
                                Error_Services('Add', ErrorMsg)
                            end
                            
                            // Send scan mismatch notification
                            Recipients	= Xlate('NOTIFICATION', 'PACKAGING', 'USER_ID', 'X')
                            SentFrom	= OperatorID
                            Subject		= 'Packaging Verification Mismatch'
                            Message		= 'Supplier lot number verification failed at packaging. Cassette placed on hold and Supervisor, Lead, or Engineering disposition required.':CRLF$ |
                            : 'RDS Label #1: ':FirstCassID:CRLF$            | 
                            : 'RDS Label #2: ':SecondCassID:CRLF$           |
                            : 'Scanned Lot Number: ':ScanSupplierID:CRLF$   |
                            : 'Expected Lot Number: ':ExpectedLotNo:CRLF$   |
                            : 'Operator: ':OperatorID
                            AttachWin	= ''
                            AttachKey	= ''
                            SendToGroup	= ''   
                            
                            Parms = Recipients:@RM:SentFrom:@RM:Subject:@RM:Message:@RM:AttachWin:@RM:AttachKey:@RM:SendToGroup
                            obj_Notes('Create',Parms)
                            errCode = ''
                            IF Get_Status(errCode) THEN Error_Services('Add', 'Error code ':errCode:' in ':Service:' service.')                            
                            
                            // Add error to error stack
                            ErrorMessage = 'Lots do not match - operation cannot continue. '|
                            : 'Lot has been put on hold - place lot on hold shelf and notify Supervisor, Lead, or Engineering'
                            Error_Services('Add', ErrorMessage)
                        end
                        
                        If Error_Services('NoError') then Response = ScanSupplierID
                        
                    end else
                        Error_Services('Add', 'Invalid supplier lot scan.')
                    end
                    
                Case ScanType EQ 'PASSWORD'
                    If ScanData[1, 3] EQ 'PWD' then
                        // Password scan.
                        Username = Get_Property(@Window:'.EDL_USER_ID_SCAN', 'TEXT')
                        Password = ScanData[4, 999]
                        Security_Services('AuthenticateLSLCredentials', Username, Password)
                        If Error_Services('NoError') then
                            Response = Password              
                        end else
                            ErrorMessage = Error_Services('GetMessage')
                            Error_Services('Add', ErrorMessage)
                        end
                        
                    end else
                        Error_Services('Add', 'Invalid password scan.')
                    end
                    
                Case ScanType EQ 'POLY'
                    If ScanData[1, 5] EQ '1POLY' AND LEN(ScanData) EQ 6 And (ScanData[6,1] EQ 'L' OR ScanData[6,1] EQ 'R') then
                        // Valid Poly scan - has to be exact match
                        Response = ScanData[2, 999]
                    end else
                        Error_Services('Add', 'Location does not equal POLY - operation cannot continue.')
                    end
                    
                Case ScanType EQ 'TRILAM'
                    If ScanData[1, 7] EQ '1TRILAM' AND LEN(ScanData) EQ 7 then
                        // Valid Trilam scan - has to be exact match
                        Response = ScanData[2, 999]
                    end else
                        Error_Services('Add', 'Location does not equal TRILAM - operation cannot continue.')
                    end
                    
                Case ScanType EQ 'CASSETTE_1'
                    
                    // 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.

					cnt = DCount(ScanData, '|')
					if cnt NE 8 then
						CassetteID = 0
						Error_Services('Add', 'Invalid Lot Label Scan.')
						return
					end else
						//RDS should be 3rd position
						CassetteID = Field(ScanData, '|', 3)
						Seq1 = Field(ScanData, '|', 8)
						Set_Property(@Window:'.EDL_SEQUENCE1', 'TEXT', Seq1)
					end

					// Strip '1T', 'I', and 'O' prefixes.
                    If CassetteID[1, 2] EQ '1T' then
                        CassetteID[1, 2] = ''
                        If ((CassetteID[1, 1] EQ 'O') or (CassetteID[1, 1] EQ 'I')) then CassetteID[1, 1] = ''
                    end                    
                    If ( (CassetteID[1, 1] EQ 'O') or (CassetteID[1, 1] EQ 'I') ) then CassetteID[1, 1] = ''
                    
                    ValidCassID = False$  ; // Assume Cassette ID is not valid for now.
                    RDSCass	= Count(CassetteID, '.') EQ 0 AND Num(CassetteID)
                    GaNCass	= Count(CassetteID, '.') EQ 1 AND not(RDSCass)
                    EPPCass	= Count(CassetteID, '.') EQ 2 AND not(RDSCass) AND not(GaNCass)

                    Begin Case
                        Case GaNCass
                            Convert '.' to '*' in CassetteID
                            // Need to check if PSN has ANKO flag. If so, throw error.
                            RDSNo = Xlate('WO_MAT', CassetteID, 'RDS_NO', 'X')
                            PSNo = Xlate('REACT_RUN', RDSNo, 'PS_NO', 'X')
                            ANKO = Xlate('PROD_SPEC', PSNo, 'ANKO', 'X')
                            If ANKO NE True$ then                           
                                WOMatRow    = Database_Services('ReadDataRow', 'WO_MAT', CassetteID)
                                If Error_Services('NoError') then
                                    ValidCassID   = True$
									HoldStatus    = Xlate('WO_MAT', CassetteID, 'HOLD', 'X')
									FQAComp       = Signature_Services('FinalSigComp', CassetteID)
                                    If (HoldStatus EQ True$) then
										ErrorMessage = 'Lot is currently on hold - operation cannot continue. ' |
										: 'Place lot on hold shelf and notify Supervisor, Lead, or Engineering.'
										Error_Services('Add', ErrorMessage)                    
									end else If FQAComp NE True$ then
                                        ErrorMessage = 'FQA is not complete, operation cannot continue. ' |
                                        : 'Please place lot in FQA area and notify Supervisor or Lead.'
                                        Error_Services('Add', ErrorMessage)
                                    end
                                end
                            end else
                                ErrorMessage = 'ANKO cassettes cannot go through packaging. They must be retained.'
                                Error_Services('Add', ErrorMessage)
                            end
                        Case EPPCass
                            Convert '.' to '*' in CassetteID
                            WMOutRow    = Database_Services('ReadDataRow', 'WM_OUT', CassetteID)
                            If Error_Services('NoError') then
                                ValidCassID   = True$
                                WOMatKey      = Field(CassetteID, '*', 1):'*':Field(CassetteID, '*', 3)
                                WMOStatus  = Xlate('WM_OUT', CassetteID, 'CURR_STATUS', 'X')
								HoldStatus = (WMOStatus EQ 'HOLD')    
								FQAComp       = Epipro_Services('GetFinalQAStatus', CassetteID)
                                LblCheckComp  = Signature_Services('CheckQALabelStatus', WOMatKey)
                                If (HoldStatus EQ True$) then
									ErrorMessage = 'Lot is currently on hold - operation cannot continue. ' |
									: 'Place lot on hold shelf and notify Supervisor, Lead, or Engineering.'
									Error_Services('Add', ErrorMessage)                    
								end else If FQAComp NE True$ then
                                    ErrorMessage = 'FQA is not complete, operation cannot continue. ' |
                                    : 'Please place lot in FQA area and notify Supervisor or Lead.'
                                    Error_Services('Add', ErrorMessage)
                                end else If LblCheckComp NE True$ then
                                    ErrorMessage = 'QA Label Check is not complete, operation cannot continue. ' |
                                    : 'Please place lot in FQA area and notify Supervisor or Lead.'
                                    Error_Services('Add', ErrorMessage)
                                end								                                
                            end
                        Case RDSCass
                            RDSRow  = Database_Services('ReadDataRow', 'RDS', CassetteID)
                            If Error_Services('NoError') then
                                ValidCassID   = True$
                                WOMatKey      = XLATE('RDS', CassetteID, 'WO_MAT_KEY' ,'X')
								HoldStatus    = Xlate('WO_MAT', WOMatKey, 'HOLD', 'X')
								FQAComp       = Rds_Services('GetFinalQAStatus', CassetteID)
                                LblCheckComp  = Signature_Services('CheckQALabelStatus', WOMatKey)
								WONo          = Field(WOMatKey, '*', 1)
                                CassNo        = Field(WOMatKey, '*', 2)
                                If (HoldStatus EQ True$) then
									ErrorMessage = 'Lot is currently on hold - operation cannot continue.' |
									: 'Place lot on hold shelf and notify Supervisor, Lead, or Engineering.'
									Error_Services('Add', ErrorMessage)                    
								end else If FQAComp NE True$ then                               
                                    ErrorMessage = 'FQA is not complete, operation cannot continue. ' |
                                    : 'Please place lot in FQA area and notify Supervisor or Lead.'
                                    Error_Services('Add', ErrorMessage)
                                end else If LblCheckComp NE True$ then
                                    ErrorMessage = 'QA Label Check is not complete, operation cannot continue. ' |
                                    : 'Please place lot in FQA area and notify Supervisor or Lead.'
                                    Error_Services('Add', ErrorMessage)
                                end
                            end 
                    End Case
 
                    If ValidCassID EQ False$ then
                        Error_Services('Add', 'Unrecognized scan data: ':ScanData)
                    end
                    
                    If ScanData[1, 3] EQ 'PWD' then
                    	ScanData = '********'
                    end else
                    	ScanData = CassetteID
                    end
                    Response = ScanData
                    
                Case ScanType EQ 'CASSETTE_2'

                    // 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.
                    
					cnt = DCount(ScanData, '|')
					if cnt NE 8 then
						CassetteID = 0
						Error_Services('Add', 'Invalid Lot Label Scan.')
						return
					end else
						//RDS should be 3rd position
						SecondCassID = Field(ScanData, '|', 3)
						Seq2 = Field(ScanData, '|', 8)
					end
                    
                    // Strip '1T', 'I', and 'O' prefixes.
                    If SecondCassID[1, 2] EQ '1T' then
                        SecondCassID[1, 2] = ''
                        If ( (SecondCassID[1, 1] EQ 'O') or (SecondCassID[1, 1] EQ 'I') ) then SecondCassID[1, 1] = ''
                    end                    
                    If ( (SecondCassID[1, 1] EQ 'O') or (SecondCassID[1, 1] EQ 'I') ) then SecondCassID[1, 1] = ''
                    
                    If ScanData[1, 3] EQ 'PWD' then ScanData = '********'
                    
                    FirstCassID  = Param1
                    OperatorID   = Param2
                    Seq1         = Param3
                    ValidCassID  = False$  ; // Assume Cassette ID is not valid for now.
                    ScanMismatch = False$
                    SeqMismatch  = False$
                    Convert '*' to '.' in FirstCassID
                    
					If Error_Services('NoError') then
                        ValidCassID = True$
						If (SecondCassID NE FirstCassID) then
							ScanMismatch = True$
						end else
							if (Seq1 EQ Seq2) then
								SeqMismatch = True$
							end
						end
					end
                    
                   
                    If ValidCassID EQ False$ then Error_Services('Add', 'Unrecognized scan data: ':ScanData)
                 
                    If ScanMismatch EQ True$ then
                        
                        // Check if first cassette ID is a valid RDS or WM_OUT key
                        Convert '.' to '*' in FirstCassID
                        ValidCassID = False$
                        Begin Case
                            Case ( RowExists('RDS', FirstCassID) EQ True$ )
                                WONo         = Xlate('RDS', FirstCassID, 'WO', 'X')
                                CassNo       = Xlate('RDS', FirstCassID, 'CASS_NO', 'X')
                                HoldEntity   = 'RDS'                             
                            Case ( RowExists('WM_OUT', FirstCassID) EQ True$ )
                                WONo         = Field(FirstCassID, '*', 1, 1)
                                CassNo       = Field(FirstCassID, '*', 3, 1)
                                HoldEntity   = 'WM_OUT'
                            Case ( RowExists('WO_MAT', FirstCassID) EQ True$ )
                                WONo         = Field(FirstCassID, '*', 1, 1)
                                CassNo       = Field(FirstCassID, '*', 2, 1)
                                HoldEntity   = 'WO_MAT'                    
                            Case Otherwise$
                                Null
                        End Case
                        Convert '*' to '.' in FirstCassID
                        
                        HoldEntityID = FirstCassID 
                        
                        // 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
                        
                        // 1st cassette - write fail packaging record in material log 
                        LogFile = 'WO_MAT'               
                        LogDTM  = DateTime()    
                        //just leave warehouse, location and action as they were 
                        WhCd    = WOMatRec<WO_MAT_INV_WH$,NumTimestamps>
                        LocCd   = WOMatRec<WO_MAT_INV_LOCATION$,NumTimestamps>
                        Action  = WOMatRec<WO_MAT_INV_ACTION$,NumTimestamps> 
                        UserID  = OperatorID
                        Tags    = 'RDS Mismatch (':SecondCassID:')'
                        ToolID  = ''
                        
                        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	= 'P'       ;* Flag to indicate a hold initiated from the packagaing form
                        Parms       = WOMatKey:@RM:HoldEntity:@RM:HoldEntityID:@RM:CtrlEntID:@RM:OriginFlag:@RM:OperatorID
                        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 SecondCassID                           
                        ValidCassID = False$
                        Begin Case
                            Case ( RowExists('RDS', SecondCassID) EQ True$ )
                                ValidCassID  = True$
                                WONo         = Xlate('RDS', SecondCassID, 'WO', 'X')
                                CassNo       = Xlate('RDS', SecondCassID, 'CASS_NO', 'X')
                                HoldEntity   = 'RDS'                              
                            Case ( RowExists('WM_OUT', SecondCassID) EQ True$ )
                                ValidCassID  = True$
                                WONo         = Field(SecondCassID, '*', 1, 1)
                                CassNo       = Field(SecondCassID, '*', 3, 1)
                                HoldEntity   = 'WM_OUT'
                            Case ( RowExists('WO_MAT', FirstCassID) EQ True$ )
                                WONo         = Field(FirstCassID, '*', 1, 1)
                                CassNo       = Field(FirstCassID, '*', 2, 1)
                                HoldEntity   = 'WO_MAT'                           
                            Case Otherwise$
                                Null
                                
                        End Case
                        Convert '*' to '.' in SecondCassID
                        
                        // Write fail packaging record in material log for second cassette ID
                        If ValidCassID EQ True$ then  
                            LogFile = 'WO_MAT'               
                            LogDTM  = DateTime()     
                            UserID  = OperatorID
                            Tags    = 'RDS Mismatch (':FirstCassID:')'
                            ToolID  = ''                                                      
                            
                            HoldEntityID = SecondCassID 
                            
                            // 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)
                            //just leave warehouse, location and action as they were 
                            WhCd    = WOMatRec<WO_MAT_INV_WH$,NumTimestamps>
                            LocCd   = WOMatRec<WO_MAT_INV_LOCATION$,NumTimestamps>
                            Action  = WOMatRec<WO_MAT_INV_ACTION$,NumTimestamps>                             
                            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 second cassette on hold
                            CtrlEntID   = False$	;* Control checked/unchecked
                            OriginFlag	= 'P'       ;* Flag to indicate a hold initiated from the packagaing form
                            Parms       = WOMatKey:@RM:HoldEntity:@RM:HoldEntityID:@RM:CtrlEntID:@RM:OriginFlag:@RM:OperatorID
                            Hold_Services('ToggleHold', WOMatKey, HoldEntity, HoldEntityID, CtrlEntID, OriginFlag, '', OperatorID)
                        end
                        
                        // Send scan mismatch notification
                        Recipients	= Xlate('NOTIFICATION', 'PACKAGING', 'USER_ID', 'X')
                        SentFrom	= OperatorID
                        Subject		= 'Packaging Verification Mismatch'
                        Message		= 'RDS label verification failed at packaging. Both cassettes placed on hold - Supervisor, Lead, or Engineering disposition required.':CRLF$ |
                        : 'RDS Label #1: ':FirstCassID:CRLF$ | 
                        : 'RDS Label #2: ':SecondCassID:CRLF$ |
                        : 'Operator: ':OperatorID
                        AttachWin	= ''
                        AttachKey	= ''
                        SendToGroup	= ''   
                        
                        Parms = Recipients:@RM:SentFrom:@RM:Subject:@RM:Message:@RM:AttachWin:@RM:AttachKey:@RM:SendToGroup
                        obj_Notes('Create',Parms)
                        errCode = ''
                        IF Get_Status(errCode) THEN Error_Services('Add', 'Error code ':errCode:' in ':Service:' service.')
                        Error_Services('Add', Message)
                        
                    end else if SeqMismatch EQ True$ then
                    	// Do sequence mismatch stuff here
                    	Message = 'RDS Label verification failed at packaging due to operator double-scanning barcode.':CRLF$ |
                    	: 'RDS: ':FirstCassID:CRLF$ | 
                        : 'Operator: ':OperatorID
                    	Error_Services('Add', Message)
                    end
                    
                    If ScanData[1, 3] EQ 'PWD' then
                    	ScanData = ''
                    end else
                    	ScanData = SecondCassID
                    end
                    Response = ScanData
                    
            End Case
            
        end
    end else
        Error_Services('Add', 'ScanData argument was missing in the ' : Service : ' service.')
    end
    
end service

Service CreatePackagingRecord(LotId, UserId, EqpId)
    
    NewRecId = ''
    ErrorMessage = ''
    
    If RowExists('LOT', LotId) then
        NewRecId = RTI_CreateGuid()
        TransDtm = Datetime()
        NewPackagingRec = ''
        NewPackagingRec<PACKAGING_LOT_ID$> = LotId
        NewPackagingRec<PACKAGING_COMPLETE$> = True$
        NewPackagingRec<PACKAGING_COMPLETE_DTM$> = TransDtm
        Database_Services('WriteDataRow', 'PACKAGING', NewRecId, NewPackagingRec, True$, 0, False$)
        If Error_Services('NoError') then
            Lot_Event_Services('CreateLotEvent', LotId, TransDtm, 'COMMENT', 'Lot Packaged.', EqpId, UserId)
        end else
            ErrorMessage = Error_Services('GetMessage')
        end
    end else
        ErrorMessage = 'LOT: ' LotId : ' not found' 
    end
    If ErrorMessage NE '' then
        LogData = ''
        LogData<1> = LoggingDTM
        LogData<2> = Error_Services('GetMessage')
        Logging_Services('AppendLog', objLog, LogData, @RM, @FM, False$)
        Error_Services('Clear')
    end
    
    Response = NewRecId
    
end service

Service AddPackToLotOperation(LotOperationId, PackagingId, UserId)
    
    ErrorMessage = ''
    
    If RowExists('LOT_OPERATION', LotOperationId) then
        If RowExists('LSL_USERS', UserId) then
            //Can add user group check here.
            LotOperationRec = Database_Services('ReadDataRow', 'LOT_OPERATION', LotOperationId, True$, 0, False$)
            LotId = LotOperationRec<LOT_OPERATION_LOT_ID$>
            ExistingPackId = LotOperationRec<LOT_OPERATION_PACKAGING_ID$>
            If ExistingPackId EQ '' then
                LotOperationRec<LOT_OPERATION_PACKAGING_ID$> = PackagingId
                Database_Services('WriteDataRow', 'LOT_OPERATION', LotOperationId, LotOperationRec)
                If Error_Services('NoError') then
                    TransDtm = Datetime()
                    Lot_Event_Services('CreateLotEvent', LotId, TransDtm, 'COMMENT', 'PACKAGING record added to operation.', '', UserId)
                end else
                    ErrorMessage = Error_Services('GetMessage')
                end
            end else
                ErrorMessage = 'A pack scan already exists for the lot operation. Please remove the pack before adding a new one.'
            end
        end else
            ErrorMessage = 'USER ID: ' UserId : ' not found.'
        end
    end else
        ErrorMessage = 'LOT_OPERATION: ' LotOperationId : ' not found.'
    end
    If ErrorMessage NE '' then
       Error_Services('Add', ErrorMessage)
    end
    
end service

