Compile function Lot_Services(@Service, @Params)
/***********************************************************************************************************************

    Name        :   Lot_Services

    Description :   Handler program for all LOT 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)
        07/01/25    djs     Corrected error throwing.

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

Declare function TEST_WAFER_PROD_SERVICES, SRP_Datetime, Datetime, Database_Services, Lot_Services, Error_Services, RTI_CREATEGUID
Declare function SRP_Array, SRP_Json, Environment_Services, Logging_Services, MemberOf, Lot_Event_Services, GetTickCount, Lot_Operation_Services
Declare function PSN_Services, Return_To_Fab_Services
Declare subroutine Database_Services, Btree.Extract, Lot_Services, Error_Services, Labeling_Services, SRP_Json, Logging_Services
Declare subroutine SRP_Run_Command, Service_Services, obj_notes, Lot_Event_Services, Mona_Services

$Insert SERVICE_SETUP
$Insert APP_INSERTS
$Insert LOT_EQUATES
$Insert TEST_WAFER_PROD_EQUATES
$Insert Lot_Operation_Equates
$Insert PRODUCT_OPERATION_EQUATES
$Insert LOT_EVENT_EQUATES
$Insert NOTIFICATION_EQUATES
$Insert VOIDED_LOT_EQUATES
$Insert RDS_EQUATES
$Insert WO_LOG_EQUATES
$Insert PROD_VER_EQUATES
$Insert OPERATION_EQUATES
$Insert IFX_EQUATES

LogPath      = Environment_Services('GetApplicationRootPath') : '\LogFiles\Lot'
LogDate      = Oconv(Date(), 'D4/')
LogTime      = Oconv(Time(), 'MTS')
LoggingDTM   = LogDate : ' ' : LogTime   ; // Logging DTM

LogFileName      = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' TestWaferLotCreation.csv'
Headers          = 'Logging DTM' : @FM : 'Product Name' : @FM : 'Username' : @FM : 'Message'
objTWCreationLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, ',', Headers, '', False$, False$)

LogFileName    = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' LotStart.csv'
Headers        = 'Logging DTM' : @FM : 'Lot Id' : @FM : 'Username' : @FM : 'Message'
objLotStartLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, ',', Headers, '', False$, False$)

LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' LotRun.csv'
Headers     = 'Logging DTM' : @FM : 'Lot Id' : @FM : 'Username' : @FM : 'Tool Id' : @FM : 'Message'
objLotRunLog   = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, ',', Headers, '', False$, False$)

LogFileName   = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' LotMove.csv'
Headers       = 'Logging DTM' : @FM : 'Lot Id' : @FM : 'Username' : @FM : 'Message'
objLotMoveLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, ',', Headers, '', False$, False$)

LogFileName    = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' LotEvent.csv'
Headers        = 'Logging DTM' : @FM : 'Lot Id' : @FM : 'Event Type' : @FM : 'Operator' : @FM : 'Begin Wafer Qty' : @FM : 'End Wafer Qty' : @FM : 'Bonus Wafer Qty' : @FM : 'Reduce Wafer Qty' : @FM : 'Message'
objLotEventLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, ',', Headers, '', False$, False$)

LogFileName    = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' TWLotClosure.csv'
Headers        = 'Logging DTM' : @FM : 'Lot Id' : @FM : 'Operator' : @FM : 'Message'
objLotClosureLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, ',', Headers, '', False$, False$)

Options EVENT_TYPES = 'MOVE_IN', 'MOVE_OUT', 'HOLD_ON', 'HOLD_OFF', 'REDUCE_WAFER_QTY', 'BONUS_WAFER_QTY', 'COMMENT', 'LOCATION', 'LOAD', 'UNSIGN_LOAD', 'TW_USE', 'CLOSE', 'SIGN_FQA', 'UNSIGN_FQA', 'SYSTEM_COMMENT'
Options LOT_TYPES = 'TW', 'RDS', 'WM_OUT', 'WM_IN', 'WO_MAT', 'LOT'
Options LEGACY_LOT_TYPES = 'RDS', 'WM_OUT', 'WM_IN', 'WO_MAT'

IsProd = Environment_Services('IsProd')
If IsProd EQ True$ then
	MonaResource = 'GRP_OPENINSIGHT_MES_OP_FE_LOTSERVICES'
end else
	MonaResource = 'GRP_OPENINSIGHT_MES_OP_FE_DEV_LOTSERVICES'
end

GoToService else
    Error_Services('Add', Service : ' is not a valid service request within the ' : ServiceModule : ' services module.')
end

Return Response or ""

//----------------------------------------------------------------------------------------------------------------------
// Services
//----------------------------------------------------------------------------------------------------------------------

Service GenerateNewLotId(LotType)
	
	NewLotID = ''
	RightNow = Datetime()
	If LotType NE '' then
		ValidLotNum    = False$
		GeneratedLotID = ''
		Begin Case
			Case LotType EQ 'TW'
				Loop
				Until ValidLotNum EQ True$
					LotPrefix = 'TW'
					LotYear   = SRP_Datetime('Year', RightNow)
					LotMonth  = SRP_Datetime('Month', RightNow)
					LotDay    = SRP_Datetime('Day', RightNow)
					LotTime   = SRP_Datetime('Time', RightNow)
					
					GeneratedLotId = LotPrefix : LotYear : LotMonth : LotDay : LotTime
					If Not(RowExists('LOT', GeneratedLotId)) then
						ValidLotNum = True$
					end else
						ValidLotNum = False$
						RightNow    = Datetime()
					end
				Repeat
			Case LotType EQ 'RDS' OR LotType EQ 'WM_IN' OR LotType EQ 'WM_OUT' OR LotType EQ 'WO_MAT'
				Loop
				Until ValidLotNum EQ True$
					LotPrefix = ''
					Begin Case
						Case LotType EQ 'RDS'
							LotPrefix = 'R'
						Case LotType EQ 'WM_IN'
							LotPrefix = 'I'
						Case LotType EQ 'WM_OUT'
							LotPrefix = 'O'
						Case LotType EQ 'WO_MAT'
							LotPrefix = 'M'
					End Case
					HaveLockLotIdSequence = Database_Services('GetKeyIDLock', 'CONFIG', 'LOT_KEY_SEQUENCE_PROD')
					If HaveLockLotIdSequence then
						CurrLotSequence = Database_Services('ReadDataRow', 'CONFIG', 'LOT_KEY_SEQUENCE_PROD', True$, 0, False$)
						NextLotSequence = CurrLotSequence + 1
						Database_Services('WriteDataRow', 'CONFIG', 'LOT_KEY_SEQUENCE_PROD', NextLotSequence, True$, False$, False$)
						Database_Services('ReleaseKeyIDLock', 'CONFIG', 'LOT_KEY_SEQUENCE_PROD')
						GeneratedLotId = LotPrefix : CurrLotSequence
						If Not(RowExists('LOT', GeneratedLotId)) AND CurrLotSequence NE '' then
							ValidLotNum = True$
						end else
							ValidLotNum = False$
						end
					end
				Repeat
			Case Otherwise$
				Null
		End Case
		If GeneratedLotID NE '' AND ValidLotNum then
			// Write the new lot id now so it's reserved
			Database_Services('WriteDataRow', 'LOT', GeneratedLotID, '')
			If RowExists('LOT', GeneratedLotID) then
				NewLotID = GeneratedLotID
			end
		end
	end
	Response = NewLotID
	
end service

Service GetLotIdByLegacyLotIdAndType(LegacyLotId, LegacyLotType=LEGACY_LOT_TYPES)
	StartTick = GetTickCount()
	MetricName = 'GetLotIdByLegacyLotIdAndType'
	
	ErrorMsg   = ''
	Open 'DICT.LOT' to DictLot then

        SearchString    = ''
        SearchString := 'LEGACY_LOT_ID':@VM:LegacyLotId:@FM
        SearchString := 'LEGACY_LOT_TYPE':@VM:LegacyLotType:@FM
        LotIdKeys          = ''
        Btree.Extract(SearchString, 'LOT', DictLot, LotIdKeys, '', '')
        ErrCode         = ''
        IF Get_Status(ErrCode) then
            ErrorMsg = 'Error in ':Service:' service. Error calling Btree.Extract. Error code ':ErrCode:'.'
        end else
            Response = LotIdKeys<1,1>
        end
        
    end else
        ErrorMsg = 'Error in ':Service:' service. Error opening LOT dictionary.'
    end
    
    EndTick = GetTickCount()
    Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)
    
    If ErrorMsg NE '' then
        Error_Services('Add', ErrorMsg)
    end
    
end service


Service CreateNewLot(LotType, ProdName, LotQty, VendorPartNo, VendorLotNo, VendorCode, Username, PrinterID, LegacyLotId)
	StartTick = GetTickCount()
	MetricName = 'CreateNewLot'

    CreatedLotNumber = ''
    ErrorMessage     = ''
    Begin Case
    	Case LotType EQ 'RDS' OR LotType EQ 'WM_IN' OR LotType EQ 'WM_OUT' OR LotType EQ 'WO_MAT'
    	    If LegacyLotId NE '' then 
                LegacyLotType = LotType
                LotId         = Lot_Services('GetLotIdByLegacyLotIdAndType', LegacyLotId, LegacyLotType)
                If LotId EQ '' then 
                    LotType  = 'PROD'
                    // Case statement for legacy lot entity types
                    NewLotId = Lot_Services('GenerateNewLotId', LegacyLotType)
                    If NewLotId NE '' then
                        If RowExists('LOT', NewLotId) NE False$ then
                            WoNo = ''
                            EpiPartNo = ''
                            ProdVerNo = ''
                            Begin Case
                                Case LegacyLotType EQ 'RDS'
                                    WoNo = XLATE('RDS', LegacyLotId, RDS_WO$, 'X')
                                Case LegacyLotType EQ 'WM_IN' OR LegacyLotType EQ 'WM_OUT' OR LegacyLotType EQ 'WO_MAT'
                                    WoNo = Field(LegacyLotId, '*', 1)
                            End Case
                            
                            ProdVerNo = XLATE('WO_LOG', WoNo, WO_LOG_PROD_VER_NO$, 'X')
                            EpiPartNo = XLATE('WO_LOG', WoNo, WO_LOG_EPI_PART_NO$, 'X')
                            ProdId = EpiPartNo : '*' : ProdVerNo
                            ProdSpecId = XLATE('WO_LOG', WoNo, WO_LOG_PROD_SPEC_ID$, 'X')
                            If ProdSpecId EQ '' then
                                Begin Case
                                    Case LegacyLotType EQ 'RDS'
                                        ProdSpecId = XLATE('RDS', LegacyLotId, RDS_PROD_SPEC_ID$, 'X')
                                    Case Otherwise$
                                        ProdSpecId = XLATE('PROD_VER', ProdVerNo, PROD_VER_PROC_STEP_PSN$, 'X')
                                End Case
                            end
                            If Not(RowExists('PRODUCT', ProdId)) then ProdId = '' 
                            LotRec                      = ''					
                            LotRec<LOT_TYPE$>           = 'PROD'
                            LotRec<LOT_PROD_ID$>        = ProdId
                            LotRec<LOT_ORIG_WAFER_QTY$> = LotQty
                            LotRec<LOT_WAFER_QTY$>      = LotQty
                            LotRec<LOT_VENDOR_PART_NO$> = VendorPartNo
                            LotRec<LOT_VENDOR_LOT_NO$>  = VendorLotNo
                            LotRec<LOT_VENDOR_CODE$>    = VendorCode
                            LotRec<LOT_LEGACY_LOT_ID$>    = LegacyLotId
                            LotRec<LOT_LEGACY_LOT_TYPE$> = LegacyLotType
                            LotRec<LOT_WO_LOG_ID$> = WONo
                            LotRec<LOT_PROD_SPEC_ID$> = ProdSpecId
                            LotRec<LOT_EPI_PART_NO$> = EpiPartNo
                            LotRec<LOT_PROD_VER_NO$> = ProdVerNo
                            Database_Services('WriteDataRow', 'LOT', NewLotId, LotRec)
                            If Error_Services('NoError') then
                                CreatedLotNumber = NewLotId
                            end else
                                ErrorMessage = 'Error in ':Service:' service. ':Error_Services('GetMessage')
                            end
                        end else
                            ErrorMessage = 'Error in ':Service:' service. Failed to generate new LOT id. LOT record "':NewLotId:'" already exists.'
                        end
                    end else
                        ErrorMessage = 'Error in ':Service:' service. Failed to generate new LOT id.'
                    end
                end else
                    ErrorMessage = 'Error in ':Service:' service. Lot Id ':LotId:' already exists for LegacyLotId ':LegacyLotId:'.'
                end
    	    end else
    	        ErrorMessage = 'Error in ':Service:' service. Null LegacyLotId passed in.'
    	    end
    	Case LotType EQ 'TW'
			If ProdName NE '' then
				TWProdID = Test_Wafer_Prod_Services('GetTestWaferProdIDsByPartName', ProdName)
				If TWProdID NE '' then
					If DCount(TWProdID, @VM) EQ 1 then
						NewLotId = Lot_Services('GenerateNewLotId', 'TW');//Don't use this
						If NewLotId NE '' then
							If RowExists('LOT', NewLotId) then
								LotRec = Database_Services('ReadDataRow', 'LOT', NewLotId)
								// Ensure the lot record is empty
								If LotRec<LOT_TYPE$> EQ '' AND LotRec<LOT_PROD_ID$> EQ '' AND LotRec<LOT_ORIG_WAFER_QTY$> EQ '' AND LotRec<LOT_WAFER_QTY$> EQ '' then
									LotRec<LOT_TYPE$>           = LotType
									LotRec<LOT_PROD_ID$>        = TWProdID
									LotRec<LOT_ORIG_WAFER_QTY$> = LotQty
									LotRec<LOT_WAFER_QTY$>      = LotQty
									LotRec<LOT_VENDOR_PART_NO$> = VendorPartNo
									LotRec<LOT_VENDOR_LOT_NO$>  = VendorLotNo
									LotRec<LOT_VENDOR_CODE$>    = VendorCode
									Database_Services('WriteDataRow', 'LOT', NewLotId, LotRec)
									// Now Ensure that the written lot matches what was just written
									LotRecCheck                 = Database_Services('ReadDataRow', 'LOT', NewLotId)
									If LotRecCheck EQ LotRec then
										// Lot Created successfully
										CreatedLotNumber = NewLotId
										If RowExists('LOT', CreatedLotNumber) then
											Lot_Services('CreateInitialLotOperationRecords', CreatedLotNumber)
											If Error_Services('NoError') then
												// Check in at first operation
												// Get Curr Operation(Should be first one in sequence)
												ThisLotCurrOpId = Lot_Services('GetLotCurrOperationId', CreatedLotNumber)
												If ThisLotCurrOpId NE ''  then
													// Set lot to open
													Lot_Services('OpenLot', CreatedLotNumber)
													if Error_Services('NoError') then
														// Move lot in
														Lot_Services('StartLot', CreatedLotNumber, 'SYSTEM')
														if Error_Services('NoError') then
															If PrinterID NE 'WEB' then
																// If PrinterID is set to WEB this means the request comes from OI_Wizard and the client is going to handle printing.
																Labeling_Services('PrintTestWaferLotLabel', CreatedLotNumber, Username, PrinterID)
																If Error_Services('HasError') then
																	ErrorMessage = 'Error in Create New Test Wafer Lot Process, lot id: ' : CreatedLotNumber : ' , ' : Error_Services('GetMessage')
																end
															end
															Lot_Services('MoveOutLot', NewLotId, Username)
															If Error_Services('NoError') then
																Lot_Services('MoveInLot', NewLotId, Username)
																If Error_Services('HasError') then
																	ErrorMessage = 'Error in Create New Test Wafer Lot Process, lot id: ' : NewLotId : ' , ' : Error_Services('GetMessage')
																end
															end else
																ErrorMessage = 'Error in Create New Test Wafer Lot Process, lot id: ' : NewLotId : ' , ' : Error_Services('GetMessage')
															end
														end else
															ErrorMessage = 'Error in Create New Test Wafer Lot Process, lot id: ' : NewLotId : ' , ' : Error_Services('GetMessage')
														end
													end else
														// Close LOT record as there was an error
														Lot_Services('CloseLot', NewLotId)
														ErrorMessage = 'Error in Create New Test Wafer Lot Process, lot id: ' : NewLotId : ' , ' : Error_Services('GetMessage')
													end
												end else
													// Close LOT record as there was an error
													Lot_Services('CloseLot', NewLotId)
													ErrorMessage = 'Error in Create New Test Wafer Lot Process, error getting current operation for lot: ' : NewLotId
												end
											end else
												// Close LOT record as there was an error
												// Database_Services('DeleteDataRow', 'LOT', NewLotId, True$, False$)
												Lot_Services('CloseLot', NewLotId)
												ErrorMessage = 'Error in Create New Test Wafer Lot Process, lot id: ' : NewLotId : ' , ' : Error_Services('GetMessage')
											end
										end
									end else
										ErrorMessage = 'Error in Create New Test Wafer Lot Process, Lot attributes did not write correctly for lot: ' : NewLotId
									end
								end else
									ErrorMessage = 'Error in Create New Test Wafer Lot Process, Lot already had data, lot: ' : NewLotId
								end
							end else
								ErrorMessage = 'Error in Create New Test Wafer Lot Process, Created lot: ' : NewLotId : ' not found in LOT table.'
							end
						end else
							ErrorMessage = 'Error in Create New Test Wafer Lot Process, no lot id returned.'
						end
					end else
						ErrorMessage = 'Error in Create New Test Wafer Lot Process, ambiguous product passed to routine OR no matching products found.'
					end
				end else
					ErrorMessage = 'Error in Create New Test Wafer Lot Process, product(':ProdName:') passed to routine not found.'
				end
			end else
				ErrorMessage = 'Null value passed in for ProdID'
			end    		
		Case Otherwise$
			ErrorMessage = 'Error in ':Service:' service. Lot Type "':LotType:'" is not currently supported.'
	End Case
	
	EndTick = GetTickCount()
	Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)    
	
	If ErrorMessage EQ '' then
		LogData    = ''
		LogData<1> = LoggingDTM
		LogData<2> = ProdName
		LogData<3> = Username
		LogData<4> = 'Successfully created lot id ' : CreatedLotNumber : ' of product type ' : ProdName '.'
		Logging_Services('AppendLog', objTWCreationLog, LogData, @RM, @FM, False$)
	end else
		LogData    = ''
		LogData<1> = LoggingDTM
		LogData<2> = ProdName
		LogData<3> = Username
		LogData<4> = ErrorMessage
		Logging_Services('AppendLog', objTWCreationLog, LogData, @RM, @FM, False$)
		Error_Services('Add', ErrorMessage)
	end
	Response = CreatedLotNumber
	
End Service


Service CreateNewTestWaferLot(LotType, ProdName, LotQty, VendorPartNo, VendorLotNo, VendorCode, Username, PrinterID)
	
	CreatedLotNumber = ''
	ErrorMessage     = ''
	If LotType EQ 'TW' then
		If ProdName NE '' then
			TWProdID = Test_Wafer_Prod_Services('GetTestWaferProdIDsByPartName', ProdName)
			If TWProdID NE '' then
				If DCount(TWProdID, @VM) EQ 1 then
					NewLotId = Lot_Services('GenerateNewLotId', 'TW')
					If NewLotId NE '' then
						If RowExists('LOT', NewLotId) then
							LotRec = Database_Services('ReadDataRow', 'LOT', NewLotId)
							// Ensure the lot record is empty
							If LotRec<LOT_TYPE$> EQ '' AND LotRec<LOT_PROD_ID$> EQ '' AND LotRec<LOT_ORIG_WAFER_QTY$> EQ '' AND LotRec<LOT_WAFER_QTY$> EQ '' then
								LotRec<LOT_TYPE$>           = LotType
								LotRec<LOT_PROD_ID$>        = TWProdID
								LotRec<LOT_ORIG_WAFER_QTY$> = LotQty
								LotRec<LOT_WAFER_QTY$>      = LotQty
								LotRec<LOT_VENDOR_PART_NO$> = VendorPartNo
								LotRec<LOT_VENDOR_LOT_NO$>  = VendorLotNo
								LotRec<LOT_VENDOR_CODE$>    = VendorCode
								Database_Services('WriteDataRow', 'LOT', NewLotId, LotRec)
								// Now Ensure that the written lot matches what was just written
								LotRecCheck                 = Database_Services('ReadDataRow', 'LOT', NewLotId)
								If LotRecCheck EQ LotRec then
									// Lot Created successfully
									CreatedLotNumber = NewLotId
									If RowExists('LOT', CreatedLotNumber) then
										Lot_Services('CreateInitialLotOperationRecords', CreatedLotNumber)
										If Error_Services('NoError') then
											// Check in at first operation
											// Get Curr Operation(Should be first one in sequence)
											ThisLotCurrOpId = Lot_Services('GetLotCurrOperationId', CreatedLotNumber)
											If ThisLotCurrOpId NE ''  then
												Lot_Services('MoveInLot', NewLotId, Username)
												if Error_Services('NoError') then
													// Set lot to open
													Lot_Services('OpenLot', CreatedLotNumber)
													if Error_Services('NoError') then
														If PrinterID NE 'WEB' then
															// If PrinterID is set to WEB this means the request comes from OI_Wizard and the client is going to handle printing.
															Labeling_Services('PrintTestWaferLotLabel', CreatedLotNumber, Username, PrinterID)
															If Error_Services('HasError') then
																ErrorMessage = 'Error in Create New Test Wafer Lot Process, lot id: ' : CreatedLotNumber : ' , ' : Error_Services('GetMessage')
															end
														end
														Lot_Services('MoveOutLot', NewLotId, Username)
														If Error_Services('NoError') then
															Lot_Services('MoveInLot', NewLotId, Username)
															If Error_Services('HasError') then
																ErrorMessage = 'Error in Create New Test Wafer Lot Process, lot id: ' : NewLotId : ' , ' : Error_Services('GetMessage')
															end
														end else
															ErrorMessage = 'Error in Create New Test Wafer Lot Process, lot id: ' : NewLotId : ' , ' : Error_Services('GetMessage')
														end
													end else
														ErrorMessage = 'Error in Create New Test Wafer Lot Process, lot id: ' : NewLotId : ' , ' : Error_Services('GetMessage')
													end
												end else
													ErrorMessage = 'Error in Create New Test Wafer Lot Process, lot id: ' : NewLotId : ' , ' : Error_Services('GetMessage')
												end
											end else
												ErrorMessage = 'Error in Create New Test Wafer Lot Process, error getting current operation for lot: ' : NewLotId
											end
										end else
											ErrorMessage = 'Error in Create New Test Wafer Lot Process, lot id: ' : NewLotId : ' , ' : Error_Services('GetMessage')
										end
									end
								end else
									ErrorMessage = 'Error in Create New Test Wafer Lot Process, Lot attributes did not write correctly for lot: ' : NewLotId
								end
							end else
								ErrorMessage = 'Error in Create New Test Wafer Lot Process, Lot already had data, lot: ' : NewLotId
							end
						end else
							ErrorMessage = 'Error in Create New Test Wafer Lot Process, Created lot: ' : NewLotId : ' not found in LOT table.'
						end
					end else
						ErrorMessage = 'Error in Create New Test Wafer Lot Process, no lot id returned.'
					end
				end else
					ErrorMessage = 'Error in Create New Test Wafer Lot Process, ambiguous product passed to routine OR no matching products found.'
				end
			end else
				ErrorMessage = 'Error in Create New Test Wafer Lot Process, product(':ProdName:') passed to routine not found.'
			end
		end else
			ErrorMessage = 'Null value passed in for ProdID'
		end
	end else
		ErrorMessage = 'Lot type was not test wafer'
	end
	If ErrorMessage EQ '' then
		LogData    = ''
		LogData<1> = LoggingDTM
		LogData<2> = ProdName
		LogData<3> = Username
		LogData<4> = 'Successfully created lot id ' : CreatedLotNumber : ' of product type ' : ProdName '.'
		Logging_Services('AppendLog', objTWCreationLog, LogData, @RM, @FM, False$)
	end else
		LogData    = ''
		LogData<1> = LoggingDTM
		LogData<2> = ProdName
		LogData<3> = Username
		LogData<4> = ErrorMessage
		Logging_Services('AppendLog', objTWCreationLog, LogData, @RM, @FM, False$)
		Error_Services('Add', ErrorMessage)
	end
	Response = CreatedLotNumber
	
End Service


Service GenerateInitialLotOperationRecords(LotId)
	
	ErrorMessage = ''
	If LotID NE '' then
		if RowExists('LOT', LotId) then
			LotRec            = Database_Services('ReadDataRow', 'LOT', LotId)
			ProdID            = LotRec<LOT_PROD_ID$>
			If ProdID NE '' then
				// Get product operations
				ThisLotOperations = Lot_Services('GetPrescribedOperationsByProdId', ProdId)
				If ThisLotOperations NE '' then
					for each ProdOperationKey in ThisLotOperations using @FM
						OperationRec      = Database_Services('ReadDataRow', 'PRODUCT_OPERATION', ProdOperationKey)
					Next operation
				end else
					ErrorMessage = 'No Operations found for ' : ProdId 
				end
			end else
				ErrorMessage = 'Prod ID not specified'
			end
			// ID will be LotNum + OperationID
			// Get product operations
		end else
			ErrorMessage = 'Lot ID does not exist.'
		end
	end else
		ErrorMessage = 'Lot ID was null'
	end
	If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
	
end service


Service GetPrescribedOperationsByProdId(ProdId, ProdType)
	
	ErrorMsg          = ''
	ProdOperationKeys = ''
	If ProdId NE '' then
		Begin Case
			Case ProdType = 'TW'
				ProdRec           = Database_Services('ReadDataRow', 'TEST_WAFER_PROD', ProdId)
				ProdOperationKeys = ProdRec<TEST_WAFER_PROD_PRODUCT_OPERATIONS$>
			Case Otherwise$
				ErrorMsg = 'Error in ':Service:' service. No matching prod type.'
		End Case
	end else
		ErrorMsg = 'Error in ':Service:' service. Null ProdId passed into service.'
	end
	
	If ErrorMsg EQ '' then 
		Response = ProdOperationKeys
	end else
		Error_Services('Add', ErrorMsg)
	end
	
end service


Service CreateInitialLotOperationRecords(LotId)
    ErrorMessage = ''
    If LotId NE '' then
        LotRec                    = Database_Services('ReadDataRow', 'LOT', LotId)
        LotType                   = LotRec<LOT_TYPE$>
        ProdId                    = LotRec<LOT_PROD_ID$>
        ThisInitialProdOperations = Lot_Services('GetPrescribedOperationsByProdId', ProdId, LotType)
        If Error_Services('NoError') AND ThisInitialProdOperations NE '' then
            For each ProdOperation in ThisInitialProdOperations using @VM
            
                ProdOperationRec  = Database_Services('ReadDataRow', 'PRODUCT_OPERATION', ProdOperation)
                OperationID       = ProdOperationRec<PRODUCT_OPERATION_OPERATION_ID$>
                OperationSequence = ProdOperationRec<PRODUCT_OPERATION_OPERATION_SEQUENCE$>
                
                LotOperationRecID = Rti_Createguid()
                If Not(RowExists('LOT_OPERATION', LotOperationRecID)) then
                    LotOperationRec                                    = ''
                    LotOperationRec<LOT_OPERATION_LOT_ID$>             = LotId
                    LotOperationRec<LOT_OPERATION_OPERATION_ID$>       = OperationID
                    LotOperationRec<LOT_OPERATION_OPERATION_SEQUENCE$> = OperationSequence
                    LotOperationRec<LOT_OPERATION_REWORK$>             = False$
                    Database_Services('WriteDataRow', 'LOT_OPERATION', LotOperationRecId, LotOperationRec)
                    If Error_Services('HasError') then
                        ErrorMessage = 'Error Writing Lot Operation Record.'
                    end
                end else
                    ErrorMessage = 'Lot Operation already existed, cannot overwrite'
                end    
            Next Operation
        end else
            ErrorMessage = 'Error getting prescribed operations for lot# ' : LotId
        end

    end else
        ErrorMessage = 'Lot ID was null'
    end
    If ErrorMessage NE '' then
        Error_Services('Add', ErrorMessage)
    end
end service

// Returns a @FM delimited list of operations in sequence
Service GetLotOperationSequence(LotId)
	
	StartTick               = GetTickCount()
	MetricName              = 'GetLotOperationSequence'
	ErrorMsg                = ''
	LotOperationsInSequence = ''
	If LotID NE '' then
		// Get Operations
		LotOperations           = Xlate('LOT', LotId, LOT_LOT_OPERATIONS$, 'X')
		for each LotOperation in LotOperations using @VM
			ThisLotOperationSequence                          = Xlate('LOT_OPERATION', LotOperation, LOT_OPERATION_OPERATION_SEQUENCE$, 'X')
			LotOperationsInSequence<ThisLotOperationSequence> = LotOperation
		Next LotOperation
	end else
		ErrorMsg = 'Error in ':Service:' service. Null LotID passed into service.'
	end
	
	EndTick = GetTickCount()
	Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)
	
	If ErrorMsg EQ '' then
		Response = LotOperationsInSequence
	end else
		Error_Services('Add', ErrorMsg)
	end
	
end service


Service GetLotCurrOperationId(LotId)
	
	ErrorMsg = ''
	StartTick = GetTickCount()
	MetricName = 'GetLotCurrOperationId'
	
	CurrOperation = ''
	If LotID NE '' then
		// Get them in sequence first
		LotOperationsInSequence = Lot_Services('GetLotOperationSequence', LotId)
		for each LotOperation in LotOperationsInSequence using @FM 
			ThisLotOperationStartDTM = Xlate('LOT_OPERATION', LotOperation, LOT_OPERATION_DATETIME_IN$, 'X')
			ThisLotOperationEndDTM   = Xlate('LOT_OPERATION', LotOperation, LOT_OPERATION_DATETIME_OUT$, 'X')
			If ThisLotOperationEndDTM Eq '' then
				CurrOperation = LotOperation
			end
		until CurrOperation
		Next LotOperation
	end else
		ErrorMsg = 'Error in ':Service:' service. Null LotID passed into service'
	end
	Response = CurrOperation
	
	EndTick = GetTickCount()
	Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)
	
	If ErrorMsg NE '' then Error_Services('Add', ErrorMsg)
	
end service


Service GetLotCurrOperationName(LotId)
	
	StartTick         = GetTickCount()
	MetricName        = 'GetLotCurrOperationName'
	ErrorMsg          = ''
	CurrOperationId   = ''
	CurrOperationName = ''
	If LotID NE '' then
		// Get them in sequence first
		LotOperationsInSequence = Lot_Services('GetLotOperationSequence', LotId)
		for each LotOperation in LotOperationsInSequence using @FM 
			ThisLotOperationStartDTM = Xlate('LOT_OPERATION', LotOperation, LOT_OPERATION_DATETIME_IN$, 'X')
			ThisLotOperationEndDTM   = Xlate('LOT_OPERATION', LotOperation, LOT_OPERATION_DATETIME_OUT$, 'X')
			If ThisLotOperationEndDTM Eq '' then
				CurrOperationId   = LotOperation
				CurrOperationName = Database_Services('ReadDataColumn', 'LOT_OPERATION', CurrOperationId, LOT_OPERATION_OPERATION_ID$, True$, 0, False$)
			end
		until CurrOperationName
		Next LotOperation
	end else
		ErrorMsg = 'Error in ':Service:' service. Null LotID passed into service.'
	end
	
	EndTick = GetTickCount()
	Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)
	
	If ErrorMsg EQ '' then
		Response = CurrOperationName
	end else
		Error_Services('Add', ErrorMsg)
	end
	
end service


Service AddLotOperationIntoSequence(LotId, NewOperationId, NewSequence, Rework)
	
	ErrorMessage = ''
	If LotId NE '' then
		If NewSequence NE '' AND Num(NewSequence) then
			// Get Current Operations in sequence
			LotCurrOperation      = Lot_Services('GetLotCurrOperationId', LotId)
			CurrOperationSequence = Xlate('LOT_OPERATION', LotCurrOperation, LOT_OPERATION_OPERATION_SEQUENCE$, 'X')
			If CurrOperationSequence LE NewSequence then
				// Get Curr Operation Sequence
				Done                     = False$
				LotCurrOperationSequence = Lot_Services('GetLotOperationSequence', LotId)
				for each Operation in LotCurrOperationSequence using @Fm setting OpPos
					ThisOperationRec      = Database_Services('ReadDataRow', 'LOT_OPERATION', Operation)
					ThisOperationSequence = ThisOperationRec<LOT_OPERATION_OPERATION_SEQUENCE$>
					If ThisOperationSequence GE NewSequence then
						NewOperationSequence                                = ThisOperationSequence + 1
						ThisOperationRec<LOT_OPERATION_OPERATION_SEQUENCE$> = NewOperationSequence
						Database_Services('WriteDataRow', 'LOT_OPERATION', Operation, ThisOperationRec)
						If Error_Services('HasError') then
							Done         = True$
							ErrorMessage = Error_Services('GetMessage')
						end
					end
				Until Done
				Next Operation
				If ErrorMessage EQ '' then
					LotOperationRecID                                  = RTI_CreateGUID()
					If Not(RowExists('LOT_OPERATION', LotOperationRecID)) then
						LotOperationRec                                    = ''
						LotOperationRec<LOT_OPERATION_LOT_ID$>             = LotId
						LotOperationRec<LOT_OPERATION_OPERATION_ID$>       = NewOperationId
						LotOperationRec<LOT_OPERATION_OPERATION_SEQUENCE$> = NewSequence
						LotOperationRec<LOT_OPERATION_REWORK$>             = Rework
						Database_Services('WriteDataRow', 'LOT_OPERATION', LotOperationRecId, LotOperationRec)
					end else
						ErrorMessage = 'Error in ':Service:' service. Lot Operation already existed, cannot overwrite'
					end
				end
			end else
				ErrorMessage = 'Error in ':Service:' service. Not allowed to add new operations prior to current operation'
			end
		end else
			ErrorMessage = 'Error in ':Service:' service. Null or invalid sequence passed to routine'
		end
	end else
		ErrorMessage = 'Error in ':Service:' service. Null LotID passed into service.'
	end
	If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
	
end service


Service IsLotMovedIn(LotId)
	
	StartTick = GetTickCount()
	MetricName = 'IsLotMovedIn'
	
	Response = ''
	If RowExists('LOT', LotId) then
		CurrOperId    = Lot_Services('GetLotCurrOperationId', LotId)
		CurrOperDtmIn = Database_Services('ReadDataColumn', 'LOT_OPERATION', CurrOperId, LOT_OPERATION_DATETIME_IN$, True$, 0, False$)
		Response      =  ( (CurrOperDtmIn LE Datetime()) AND (CurrOperDtmIn NE '') )
	end
	
	EndTick = GetTickCount()
	Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)
	
end service

Service IsLastOperation(LotId, LotOperationId)
    
    ErrorMsg = ''
    Response = False$
    
    If LotId NE '' then
        If LotOperationId NE '' then
            AllOpsInSeq = Lot_Services('GetLotOperationSequence', LotId)
            if Error_Services('NoError') then
                LastLotOperation = AllOpsInSeq[-1, "B":@FM]
                If LotOperationId EQ LastLotOperation then Response = True$
            end else
                ErrorMsg = Error_Services('GetMessage')
            end
        end else
            ErrorMsg = 'LotOperationId was null.'
        end
    end else
        ErrorMsg = 'LotId was null.'
    end
    
    If ErrorMsg NE '' then
        Error_Services('Add', ErrorMsg)
    end
    
end service


Service IsOperationCompleted(LotOperationId)
	
	OperationCompleted = False$
	ErrorMessage = ''
	If RowExists('LOT_OPERATION', LotOperationId) then
		LotOperationDateTimeOut = Database_Services('ReadDataColumn', 'LOT_OPERATION', LotOperationId, LOT_OPERATION_DATETIME_OUT$, True$, 0, False$)
		If LotOperationDateTimeOut NE '' then OperationCompleted = true$
	end else
		ErrorMessage = 'Invalid lot operation id passed to routine.'
	end
	If ErrorMessage NE '' then
		Error_Services('Add', 'Error in ' : Service : ' with Lot OperationId ' : LotOperationId : ' : ' : ErrorMessage)
	end
	Response = OperationCompleted
	
end service


Service StartLot(LotId, Operator)
	
	StartTick = GetTickCount()
	ErrorMessage           = ''
	If LotId NE '' then
		If RowExists('LOT', LotId) then
			Lot_Event_Services('CreateLotEvent', LotId, Datetime(), 'LOT_START', 'Lot started.', '', Operator)
			If Error_Services('NoError') then
			    CurrOperation = Lot_Services('GetLotCurrOperationName', LotId)
			    Lot_Event_Services('CreateLotEvent', LotId, Datetime(), 'ARRIVAL', 'Lot arrived at operation ' : CurrOperation : '.', '', Operator)
			    if Error_Services('NoError') then
                    Lot_Services('MoveInLot', LotId, Operator)
                    If Error_Services('HasError') then ErrorMessage = Error_Services('GetMessage')
				end else
				    ErrorMessage = Error_Services('GetMessage')
				end
			end else
				ErrorMessage = Error_Services('GetMessage')
			end
		end else
			ErrorMessage = 'Error in ':Service:' process for Lot id ' : LotId : '. Lot ID not found in LOT table.'
		end
	end else
		ErrorMessage = 'Error in ':Service:' process. Lot id was not passed to routine.'
	end
	
	EndTick = GetTickCount()
	Mona_Services('QueueLatencyAndCountMetrics', MonaResource, Service, StartTick, EndTick)     
	
	If ErrorMessage EQ '' then
		LogData    = ''
		LogData<1> = LoggingDTM
		LogData<2> = LotID
		LogData<3> = Operator
		LogData<4> = 'Successfully started lot.'
		Logging_Services('AppendLog', objLotStartLog, LogData, @RM, @FM, False$)
	end else
		LogData    = ''
		LogData<1> = LoggingDTM
		LogData<2> = LotID
		LogData<3> = Operator
		LogData<4> = 'Error starting lot.'
		Logging_Services('AppendLog', objLotStartLog, LogData, @RM, @FM, False$)
		Error_Services('Add', ErrorMessage)    	
	end
	
end service


Service BeginRun(LotId, Operator, ToolId)
	
	StartTick    = GetTickCount()
	ErrorMessage = ''
	If ( (LotId NE '') and (Operator NE '') and (ToolId NE '') ) then
		If RowExists('LOT', LotId) then
			Lot_Event_Services('CreateLotEvent', LotId, Datetime(), 'BEGIN_RUN', 'Begin run', ToolId, Operator)
			If Error_Services('HasError') then ErrorMessage = Error_Services('GetMessage')
		end else
			ErrorMessage = 'Error in ':Service:' process for Lot id ' : LotId : '. Lot ID not found in LOT table.'
		end
	end else
		ErrorMessage = 'Error in ':Service:' process. Lot id, Operator, or ToolId was not passed to routine.'
	end
	
	EndTick = GetTickCount()
	Mona_Services('QueueLatencyAndCountMetrics', MonaResource, Service, StartTick, EndTick)    
	
	If ErrorMessage EQ '' then
		LogData    = ''
		LogData<1> = LoggingDTM
		LogData<2> = LotID
		LogData<3> = Operator
		LogData<4> = ToolId
		LogData<4> = 'Successfully began run.'
		Logging_Services('AppendLog', objLotRunLog, LogData, @RM, @FM, False$)
	end else
		LogData    = ''
		LogData<1> = LoggingDTM
		LogData<2> = LotID
		LogData<3> = Operator
		LogData<4> = ToolId
		LogData<4> = 'Error beginning run.'
		Logging_Services('AppendLog', objLotRunLog, LogData, @RM, @FM, False$)
		Error_Services('Add', ErrorMessage)    	
	end
	
end service


Service EndRun(LotId, Operator, ToolId)
	
	StartTick    = GetTickCount()
	ErrorMessage = ''
	If ( (LotId NE '') and (Operator NE '') and (ToolId NE '') ) then
		If RowExists('LOT', LotId) then
			Lot_Event_Services('CreateLotEvent', LotId, Datetime(), 'END_RUN', 'End run', ToolId, Operator)
			If Error_Services('HasError') then ErrorMessage = Error_Services('GetMessage')
		end else
			ErrorMessage = 'Error in ':Service:' process for Lot id ' : LotId : '. Lot ID not found in LOT table.'
		end
	end else
		ErrorMessage = 'Error in ':Service:' process. Lot id, Operator, or ToolId was not passed to routine.'
	end
	
	EndTick = GetTickCount()
	Mona_Services('QueueLatencyAndCountMetrics', MonaResource, Service, StartTick, EndTick)    
	
	If ErrorMessage EQ '' then
		LogData    = ''
		LogData<1> = LoggingDTM
		LogData<2> = LotID
		LogData<3> = Operator
		LogData<4> = ToolId
		LogData<4> = 'Successfully ended run.'
		Logging_Services('AppendLog', objLotRunLog, LogData, @RM, @FM, False$)
	end else
		LogData    = ''
		LogData<1> = LoggingDTM
		LogData<2> = LotID
		LogData<3> = Operator
		LogData<4> = ToolId
		LogData<4> = 'Error ending run.'
		Logging_Services('AppendLog', objLotRunLog, LogData, @RM, @FM, False$)
		Error_Services('Add', ErrorMessage)    	
	end
	
end service


Service MoveInLot(LotID, Operator)
	
	StartTick              = GetTickCount()
	MetricName             = 'MoveInLot'
	ErrorMessage           = ''
	ThisLotCurrOperationID = ''
	If LotId NE '' then
		If RowExists('LOT', LotId) then
			ThisLotRec = Database_Services('ReadDataRow', 'LOT', LotId)
			WaferQty   = ThisLotRec<LOT_WAFER_QTY$>
			//Possibly Add locking here
			If Error_Services('NoError') then
				ThisLotCurrOperationID                                 = Lot_Services('GetLotCurrOperationId', LotId)
				If ThisLotCurrOperationID NE '' then
					ThisLotCurrOperationRec                                = Database_Services('ReadDataRow', 'LOT_OPERATION', ThisLotCurrOperationID)
					ThisLotCurrOperationRec<LOT_OPERATION_DATETIME_IN$>    = Datetime()
					ThisLotCurrOperationRec<LOT_OPERATION_WAFER_IN_QTY$>   = WaferQty
					ThisLotCurrOperationRec<LOT_OPERATION_OPERATOR_IN_ID$> = Operator
					Database_Services('WriteDataRow', 'LOT_OPERATION', ThisLotCurrOperationID, ThisLotCurrOperationRec)
					If Error_Services('NoError') then
						LotType       = XLATE('LOT', LotId, LOT_TYPE$, 'X')
						CurrOperation = XLATE('LOT_OPERATION', ThisLotCurrOperationID, LOT_OPERATION_OPERATION_ID$, 'X')
						If Error_Services('NoError') then
							Lot_Event_Services('CreateLotEvent', LotId, Datetime(), 'MOVE_IN', 'Lot moved into operation ' : CurrOperation : '.', '', Operator)
							If Error_Services('HasError') then ErrorMessage = Error_Services('GetMessage')
						end else
							ErrorMessage = 'Error in Move In process for Lot id ' : LotId : '. ' : Error_Services('GetMessage') 
						end
					end else
						ErrorMessage = 'Error in Move In process for Lot id ' : LotId : '. Error writing to lot operation record. ' : Error_Services('GetMessage')    
					end
				end else
					ErrorMessage = 'Error in Move In process for Lot id ' : LotId : '. Error getting lots current operation.'
				end
			end else
				ErrorMessage = 'Error in Move In process for Lot id ' : LotId : '. ' : Error_Services('GetMessage')
			end
		end else
			ErrorMessage = 'Error in Move In process for Lot id ' : LotId : '. Lot ID not found in LOT table.'
		end
	end else
		ErrorMessage = 'Error in Move In process. Lot id was not passed to routine.'
	end
	
	EndTick = GetTickCount()
	Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)    
	
	If ErrorMessage EQ '' then
		LogData    = ''
		LogData<1> = LoggingDTM
		LogData<2> = LotID
		LogData<3> = Operator
		LogData<4> = 'Successfully Moved Lot into operation ' : ThisLotCurrOperationID : '.'
		Logging_Services('AppendLog', objLotMoveLog, LogData, @RM, @FM, False$)
	end else
		LogData    = ''
		LogData<1> = LoggingDTM
		LogData<2> = LotID
		LogData<3> = Operator
		LogData<4> = 'Error moving Lot into operation ' : ThisLotCurrOperationID : '.'
		Logging_Services('AppendLog', objLotMoveLog, LogData, @RM, @FM, False$)
		Error_Services('Add', ErrorMessage)
	end
	
end service


Service MoveOutLot(LotID, Operator)
	
	StartTick              = GetTickCount()
	MetricName             = 'MoveOutLot'
	ErrorMessage           = ''
	ThisLotCurrOperationID = ''
	If LotId NE '' then
		If RowExists('LOT', LotId) then
			ThisLotRec = Database_Services('ReadDataRow', 'LOT', LotId)
			WaferQty   = ThisLotRec<LOT_WAFER_QTY$>
			If Error_Services('NoError') then
				ThisLotCurrOperationID                                  = Lot_Services('GetLotCurrOperationId', LotId)
				ThisLotCurrOperationName = XLATE('LOT_OPERATION', ThisLotCurrOperationID, LOT_OPERATION_OPERATION_ID$, 'X')
				If ThisLotCurrOperationID NE '' then
					If Lot_Services('IsLotMovedIn', LotId) then
						ThisLotCurrOperationRec                                 = Database_Services('ReadDataRow', 'LOT_OPERATION', ThisLotCurrOperationID)
						ThisLotCurrOperationRec<LOT_OPERATION_DATETIME_OUT$>    = Datetime()
						ThisLotCurrOperationRec<LOT_OPERATION_WAFER_OUT_QTY$>   = WaferQty
						ThisLotCurrOperationRec<LOT_OPERATION_OPERATOR_OUT_ID$> = Operator
						Database_Services('WriteDataRow', 'LOT_OPERATION', ThisLotCurrOperationID, ThisLotCurrOperationRec)
						If Error_Services('NoError') then
							LotType       = XLATE('LOT', LotId, LOT_TYPE$, 'X')
							CurrOperation = XLATE('LOT_OPERATION', ThisLotCurrOperationID, LOT_OPERATION_OPERATION_ID$, 'X')
							If Error_Services('NoError') then
							    IsLastLotOp = Lot_Services('IsLastOperation', LotId, ThisLotCurrOperationID)
							    if Error_Services('NoError') then
								    Lot_Event_Services('CreateLotEvent', LotId, Datetime(), 'MOVE_OUT', 'Lot moved out of operation ' : ThisLotCurrOperationName : '.', '', Operator)
                                    If Error_Services('NoError') then
                                        If Not(IsLastLotOp) then
                                            NewCurrOperation = Lot_Services('GetLotCurrOperationName', LotId)
                                            Lot_Event_Services('CreateLotEvent', LotId, Datetime(), 'ARRIVAL', 'Lot arrived at operation ' : NewCurrOperation : '.', '', Operator)
                                        end else
                                            Lot_Services('CloseLot', LotId)
                                            if Error_Services('HasError') then
                                                ErrorMessage = Error_Services('GetMessage')
                                            end
                                        end
                                    end
								end else
								    ErrorMessage = Error_Services('GetMessage')
								end
							end else
								ErrorMessage = 'Error in Move In process for Lot id ' : LotId : '. ' : Error_Services('GetMessage') 
							end
						end else
							ErrorMessage = 'Error in Move Out process for Lot id ' : LotId : '. Error writing to lot operation record. ' : Error_Services('GetMessage')
						end
					end else
						CurrOperationName = Field(ThisLotCurrOperationID, '*', 2)
						ErrorMessage = 'Error in Move Out process for Lot id ' : LotId : '. Cannot move out lot from ' :CurrOperationName: ' because it is not moved in.'
					end
				end else
					ErrorMessage = 'Error in Move Out process for Lot id ' : LotId : '. Error getting lots current operation.'
				end
			end else
				ErrorMessage = 'Error in Move Out process for Lot id ' : LotId : '. ' : Error_Services('GetMessage')
			end
		end else
			ErrorMessage = 'Error in Move Out process for Lot id ' : LotId : '. Lot ID not found in LOT table.'
		end
	end else
		ErrorMessage = 'Error in Move Out process. Lot id was not passed to routine.'
	end
	
	EndTick = GetTickCount()
	Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)    
	
	If ErrorMessage EQ '' then
		LogData    = ''
		LogData<1> = LoggingDTM
		LogData<2> = LotID
		LogData<3> = Operator
		LogData<4> = 'Successfully Moved Lot out of operation ' : ThisLotCurrOperationID : '.'
		Logging_Services('AppendLog', objLotMoveLog, LogData, @RM, @FM, False$)
	end else
		LogData    = ''
		LogData<1> = LoggingDTM
		LogData<2> = LotID
		LogData<3> = Operator
		LogData<4> = 'Error in Move Out process for Lot id ' : LotId : '. ' : ErrorMessage
		Logging_Services('AppendLog', objLotMoveLog, LogData, @RM, @FM, False$)
		Error_Services('Add', ErrorMessage)
	end
	
end service


Service ConvertLotRecordToJson(LotId, ItemURL, CurrUser, FullObject=BOOLEAN)

    ErrorMessage = ''
    JsonString   = ''
    If FullObject EQ '' then
        FullObject = True$
    end
    If RowExists('LOT', LotId) then
        objJSON = ''
        If SRP_JSON(objJSON, 'New', 'Object') then
            LotRec = Database_Services('ReadDataRow', 'LOT', LotId)
            If Error_Services('NoError') then
                If SRP_JSON(objLot, 'New', 'Object') then
                
                    SRP_JSON(objLot, 'SetValue', 'LotId', LotId)
                    SRP_JSON(objLot, 'SetValue', 'LegacyLotId', LotRec<LOT_LEGACY_LOT_ID$>, 'String')
                    SRP_JSON(objLot, 'SetValue', 'LegacyLotType', LotRec<LOT_LEGACY_LOT_TYPE$>, 'String')
                    CanUserModifyLot = Lot_Services('CanUserModifyLot', CurrUser)
                    SRP_JSON(objLot, 'SetValue', 'CanUserModifyLot', CanUserModifyLot, 'Boolean')
                    SRP_JSON(objLot, 'SetValue', 'WorkOrderNo', LotRec<LOT_WO_LOG_ID$>, 'String')
                    SRP_JSON(objLot, 'SetValue', 'EpiPartNo', LotRec<LOT_EPI_PART_NO$>, 'String')
                    SRP_JSON(objLot, 'SetValue', 'Type', LotRec<LOT_TYPE$>)
                    SRP_JSON(objLot, 'SetValue', 'ProdId', LotRec<LOT_PROD_ID$>)
                    ProdSpecNo = LotRec<LOT_PROD_SPEC_ID$>
                    SRP_JSON(objLot, 'SetValue', 'ProdSpecNo', ProdSpecNo, 'String')
                    Begin Case
                        Case LotRec<LOT_TYPE$> = 'TW'
                            ProdName = XLATE('TEST_WAFER_PROD', LotRec<LOT_PROD_ID$>, TEST_WAFER_PROD_PART_NAME$, 'X')
                        Case Otherwise$
                            ProdName = ''
                    End Case
                    SRP_JSON(objLot, 'SetValue', 'ProdName', ProdName)
                    SRP_JSON(objLot, 'SetValue', 'OrigWaferQty', LotRec<LOT_ORIG_WAFER_QTY$>)
                    SRP_JSON(objLot, 'SetValue', 'WaferQty', LotRec<LOT_WAFER_QTY$>)
                    SRP_JSON(objLot, 'SetValue', 'VendorPartNo', LotRec<LOT_VENDOR_PART_NO$>)
                    SRP_JSON(objLot, 'SetValue', 'VendorLotNo', LotRec<LOT_VENDOR_LOT_NO$>)
                    SRP_JSON(objLot, 'SetValue', 'Vendor', LotRec<LOT_VENDOR_CODE$>)
                    CurrLotOperationId = Lot_Services('GetLotCurrOperationId', LotId)
                    CurrOperation = XLATE('LOT_OPERATION', CurrLotOperationId, LOT_OPERATION_OPERATION_ID$, 'X')
                    CurrOperationDesc = XLATE('OPERATION', CurrOperation, OPERATION_OPERATION_DESCRIPTION$, 'X')
                    SRP_JSON(objLot, 'SetValue', 'CurrLotOperationId', CurrLotOperationId)
                    SRP_JSON(objLot, 'SetValue', 'CurrOperation', CurrOperation)
                    SRP_JSON(objLot, 'SetValue', 'CurrOperationDesc', CurrOperationDesc, 'String')
                    MostRecentEventId = LotRec<LOT_MOST_RECENT_LOT_EVENT_ID$>
                    MostRecentEventDtmIConv = Xlate('LOT_EVENT', MostRecentEventId, LOT_EVENT_EVENT_DATETIME$, 'X')
                    MostRecentEventDtmOConv = OConv(MostRecentEventDtmIConv, 'DT')
                    SRP_JSON(objLot, 'SetValue', 'MostRecentEventDateTime', MostRecentEventDtmOConv)
                    if LotRec<LOT_LEGACY_LOT_ID$> NE '' then
                        ActiveRTFId = Return_To_Fab_Services('GetOpenReturnToFabRecordIdByCassId', LotRec<LOT_LEGACY_LOT_ID$>)
                    end else
                        ActiveRTFId = Return_To_Fab_Services('GetOpenReturnToFabRecordIdByCassId', LotId)
                    end
                    SRP_JSON(objLot, 'SetValue', 'ActiveRTFId', ActiveRTFId, 'String')
                    If FullObject then
                        //Events Array
                        EventsArrayJson     = ''
                        If SRP_Json(EventsArrayJson, 'New', 'Array') then
                            LotEventKeys = Lot_Event_Services('GetLotEventsInSequence', LotId)
                            for each LotEventKey in LotEventKeys using @FM
                                objEvent = ''
                                EventRec = Database_Services('ReadDataRow', 'LOT_EVENT', LotEventKey)
                                If SRP_Json(objEvent, 'New', 'Object') then
                                    SRP_JSON(objEvent, 'SetValue', 'LotEventId', LotEventKey)
                                    SRP_JSON(objEvent, 'SetValue', 'LotId', EventRec<LOT_EVENT_LOT_ID$>)
                                    SRP_JSON(objEvent, 'SetValue', 'LotEventType', EventRec<LOT_EVENT_LOT_EVENT_TYPE$>)
                                    SRP_JSON(objEvent, 'SetValue', 'EventDatetime', OConv(EventRec<LOT_EVENT_EVENT_DATETIME$>, 'DT'))
                                    SRP_JSON(objEvent, 'SetValue', 'EventNote', EventRec<LOT_EVENT_EVENT_NOTE$>)
                                    SRP_JSON(objEvent, 'SetValue', 'EquipmentId', EventRec<LOT_EVENT_EQUIPMENT_ID$>)
                                    SRP_JSON(objEvent, 'SetValue', 'EventReduceWaferQty', EventRec<LOT_EVENT_EVENT_REDUCE_WAFER_QTY$>)
                                    SRP_JSON(objEvent, 'SetValue', 'EventBonusWaferQty', EventRec<LOT_EVENT_EVENT_BONUS_WAFER_QTY$>)
                                    SRP_JSON(objEvent, 'SetValue', 'EventBeginWaferQty', EventRec<LOT_EVENT_EVENT_BEGIN_WAFER_QTY$>)
                                    SRP_JSON(objEvent, 'SetValue', 'EventEndWaferQty', EventRec<LOT_EVENT_EVENT_END_WAFER_QTY$>)
                                    SRP_JSON(objEvent, 'SetValue', 'EventOperationId', EventRec<LOT_EVENT_EVENT_OPERATION_ID$>)
                                    SRP_JSON(objEvent, 'SetValue', 'EventOperatorId', EventRec<LOT_EVENT_EVENT_OPERATOR_ID$>)
                                    SRP_JSON(objEvent, 'SetValue', 'Sequence', EventRec<LOT_EVENT_SEQUENCE$>)
                                    SRP_JSON(EventsArrayJson, 'Add', objEvent)
                                    SRP_JSON(objEvent, 'Release')
                                end
                            Next LotEventKey
                            SRP_JSON(objLot, 'Set', 'LotEvents', EventsArrayJson)
                            SRP_JSON(EventsArrayJson, 'Release')
                        end else
                            ErrorMessage = 'Error Creating Events JSON Array'
                        end
                        OperationsArrayJson = ''
                        If SRP_Json(OperationsArrayJson, 'New', 'Array') then
                            LotOperationKeys = Lot_Services('GetLotOperationSequence', LotId)
                            for each LotOperationKey in LotOperationKeys using @FM
                                ThisLotOperationJsonString = Lot_Operation_Services('ConvertRecordToJson', LotOperationKey)
                                If SRP_Json(objLotOperation, 'PARSE', ThisLotOperationJsonString) EQ '' then
                                    SRP_Json(OperationsArrayJson, 'Add', objLotOperation)
                                    SRP_Json(objLotOperation, 'Release')
                                end
                            Next LotOperationKey
                            SRP_JSON(objLot, 'Set', 'LotOperations', OperationsArrayJson)
                            SRP_JSON(OperationsArrayJson, 'Release')
                            Recipes = PSN_Services('GetAllMetrologyRecipes', ProdSpecNo, True$, True$, True$, True$)
                            //Add in all lot recipe parameters
                            If SRP_JSON(objRecipes, 'New', 'Array') then
                                for each Recipe in Recipes using @FM
                                    //ToolClass : @VM : Recipe : @VM : Stage
                                    ToolClass = Recipe<1,1>
                                    RecipeName = Recipe<1,2>
                                    Stage = Recipe<1,3>
                                    If SRP_JSON(objRecipe, 'New', 'Object') then
                                        SRP_JSON(objRecipe, 'SetValue', 'ToolClass', ToolClass, 'String')
                                        SRP_JSON(objRecipe, 'SetValue', 'RecipeName', RecipeName, 'String')
                                        SRP_JSON(objRecipe, 'SetValue', 'StageName', Stage, 'String')
                                        SRP_JSON(objRecipes, 'Add', objRecipe)
                                        SRP_JSON(objRecipe, 'Release')
                                    end
                                Next Recipe
                                SRP_JSON(objLot, 'Set', 'AllRecipes', objRecipes)
                                SRP_JSON(objRecipes, 'Release')
                            end
                        end else
                            ErrorMessage = 'Error Creating Operations JSON Array'
                        end
                    end
                    SRP_JSON(objJSON, 'Set', 'Lot', objLot)
                    SRP_JSON(objLot, 'Release')
                end else
                    ErrorMessage = 'Error creating new Lot Json Object'
                end
            end else
                ErrorMessage = 'Error reading ':LotId:' from lot table.'
            end
            JsonString = SRP_JSON(objJSON, 'Stringify', 'Styled')
            SRP_JSON(objJSON, 'Release')
        end else
            ErrorMessage = 'Error creating new Json Object'
        end
    end else
        ErrorMessage = 'Invalid or null lot number passed to routine.'
    end
    If ErrorMessage NE '' then
        Error_Services('Add', ErrorMessage)
    end
    Response = JsonString
    
end service


Service OpenLot(LotId)
	
	StartTick = GetTickCount()
	MetricName = 'OpenLot'
	
	ErrorMessage = ''
	If RowExists('LOT', LotId) then
		LotRec            = Database_Services('ReadDataRow', 'LOT', LotId, True$, 0, False$)
		LotRec<LOT_OPEN$> = True$
		Database_Services('WriteDataRow', 'LOT', LotId, LotRec, True$, False$, False$)
		If Error_Services('HasError') then
			ErrorMessage = 'Error setting lot ' : LotId : ' to open.' : Error_Services('GetMessage')
		end
	end else
		ErrorMessage = 'Unable to Open Lot ' : LotId : '. Lot ID was not found in LOT table.'
	end
	
	EndTick = GetTickCount()
	Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)    
	
	If ErrorMessage NE ''  then Error_Services('Add', ErrorMessage)
	
end service


Service CloseLot(LotId)
	
	StartTick    = GetTickCount()
	MetricName   = 'CloseLot'
	ErrorMessage = ''
	If RowExists('LOT', LotId) then
		LotRec            = Database_Services('ReadDataRow', 'LOT', LotId, True$, 0, False$)
		LotRec<LOT_OPEN$> = False$
		Database_Services('WriteDataRow', 'LOT', LotId, LotRec, True$, False$, False$)
		If Error_Services('NoError') then
			Lot_Event_Services('CreateLotEvent', LotId, Datetime(), 'CLOSE', 'Lot status set to Closed', '', 'SYSTEM')
		end else
			ErrorMessage = 'Error setting lot ' : LotId : ' to closed.' : Error_Services('GetMessage')    
		end
	end else
		ErrorMessage = 'Unable to Close Lot ' : LotId : '. Lot ID was not found in LOT table.'
	end
	
	EndTick = GetTickCount()
	Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)    
	
	If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
	
end service


Service AutoCloseLot(LotId, CloseUserId)

	AutoCloseTime = Datetime()
	ErrorMessage = ''
	If MemberOf(CloseUserId, 'LEAD') OR MemberOf(CloseUserId, 'SUPERVISOR') OR CloseUserId EQ 'SYSTEM' then
		LotOperations = Lot_Services('GetLotOperationSequence', LotId)
		for LotOperationIndex = 1 to DCount(LotOperations, @FM)
		Until ErrorMessage NE ''
			ThisLotOperationId = LotOperations<LotOperationIndex>
			ThisOperationCompleted = Lot_Services('IsOperationCompleted', ThisLotOperationId)
			If Error_Services('NoError') then
				if Not(ThisOperationCompleted) then
				    MovedIn = Lot_Services('IsLotMovedIn', LotId)
				    If MovedIn then
				        Lot_Services('MoveOutLot', LotId, CloseUserId)
				        If Error_Services('HasError') then
				            ErrorMessage = Error_Services('GetMessage')
				        end else
				            ErrorMessage = Error_Services('GetMessage')
				        end
				    end else
				        Lot_Services('MoveInLot', LotId, CloseUserId)
				        If Error_Services('NoError') then
				            If Lot_Services('IsLastOperation', LotId, ThisLotOperationId)  then
				                Lot_Services('MoveOutLot', LotId, CloseUserId)
				                If Error_Services('HasError') then
				                    ErrorMessage = Error_Services('GetMessage')
				                end
				            end
				        end else
				            ErrorMessage = Error_Services('GetMessage')
				        end
				    end
				end
			end else
				ErrorMessage = Error_Services('GetMessage')
			end
		Next LotOperationIndex
	end else
		ErrorMessage = CloseUserId : ' does not have permission to close this lot.'
	end
	If ErrorMessage EQ ''  then
        LogData    = ''
        LogData<1> = LoggingDTM
        LogData<2> = LotId
        LogData<3> = CloseUserId
        LogData<4> = 'Successfully marked lot as closed.'
        Logging_Services('AppendLog', objLotClosureLog, LogData, @RM, @FM, False$)
	end else
		ErrorMessage = 'Error in ' : service : ' with lot ' : LotId : ':' : ErrorMessage
		LogData    = ''
		LogData<1> = LoggingDTM
		LogData<2> = LotId
		LogData<3> = CloseUserId
		LogData<4> = 'Error marking lot as closed.' : ErrorMessage
		Logging_Services('AppendLog', objLotClosureLog, LogData, @RM, @FM, False$)
		// Send a message to FI
		Recipients   = Xlate('NOTIFICATION', 'FI_SUPPORT', NOTIFICATION_USER_ID$, 'X')
		SentFrom     = 'SYSTEM'
		Subject      = 'TEST WAFER LOT AUTO-CLOSURE FAILURE'
		Message      = 'Error in ' : service : ' with lot id ' : LotId : ' attempting to Auto Close Lot. ' : ErrorMessage
		Obj_Notes('Create',Recipients:@RM:SentFrom:@RM:Subject:@RM:Message)
		Error_Services('Add', 'Error in ' : service : ' with lot ' : LotId : ':' : ErrorMessage)
	end
	
end service


Service HoldLot(LotId, OperatorId)
	
	StartTick = GetTickCount()
	MetricName = 'HoldLot'
	
	EndTick = GetTickCount()
	Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)
	
end service


Service UnholdLot(LotId, OperatorId)
	
	StartTick = GetTickCount()
	MetricName = 'UnholdLot'
	
	EndTick = GetTickCount()
	Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)
	
end service


Service ReduceLotWaferCount(LotId, ReductionQty, OperatorId)
	
	StartTick = GetTickCount()
	MetricName = 'ReduceLotWaferCount'
	
	ErrorMessage = ''
	If RowExists('LOT', LotId) then
		LotRec        = Database_Services('ReadDataRow', 'LOT', LotId, True$, 0, False$)
		LotType		  = LotRec<LOT_TYPE$>
		LotHold       = LotRec<LOT_HOLD$>
		LotOpen       = LotRec<LOT_OPEN$>
		LotCurrWfrQty = LotRec<LOT_WAFER_QTY$>
		LotNewWfrQty  = LotCurrWfrQty - ReductionQty
		If LotNewWfrQty LE 25 AND LotNewWfrQty GE 0 then
			LotRec<LOT_WAFER_QTY$> = LotNewWfrQty
			Database_Services('WriteDataRow', 'LOT', LotId, LotRec, True$, False$, False$)
			If Error_Services('NoError') then
				// Write Lot Event
				Lot_Event_Services('CreateLotEvent', LotId, Datetime(), 'REDUCE_WAFER_QTY', 'Reduced wafer count by ' : ReductionQty, '', OperatorId, False$, '')
				if LotNewWfrQty EQ 0  AND LotType EQ 'TW' then
					ServiceParms = 'AutoCloseLot' : SD$ : LotId : SD$ : 'SYSTEM'
					Service_Services('PostProcedure', 'LOT_SERVICES', ServiceParms)
					If Error_Services('HasError') then
						Recipients   = Xlate('NOTIFICATION', 'FI_SUPPORT', NOTIFICATION_USER_ID$, 'X')
						SentFrom     = 'SYSTEM'
						Subject      = 'TEST WAFER LOT AUTO-CLOSURE FAILURE'
						Message      = 'Error in ' : service : ' attempting to add auto lot closure for lot id ' : LotId : ' to the service queue.'
						AttachWindow = ''
						AttachKeys   = ''
						SendToGroup  = ''
						Obj_Notes('Create',Recipients:@RM:SentFrom:@RM:Subject:@RM:Message)
					end
				end
			end else
				ErrorMessage = 'Error in Increase Lot Wafer Count service, Error writing Lot Id ' : LotId : ' with new quantity.'
			end
		end else
			Begin Case
				Case LotNewWfrQty GT 25
					ErrorMessage = 'Error in Reduce Lot Wafer Count service, Lot Id ' : LotId : ' cannot have a quantity great than 25.'
				Case LotNewWfrQty LT 0
					ErrorMessage = 'Error in Reduce Lot Wafer Count service, Lot Id ' : LotId : ' cannot have a quantity less than 0.'
			End Case
		end
	end else
		ErrorMessage = 'Error in Reduce Lot Wafer Count service, Lot Id ' : LotId : ' not found in lot table.' 
	end
	
	EndTick = GetTickCount()
	Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)
	
	If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
	
end service


Service IncreaseLotWaferCount(LotId, IncreaseQty, OperatorId)
	
	StartTick = GetTickCount()
	MetricName = 'IncreaseLotWaferCount'
	
	ErrorMessage = ''
	If RowExists('LOT', LotId) then
		LotRec        = Database_Services('ReadDataRow', 'LOT', LotId, True$, 0, False$)
		LotHold       = LotRec<LOT_HOLD$>
		LotOpen       = LotRec<LOT_OPEN$>
		if LotOpen then
			If Not(LotHold) then
				LotCurrWfrQty = LotRec<LOT_WAFER_QTY$>
				LotNewWfrQty  = LotCurrWfrQty + IncreaseQty
				
				If LotNewWfrQty LE 25 AND LotNewWfrQty GT 0 then
					LotRec<LOT_WAFER_QTY$> = LotNewWfrQty
					Database_Services('WriteDataRow', 'LOT', LotId, LotRec, True$, False$, False$)
					If Error_Services('NoError') then
						// Write Lot Event
						Lot_Event_Services('CreateLotEvent', LotId, Datetime(), 'BONUS_WAFER_QTY', 'Increased wafer count by ' : IncreaseQty, '', OperatorId, False$, '')
					end else
						ErrorMessage = 'Error in Increase Lot Wafer Count service, Error writing Lot Id ' : LotId : ' with new quantity.' 
					end
				end else
					Begin Case
						Case LotNewWfrQty GT 25
							ErrorMessage = 'Error in Increase Lot Wafer Count service, Lot Id ' : LotId : ' cannot have a quantity great than 25.'
						Case LotNewWfrQty LT 0
							ErrorMessage = 'Error in Increase Lot Wafer Count service, Lot Id ' : LotId : ' cannot have a quantity less than 0.'
					End Case
				end    
			end else
				ErrorMessage = 'Error in Increase Lot Wafer Count service, Lot Id ' : LotId : ' is currently on hold.'
			end
		end else
			ErrorMessage = 'Error in Increase Lot Wafer Count service, Lot Id ' : LotId : ' is closed.'
		end
		
	end else
		ErrorMessage = 'Error in Increase Lot Wafer Count service, Lot Id ' : LotId : ' not found in lot table.' 
	end
	
	EndTick = GetTickCount()
	Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)    
	
	If ErrorMessage NE '' then Error_Services('Add', ErrorMessage)
	
end service


Service OpenOIWizardCreateTWLotInBrowser()
	
	Command = 'explorer https://goto.infineon.com/oiwizard/Lot/CreateTestWaferLot/'
	SRP_Run_Command(Command)
	
end service


Service CreateNewVoidedLotRecord(LotId, LegacyLotId, LotType=LOT_TYPES, Username)
	
	StartTick = GetTickCount()
	MetricName = 'CreateNewVoidedLotRecord'
	
	ErrorMessage = ''
	If RowExists('LSL_USERS', Username) then
		If LotType NE '' then
			NewVoidedLotRecordId = RTI_CreateGUID()
			If Not(RowExists('VOIDED_LOT', NewVoidedLotRecordId)) then
				NewVoidedLotRec = ''
				Begin Case
					Case LotType EQ 'RDS'
						WONo = XLate('RDS', LegacyLotId, RDS_WO$, 'X')
						CassNo = XLATE('RDS', LotId, RDS_CASS_NO$, 'X')
						WoMatKey = WONo : '*' : CassNo
						If RowExists('RDS', LegacyLotId) then
							WoNo = Field(LotId, '*', 1)
							NewVoidedLotRec<VOIDED_LOT_WORK_ORDER_LOG_ID$> = WoNo
							NewVoidedLotRec<VOIDED_LOT_ENTITY_TYPE$> = 'RDS'
							NewVoidedLotRec<VOIDED_LOT_LEGACY_LOT_ID$> = LegacyLotId
						end else
							ErrorMessage = 'Error when creating a voided lot record for ' : LotId : ' of type ' : LotType : '. RDS record not found.'
						end
					Case LotType EQ 'WO_MAT'
						If RowExists('WO_MAT', LegacyLotId) then
							WoNo = Field(LotId, '*', 1)
							NewVoidedLotRec<VOIDED_LOT_WORK_ORDER_LOG_ID$> = WoNo
							NewVoidedLotRec<VOIDED_LOT_ENTITY_TYPE$> = 'WO_MAT'
							NewVoidedLotRec<VOIDED_LOT_LEGACY_LOT_ID$> = LegacyLotId
						end else
							ErrorMessage = 'Error when creating a voided lot record for ' : LotId : ' of type ' : LotType : '. WoMat record not found.'
						end
					Case LotType EQ 'WM_IN'
						If RowExists('WM_IN', LegacyLotId) then
							WoNo = Field(LotId, '*', 1)
							NewVoidedLotRec<VOIDED_LOT_WORK_ORDER_LOG_ID$> = WoNo
							NewVoidedLotRec<VOIDED_LOT_ENTITY_TYPE$> = 'WM_IN'
							NewVoidedLotRec<VOIDED_LOT_LEGACY_LOT_ID$> = LegacyLotId
						end else
							ErrorMessage = 'Error when creating a voided lot record for ' : LotId : ' of type ' : LotType : '. WM_IN record not found.'
						end
					Case LotType EQ 'WM_OUT'
						If RowExists('WM_OUT', LegacyLotId) then
							WoNo = Field(LotId, '*', 1)
							NewVoidedLotRec<VOIDED_LOT_WORK_ORDER_LOG_ID$> = WoNo
							NewVoidedLotRec<VOIDED_LOT_ENTITY_TYPE$> = 'WM_OUT'
							NewVoidedLotRec<VOIDED_LOT_LEGACY_LOT_ID$> = LegacyLotId
						end else
							ErrorMessage = 'Error when creating a voided lot record for ' : LotId : ' of type ' : LotType : '. WM_OUT record not found.'
						end
					Case Otherwise$
						ErrorMessage = 'Error when creating a voided lot record for ' : LotId : ' of type ' : LotType : '. Non supported lot type.'
				End Case
				If ErrorMessage EQ '' then
					NewVoidedLotRec<VOIDED_LOT_VOID_BY$> = Username
					NewVoidedLotRec<VOIDED_LOT_VOID_DTM$> = Datetime()
					NewVoidedLotRec<VOIDED_LOT_LOT_ID$> = LotId
					Database_Services('WriteDataRow', 'VOIDED_LOT', NewVoidedLotRecordId, NewVoidedLotRec)
					If Error_Services('HasError') then
						ErrorMessage = Error_Services('GetMessage')
					end
				end
			end else
				ErrorMessage = 'Go buy a lottery ticket, we ran into a GUID collision with an existing GUID ' : NewVoidedLotRecordId : ' when trying to create a Voided Lot record for ' : LotId
			end
		end else
			ErrorMessage = 'Null lot type of passed to CreateVoidedLotRecord routine.'
		end
	end else
		ErrorMessage = 'Username ' : Username : ' passed to CreateVoidedLotRecord routine does not exist.'
	end
	
	EndTick = GetTickCount()
	Mona_Services('QueueLatencyAndCountMetrics', MonaResource, MetricName, StartTick, EndTick)    
    
	If ErrorMessage NE ''  then Error_Services('Add', ErrorMessage)
	
end service


Service CanUserModifyLot(UserId)
    
    Begin Case
        
        Case MemberOf(UserId, 'OI_ADMIN')
            Response = true$
        Case MemberOf(UserId, 'ENGINEERING')
            Response = true$
        Case MemberOf(UserId, 'ENG_TECH')
            Response = true$
        Case MemberOf(UserId, 'SUPERVISOR')
            Response = true$
        Case MemberOf(UserId, 'LEAD')
            Response = true$
        Case MemberOf(UserId, 'MATERIAL_HANDLER')
            Response = true$
        Case UserId EQ 'SYSTEM'
            Response = true$
        Case Otherwise$
            Response = false$
        
    End Case
    
end service

//----------------------------------------------------------------------------------------------------------------------
// Internal GoSubs
//----------------------------------------------------------------------------------------------------------------------



