open-insight/LSL2/STPROC/CLEAN_INSP_SERVICES.txt
2025-02-24 11:12:57 -07:00

473 lines
28 KiB
Plaintext

Compile function Clean_Insp_Services(@Service, @Params)
/***********************************************************************************************************************
Name : Clean_Insp_Services
Description : Handler program for all CLEAN_INSP services.
Notes : Application errors should be logged using the Error Services module. There are a few methodological
assumptions built into way errors are managed which are important to understand in order to properly
work with Error Services:
- The term 'top' refers to the originating procedure of a call stack and the term 'bottom' refers to
the last routine (or the current routine) within a call stack. Within the OpenInsight Debugger
this will appear backwards since the originating procedure always appears at the bottom of the
list and the current routine appears at the top of the list. We are using this orientation because
it is common to refer to the process of calling other procedures as 'drilling down'.
- The reason for defining the orientation of the call stack is because Error_Services allows for
multiple error conditions to be appended to an original error. In most cases this will happen when
a procedure at the bottom of the stack generates an error condition and then returns to its
calling procedure. This higher level procedure can optionally add more information relevant to
itself. This continues as the call stack 'bubbles' its way back to the top to where the
originating procedure is waiting.
- Native OpenInsight commands that handle errors (e.g., Set_Status, Set_FSError, Set_EventStatus)
preserve their error state until explicitly cleared. This can hinder the normal execution of code
since subsequent procedures (usually SSPs) will fail if a pre-existing error condition exists.
Our philosophy is that error conditions should automatically be cleared before a new procedure
is executed to avoid this problem. However, the nature of Basic+ does not make this easy to
automate for any given stored procedure. Therefore, if a stored procedure wants to conform to our
philosophy then it should include a call into the 'Clear' service request at the top of the
program. Alternatively this can be done through a common insert (see SERVICE_SETUP for example.)
- Service modules will use the SERVICE_SETUP insert and therefore automatically clear out any
error conditions that were set before.
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)
11/16/22 djs Original programmer.
04/12/23 djs Fixed WO_STEP_PROD_SPEC_ID$ equate typo within UpdateCleanInsp service.
***********************************************************************************************************************/
#pragma precomp SRP_PreCompiler
$Insert LOGICAL
$Insert SERVICE_SETUP
$Insert WO_STEP_EQUATES
$Insert CLEAN_INSP_EQUATES
$Insert PRS_STAGE_EQUATES
$Insert WO_MAT_EQUATES
Declare function Database_Services, SRP_JSON, Error_Services, obj_Clean_Insp
Declare subroutine Database_Services, SRP_JSON, Error_Services, obj_React_Run, Clean_Insp_Services, React_Run_Services, Btree.Extract
GoToService
Return Response or ""
//-----------------------------------------------------------------------------
// SERVICES
//-----------------------------------------------------------------------------
Service ConvertRecordToJSON(KeyID, Record, ItemURL)
jsonRecord = ''
If KeyID NE '' then
If Record EQ '' then Record = Database_Services('ReadDataRow', 'CLEAN_INSP', KeyID)
If Error_Services('NoError') then
@DICT = Database_Services('GetTableHandle', 'DICT.CLEAN_INSP')
@ID = KeyID
@RECORD = Record
If SRP_JSON(objJSON, 'New', 'Object') then
If SRP_JSON(objCleanInsp, 'New', 'Object') then
SRP_JSON(objCleanInsp, 'SetValue', 'keyId', @ID)
SRP_JSON(objCleanInsp, 'SetValue', 'stage', {STAGE})
// Add Cleans Object
If SRP_JSON(objCleans, 'New', 'Object') then
// Add Cleans Specs Object
If SRP_JSON(objSpecCleans, 'New', 'Object') then
SRP_JSON(objSpecCleans, 'SetValueArray', 'tools', {SPEC_CLEAN_TOOL}, @VM)
SRP_JSON(objSpecCleans, 'SetValueArray', 'recipes', {SPEC_CLEAN_RECIPE}, @VM)
SRP_JSON(objCleans, 'Set', 'specs', objSpecCleans)
SRP_JSON(objSpecCleans, 'Release')
end
// Add Cleans Operations
If SRP_JSON(objCleansOpsArray, 'New', 'Array') then
CleanTools = {CLEAN_TOOL}
If CleanTools NE '' then
BoatIds = {CLEAN_BOAT_ID}
SRDNos = {CLEAN_SRD_NO}
CleanUsers = {CLEAN_SIG}
CleanDTMs = {CLEAN_SIG_DTM}
For each CleanTool in CleanTools using @VM setting vPos
If SRP_JSON(objCleansOp, 'New', 'Object') then
SRP_JSON(objCleansOp, 'SetValue', 'cleanTool', CleanTool)
SRP_JSON(objCleansOp, 'SetValue', 'boatId', BoatIds<0, vPos>)
SRP_JSON(objCleansOp, 'SetValue', 'srdNo', SRDNos<0, vPos>)
SRP_JSON(objCleansOp, 'SetValue', 'userId', CleanUsers<0, vPos>)
SRP_JSON(objCleansOp, 'SetValue', 'cleadDtm', OConv(CleanDTMs<0, vPos>, 'DT2/^H'))
SRP_JSON(objCleansOpsArray, 'Add', objCleansOp)
SRP_JSON(objCleansOp, 'Release')
end
Next CleanTool
end
SRP_JSON(objCleans, 'Set', 'operations', objCleansOpsArray)
SRP_JSON(objCleansOpsArray, 'Release')
end
SRP_JSON(objCleanInsp, 'Set', 'cleans', objCleans)
SRP_JSON(objCleans, 'Release')
end
// Add Inspection Object
If SRP_JSON(objInsp, 'New', 'Object') then
// Add Specs Object
If SRP_JSON(objInspSpecs, 'New', 'Object') then
SRP_JSON(objInspSpecs, 'SetValue', 'microscopeReq', {SPEC_MICROSCOPE}, 'Boolean')
SRP_JSON(objInspSpecs, 'SetValue', 'brightlightReq', {SPEC_BRIGHTLIGHT}, 'Boolean')
SRP_JSON(objInspSpecs, 'SetValue', 'lpd', {SPEC_LPD})
SRP_JSON(objInspSpecs, 'SetValue', 'scratches', {SPEC_SCRATCHES})
SRP_JSON(objInspSpecs, 'SetValue', 'scratchLen', {SPEC_SCRATCH_LEN})
SRP_JSON(objInspSpecs, 'SetValue', 'pits', {SPEC_PITS})
SRP_JSON(objInspSpecs, 'SetValue', 'mounds', {SPEC_MOUNDS})
SRP_JSON(objInspSpecs, 'SetValue', 'stackFaults', {SPEC_STACK_FAULTS})
SRP_JSON(objInspSpecs, 'SetValue', 'spikes', {SPEC_SPIKES})
SRP_JSON(objInspSpecs, 'SetValue', 'spots', {SPEC_SPOTS})
SRP_JSON(objInspSpecs, 'SetValue', 'fov', {SPEC_FOV})
SRP_JSON(objInspSpecs, 'SetValue', 'blDefects', {SPEC_BL_DEFECTS})
SRP_JSON(objInspSpecs, 'SetValue', 'bsideScratches', {SPEC_BSIDE_SCRATCHES})
SRP_JSON(objInspSpecs, 'SetValue', 'bsideScratchLen', {SPEC_BSIDE_SCRATCH_LEN})
SRP_JSON(objInspSpecs, 'SetValue', 'bsideNodules', {SPEC_BSIDE_NODULES})
SRP_JSON(objInspSpecs, 'SetValue', 'bsideSpikes', {SPEC_BSIDE_SPIKES})
SRP_JSON(objInsp, 'Set', 'specs', objInspSpecs)
SRP_JSON(objInspSpecs, 'Release')
end
SRP_JSON(objCleanInsp, 'Set', 'inspection', objInsp)
SRP_JSON(objInsp, 'Release')
end
// Add Surfscan Object
If SRP_JSON(objSurfScan, 'New', 'Object') then
If SRP_JSON(objSpecSurfScanArray, 'New', 'Array') then
SurfScanRecipes = {SPEC_SURFSCAN_RECIPE}
SpecDefects = {SPEC_SURF_DEFECTS}
SpecHazes = {SPEC_SURF_HAZE}
SpecSampleQtys = {SPEC_SS_SAMP_QTY}
If SurfScanRecipes NE '' then
For each SurfScanRecipe in SurfScanRecipes using @VM setting vPos
If SRP_JSON(objSpecSurfScan, 'New', Object) then
SRP_JSON(objSpecSurfScan, 'SetValue', 'recipe', SurfScanRecipe)
SRP_JSON(objSpecSurfScan, 'SetValue', 'defect', SpecDefects<0, vPos>)
SRP_JSON(objSpecSurfScan, 'SetValue', 'haze', SpecHazes<0, vPos>)
SRP_JSON(objSpecSurfScan, 'SetValue', 'sampleQty', SpecSampleQtys<0, vPos>)
SRP_JSON(objSpecSurfScanArray, 'Add', objSpecSurfScan)
SRP_JSON(objSpecSurfScan, 'Release')
end
Next SurfScanRecipe
end
SRP_JSON(objSurfScan, 'Set', 'specs', objSpecSurfScanArray)
SRP_JSON(objSpecSurfScanArray, 'Release')
end
SRP_JSON(objCleanInsp, 'Set', 'surfScan', objSurfScan)
SRP_JSON(objSurfScan, 'Release')
end
SRP_JSON(objJSON, 'Set', 'cleanInsp', objCleanInsp)
SRP_JSON(objCleanInsp, 'Release')
end
If itemURL NE '' then
// The itemURL was passed in so add HAL+JSON properties.
// Create the _links property and then all link objects needed for this resource.
If SRP_JSON(objLinks, 'New', 'Object') then
// Create a self link.
If SRP_JSON(objLink, 'New', 'Object') then
SRP_JSON(objLink, 'SetValue', 'href', ItemURL, 'String')
SRP_JSON(objLink, 'SetValue', 'title', 'Self', 'String')
SRP_JSON(objLinks, 'Set', 'self', objLink)
SRP_JSON(objLink, 'Release')
end
SRP_JSON(objJSON, 'Set', '_links', objLinks)
SRP_JSON(objLinks, 'Release')
end
// Create the _class property for this resource.
SRP_JSON(objJSON, 'SetValue', '_class', 'resource')
end
jsonRecord = SRP_JSON(objJSON, 'Stringify', 'Styled')
SRP_JSON(objJSON, 'Release')
end else
Error_Services('Add', 'Unable to create JSON representation in the ' : Service : ' service.')
end
end
end else
Error_Services('Add', 'KeyID argument was missing in the ' : Service : ' service.')
end
Response = jsonRecord
End Service
Service ConvertJSONToRecord(JSON)
If JSON NE '' then
If SRP_JSON(objJSON, 'Parse', JSON) EQ '' then
objCleanInsp = SRP_JSON(objJSON, 'Get', 'cleanInsp')
@ID = SRP_JSON(objCleanInsp, 'GetValue', 'keyId')
If @ID NE '' then
@Record = Database_Services('ReadDataRow', 'CLEAN_INSP', @ID)
If Error_Services('NoError') then
@Dict = Database_Services('GetTableHandle', 'DICT.CLEAN_INSP')
end
end else
Error_Services('Add', 'Error in ':Service:' service. Null value for cleanInsp.keyID.')
end
SRP_JSON(objCleanInsp, 'Release')
SRP_JSON(objJSON, 'Release')
end else
Error_Services('Add', 'Error in ':Service:' service. Unable to parse JSON payload.')
end
end else
Error_Services('Add', 'Error in ':Service:' service. Null JSON passed in.')
end
Response = @Record
End Service
//----------------------------------------------------------------------------------------------------------------------
// UpdateCleanInsp
//
// CleanInspKey - [Required]
//
// Updates or deletes a CLEAN_INSP record associated with an RDS or WM_OUT record per the PSN.
// This is useful when a PSN has changed such that PRS_STAGES have been added and/or removed and/or modified.
//----------------------------------------------------------------------------------------------------------------------
Service UpdateCleanInsp(CleanInspKey)
CIRec = ''
ErrorMsg = ''
If CleanInspKey NE '' then
CIRec = Database_Services('ReadDataRow', 'CLEAN_INSP', CleanInspKey)
If CIRec NE '' then
WONo = CIRec<CLEAN_INSP_WO_NO$>
WOStep = CIRec<CLEAN_INSP_WO_STEP$>
WOStepKey = WONo:'*':WOStep
ReactType = Xlate('WO_STEP', WOStepKey, 'REACTOR_TYPE', 'X')
If ReactType NE 'GAN' then
CassNo = CIRec<CLEAN_INSP_CASS_NO$>
Stage = CIRec<CLEAN_INSP_STAGE$>
RDSNo = CIRec<CLEAN_INSP_RDS_NO$>
PSNo = XLATE('WO_STEP', WONo:'*':WOStep, WO_STEP_PROD_SPEC_ID$, 'X')
If ( (PSNo NE '') and (Stage NE '') ) then
PrsStageKey = PSNo:'*':Stage
PRSStageRec = Database_Services('ReadDataRow', 'PRS_STAGE', PrsStageKey)
IF PRSStageRec NE '' THEN
CIRec<CLEAN_INSP_SPEC_INSP_REQ$> = PRSStageRec<PRS_STAGE_INSP_SIG_REQ$>
CIRec<CLEAN_INSP_SPEC_SURFSCAN_REQ$> = PRSStageRec<PRS_STAGE_SURFSCAN_SIG_REQ$>
CIRec<CLEAN_INSP_SPEC_CLEAN_REQ$> = PRSStageRec<PRS_STAGE_CLEAN_SIG_REQ$>
CIRec<CLEAN_INSP_SPEC_BRIGHTLIGHT$> = PRSStageRec<PRS_STAGE_BRIGHTLIGHT$> ;* Visual Inspection
CIRec<CLEAN_INSP_SPEC_MICROSCOPE$> = PRSStageRec<PRS_STAGE_MICROSCOPE$> ;* Visual Inspection
CIRec<CLEAN_INSP_SPEC_EDGE$> = PRSStageRec<PRS_STAGE_EDGE$> ;* Visual Inspection
CIRec<CLEAN_INSP_SPEC_PITS$> = PRSStageRec<PRS_STAGE_PITS$> ;* Visual Inspection
CIRec<CLEAN_INSP_SPEC_MOUNDS$> = PRSStageRec<PRS_STAGE_MOUNDS$> ;* Visual Inspection
CIRec<CLEAN_INSP_SPEC_BL_DEFECTS$> = PRSStageRec<PRS_STAGE_BL_DEFECTS$> ;* Visual Inspection
CIRec<CLEAN_INSP_SPEC_SPOTS$> = PRSStageRec<PRS_STAGE_SPOTS$> ;* Visual Inspection
CIRec<CLEAN_INSP_SPEC_FOV$> = PRSStageRec<PRS_STAGE_FOV$> ;* Visual Inspection
CIRec<CLEAN_INSP_SPEC_SCRATCHES$> = PRSStageRec<PRS_STAGE_SCRATCHES$> ;* Visual Inspection
CIRec<CLEAN_INSP_SPEC_SCRATCH_LEN$> = PRSStageRec<PRS_STAGE_SCRATCH_LEN$> ;* Visual Inspection
CIRec<CLEAN_INSP_SPEC_LPD$> = PRSStageRec<PRS_STAGE_LPD$> ;* Visual Inspection
CIRec<CLEAN_INSP_SPEC_STACK_FAULTS$> = PRSStageRec<PRS_STAGE_STACK_FAULTS$> ;* Visual Inspection
CIRec<CLEAN_INSP_SPEC_SPIKES$> = PRSStageRec<PRS_STAGE_SPIKES$> ;* Visual Inspection
CIRec<CLEAN_INSP_INSP_INTERVAL$> = PRSStageRec<PRS_STAGE_INSP_INTERVAL$> ;* Visual Inspection
CIRec<CLEAN_INSP_SPEC_SURFSCAN_RECIPE$> = PRSStageRec<PRS_STAGE_SURFSCAN_RECIPE$> ;* Surface Scan
CIRec<CLEAN_INSP_SPEC_SURF_HAZE$> = PRSStageRec<PRS_STAGE_SURF_HAZE$> ;* Surface Scan
CIRec<CLEAN_INSP_SPEC_SURF_DEFECTS$> = PRSStageRec<PRS_STAGE_SURF_DEFECTS$> ;* Surface Scan
CIRec<CLEAN_INSP_SPEC_SS_SAMP_QTY$> = PRSStageRec<PRS_STAGE_SS_SAMP_QTY$> ;* Surface Scan
CIRec<CLEAN_INSP_SPEC_CLEAN_TOOL$> = PRSStageRec<PRS_STAGE_CLEAN_TOOL$> ;* Cleans
CIRec<CLEAN_INSP_SPEC_CLEAN_RECIPE$> = PRSStageRec<PRS_STAGE_CLEAN_RECIPE$> ;* Cleans
Database_Services('WriteDataRow', 'CLEAN_INSP', CleanInspKey, CIRec, True$, False$, True$)
end else
// PRS Stage no longer exists, so delete the clean & insp record
Database_Services('DeleteDataRow', 'CLEAN_INSP', CleanInspKey)
CIRec = ''
// Remove references to the CLEAN_INSP record
Begin Case
Case ReactType EQ 'EPP'
// Remove CleanInspKey from WO_MAT_EPI_CI_NO$/WO_MAT_EPO_CI_NO$
WOMatKey = WONo:'*':CassNo
WOMatRec = Database_Services('ReadDataRow', 'WO_MAT', WOMatKey)
If Error_Services('NoError') then
Begin Case
Case Stage EQ 'PRE'
WOMatRec<WO_MAT_EPI_CI_NO$> = ''
Database_Services('WriteDataRow', 'WO_MAT', WOMatKey, WOMatRec)
Case Stage EQ 'POST'
WOMatRec<WO_MAT_EPO_CI_NO$> = ''
Database_Services('WriteDataRow', 'WO_MAT', WOMatKey, WOMatRec)
Case Otherwise$
// We should never get here as EPP only supports CLEAN_INSP on the PRE and POST stages
Null
End Case
end
Case Otherwise$
// Remove CleanInspKey and stage from REACT_RUN record
React_Run_Services('RemCleanInsp', RDSNo, CleanInspKey, Stage)
End Case
end
end else
ErrorMsg = 'Error in ':Service:' service. Could not determine PRS_STAGE key for CLEAN_INSP record ':CleanInspKey:'.'
end
end else
ErrorMsg = 'Error in ':Service:' service. GaN is not supported.'
end
end else
ErrorMsg = 'Error in ':Service:' service. CLEAN_INSP record ':CleanInspKey:' does not exist.'
end
end else
ErrorMsg = 'Error in ':Service:' service. Null CleanInsp key passed into service.'
end
If ErrorMsg NE '' then
Error_Services('Add', ErrorMsg)
end else
Response = CIRec
end
end service
//----------------------------------------------------------------------------------------------------------------------
// UpdateAllCleanInsp
//
// WOMatKey - [Required]
//
// Creates/Deletes/Updates all CLEAN_INSP records associated with an RDS or WM_OUT record per the PSN.
// This is useful when a PSN has changed such that PRS_STAGES have been added and/or removed and/or modified.
//----------------------------------------------------------------------------------------------------------------------
Service UpdateAllCleanInsp(WOMatKey)
ErrorMsg = ''
If WOMatKey NE '' then
WONo = Field(WOMatKey, '*', 1)
CassNo = Field(WOMatKey, '*', 2)
PSNo = Xlate('WO_STEP', WONo:'*1', WO_STEP_PROD_SPEC_ID$, 'X')
If PSNo NE '' then
ReactType = Xlate('PROD_SPEC', PSNo, 'REACTOR_TYPE', 'X')
Begin Case
Case ReactType EQ 'EPP'
WOMatRec = Database_Services('ReadDataRow', 'WO_MAT', WOMatKey)
// Update CLEAN_INSP records associated with the WM_OUT record
// The app on supports CLEAN_INSP records on the PRE and POST stages
PreCINo = Xlate('WO_MAT', WOMatKey, WO_MAT_EPI_CI_NO$, 'X')
Begin Case
Case PreCINo NE ''
// Update/delete the CLEAN_INSP record
Clean_Insp_Services('UpdateCleanInsp', PreCINo)
Case ( (PreCINo EQ '') and (RowExists('PRS_STAGE', PSNo:'*PRE') ) )
// Create the CLEAN_INSP record
ociParms = WONo:@RM ;* WONo
ociParms := 1:@RM ;* WOStep
ociParms := CassNo:@RM ;* CassNo
ociParms := 'PRE':@RM ;* Stage ;* Pre Epi Cleaning on inbound material
ociParms := '':@RM ;* RDSNo ;* No specific RDS on Epi Pro inbound material
ociParms := PSNo:@RM ;* PSNo
ociParms := '' ;* PSRec ;* Optional
WOMatRec<WO_MAT_EPI_CI_NO$> = obj_Clean_Insp('Create',ociParms)
Database_Services('WriteDataRow', 'WO_MAT', WOMatKey, WOMatRec)
End Case
PostCINo = Xlate('WO_MAT', WOMatKey, WO_MAT_EPO_CI_NO$, 'X')
Begin Case
Case PostCINo NE ''
// Update/delete the CLEAN_INSP record
Clean_Insp_Services('UpdateCleanInsp', PostCINo)
Case ( (PostCINo EQ '') and (RowExists('PRS_STAGE', PSNo:'*POST') ) )
// Create the CLEAN_INSP record
ociParms = WONo:@RM ;* WONo
ociParms := 1:@RM ;* WOStep
ociParms := CassNo:@RM ;* CassNo
ociParms := 'POST':@RM ;* Stage ;* Pre Epi Cleaning on inbound material
ociParms := '':@RM ;* RDSNo ;* No specific RDS on Epi Pro inbound material
ociParms := PSNo:@RM ;* PSNo
ociParms := '' ;* PSRec ;* Optional
WOMatRec<WO_MAT_EPO_CI_NO$> = obj_Clean_Insp('Create',ociParms)
Database_Services('WriteDataRow', 'WO_MAT', WOMatKey, WOMatRec)
End Case
Case ReactType EQ 'GAN'
// Not supported
ErrorMsg = 'Error in ':Service:' service. GAN is not supported.'
Case Otherwise$
// Update CLEAN_INSP records associated with the RDS record
RDSNo = Xlate('WO_MAT', WOMatKey, 'RDS_NO', 'X')
Stages = 'PRE,FWI,LWI,POST'
For each Stage in Stages using ','
CICol = Stage:'_CI_NO'
StageCINo = Xlate('RDS', RDSNo, CICol, 'X')
Begin Case
Case StageCINo NE ''
// Update/delete the CLEAN_INSP record
Clean_Insp_Services('UpdateCleanInsp', StageCINo)
Case ( (StageCINo EQ '') and (RowExists('PRS_STAGE', PSNo:'*':Stage) ) )
// Create the CLEAN_INSP record
ociParms = WONo:@RM ;* WONo
ociParms := 1:@RM ;* WOStep
ociParms := CassNo:@RM ;* CassNo
ociParms := Stage:@RM ;* Stage ;* Pre Epi Cleaning on inbound material
ociParms := RDSNo:@RM ;* RDSNo
ociParms := PSNo:@RM ;* PSNo
ociParms := '' ;* PSRec ;* Optional
NewCINo = obj_Clean_Insp('Create',ociParms)
// Add the stage and key ID to the REACT_RUN record
React_Run_Services('AddCleanInsp', RDSNo, NewCINo, Stage)
End Case
Next Stage
End Case
end else
ErrorMsg = 'Error in ':Service:' service. Could not determine PS_NO for WO_MAT record ':WOMatKey:'.'
end
end else
ErrorMsg = 'Error in ':Service:' service. Null WO_MAT key passed into service.'
end
If ErrorMsg NE '' then
Error_Services('Add', ErrorMsg)
Response = False$
end else
Response = True$
end
end service
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This service functions as a means to get the latest CINo for a specified RDS No where the defectivity measurements took place
// This is specifically used to have the latest defectivity data
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Service GetLatestDefectCINoByRDSId(RDSNo)
LatestCINo = ''
LatestInspDtm = ''
Open 'DICT CLEAN_INSP' to @DICT then
SrchString = 'RDS_NO':@VM:RDSNo:@FM
CIList = ''
Option = ''
Flag = ''
Btree.Extract(SrchString, 'CLEAN_INSP', @DICT, CIList, Option, Flag)
If CIList NE '' then
for each CleanInspKey in CIList using @VM
ThisCIInspDtm = Database_Services('ReadDataColumn', 'CLEAN_INSP', CleanInspKey, CLEAN_INSP_SCAN_SIG_DTM$ , True$, 0, False$)<1,1>
ThisCIInspDefAvg = Database_Services('ReadDataColumn', 'CLEAN_INSP', CleanInspKey, CLEAN_INSP_SCAN_SUM_OF_DEF_AVG$ , True$, 0, False$)
If ThisCIInspDtm GT LatestInspDtm AND ThisCIInspDefAvg NE '' then
LatestCINo = CleanInspKey
LatestInspDtm = ThisCIInspDtm
end
Next CleanInspKey
end
end
Response = LatestCINo
end service
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Internal GoSubs
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////