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 CassNo = RdsRec 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, @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, @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