Compile function PTI_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 : PTI_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. ***********************************************************************************************************************/ #pragma precomp SRP_PreCompiler $insert SERVICE_SETUP $insert SCANS_EQUATES $insert APP_INSERTS $insert WO_MAT_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 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, Hold_Services 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', 'SUPPLIER', 'PASSWORD' //----------------------------------------------------------------------------- // SERVICES //----------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------- // ProcessScanData // // 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 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 PackagingFlag = True$ ;* Flag to indicate a hold initiated from the packagaing form Parms = WOMatKey:@RM:HoldEntity:@RM:HoldEntityID:@RM:CtrlEntID:@RM:PackagingFlag:@RM:OperatorID //obj_WO_Mat('ToggleHold', Parms) Hold_Services('ToggleHold', WOMatKey, HoldEntity, HoldEntityID, CtrlEntID, PackagingFlag, '', 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 = 'PACK' 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 AttachWindow = '' AttachKey = '' SendToGroup = '' Parms = Recipients:@RM:SentFrom:@RM:Subject:@RM:Message:@RM:AttachWindow:@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 5 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. // Strip '1T', 'I', and 'O' prefixes. If ScanData[1, 2] EQ '1T' then ScanData[1, 2] = '' If ( (ScanData[1, 1] EQ 'O') or (ScanData[1, 1] EQ 'I') ) then ScanData[1, 1] = '' end If ( (ScanData[1, 1] EQ 'O') or (ScanData[1, 1] EQ 'I') ) then ScanData[1, 1] = '' If ScanData[1, 3] EQ 'PWD' then ScanData = '********' CassetteID = ScanData ValidCassetteID = 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 // GaN 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 ValidCassetteID = True$ FQAComp = Signature_Services('FinalSigComp', CassetteID) If FQAComp EQ True$ then Null end else 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 // EpiPro Convert '.' to '*' in CassetteID WMOutRow = Database_Services('ReadDataRow', 'WM_OUT', CassetteID) If Error_Services('NoError') then ValidCassetteID = True$ FQAComp = Epipro_Services('GetFinalQAStatus', CassetteID) If FQAComp EQ True$ then HoldStatus = Xlate('WM_OUT', CassetteID, 'HOLD', 'X') 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 end else 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 Case RDSCass // RDS RDSRow = Database_Services('ReadDataRow', 'RDS', CassetteID) If Error_Services('NoError') then ValidCassetteID = True$ FQAComp = Rds_Services('GetFinalQAStatus', CassetteID) If FQAComp EQ True$ then HoldStatus = Xlate('RDS', CassetteID, 'HOLD', 'X') 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 end else 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 Case If ValidCassetteID EQ False$ then Error_Services('Add', 'Unrecognized scan data: ':ScanData) 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. // Strip '1T', 'I', and 'O' prefixes. If ScanData[1, 2] EQ '1T' then ScanData[1, 2] = '' If ( (ScanData[1, 1] EQ 'O') or (ScanData[1, 1] EQ 'I') ) then ScanData[1, 1] = '' end If ( (ScanData[1, 1] EQ 'O') or (ScanData[1, 1] EQ 'I') ) then ScanData[1, 1] = '' If ScanData[1, 3] EQ 'PWD' then ScanData = '********' SecondCassID = ScanData FirstCassID = Param1 OperatorID = Param2 ValidCassetteID = False$ ; // Assume Cassette ID is not valid for now. ScanMismatch = False$ Begin Case Case DCount(SecondCassID, '.') EQ 3 // EpiPro Convert '.' to '*' in SecondCassID WMOutRow = Database_Services('ReadDataRow', 'WM_OUT', SecondCassID) Convert '*' to '.' in SecondCassID If Error_Services('NoError') then ValidCassetteID = True$ If (SecondCassID NE FirstCassID) then ScanMismatch = True$ end end Case Index(SecondCassID, '.', 1) > 1 // GaN Convert '.' to '*' in SecondCassID Convert '.' to '*' in FirstCassID WOMatRow = Database_Services('ReadDataRow', 'WO_MAT', SecondCassID) If Error_Services('NoError') then ValidCassetteID = True$ If (SecondCassID NE FirstCassID) then ScanMismatch = True$ end end Case Num(SecondCassID) // RDS RDSRow = Database_Services('ReadDataRow', 'RDS', SecondCassID) If Error_Services('NoError') then ValidCassetteID = True$ If (SecondCassID NE FirstCassID) then ScanMismatch = True$ end end End Case If ValidCassetteID EQ False$ then Error_Services('Add', 'Unrecognized scan data: ':ScanData) If ScanMismatch EQ True$ then // Write fail packaging record in material log for first cassette ID LogFile = 'WO_MAT' LogDTM = DateTime() Action = 'PACK' WhCd = 'CR' LocCd = 'PACK' UserID = OperatorID Tags = 'RDS Mismatch (':SecondCassID:')' ToolID = '' // Check if first cassette ID is a valid RDS or WM_OUT key Convert '.' to '*' in FirstCassID ValidCass = 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 // To do: Throw Error 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, @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 PackagingFlag = True$ ;* Flag to indicate a hold initiated from the packagaing form Parms = WOMatKey:@RM:HoldEntity:@RM:HoldEntityID:@RM:CtrlEntID:@RM:PackagingFlag:@RM:OperatorID //obj_WO_Mat('ToggleHold', Parms) Hold_Services('ToggleHold', WOMatKey, HoldEntity, HoldEntityID, CtrlEntID, PackagingFlag, '', OperatorID) // Check if second cassette ID is a valid RDS or WM_OUT key Convert '.' to '*' in SecondCassID ValidCass = False$ Begin Case Case ( RowExists('RDS', SecondCassID) EQ True$ ) ValidCass = True$ WONo = Xlate('RDS', SecondCassID, 'WO', 'X') CassNo = Xlate('RDS', SecondCassID, 'CASS_NO', 'X') HoldEntity = 'RDS' Case ( RowExists('WM_OUT', SecondCassID) EQ True$ ) ValidCass = 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 ValidCass EQ True$ then LogFile = 'WO_MAT' LogDTM = DateTime() Action = 'PACK' WhCd = 'CR' LocCd = 'PACK' 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, @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 CtrlEntID = False$ ;* Control checked/unchecked PackagingFlag = True$ ;* Flag to indicate a hold initiated from the packagaing form Parms = WOMatKey:@RM:HoldEntity:@RM:HoldEntityID:@RM:CtrlEntID:@RM:PackagingFlag:@RM:OperatorID //obj_WO_Mat('ToggleHold', Parms) Hold_Services('ToggleHold', WOMatKey, HoldEntity, HoldEntityID, CtrlEntID, PackagingFlag, '', 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 AttachWindow = '' AttachKey = '' SendToGroup = '' Parms = Recipients:@RM:SentFrom:@RM:Subject:@RM:Message:@RM:AttachWindow:@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', 'Cassette scans did not match! Both cassettes placed on hold - Supervisor, Lead, or Engineering disposition required.') end If ScanData[1, 3] EQ 'PWD' then ScanData = '' Response = ScanData End Case end end else Error_Services('Add', 'ScanData argument was missing in the ' : Service : ' service.') end end service Service CompletePackaging(CassetteID, OperatorID) * 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) 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 // Write success packaging LogFile = 'WO_MAT' Action = 'PACK' WhCd = 'CR' LocCd = 'PACK' UserID = OperatorID Tag = 'Packaging complete' ToolID = '' 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) 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) Error_Services('Add', ErrorMsg) if LEN(errCode) > 5 then Gosub SendErrorNotification end end // Add CassComp transaction to SAP queue SAP_Services('AddCassCompTransaction', WOMatKey) end end service SendErrorNotification: Recipients = XLATE('SEC_GROUPS', 'OI_ADMIN', 'USER', '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 AttachWindow = '' SendToGroup = '' Parms = Recipients:@RM:SentFrom:@RM:Subject:@RM:Message:@RM:AttachWindow:@RM:AttachKey:@RM:SendToGroup obj_Notes('Create',Parms) return