Function Work_Order_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        :   Work_Order_Services

    Description :   Handler program for all module related services.

    Notes       :   The generic parameters should contain all the necessary information to process the services. Often
                    this will be information like the data Record and Key ID.

    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)
        06/16/17    dmb     Original programmer. - [EPIOI-8]
        11/23/20    djs     Added CreateOutbound service.

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

$insert APP_INSERTS
$insert SERVICE_SETUP
$insert WO_LOG_EQUATES
$insert EPI_PART_EQUATES
$insert WO_MAT_EQUATES
$insert WO_STEP_EQUATES
$insert WO_SCHEDULE_EQUATES
$insert WO_SCHEDULE_NG_EQUATES
$insert RDS_EQUATES
$insert PROD_SPEC_EQUATES
$insert PRS_LAYER_EQU
$insert PROD_VER_EQUATES
$insert WO_MAT_WFR_EQUATES
$insert MSG_EQUATES
$insert POPUP_EQUATES
$insert LOGICAL
$insert RLIST_EQUATES
$insert COMPANY_EQUATES
$Insert NOTIFICATION_EQUATES
$Insert WM_IN_EQUATES
$Insert WM_OUT_EQUATES
$Insert VOIDED_LOT_EQUATES
$Insert IFX_EQUATES
$Insert CUST_EPI_PART_EQUATES
$Insert REACT_RUN_EQUATES
$Insert RDS_LAYER_EQUATES
$Insert RDS_TEST_EQUATES

Equ MAX_NUM_CASS$ to 150
Equ NUM_ATTEMPTS$ to 10

Declare subroutine  Error_Services, Work_Order_Services, Memory_Services, RList, Database_Services, SRP_JSON
Declare subroutine  Btree.Extract, Set_Status, obj_WO_Log, obj_Notes, Print_Wo_Mat_In_Labels, Print_Wo_Mat_Out_Labels
Declare subroutine  Print_Wmi_Labels, Print_Wmo_Labels, ErrMsg, Print_Cass_Labels, Logging_Services, Service_Services
Declare subroutine  obj_WO_Mat_Log, WO_Mat_Services, Work_Order_Services, Transaction_Services, Extract_Si_Keys
Declare subroutine  Mona_Services, Lot_Event_Services, RDS_Services, Lot_Services, WM_In_Services, WM_Out_Services
Declare subroutine  obj_WO_Mat, obj_Post_Log, Delay, Archive_Services
Declare function    SRP_Array, Work_Order_Services, Memory_Services, Database_Services, SRP_Sort_Array, SRP_JSON
Declare function    Company_Services, obj_Prod_Spec, Schedule_Services, obj_WO_Log, obj_WO_Step, Memberof, Datetime
Declare function    Environment_Services, Logging_Services, Hold_Services, Signature_Services, Lot_Services
Declare function    SRP_Datetime, RTI_CreateGUID, RDS_Services, UCase, Date_Services, WO_Mat_Services

LogPath       = Environment_Services('GetApplicationRootPath') : '\LogFiles\WO_LOG'
LogDate       = Oconv(Date(), 'D4/')
LogTime       = Oconv(Time(), 'MTS')
LogFileName   = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' Release Log.csv'
Headers       = 'Logging DTM' : @FM : 'User' : @FM : 'Notes'
objReleaseLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$,',', Headers, '', False$, False$)

LogPath     = Environment_Services('GetApplicationRootPath') : '\LogFiles\WO_LOG'
LogDate     = Oconv(Date(), 'D4/')
LogTime     = Oconv(Time(), 'MTS')
LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' Void Log.csv'
Headers     = 'Logging DTM' : @FM : 'User' : @FM : 'CassetteId' : @FM : 'Notes'
objVoidLog  = Logging_Services('NewLog', LogPath, LogFileName, CRLF$,',', Headers, '', False$, False$)

LoggingDTM    = LogDate : ' ' : LogTime   ; // Logging DTM

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

Return Response else ''

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Service Parameter Options
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Options BOOLEAN     = True$, False$

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Services
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//----------------------------------------------------------------------------------------------------------------------
// GetUnscheduledWorkOrders
//
// Returns an array of unscheduled work order details based on the arguments provided.
//----------------------------------------------------------------------------------------------------------------------
Service GetUnscheduledWorkOrders(ReactorType, SusceptorSize, ReactorNo)
    
    WorkOrders          = ''
    // Select WO_LOG rows that do not have a closed date.
    OpenWOLogKeyIDs     = Database_Services('SearchIndex', 'WO_LOG', 'CLOSE_DATE', '=', True$)
    // Check each WO_LOG Key ID against the WO_SCHEDULE table. If the row does not exist or if the row does not have any
    // SCED_DET Key IDs, exists then it is not scheduled an should be included.
    WOLogKeyIDs     = Str(\00\, 100000)
    If OpenWOLogKeyIDs NE '' then
        hWOSchedule     = Database_Services('GetTableHandle', 'WO_SCHEDULE')
        If Error_Services('NoError') then
            cPos    = 1
            For Each WOLogKeyID in OpenWOLogKeyIDs using @FM
                Read WOScheduleRow from hWOSchedule, WOLogKeyID then
                    If WOScheduleRow<WO_SCHEDULE_SCHED_DET_KEY_IDS$> EQ '' then
                        WOLogKeyIDs[cPos, Len(WOLogKeyID) + 1]  = WOLogKeyID : @FM
                        cPos   += Len(WOLogKeyID) + 1
                    end
                end else
                    WOLogKeyIDs[cPos, Len(WOLogKeyID) + 1]  = WOLogKeyID : @FM
                    cPos   += Len(WOLogKeyID) + 1
                end
            Next WOLogKeyID
        end
    end
    Convert \00\ to '' in WOLogKeyIDs
    WOLogKeyIDs[-1, 1]  = ''
    
    If Error_Services('NoError') then
        For Each WOLogKeyID in WOLogKeyIDs using @FM setting fPos
            WOLogRow        = Database_Services('ReadDataRow', 'WO_LOG', WOLogKeyID)
            EpiPartNo       = WOLogRow<WO_LOG_EPI_PART_NO$>
            ProdVerNo       = WOLogRow<WO_LOG_PROD_VER_NO$>
            ProdVerRow      = Database_Services('ReadDataRow', 'PROD_VER', ProdVerNo)
            PSN             = ProdVerRow<PROD_VER_PROC_STEP_PSN$>
            ProdSpecRow     = Database_Services('ReadDataRow', 'PROD_SPEC', PSN)
            BlockedReactors = ProdSpecRow<PROD_SPEC_BLOCKED_REACTS$>
            PSNBlock        = False$
            If EpiPartNo NE '' then
                AcceptWorkOrder = True$ ; // Assume this work order is acceptable for now.
                ReactType       = WOLogRow<WO_LOG_REACT_TYPE$>
                // The following two lines of code are temporary but necessary to allows work orders
                // for ASM to be scheduled on ASM+ and vice-versa.
                If ReactType _EQC 'ASM+' then ReactType = 'ASM'
                If ReactorType _EQC 'ASM+' then ReactorType = 'ASM'
                If (ReactorType NE '') AND (ReactType NE ReactorType) then
                    // The reactor type for this work order does not match the required reactor type.
                    AcceptWorkOrder = False$
                end
                If AcceptWorkOrder AND SusceptorSize NE '' then
                    EpiPart         = Database_Services('ReadDataRow', 'EPI_PART', EpiPartNo)
                    SuscSize        = EpiPart<EPI_PART_SUB_WAFER_SIZE$>
                    If SuscSize NE SusceptorSize then
                        // The susceptor size for this work order does not match the required susceptor size.
                        AcceptWorkOrder = False$
                    end
                end
                If (BlockedReactors NE '') AND (ReactorNo NE '') then
                    // This work order uses a part spec that is blocked on the current reactor.
                    Locate ReactorNo in BlockedReactors using @VM setting vPos then PSNBlock = True$
                end
                If AcceptWorkOrder then
                    CustNo          = WOLogRow<WO_LOG_CUST_NO$>
                    CompanyRow      = Database_Services('ReadDataRow', 'COMPANY', CustNo)
                    CustName        = CompanyRow<42>
                    Swap True$ with 'Yes' in PSNBlock
                    Swap False$ with '' in PSNBlock
                    WorkOrders     := WOLogKeyID : @VM : EpiPartNo : @VM : CustName : @VM : ReactType : @VM : PSNBlock : @FM
                end
            end
        Next WOLogKeyID
        
        WorkOrders[-1, 1]   = ''
        WorkOrders          = SRP_Array('SortRows', WorkOrders, 'AR1' : @FM : 'AR2', 'LIST', @FM, @VM)
    end
    
    Response    = WorkOrders
    
end service


//----------------------------------------------------------------------------------------------------------------------
// GetWorkOrderLogDetail
//
// Returns the WO_LOG row for the indicated Key ID.
//----------------------------------------------------------------------------------------------------------------------
Service GetWorkOrderLogDetail(WorkOrderLogID)
    
    WorkOrderDetail = ''
    
    TableName       = 'WO_LOG'
    If WorkOrderLogID NE '' then
        WorkOrderDetail = Database_Services('ReadDataRow', Tablename, WorkOrderLogID)
    end else
        Error_Services('Add', 'WorkOrderLogID argument was missing from the ' : Service : ' service.')
    end
    
    Response    = WorkOrderDetail
    
end service


//----------------------------------------------------------------------------------------------------------------------
// GetWorkOrder
//
// Returns a JSON formatted object of information related to the indicated work order number. The ExtendedData argument
// determines if extended data related to wafers, product versions, and recipe should be included. The default is false.
//----------------------------------------------------------------------------------------------------------------------
Service GetWorkOrder(WorkOrderNo, ExtendedData)
    SRP_Stopwatch('Start', Service)
    If ExtendedData NE True$ then ExtendedData = False$
    
    ServiceKeyID   := '*' : WorkOrderNo
    WorkOrder         = ''
    
    If Memory_Services('IsValueCurrent', ServiceKeyID, 15, True$) then
        WorkOrder     = Memory_Services('GetValue', ServiceKeyID)
    end else
        If WorkOrderNo NE '' then
            WorkOrderRow = Database_Services('ReadDataRow', 'WO_LOG', WorkOrderNo, True$, 15)
            objWorkOrder = ''
            If SRP_JSON(objWorkOrder, 'NEW', 'OBJECT') then
                SRP_JSON(objWorkOrder, 'SETVALUE', 'WorkOrderNumber', WorkOrderNo, 'STRING')
                SRP_JSON(objWorkOrder, 'SETVALUE', 'EpiPartNumber', WorkOrderRow<WO_LOG_EPI_PART_NO$>, 'STRING')
                SRP_JSON(objWorkOrder, 'SETVALUE', 'ReactorType', WorkOrderRow<WO_LOG_REACT_TYPE$>, 'STRING')
                ProdVerNo   = WorkOrderRow<WO_LOG_PROD_VER_NO$>
                SRP_JSON(objWorkOrder, 'SETVALUE', 'ProdVerNumber', ProdVerNo, 'STRING')
                TotalWafers = WorkOrderRow<WO_LOG_WO_QTY$>
                SRP_JSON(objWorkOrder, 'SETVALUE', 'TotalWafers', TotalWafers, 'STRING')
                SRP_JSON(objWorkOrder, 'SETVALUE', 'Closed', Oconv(WorkOrderRow<WO_LOG_CLOSE_DATE$> NE '', 'BYes,No'), 'STRING')
                SRP_JSON(objWorkOrder, 'SETVALUE', 'ClosedDate', Oconv(WorkOrderRow<WO_LOG_CLOSE_DATE$>, 'D4/'), 'STRING')
                SRP_JSON(objWorkOrder, 'SETVALUE', 'HotLot', Oconv(WorkOrderRow<WO_LOG_HOT_FLAG$> NE '', 'BYes,No'), 'STRING')
                CustomerNo  = WorkOrderRow<WO_LOG_CUST_NO$>
                Company     = Company_Services('GetCompany', CustomerNo)
                objCompany  = ''
                If SRP_JSON(objCompany, 'PARSE', Company) EQ '' then
                    SRP_JSON(objWorkOrder, 'SET', 'Company', objCompany)
                    SRP_JSON(objCompany, 'RELEASE')
                end
                If ExtendedData EQ True$ then
                    WafersRemaining   = Work_Order_Services('GetRemainingWafers', WorkOrderNo)
                    SRP_JSON(objWorkOrder, 'SETVALUE', 'WafersRemaining', WafersRemaining, 'STRING')
                    If TotalWafers GT 0 then
                        PercentComplete = (TotalWafers - WafersRemaining) / TotalWafers * 100
                    end else
                        PercentComplete = 0
                    end
                    PercentComplete = Oconv(Iconv(PercentComplete, 'MD2'), 'MD2%S')
                    SRP_JSON(objWorkOrder, 'SETVALUE', 'PercentComplete', PercentComplete, 'STRING')
                    ProdVerRow  = Database_Services('ReadDataRow', 'PROD_VER', ProdVerNo, True$, 15)
                    PSN         = ProdVerRow<PROD_VER_PROC_STEP_PSN$>
                    SRP_JSON(objWorkOrder, 'SETVALUE', 'PSN', PSN, 'STRING')
                    LayerSpecs  = obj_Prod_Spec('GetLayerProp', PSN : @RM : @RM : 1)    ; // Returns specs for all layers in internal format.
                    LayerSpec 	= Field(LayerSpecs, @RM, 1)				                ; // Take the first Layer
                    LayerSet	= Field(LayerSpec, @FM, 1)				                ; // Not used here but shown for clarity
                    LayerSpec	= Field(LayerSpec, @FM, 2, 99)				            ; // LayerSpec without the LayerSet 
                    Recipe      = LayerSpec<PRS_LAYER_RECIPE$>
                    SRP_JSON(objWorkOrder, 'SETVALUE', 'Recipe', Recipe, 'STRING')
                end
                WorkOrder       = SRP_JSON(objWorkOrder, 'STRINGIFY', 'FAST')
                Memory_Services('SetValue', ServiceKeyID, WorkOrder)
                SRP_JSON(objWorkOrder, 'RELEASE')
            end else
                Error_Services('Add', 'Error creating objWorkOrder in the ' : Service : ' service.')
            end
        end else
            Error_Services('Add', 'WorkOrder argument was missing from the ' : Service : ' service.')
        end
    end
    
    Response    = WorkOrder
    SRP_Stopwatch('Stop', Service)
end service


//----------------------------------------------------------------------------------------------------------------------
// GetRemainingWafers
//
// Returns the number of remaining wafers needed to be scheduled for the indicated work order.
//----------------------------------------------------------------------------------------------------------------------
Service GetRemainingWafers(WorkOrderNo)
    
    RemainingWafers = ''
    
    If WorkOrderNo NE '' then
        WOLogRow    = Database_Services('ReadDataRow', 'WO_LOG', WorkOrderNo)
        
        If Error_Services('NoError') then
            // Get the total number of unreleased work orders.
            TotalWafers     = WOLogRow<WO_LOG_WO_QTY$>
            TotalReleased   = 0
            WOMatKeys       = WOLogRow<WO_LOG_WO_MAT_KEY$>
            For Each WOMatKey in WOMatKeys using @VM
                WOMatRec        = Database_Services('ReadDataRow', 'WO_MAT', WOMatKey)
                TotalReleased  += WOMatRec<WO_MAT_WAFER_QTY$>
            Next WOMatKey
            TotalUnreleased = TotalWafers - TotalReleased
            
            WOStepKeys          = WOLogRow<WO_LOG_WO_STEP_KEY$>
            TotalUnprocessed    = 0
            If WOStepKeys NE '' then
                ReactorType = WOLogRow<WO_LOG_REACT_TYPE$>
                For Each WOStepKey in WOStepKeys using @VM
                    WOStepRec   = Database_Services('ReadDataRow', 'WO_STEP', WOStepKey)
                    If Error_Services('NoError') then
                        // Get the total released but unprocessed.
                        If ReactorType EQ 'EPP' then
                            // EpiPro work orders have their own way of calculating.
                            StatusCodes         = Xlate('WM_IN', WOStepRec<WO_STEP_WM_IN_KEYS$>, 'CURR_STATUS', 'X')
                            RFWCnt              = Count(StatusCodes, 'RFW')
                            TotalUnprocessed    = RFWCnt * 25
                        end else
                            // All other reactor types use this method.
                            RDSKeys = WOStepRec<WO_STEP_RDS_KEY$>
                            If RDSKeys NE '' then
                                For Each RDSKey in RDSKeys using @VM
                                    RDSRec  = Database_Services('ReadDataRow', 'RDS', RDSKey)
                                    If RDSRec<RDS_DATE_IN$> EQ '' then
                                        TotalUnprocessed   += 25
                                    end
                                Next RDSKey
                            end
                        end
                    end
                Next WOStepKey
            end
            
            // Add the two.
            RemainingWafers = TotalUnreleased + TotalUnprocessed
        end
    end else
        Error_Services('Add', 'WorkOrderNo argument was missing from the ' : Service : ' service.')
    end
    
    Response    = RemainingWafers
    
end service


! This service should be deprecated as it returns inaccurate information for EpiPro.
! Use GetRemainingWafers service insead.
//----------------------------------------------------------------------------------------------------------------------
// GetUnprocessedWafers
//
// Returns the number of released wafers that have not yet been processed.
//----------------------------------------------------------------------------------------------------------------------
Service GetUnprocessedWafers(WorkOrderNo)
    
    RemainingWafers = ''
    
    If WorkOrderNo NE '' then
        WOLogRow    = Database_Services('ReadDataRow', 'WO_LOG', WorkOrderNo)
        
        If Error_Services('NoError') then
            // Get the total number of unreleased work orders.
            TotalWafers     = WOLogRow<WO_LOG_WO_QTY$>
            TotalReleased   = 0
            WOMatKeys       = WOLogRow<WO_LOG_WO_MAT_KEY$>
            For Each WOMatKey in WOMatKeys using @VM
                WOMatRec        = Database_Services('ReadDataRow', 'WO_MAT', WOMatKey)
                TotalReleased  += WOMatRec<WO_MAT_WAFER_QTY$>
            Next WOMatKey
            TotalUnreleased = TotalWafers - TotalReleased
            
            WOStepKeys          = WOLogRow<WO_LOG_WO_STEP_KEY$>
            TotalUnprocessed    = 0
            If WOStepKeys NE '' then
                ReactorType = WOLogRow<WO_LOG_REACT_TYPE$>
                For Each WOStepKey in WOStepKeys using @VM
                    WOStepRec   = Database_Services('ReadDataRow', 'WO_STEP', WOStepKey)
                    If Error_Services('NoError') then
                        // Get the total released but unprocessed.
                        RDSKeys = WOStepRec<WO_STEP_RDS_KEY$>
                        If RDSKeys NE '' then
                            For Each RDSKey in RDSKeys using @VM
                                RDSRec     = Database_Services('ReadDataRow', 'RDS', RDSKey)
                                WOMatKey   = Xlate('RDS', RDSKey, 'WO_MAT_KEY', 'X')
                                CurrStatus = Xlate('WO_MAT', WOMatKey, 'CURR_STATUS', 'X')
                                If ( (RDSRec<RDS_DATE_OUT$> EQ '') and (CurrStatus NE 'REJ') and (CurrStatus NE 'MT') ) then
                                    TotalUnprocessed += RDSRec<RDS_CASS_WAFER_QTY$>
                                end
                            Next RDSKey
                        end
                    end
                Next WOStepKey
            end
            
            // Add the two.
            RemainingWafers = TotalUnreleased + TotalUnprocessed
        end
    end else
        Error_Services('Add', 'WorkOrderNo argument was missing from the ' : Service : ' service.')
    end
    
    Response    = RemainingWafers
    
end service


//----------------------------------------------------------------------------------------------------------------------
// GetCurrCassNo
//
// Returns the next cassette number to be unloaded.
//----------------------------------------------------------------------------------------------------------------------
Service GetCurrCassNo(WorkOrderNo)
    
    If RowExists('WO_LOG', WorkOrderNo) then
        CurrCassNo = ''
        DateIn     = ''
        RDSKeys    = Xlate('WO_STEP', WorkOrderNo:'*1', 'RDS_KEY', 'X')
        NumKeys    = DCount(RDSKeys, @VM)
        For KeyIndex = NumKeys to 1 Step -1
            RDSKey = RDSKeys<0, KeyIndex>
            DateIn = Xlate('RDS', RDSKey, 'DATE_IN', 'X')
        Until DateIn NE ''
        Next KeyIndex
        If DateIn NE '' then CurrCassNo = Xlate('RDS', RDSKey, 'CASS_NO', 'X')
        Response = CurrCassNo
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// GetLastCassOut
//
// Returns the last cassette number unloaded.
//----------------------------------------------------------------------------------------------------------------------
Service GetLastCassOut(WorkOrderNo)
    
    If RowExists('WO_LOG', WorkOrderNo) then
        LastCassOut = ''
        DateOut     = ''
        RDSKeys     = Xlate('WO_STEP', WorkOrderNo:'*1', 'RDS_KEY', 'X')
        NumKeys     = DCount(RDSKeys, @VM)
        For KeyIndex = NumKeys to 1 Step -1
            RDSKey  = RDSKeys<0, KeyIndex>
            DateOut = Xlate('RDS', RDSKey, 'DATE_OUT', 'X')
        Until DateOut NE ''
        Next KeyIndex
        If DateOut NE '' then LastCassOut = Xlate('RDS', RDSKey, 'CASS_NO', 'X')
        Response = LastCassOut
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// GetNextRunNo
//
// Returns the next run number in the work order.
//----------------------------------------------------------------------------------------------------------------------
Service GetNextRunNo(WorkOrderNo, WorkOrderStep)
    
    hSysLists   = Database_Services('GetTableHandle', 'SYSLISTS')
    HaveLock    = False$
    TimeElapsed = 0
    RequestTime = Time()
    If WorkOrderNo NE '' then
        Loop
            Lock hSysLists, ServiceKeyID then
                HaveLock = True$     
                If WorkOrderStep = '' then WorkOrderStep = 1
                StepRDSNos = XLATE('WO_STEP', WorkOrderNo:'*':WorkOrderStep, 'RDS_KEY', 'X')
                CurrRuns   = 0
                NumRDS     = DCount(StepRDSNos, @VM)
                For I = 1 to NumRDS
                    If RowExists('RDS', StepRDSNos<1,I>) then
                        CurrRuns += 1
                    end
                next I
                NextRunNo = CurrRuns + 1
                Response = NextRunNo       
                Unlock hSysLists, ServiceKeyID else Null
            end
            TimeElapsed = Time() - RequestTime
        Until HaveLock EQ True$ or TimeElapsed GT 60
        Repeat  
    end
    If TimeElapsed GT 60 then
        Error_Services('Add', 'Error occurred within the ':Service:' service module')
    end
    
end service


//----------------------------------------------------------------------------------------------------------------------
// CreateOutbound
//
// Creates an empty outbound cassette for a GaN work order.
//----------------------------------------------------------------------------------------------------------------------
Service CreateOutbound(WONo)
    
    If WONo NE '' then
        WORec        = Database_Services('ReadDataRow', 'WO_LOG', WONo)
        CassNos      = WORec<WO_LOG_WO_MAT_KEY$>
        LastCassNo   = DCount(CassNos, @VM)
        NextCassNo   = LastCassNo + 1
        LastCassID   = WONo : '*' : LastCassNo
        NewCassID    = WONo : '*' : NextCassNo
        SchedCassQty = Xlate('WO_MAT', LastCassID, 'SCHED_QTY', 'X')
        WOMatWfrRec  = Database_Services('ReadDataRow', 'WO_MAT_WFR', NewCassID)
        If WOMatWfrRec EQ '' then
            for WfrCount = 1 to SchedCassQty
                WOMatWfrRec<WO_MAT_WFR_OUT_PREV_WFR_ID$,WfrCount> = NewCassID : '*' : WfrCount
            Next WfrCount
            Database_Services('WriteDataRow', 'WO_MAT_WFR', NewCassID, WOMatWfrRec)                        
            WOMatRecToCopy                             = Database_Services('ReadDataRow', 'WO_MAT', LastCassID)
            WOMatRecToCopy<WO_MAT_RDS_FINAL_SIG_ORG$>  = ''
            WOMatRecToCopy<WO_MAT_CASS_FINAL_SIG_DTM$> = ''
            Database_Services('WriteDataRow', 'WO_MAT', NewCassID, WOMatRecToCopy)           
            WORec                                      = Insert(WORec, WO_LOG_WO_MAT_KEY$, -1, 0, NewCassID)
            Database_Services('WriteDataRow', 'WO_LOG', WONo, WORec)           
        end else
            ErrorMessage = 'Error in service ':Service:'. WO_MAT_WFR record ':NewCassID:' already exists!'
            Error_Services('Add', ErrorMessage)
        end
    end
    
end service


Service UpdateUnscheduledQuantities(WOList)
    
    hSysLists = Database_Services('GetTableHandle', 'SYSLISTS')
    Lock hSysLists, ServiceKeyID then
        SchedList       = Database_Services('ReadDataRow', 'APP_INFO', 'SCHED_WO_LIST')
        // Select WO_LOG rows that do not have a closed date.
        OpenWOLogKeyIDs = ''
        If WOList NE '' then OpenWOLogKeyIDs = WOList
        If OpenWOLogKeyIDs EQ '' then
            Open 'DICT.WO_LOG' to hWOLogDict then
                Set_Status(0)
                Flag      = ''
                Btree.Extract('CLOSE_DATE' : @VM : '' : @FM, 'WO_LOG', hWOLogDict, OpenWOLogKeyIDs, 'S', Flag)
                If Flag NE 0 then
                    Status = Get_Status(errCode)
                    Error_Services('Add', 'Error in ':Service:' service. Error code ':errCode:'.')
                end
            end
        end
        If ( Error_Services('NoError') and (OpenWOLogKeyIDs NE '') ) then
            SchedList = SRP_Array('Rotate', SchedList, @FM, @VM)
            For Each WONo in OpenWOLogKeyIDs using @VM setting fPos
                UnscheduledWfrQty = Schedule_Services('GetUnscheduledWfrQty', WONo)
                WONos             = SchedList<1>
                If UnscheduledWfrQty GT 0 then
                    Locate WONo in WONos using @VM setting vPos else vPos = -1
                    WOLogRow        = Database_Services('ReadDataRow', 'WO_LOG', WONo)
                    EpiPartNo       = WOLogRow<WO_LOG_EPI_PART_NO$>
                    CustNo          = WOLogRow<WO_LOG_CUST_NO$>
                    CompanyRow      = Database_Services('ReadDataRow', 'COMPANY', CustNo)
                    CustName        = CompanyRow<COMPANY_ABBREV$>
                    ReactorType     = WOLogRow<WO_LOG_REACT_TYPE$>
                    SchedList<1, vPos> = WONo
                    SchedList<2, vPos> = EpiPartNo
                    SchedList<3, vPos> = CustName
                    SchedList<4, vPos> = ReactorType
                    WOSchedRec         = Database_Services('ReadDataRow', 'WO_SCHEDULE_NG', WONo)
                    CurrVal            = WOSchedRec<WO_SCHEDULE_NG.UNSCHED_QTY$>
                    If CurrVal NE UnscheduledWfrQty then
                        WOSchedRec<WO_SCHEDULE_NG.UNSCHED_QTY$> = UnscheduledWfrQty
                        Database_Services('WriteDataRow', 'WO_SCHEDULE_NG', WONo, WOSchedRec, True$, False$, True$)
                    end
                end else
                    Locate WONo in WONos using @VM setting vPos then
                        SchedList = Delete(SchedList, 1, vPos, 0)
                        SchedList = Delete(SchedList, 2, vPos, 0)
                        SchedList = Delete(SchedList, 3, vPos, 0)
                        SchedList = Delete(SchedList, 4, vPos, 0)
                        Database_Services('DeleteDataRow', 'WO_SCHEDULE_NG', WONo)
                    end
                end
            Next WONo
        end
        SchedList = SRP_Array('Rotate', SchedList, @FM, @VM)
        If WOList EQ '' then
            // Clear out WO_SCHEDULE_NG table of work orders that no longer have any wafers left to schedule.
            EOF   = False$
            Query = 'SELECT WO_SCHEDULE_NG WITH UNSCHED_QTY EQ 0'
            RList(Query, TARGET_ACTIVELIST$, '', '', '')
            Loop
                Readnext WOLogKeyID else EOF = True$
            Until EOF = True$
                Database_Services('DeleteDataRow', 'WO_SCHEDULE_NG', WOLogKeyID)
            Repeat
        end
        SchedList = SRP_Array('SortSimpleList', SchedList, 'DescendingNumbers', @FM)
        Database_Services('WriteDataRow', 'APP_INFO', 'SCHED_WO_LIST', SchedList)
        Unlock hSysLists, ServiceKeyID else Null
    end
    
end service


Service ReceiveCassette(WONo, ReceiveUser, LotNo, CassQty, SubPartNo, SubVendorCode, RecDtm)

    WOMatKey = ''
    ErrorMsg = ''
    Begin Case
        Case ( (WONo EQ '') or Not( RowExists('WO_LOG', WONo) ) )
            ErrorMsg = 'Error in ':Service:' service. Invalid WONo ':Quote(WONo):'.'
        Case ( (ReceiveUser EQ '') or Not(RowExists('LSL_USERS', ReceiveUser) ) )
            ErrorMsg = 'Error in ':Service:' service. Invalid ReceiveUser ':Quote(ReceiveUser):'.'
        Case (LotNo EQ '')
            ErrorMsg = 'Error in ':Service:' service. Invalid LotNo ':Quote(LotNo):'.'
        Case ( (CassQty EQ '') or Not( Num(CassQty) ) )
            ErrorMsg = 'Error in ':Service:' service. Invalid CassQty ':Quote(CassQty):'.'
        Case (SubPartNo EQ '')
            ErrorMsg = 'Error in ':Service:' service. Invalid SubPartNo ':Quote(SubPartNo):'.'
        Case (SubVendorCode EQ '')
            ErrorMsg = 'Error in ':Service:' service. Invalid SubVendorCode ':Quote(SubVendorCode):'.'
    End Case
    
    If (ErrorMsg EQ '') then
        If RecDtm EQ '' then RecDtm = Datetime()
        WORec = Database_Services('ReadDataRow', 'WO_LOG', WONo)
        If Error_Services('NoError') then
            
            OrderItemNo    = 1
            CustPartNo     = WORec<WO_LOG_CUST_PART_NO$>
            ProdOrderNo    = WORec<WO_LOG_PROD_ORD_NO$>
            ProdVerNo      = WORec<WO_LOG_PROD_VER_NO$>
            CustNo         = WORec<WO_LOG_CUST_NO$>
            ProdVerRec     = Xlate('PROD_VER',ProdVerNo,'','X')
            ProcStepPSNs   = ProdVerRec<PROD_VER_PROC_STEP_PSN$>
            ReactType      = ProdVerRec<PROD_VER_REACT_TYPE$>						
            SpecType       = Xlate('PROD_SPEC',ProcStepPSNs[-1,'B':@VM],'SPEC_TYPE','X')
            EpiPartNo      = WORec<WO_LOG_EPI_PART_NO$>
            CustEpiPartRec = Xlate('CUST_EPI_PART',CustNo:'*':EpiPartNo,'','X')	
            MUWaferFlag    = CustEpiPartRec<CUST_EPI_PART_MAKEUP_WAFERS$>			
            RetRejects     = CustEpiPartRec<CUST_EPI_PART_RET_REJECTS$>			
            If SpecType EQ 'Q' then
                CassShipQty	= ''												
            end else
                CassShipQty = CustEpiPartRec<CUST_EPI_PART_CASS_SHIP_QTY$>
            end
            MinCassShipQty = CustEpiPartRec<CUST_EPI_PART_MIN_CASS_SHIP_QTY$>
            ShipShort      = CustEpiPartRec<CUST_EPI_PART_EPI_PRO_SHIP_SHORT$>		
            Reprocessed    = ''
            EpiPartRec     = Database_Services('ReadDataRow', 'EPI_PART', EpiPartNo)
            
            If Error_Services('NoError') then
                
                SubSuppBy    = EpiPartRec<EPI_PART_SUB_SUPP_BY$>		;* L - EpiSvcs supplied, C - Customer Supplied
                WOMatKeys    = WORec<WO_LOG_WO_MAT_KEY$>
                WOMatKeys    = SRP_Array('SortRows', WOMatKeys, 'AR2', 'LIST', @VM, '*')
                
                If WOMatKeys NE '' then
                    LastCassNo = WOMatKeys[-1, 'B*']
                end else
                    LastCassNo = 0
                end
                
                hSysLists = Database_Services('GetTableHandle', 'SYSLISTS')
                CassNo = ''
                CandidateCassNo   = LastCassNo + 1
                Loop
                    CandidateWOMatKey = WONo:'*':CandidateCassNo
                    If Not(RowExists('WO_MAT', CandidateWOMatKey)) then 
                        Lock hSysLists, CandidateWOMatKey then
                            CassNo = CandidateCassNo
                        end
                    end
                Until (CassNo NE '')
                    CandidateCassNo   = CandidateCassNo + 1
                Repeat
                
                Parms  = WONo:@RM
                Parms := CassNo:@RM
                Parms := ProdVerNo:@RM
                Parms := LotNo:@RM
                Parms := CassQty:@RM
                Parms := CustPartNo:@RM		
                Parms := OrderItemNo:@RM
                Parms := ReactType:@RM									
                Parms := SubPartNo:@RM
                Parms := 'SR':@RM			;* Warehouse = 'SR' - Shipping/Receiving Area
                Parms := 'RB':@RM			;* Location  = 'RB' - Receiving Bench
                Parms := OConv(RecDtm, 'DT2/^H'):@RM
                Parms := ReceiveUser:@RM
                Parms := SubSuppBy:@RM
                Parms := MUWaferFlag:@RM
                Parms := RetRejects	:@RM							
                Parms := Reprocessed:@RM							
                Parms := CassShipQty:@RM							
                Parms := ShipShort:@RM								
                Parms := SubVendorCode:@RM							    
                Parms := MinCassShipQty                             
                
                obj_WO_Mat('Create',Parms)		
                ErrCode = ''
                If Not(Get_Status(ErrCode)) then 
                    
                    WOMatKey  = WONo:'*':CassNo

                    Locate WOMatKey in WOMatKeys by 'AR' using @VM setting NewPos else
                        Done = False$
                        For AttemptNo = 1 to NUM_ATTEMPTS$
                            HaveLock = Database_Services('GetKeyIDLock', 'WO_LOG', WONo, True$)
                            If HaveLock then
                                WOMatKeys            = Database_Services('ReadDataColumn', 'WO_LOG', WONo, WO_LOG_WO_MAT_KEY$)
                                WOMatKeys<0, CassNo> = WOMatKey
                                Database_Services('WriteDataColumn', 'WO_LOG', WONo, WO_LOG_WO_MAT_KEY$, WOMatKeys, True$, False$, False$)
                                If Error_Services('NoError') then Done = True$
                            end
                        Until Done
                        Next AttemptNo
                        If Not(Done) then
                            Database_Services('ReleaseKeyIDLock', 'WO_LOG', WONo)
                            Transaction_Services('PostWriteFieldTransaction', 'WO_LOG', WONo, WO_LOG_WO_MAT_KEY$, WOMatKey, CassNo)
                            If Error_Services('HasError') then ErrorMsg = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
                        end
                    end
                    
                    If (ErrorMsg EQ '') then
                        Recipients   = ''
                        SentFrom     = ReceiveUser
                        Subject      = 'Cassette ':CassNo:' Received for Work Order ':WONo
                        Message      ='Cassette ':CassNo:' Received for Work Order ':WONo
                        AttachWindow = 'NDW_WO_LOG'
                        AttachKey    = WONo
                        SendToGroup  = 'MATERIAL_RECEIPT'
                        Parms        = Recipients:@RM:SentFrom:@RM:Subject:@RM:Message:@RM:AttachWindow:@RM:AttachKey:@RM:SendToGroup
                        obj_Notes('Create',Parms)
                        If Error_Services('HasError') then
                            ErrorMsg = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
                        end
                    end
                end
                Unlock hSysLists, CandidateWOMatKey else Null
            end else
                ErrorMsg = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
            end
        end else
            ErrorMsg = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
        end
    end
    
    If (ErrorMsg EQ '') then
        Response = WOMatKey
    end else
        Response = False$
        Error_Services('Add', ErrorMsg)
    end

end service


//----------------------------------------------------------------------------------------------------------------------
// ReleaseCassette
//
// Input:
//      WOMatKey    [Required] - Cassette to be released
//      ReleaseUser [Required] - User to be recorded in REL transaction
//      RelDtm      [Optional] - Will be used as the REL dtm if provided, otherwise it will use the current dtm.
//
// Output:
//      Returns true if successful, false otherwise. If false, the error will be added to Error_Services.
//
// Releases a given cassette. This will create all child records for a given WO_MAT record and record a REL transaction
// in the material log. 
//----------------------------------------------------------------------------------------------------------------------
Service ReleaseCassette(WOMatKey, ReleaseUser, RelDtm)
    
    ErrorMsg = ''
    Begin Case
        Case (WOMatKey EQ '')
            ErrorMsg = 'Error in ':Service:' service. Null WOMatKey passed into service.'
        Case Not( RowExists('WO_MAT', WOMatKey) )
            ErrorMsg = 'Error in ':Service:' service. WO_MAT ':Quote(WOMatKey):' does not exist.'
        Case (ReleaseUser EQ '')
            ErrorMsg = 'Error in ':Service:' service. Null ReleaseUser passed into service.'
        Case Not( RowExists('LSL_USERS', ReleaseUser) )
            ErrorMsg = 'Error in ':Service:' service. User ':ReleaseUser:' does not exist.'
    End Case
    
    If (ErrorMsg EQ '') then
        
        PSNo   = Xlate('WO_LOG', WONo, 'PS_NO', 'X')
        PSMode = Xlate('PROD_SPEC',PSNo,'SPEC_TYPE','X')
        If PSMode = 'Q' OR PSMode = 'U' then
            ReactType = Xlate('WO_LOG', WONo, 'REACT_TYPE', 'X')
            If ReactType = 'EPP' OR ReactType = 'EpiPro' then
                OutLoadQty = Xlate('WO_LOG', WONo, 'CUST_EPI_PART_WMO_LOAD_CNT', 'X')
                If OutLoadQty = '' then
                    ErrorMsg = 'Outbound Load Cnt is required for ':OCONV(PSMode,'[PROD_SPEC_MODE_CONV]'):' Work Orders.'
                end                
            end else
                OutLoadQty = Xlate('WO_LOG', WONo, 'CUST_EPI_PART_SHIP_QTY', 'X')
                If OutLoadQty = '' then
                    ErrorMsg = 'Cass Ship Qty is required for ':OCONV(PSMode,'[PROD_SPEC_MODE_CONV]'):' Work Orders.'
                end
            end            
        end
        
        If ErrorMsg EQ '' then
            
            CassRelDtm = Xlate('WO_MAT', WOMatKey, 'REL_DTM', 'X')
            CassRxDtm  = Xlate('WO_MAT', WOMatKey, 'RX_DTM', 'X')
            
            If ( (CassRelDtm EQ '') and ( CassRxDtm NE '' ) ) then
                RelWONo   = Field(WOMatKey, '*', 1, 1)
                RelCassNo = Field(WOMatKey, '*', 2, 1)
                Set_Status(0)
                obj_WO_Log('ReleaseCassette',RelWONo:@RM:RelCassNo:@RM:ReleaseUser:@RM:RelDtm)      
                errCode   = ''
                If Get_Status(errCode) then
                    ErrorMsg = 'Error in ':Service:' service. Error code ':errCode:'.'
                    If Database_Services('IsKeyIDLocked', 'WO_LOG', RelWONo) then
                        Database_Services('ReleaseKeyIDLock', 'WO_LOG', RelWONo)
                    end
                end else       
                    Recipients   = ''
                    SentFrom     = ReleaseUser
                    Subject      = 'Cassette ':WOMatKey:' released for work order ':RelWONo
                    Message      = 'Cassette ':WOMatKey:' released for work order ':RelWONo
                    AttachWindow = 'WO_MAT'
                    AttachKey    = WOMatKey
                    SendToGroup  = 'WO_ENTRY'   
                    Parms = Recipients:@RM:SentFrom:@RM:Subject:@RM:Message:@RM:AttachWindow:@RM:AttachKey:@RM:SendToGroup
                    obj_Notes('Create',Parms)
                end
            end else
                ErrorMsg = 'Error in ':Service:' service. WO_MAT ':WOMatKey:' is ineligible to be released.'
            end
        end
    end
    
    If ErrorMsg EQ '' then
        Response = True$
    end else
        Response = False$
        Error_Services('Add', ErrorMsg)
    end
    
end service


Service ReceiveReleaseCassette(WONo, ReceiveUser, LotNo, CassQty, SubPartNo, SubVendorCode, RecDtm)
    
    WOMatKey = ''
    ErrorMsg = ''
    Begin Case
        Case ( (WONo EQ '') or Not( RowExists('WO_LOG', WONo) ) )
            ErrorMsg = 'Error in ':Service:' service. Invalid WONo ':Quote(WONo):'.'
        Case ( (ReceiveUser EQ '') or Not(RowExists('LSL_USERS', ReceiveUser) ) )
            ErrorMsg = 'Error in ':Service:' service. Invalid ReceiveUser ':Quote(ReceiveUser):'.'
        Case (LotNo EQ '')
            ErrorMsg = 'Error in ':Service:' service. Invalid LotNo ':Quote(LotNo):'.'
        Case ( (CassQty EQ '') or Not( Num(CassQty) ) )
            ErrorMsg = 'Error in ':Service:' service. Invalid CassQty ':Quote(CassQty):'.'
        Case (SubPartNo EQ '')
            ErrorMsg = 'Error in ':Service:' service. Invalid SubPartNo ':Quote(SubPartNo):'.'
        Case (SubVendorCode EQ '')
            ErrorMsg = 'Error in ':Service:' service. Invalid SubVendorCode ':Quote(SubVendorCode):'.'
    End Case
    
    If (ErrorMsg EQ '') then
        If RecDtm EQ '' then RecDtm = Datetime()
        WOMatKey = Work_Order_Services('ReceiveCassette', WONo, ReceiveUser, LotNo, CassQty, SubPartNo, SubVendorCode, RecDtm)
        If Error_Services('NoError') then 
            If RowExists('WO_MAT', WOMatKey) then
                RelDtm = SRP_Datetime('AddSeconds', RecDtm, 30)
                If Not(Work_Order_Services('ReleaseCassette', WOMatKey, ReceiveUser, RelDtm)) then
                    ErrorMsg = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
                end
            end else
                ErrorMsg = 'Error in ':Service:' service. Error receiving cassette. WO_MAT ':WOMatKey:' does not exist.'
            end
        end else
            ErrorMsg = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
        end
    end
    
    If (ErrorMsg EQ '') then
        Response = WOMatKey
    end else
        Response = False$
        Error_Services('Add', ErrorMsg)
    end
    
end service


Service PrintLabels(WONo)
    
    If WONo NE '' then
        
        WOSteps = XLATE('WO_LOG', WONo, WO_LOG_WO_STEP_KEY$, 'X')
        
        If INDEX(WOSteps,@VM,1) then
            Buttons = ''
            FOR I = 1 TO COUNT(WOSteps,@VM) + (WOSteps NE '')
                Buttons<I> = 'Step ':I
            NEXT I
            SWAP @FM WITH ',' IN Buttons
            Buttons = 'B':Buttons
            
            TypeOver = ''
            TypeOver<MTEXT$> 	= 'Which Work Order step do you wish to print labels for?'
            TypeOver<MTYPE$> 	= Buttons
            TypeOver<MICON$> 	= '?'
            TypeOver<MCAPTION$>	= 'MultiStep WorkOrder Selection'
            WOStep = Msg(@window, TypeOver)
            MultiStepFlag = WOStep
        end ELSE
            WOStep = 1
            MultiStepFlag = 0
        end
        
        If WOStep = 1 then
            PSNo = XLATE('WO_STEP',WONo:'*1',WO_STEP_PROD_SPEC_ID$,'X')
            ReactorType = XLATE('PROD_SPEC',PSNo,PROD_SPEC_REACTOR_TYPE$,'X')
            
            If ReactorType = 'GAN' then
                Print_WO_Mat_In_Labels(WONo,'')
                Print_WO_Mat_Out_Labels(WONo,'')
            end
            
            If ReactorType = 'EPP' OR ReactorType = 'P' then
                Print_WMI_Labels(WONo,1)
                Print_WMO_Labels(WONo,1)
            end
        end
        
        NonEpiPro = ( (ReactorType NE 'GAN') and (ReactorType NE 'EPP') and (ReactorType NE 'P') )
        
        If NonEpiPro then
            
            If WOStep > 1 then
                
                Def = ""
                Def<MTEXT$> = "Adjusting Step ":WOStep:" Scheduled Quantities..."
                Def<MTYPE$> = "U"
                
                MsgUp = Msg(@window, Def)
                
                RDSNos = obj_WO_Step('AdjStepRDSQtys', WOStep:@RM:WOSteps)
                
                If Get_Status(errCode) then
                    
                    ErrMsg(errCode)											;* Display error from the update
                    Msg(@WINDOW,MsgUp)										;* Take down the processing message
                    
                    RETURN
                end
                Msg(@WINDOW,MsgUp)
                
            end ELSE
                RDSNos    = RDS_Services('GetRDSKeys', WONo)
            end
            
            If INDEX(RDSNos,@VM,1) then
                TypeOver              = ''
                RDSNos                = SRP_Array('Clean', RDSNos, 'TrimAndMakeUnique', @VM)
                TypeOver<PDISPLAY$>   = RDSNos
                TypeOver<PFIELD$>     = 2
                TypeOver<PTYPE$>      = 'K'
                TypeOver<PSHOWGAUGE$> = True$
                
                RDSKeys = Popup(@WINDOW,TypeOver,'WO_LOG_RTS')
                
            end ELSE
                RDSKeys = RDSNos
            end
            
            If RDSKeys = '' then 
                ErrMsg('No RDS Numbers Selected or Work Order Not Yet Released to Production.')
            end ELSE
                CONVERT @VM TO @FM IN RDSKeys
                Print_Cass_Labels(RDSKeys,MultiStepFlag)				;* Updated labels with Akrion Bar Codes
            end
        end
    end
    
end service


Service UpdateWOStepStatus(WONo)
    
    If WONo NE '' then
        WOStepKey        = WONo:'*':1
        WOStepRec        = Database_Services('ReadDataRow', 'WO_STEP', WOStepKey)
        WOStepCurrStatus = obj_WO_Step('CurrStatus', WOStepKey:@RM:WOStepRec)
        // Get a fresh copy of the record
        WOStepRec              = Database_Services('ReadDataRow', 'WO_STEP', WOStepKey)
        WOStepCurrStatusStatic = WOStepRec<WO_STEP_CURR_STATUS_STATIC$>
        If WOStepCurrStatus NE WOStepCurrStatusStatic then
            WOStepRec<WO_STEP_CURR_STATUS_STATIC$> = WOStepCurrStatus
            Database_Services('WriteDataRow', 'WO_STEP', WOStepKey, WOStepRec, True$, False$, True$)
        end
        Database_Services('ReleaseKeyIDLock', 'WO_STEP', WOStepKey)
    end
    
end service


Service PostWOStepUpdateRequest(WONo)
    
    If WONo NE '' then
        StepNo    = 1
        WOStepKey = WONo:'*':StepNo
        // Look for a pre-existing request in the queue
        If (RowExists('WO_STEP', WOStepKey) EQ False$) then
            // This is a new request
            RequestKeyID                            = WOStepKey
            RequestRow                              = ''
            RequestRow<WO_STEP_CURR_STATUS_STATIC$> = Datetime()
            Database_Services('WriteDataRow', 'WO_STEP_QUEUE', RequestKeyID, RequestRow, False$, False$, False$)
        end else
            // Duplicate request
            Null
        end
    end else
        Error_Services('Add', 'Null parameter passed into service call. All parameters are required.')
    end
    
end service


Service ProcessWOStepUpdateRequests()
    
    hSysLists   = Database_Services('GetTableHandle', 'SYSLISTS')
    Lock hSysLists, ServiceKeyID then
        Open 'WO_STEP_QUEUE' to hTable then
            Select hTable
            EOF = False$
            Loop
                ReadNext RequestKeyID else EOF = True$
            Until EOF EQ True$
                WONo = Field(RequestKeyID, '*', 1)
                Work_Order_Services('UpdateWOStepStatus', WONo)
                If Error_Services('NoError') then
                    // Successfully updated the current status, so delete the request from the queue.
                    Database_Services('DeleteDataRow', 'WO_STEP_QUEUE', RequestKeyID, True$, True$)
                end
            Repeat
        end else
            ErrorMsg = 'Error opening WO_STEP_QUEUE in ':Service:' service.'
        end
        Unlock hSysLists, ServiceKeyID else Null    
    end
    
end service


Service SetHotFlag(WONo, HotFlag=BOOLEAN)
    
    If ( ( RowExists('WO_LOG', WONo) and ( (HotFlag EQ True$) or (HotFlag EQ False$) ) ) ) then
        Open 'WO_LOG' to hTable then
            WriteV HotFlag on hTable, WONo, WO_LOG_HOT_FLAG$ else
                Error_Services('Add', 'Error in ':Service:' service. File error: ':@File_Error)
            end
        end
    end else
        Error_Services('Add', 'Error in ':Service:' service. Invalid parameters passed into service.')
    end
    
end service


Service GetHotFlag(WONo)
    
    HotFlag = ''
    If RowExists('WO_LOG', WONo) then
        Open 'WO_LOG' to hTable then
            ReadV HotFlag from hTable, WONo, WO_LOG_HOT_FLAG$ else
                Error_Services('Add', 'Error in ':Service:' service. File error: ':@File_Error)
            end
        end
    end else
        Error_Services('Add', 'Error in ':Service:' service. Invalid parameters passed into service.')
    end
    Response = HotFlag
    
end service


Service UpdateReleasedQty(WONo)
	
	LogPath      = Environment_Services('GetApplicationRootPath') : '\LogFiles\WO_LOG'
	LogDate      = Oconv(Date(), 'D4/')
	LogTime      = Oconv(Time(), 'MTS')
	LogFileName  = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' Update Released Qty Log.csv'
	Headers      = 'Logging DTM' : @FM : 'WONo' : @FM : 'Notes'
	objUpRelLog  = Logging_Services('NewLog', LogPath, LogFileName, CRLF$,',', Headers, '', False$, False$)
	LoggingDTM   = LogDate : ' ' : LogTime   ; // Logging DTM	
	RelQty       = ''
	UnRelQty     = ''
	ErrorMsg     = ''
	If WONo NE '' then
		If RowExists('WO_LOG', WONo) then
			WORec = Database_Services('ReadDataRow', 'WO_LOG', WONo)
			If Error_Services('NoError') then
                OrigRelQty   = WORec<WO_LOG_REL_QTY_STATIC$>
                OrigUnRelQty = WORec<WO_LOG_UNREL_QTY_STATIC$>			    
				RelQty       = obj_WO_Log('RelQty', WONo:@RM:WORec)
				WOQty        = WORec<WO_LOG_QTY$>
				If RelQty GT WOQty then
					UnRelQty = 0
				end else
					UnRelQty = WOQty - RelQty
				end				
                If ( (OrigRelQty NE RelQty) or (OrigUnRelQty NE UnRelQty) ) then
                    Done = False$
                    For AttemptNo = 1 to NUM_ATTEMPTS$
                        If AttemptNo GT 1 then Delay(1)
                        HaveLock = Database_Services('GetKeyIDLock', 'WO_LOG', WONo)
                        If HaveLock then
                            WORec<WO_LOG_REL_QTY_STATIC$>   = RelQty
                            WORec<WO_LOG_UNREL_QTY_STATIC$> = UnRelQty
                            Database_Services('WriteDataRow', 'WO_LOG', WONo, WORec, True$, False$, False$)
                            If Error_Services('NoError') then
                                // Note: WriteDataRow will have unlocked the record
                                Done = True$
                            end else
                                If AttemptNo GE NUM_ATTEMPTS$ then 
                                    ErrorMsg  = 'Error in ':Service:' service. Failed to write released quantity ':RelQty
                                    ErrorMsg := ' to the REL_QTY_STATIC field of WO_LOG record ':WONo:'. '
                                    ErrorMsg := 'Failed to write unreleased quantity ':UnRelQty:' to the UNREL_QTY_STATIC '
                                    ErrorMsg := 'field of the WO_LOG record ':WONo:'. '
                                    ErrorMsg := 'Error message: ':Error_Services('GetMessage')
                                    Database_Services('ReleaseKeyIDLock', 'WO_LOG', WONo)
                                end
                            end
                        end
                    Until Done
                    Next AttemptNo
                end
			end else
				ErrorMsg  = 'Error in ':Service:' service. Failed to read record ':WONo:' from the WO_LOG table. '
				ErrorMsg := 'Error message: ':Error_Services('GetMessage')
			end
		end else
			ErrorMsg = 'Error in ':Service:' service. ':WONo:' does not exist in the WO_LOG table.'
		end
	end else
		ErrorMsg = 'Error in ':Service:' service. Null WONo passed into service'
	end
    
    LogData    = ''
    LogData<1> = LoggingDtm
    LogData<2> = WONo
    If ErrorMsg EQ '' then
    	LogResult   = 'Successfully updated WO_LOG record ':WONo:' field REL_QTY_STATIC with released quantity ':RelQty
    	LogResult  := ' and field UNREL_QTY_STATIC with unreleased quantity ':UnRelQty:'.'
    	LogData<3>  = LogResult
    	Logging_Services('AppendLog', objUpRelLog, LogData, @RM, @FM)
    end else
    	LogResult   = 'Failed to update WO_LOG record ':WONo:' field REL_QTY_STATIC with released quantity ':RelQty
    	LogResult  := ' and field UNREL_QTY_STATIC with unreleased quantity ':UnRelQty:'. Error message: ':ErrorMsg
    	LogData<3>  = LogResult
    	Logging_Services('AppendLog', objUpRelLog, LogData, @RM, @FM)
    	Error_Services('Add', ErrorMsg)
    end		
    
end service


Service AdjustReleasedQty(WONo, AdjustQty)

	LogPath     = Environment_Services('GetApplicationRootPath') : '\LogFiles\WO_LOG'
	LogDate     = Oconv(Date(), 'D4/')
	LogTime     = Oconv(Time(), 'MTS')
	LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' Adjust Released Qty Log.csv'
	Headers     = 'Logging DTM' : @FM : 'WONo' : @FM : 'Notes'
	objAdjRelLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$,',', Headers, '', False$, False$)
	LoggingDTM  = LogDate : ' ' : LogTime   ; // Logging DTM	
	OrigRelQty  = ''
	RelQty      = ''
	UnrelQty    = ''
	ErrorMsg    = ''
	Begin Case
	    Case (WONo EQ '')
	        ErrorMsg = 'Error in ':Service:' service. Null WONo passed into service'
        Case (AdjustQty EQ '')
            ErrorMsg = 'Error in ':Service:' service. Null AdjustQty passed into service'
        Case Not(Num(AdjustQty))
            ErrorMsg = 'Error in ':Service:' service. AdjustQty is not a number'
        Case Index(AdjustQty, '.', 1)
            ErrorMsg = 'Error in ':Service:' service. AdjustQty cannot be a decimal value'
        Case Otherwise$
            Null
	End Case
	If (ErrorMsg EQ '' ) then
		If RowExists('WO_LOG', WONo) then
		    Done = False$
		    For Attempt = 1 to NUM_ATTEMPTS$
		        If Attempt GT 1 then Delay(1)
		        HaveLock = Database_Services('GetKeyIDLock', 'WO_LOG', WONo, True$)
		        If HaveLock then
                    WORec = Database_Services('ReadDataRow', 'WO_LOG', WONo)
                    If Error_Services('NoError') then
                        WOQty      = WORec<WO_LOG_WO_QTY$>
						OrigRelQty = WORec<WO_LOG_REL_QTY_STATIC$>
						RelQty     = OrigRelQty + AdjustQty
						UnrelQty   = WOQty - RelQty
						If UnrelQty LT 0 then UnrelQty = 0
                        WORec<WO_LOG_REL_QTY_STATIC$>   = RelQty
                        WORec<WO_LOG_UNREL_QTY_STATIC$> = UnRelQty
                        Database_Services('WriteDataRow', 'WO_LOG', WONo, WORec, True$, False$, False$)
                        If Error_Services('NoError') then
                            Done = True$
                        end else
                            If Attempt GE NUM_ATTEMPTS$ then
                                ErrorMsg  = 'Error in ':Service:' service. Failed to write ':RelQty:' on REL_QTY_STATIC '
                                ErrorMsg := 'field and ':UnrelQty:' on UNREL_QTY_STATIC field of WO_LOG record ':WONo:'.'
                                ErrorMsg := 'Error message: ':Error_Services('GetMessage')
                                Database_Services('ReleaseKeyIDLock', 'WO_LOG', WONo)
                            end
                        end
                    end else
                        If Attempt GE NUM_ATTEMPTS$ then 
                            ErrorMsg  = 'Error in ':Service:' service. Failed to read record ':WONo:' from the WO_LOG table. '
                            ErrorMsg := 'Error message: ':Error_Services('GetMessage')
                            Database_Services('ReleaseKeyIDLock', 'WO_LOG', WONo)
                        end
                    end
		        end
		        If ( Not(HaveLock) and (Attempt GE NUM_ATTEMPTS$) ) then
		            ErrorMsg = 'Error in ':Service:' service. Failed to lock WO_LOG ':WoNo:' for update after ':Attempt:' attempts.'
		        end
		    Until Done
		    Next Attempt
		end else
			ErrorMsg = 'Error in ':Service:' service. ':WONo:' does not exist in the WO_LOG table.'
		end
	end
    
    LogData    = ''
    LogData<1> = LoggingDtm
    LogData<2> = WONo
    If ErrorMsg EQ '' then
    	LogResult   = 'Successfully adjusted WO_LOG ':WONo:' released and unreleased quantities by ':AdjustQty
    	LogResult  := '. Updated WO_LOG record ':WONo:' field REL_QTY_STATIC with released quantity ':RelQty
    	LogResult  := ' and field UNREL_QTY_STATIC with unreleased quantity ':UnRelQty:'.'
    	LogData<3>  = LogResult
    	Logging_Services('AppendLog', objAdjRelLog, LogData, @RM, @FM)
    end else
    	LogResult   = 'Failed to adjust WO_LOG ':WONo:' released and unreleased quantities by ':AdjustQty:'.'
    	LogData<3>  = LogResult
    	Logging_Services('AppendLog', objAdjRelLog, LogData, @RM, @FM)
    	Error_Services('Add', ErrorMsg)
    end		
    
end service


Service UpdateReceivedQty(WONo)

	LogPath     = Environment_Services('GetApplicationRootPath') : '\LogFiles\WO_LOG'
	LogDate     = Oconv(Date(), 'D4/')
	LogTime     = Oconv(Time(), 'MTS')
	LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' Update Received Qty Log.csv'
	Headers     = 'Logging DTM' : @FM : 'WONo' : @FM : 'Notes'
	objUpRecLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$,',', Headers, '', False$, False$)
	LoggingDTM  = LogDate : ' ' : LogTime   ; // Logging DTM	
	RxQty       = ''
	OpenQty     = ''
	ErrorMsg    = ''
	If WONo NE '' then
		If RowExists('WO_LOG', WONo) then
			WORec = Database_Services('ReadDataRow', 'WO_LOG', WONo)
			If Error_Services('NoError') then
			    WOQty       = WORec<WO_LOG_WO_QTY$>
                OrigRxQty   = WORec<WO_LOG_RX_QTY_STATIC$>
                OrigOpenQty = WORec<WO_LOG_OPEN_QTY_STATIC$>			    
				RxQty       = obj_WO_Log('RxQty', WONo:@RM:WORec)
				OpenQty     = WOQty - RxQty
                If ( (OrigRxQty NE RxQty) or (OrigOpenQty NE OpenQty) ) then
                    WORec<WO_LOG_RX_QTY_STATIC$>   = RxQty
                    WORec<WO_LOG_OPEN_QTY_STATIC$> = OpenQty
                    Done                           = False$
                    For AttemptNo = 1 to NUM_ATTEMPTS$
                        If AttemptNo GT 1 then Delay(1)
                        HaveLock = Database_Services('GetKeyIDLock', 'WO_LOG', WONo)
                        If HaveLock then
                            Database_Services('WriteDataRow', 'WO_LOG', WONo, WORec, True$, False$, False$)
                            If Error_Services('NoError') then
                                // Note: WriteDataRow will have unlocked the record
                                Done = True$
                            end else
                                If AttemptNo GE NUM_ATTEMPTS$ then 
                                    ErrorMsg  = 'Error in ':Service:' service. Failed to write received quantity ':RXQty
                                    ErrorMsg := ' to the RX_QTY_STATIC field of WO_LOG record ':WONo:'. '
                                    ErrorMsg := 'Failed to write open quantity ':OpenQty
                                    ErrorMsg := ' to the OPEN_QTY_STATIC field of WO_LOG record ':WONo:'. '
                                    ErrorMsg := 'Error message: ':Error_Services('GetMessage')
                                    Database_Services('ReleaseKeyIDLock', 'WO_LOG', WONo)
                                end
                            end
                        end
                    Until Done
                    Next AttemptNo
                end
			end else
				ErrorMsg  = 'Error in ':Service:' service. Failed to read record ':WONo:' from the WO_LOG table. '
				ErrorMsg := 'Error message: ':Error_Services('GetMessage')
			end
		end else
			ErrorMsg = 'Error in ':Service:' service. ':WONo:' does not exist in the WO_LOG table.'
		end
	end else
		ErrorMsg = 'Error in ':Service:' service. Null WONo passed into service'
	end
    
    LogData    = ''
    LogData<1> = LoggingDtm
    LogData<2> = WONo
    If ErrorMsg EQ '' then
    	LogMsg      = 'Successfully updated WO_LOG record ':WONo:' field RX_QTY_STATIC with received quantity ':RxQty:'.'
    	LogMsg     := 'Successfully updated WO_LOG record ':WONo:' field OPEN_QTY_STATIC with open quantity ':OpenQty:'.'
    	LogData<3>  = LogMsg
    	Logging_Services('AppendLog', objUpRecLog, LogData, @RM, @FM)
    end else
        LogMsg      = 'Failed to update WO_LOG record ':WONo:' field RX_QTY_STATIC with received quantity ':RxQty:'. '
        LogMsg     := 'Failed to update WO_LOG record ':WONo:' field OPEN_QTY_STATIC with open quantity ':OpenQty:'. '
        LogMsg     := 'Error message: ':ErrorMsg
    	LogData<3>  = LogMsg
    	Logging_Services('AppendLog', objUpRecLog, LogData, @RM, @FM)
    	Error_Services('Add', ErrorMsg)
    end
    
end service


Service AdjustReceivedQty(WONo, AdjustQty)

	LogPath      = Environment_Services('GetApplicationRootPath') : '\LogFiles\WO_LOG'
	LogDate      = Oconv(Date(), 'D4/')
	LogTime      = Oconv(Time(), 'MTS')
	LogFileName  = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' Adjust Received Qty Log.csv'
	Headers      = 'Logging DTM' : @FM : 'WONo' : @FM : 'Notes'
	objAdjRecLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$,',', Headers, '', False$, False$)
	LoggingDTM   = LogDate : ' ' : LogTime   ; // Logging DTM	
	RXQty        = ''
	OrigRXQty    = ''
	ErrorMsg     = ''
	Begin Case
	    Case (WONo EQ '')
	        ErrorMsg = 'Error in ':Service:' service. Null WONo passed into service'
        Case (AdjustQty EQ '')
            ErrorMsg = 'Error in ':Service:' service. Null AdjustQty passed into service'
        Case Not(Num(AdjustQty))
            ErrorMsg = 'Error in ':Service:' service. AdjustQty is not a number'
        Case Index(AdjustQty, '.', 1)
            ErrorMsg = 'Error in ':Service:' service. AdjustQty cannot be a decimal value'
        Case Otherwise$
            Null
	End Case
	If (ErrorMsg EQ '' ) then
		If RowExists('WO_LOG', WONo) then
		    Done = False$
		    For Attempt = 1 to NUM_ATTEMPTS$
		        If Attempt GT 1 then Delay(1)
		        HaveLock = Database_Services('GetKeyIDLock', 'WO_LOG', WONo, True$)
		        If HaveLock then
                    WORec = Database_Services('ReadDataRow', 'WO_LOG', WONo)
                    If Error_Services('NoError') then
                        WOQty                          = WORec<WO_LOG_WO_QTY$>
                        OrigRxQty                      = WORec<WO_LOG_RX_QTY_STATIC$>
                        RxQty                          = OrigRxQty + AdjustQty
                        OpenQty                        = WOQty - RxQty
                        WORec<WO_LOG_RX_QTY_STATIC$>   = RxQty
                        WORec<WO_LOG_OPEN_QTY_STATIC$> = OpenQty
                        Database_Services('WriteDataRow', 'WO_LOG', WONo, WORec, True$, False$, False$)
                        If Error_Services('NoError') then
                            Done = True$
                        end else
                            If Attempt GE NUM_ATTEMPTS$ then 
                                ErrorMsg  = 'Error in ':Service:' service. Failed to write ':RxQty:' on RX_QTY_STATIC field '
                                ErrorMsg := 'and ':OpenQty:' on OPEN_QTY_STATIC to WO_LOG ':WONo:'. Error message: '
                                ErrorMsg := Error_Services('GetMessage')
                                Database_Services('ReleaseKeyIDLock', 'WO_LOG', WONo)
                            end
                        end
                    end else
                        If Attempt GE NUM_ATTEMPTS$ then 
                            ErrorMsg  = 'Error in ':Service:' service. Failed to read record ':WONo:' from the WO_LOG table. '
                            ErrorMsg := 'Error message: ':Error_Services('GetMessage')
                            Database_Services('ReleaseKeyIDLock', 'WO_LOG', WONo)
                        end
                    end		           
		        end
		        If ( Not(HaveLock) and (Attempt GE NUM_ATTEMPTS$) ) then
		            ErrorMsg = 'Error in ':Service:' service. Failed to lock WO_LOG ':WoNo:' for update after ':Attempt:' attempts.'
		        end
		    Until Done
		    Next Attempt
		end else
			ErrorMsg = 'Error in ':Service:' service. ':WONo:' does not exist in the WO_LOG table.'
		end
	end
	
	LogData    = ''
    LogData<1> = LoggingDtm
    LogData<2> = WONo
    If ErrorMsg EQ '' then
    	LogResult   = 'Successfully adjusted WO_LOG ':WONo:' received and open quantities by ':AdjustQty
    	LogResult  := '. Updated WO_LOG record ':WONo:' field RX_QTY_STATIC with received quantity ':RXQty
    	LogResult  := ' and field OPEN_QTY_STATIC with open quantity ':OpenQty:'.'
    	LogData<3>  = LogResult
    	Logging_Services('AppendLog', objAdjRecLog, LogData, @RM, @FM)
    end else
    	LogResult   = 'Failed to adjust WO_LOG ':WONo:' received and open quantities by ':AdjustQty
    	LogData<3>  = LogResult
    	Logging_Services('AppendLog', objAdjRecLog, LogData, @RM, @FM)
    	Error_Services('Add', ErrorMsg)
    end	
    
end service


Service AdjustScrappedQty(WONo, AdjustQty)

	LogPath        = Environment_Services('GetApplicationRootPath') : '\LogFiles\WO_LOG'
	LogDate        = Oconv(Date(), 'D4/')
	LogTime        = Oconv(Time(), 'MTS')
	LogFileName    = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' Adjust Scrapped Qty Log.csv'
	Headers        = 'Logging DTM' : @FM : 'WONo' : @FM : 'Notes'
	objAdjScrapLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$,',', Headers, '', False$, False$)
	LoggingDTM     = LogDate : ' ' : LogTime   ; // Logging DTM	
	RXQty          = ''
	OrigRXQty      = ''
	ErrorMsg       = ''
	Begin Case
	    Case (WONo EQ '')
	        ErrorMsg = 'Error in ':Service:' service. Null WONo passed into service'
        Case (AdjustQty EQ '')
            ErrorMsg = 'Error in ':Service:' service. Null AdjustQty passed into service'
        Case Not(Num(AdjustQty))
            ErrorMsg = 'Error in ':Service:' service. AdjustQty is not a number'
        Case Index(AdjustQty, '.', 1)
            ErrorMsg = 'Error in ':Service:' service. AdjustQty cannot be a decimal value'
        Case Otherwise$
            Null
	End Case
	If (ErrorMsg EQ '' ) then
		If RowExists('WO_LOG', WONo) then
		    Done = False$
		    For Attempt = 1 to NUM_ATTEMPTS$
		        If Attempt GT 1 then Delay(1)
		        HaveLock = Database_Services('GetKeyIDLock', 'WO_LOG', WONo, True$)
		        If HaveLock then
                    WORec = Database_Services('ReadDataRow', 'WO_LOG', WONo)
                    If Error_Services('NoError') then
                        OrigScrapQty                    = WORec<WO_LOG_SCRAP_QTY_STATIC$>
                        ScrapQty                        = OrigScrapQty + AdjustQty
                        Database_Services('WriteDataColumn', 'WO_LOG', WONo, WO_LOG_SCRAP_QTY_STATIC$, ScrapQty, True$, False$, False$)
                        If Error_Services('NoError') then
                            // Note: WriteDataColumn will release the lock
                            Done = True$
                        end else
                            If Attempt GE NUM_ATTEMPTS$ then 
                                ErrorMsg  = 'Error in ':Service:' service. Failed to write ':ScrapQty:' on SCRAP_QTY_STATIC field. '
                                ErrorMsg := 'Error message: ':Error_Services('GetMessage')
                                Database_Services('ReleaseKeyIDLock', 'WO_LOG', WONo)
                            end
                        end
                    end else
                        If Attempt GE NUM_ATTEMPTS$ then 
                            ErrorMsg  = 'Error in ':Service:' service. Failed to read record ':WONo:' from the WO_LOG table. '
                            ErrorMsg := 'Error message: ':Error_Services('GetMessage')
                            Database_Services('ReleaseKeyIDLock', 'WO_LOG', WONo)
                        end
                    end		           
		        end
		        If ( Not(HaveLock) and (Attempt GE NUM_ATTEMPTS$) ) then
		            ErrorMsg = 'Error in ':Service:' service. Failed to lock WO_LOG ':WoNo:' for update after ':Attempt:' attempts.'
		        end		        
		    Until Done
		    Next Attempt
		end else
			ErrorMsg = 'Error in ':Service:' service. ':WONo:' does not exist in the WO_LOG table.'
		end
	end
	
	LogData    = ''
    LogData<1> = LoggingDtm
    LogData<2> = WONo
    If ErrorMsg EQ '' then
    	LogResult   = 'Successfully adjusted WO_LOG ':WONo:' scrapped quantity by ':AdjustQty
    	LogResult  := '. Updated WO_LOG record ':WONo:' field SCRAP_QTY_STATIC with scrap quantity ':ScrapQty:'.'
    	LogData<3>  = LogResult
    	Logging_Services('AppendLog', objAdjScrapLog, LogData, @RM, @FM)
    end else
    	LogResult   = 'Failed to adjust WO_LOG ':WONo:' scrapped quantity by ':AdjustQty
    	LogData<3>  = LogResult
    	Logging_Services('AppendLog', objAdjScrapLog, LogData, @RM, @FM)
    	Error_Services('Add', ErrorMsg)
    end	
    
end service


Service AdjustShippedQty(WONo, AdjustQty)

	LogPath       = Environment_Services('GetApplicationRootPath') : '\LogFiles\WO_LOG'
	LogDate       = Oconv(Date(), 'D4/')
	LogTime       = Oconv(Time(), 'MTS')
	LogFileName   = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' Adjust Shipped Qty Log.csv'
	Headers       = 'Logging DTM' : @FM : 'WONo' : @FM : 'Notes'
	objAdjShipLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$,',', Headers, '', False$, False$)
	LoggingDTM    = LogDate : ' ' : LogTime   ; // Logging DTM	
	RXQty         = ''
	OrigRXQty     = ''
	ErrorMsg      = ''
	Begin Case
	    Case (WONo EQ '')
	        ErrorMsg = 'Error in ':Service:' service. Null WONo passed into service'
        Case (AdjustQty EQ '')
            ErrorMsg = 'Error in ':Service:' service. Null AdjustQty passed into service'
        Case Not(Num(AdjustQty))
            ErrorMsg = 'Error in ':Service:' service. AdjustQty is not a number'
        Case Index(AdjustQty, '.', 1)
            ErrorMsg = 'Error in ':Service:' service. AdjustQty cannot be a decimal value'
        Case Otherwise$
            Null
	End Case
	If (ErrorMsg EQ '' ) then
		If RowExists('WO_LOG', WONo) then
		    Done = False$
		    For Attempt = 1 to NUM_ATTEMPTS$
		        If Attempt GT 1 then Delay(1)
		        HaveLock = Database_Services('GetKeyIDLock', 'WO_LOG', WONo, True$)
		        If HaveLock then
                    WORec = Database_Services('ReadDataRow', 'WO_LOG', WONo)
                    If Error_Services('NoError') then
                        OrigShipQty = WORec<WO_LOG_SHIP_QTY_STATIC$>
                        ShipQty     = OrigShipQty + AdjustQty
                        Database_Services('WriteDataColumn', 'WO_LOG', WONo, WO_LOG_SHIP_QTY_STATIC$, ShipQty, True$, False$, False$)
                        If Error_Services('NoError') then
                            // Note: WriteDataColumn will release the lock
                            Done = True$
                        end else
                            If Attempt GE NUM_ATTEMPTS$ then 
                                ErrorMsg  = 'Error in ':Service:' service. Failed to write ':ShipQty:' on SHIP_QTY_STATIC field. '
                                ErrorMsg := 'Error message: ':Error_Services('GetMessage')
                                Database_Services('ReleaseKeyIDLock', 'WO_LOG', WONo)
                            end
                        end
                    end else
                        If Attempt GE NUM_ATTEMPTS$ then 
                            ErrorMsg  = 'Error in ':Service:' service. Failed to read record ':WONo:' from the WO_LOG table. '
                            ErrorMsg := 'Error message: ':Error_Services('GetMessage')
                            Database_Services('ReleaseKeyIDLock', 'WO_LOG', WONo)
                        end
                    end		           
		        end
		        If ( Not(HaveLock) and (Attempt GE NUM_ATTEMPTS$) ) then
		            ErrorMsg = 'Error in ':Service:' service. Failed to lock WO_LOG ':WoNo:' for update after ':Attempt:' attempts.'
		        end		        
		    Until Done
		    Next Attempt
		end else
			ErrorMsg = 'Error in ':Service:' service. ':WONo:' does not exist in the WO_LOG table.'
		end
	end
	
	LogData    = ''
    LogData<1> = LoggingDtm
    LogData<2> = WONo
    If ErrorMsg EQ '' then
    	LogResult   = 'Successfully adjusted WO_LOG ':WONo:' shipped quantity by ':AdjustQty
    	LogResult  := '. Updated WO_LOG record ':WONo:' field SHIP_QTY_STATIC with scrap quantity ':ShipQty:'.'
    	LogData<3>  = LogResult
    	Logging_Services('AppendLog', objAdjShipLog, LogData, @RM, @FM)
    end else
    	LogResult   = 'Failed to adjust WO_LOG ':WONo:' shipped quantity by ':AdjustQty
    	LogData<3>  = LogResult
    	Logging_Services('AppendLog', objAdjShipLog, LogData, @RM, @FM)
    	Error_Services('Add', ErrorMsg)
    end	
    
end service


Service UpdateShippedQty(WONo)
	
	LogPath      = Environment_Services('GetApplicationRootPath') : '\LogFiles\WO_LOG'
	LogDate      = Oconv(Date(), 'D4/')
	LogTime      = Oconv(Time(), 'MTS')
	LogFileName  = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' Update Shipped Qty Log.csv'
	Headers      = 'Logging DTM' : @FM : 'WONo' : @FM : 'Notes'
	objUpShipLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$,',', Headers, '', False$, False$)
	LoggingDTM   = LogDate : ' ' : LogTime   ; // Logging DTM	
	ShipQty      = ''
	ErrorMsg     = ''
	If WONo NE '' then
		If RowExists('WO_LOG', WONo) then
			WORec = Database_Services('ReadDataRow', 'WO_LOG', WONo)
			If Error_Services('NoError') then
			    OrigShipQty = WORec<WO_LOG_SHIP_QTY_STATIC$>
				ShipQty     = obj_WO_Log('ShipQty', WONo:@RM:WORec)
                If OrigShipQty NE ShipQty then
                    Done = False$
                    For AttemptNo = 1 to NUM_ATTEMPTS$
                        If AttemptNo GT 1 then Delay(1)
                        HaveLock = Database_Services('GetKeyIDLock', 'WO_LOG', WONo)
                        If HaveLock then
                            Database_Services('WriteDataColumn', 'WO_LOG', WONo, WO_LOG_SHIP_QTY_STATIC$, ShipQty, True$, False$, False$)
                            If Error_Services('NoError') then 
                                Done = True$
                            end else
                                If AttemptNo GE NUM_ATTEMPTS$ then 
                                    ErrorMsg  = 'Error in ':Service:' service. Failed to write ':ShipQty
                                    ErrorMsg := ' to the SHIP_QTY_STATIC field of WO_LOG record ':WONo:'. '
                                    ErrorMsg := 'Error message: ':Error_Services('GetMessage')
                                end
                            end
                        end
                    Until Done
                    Next AttemptNo
                    If Database_Services('IsKeyIDSelfLocked', 'WO_LOG', WONo) then Database_Services('ReleaseKeyIDLock', 'WO_LOG', WONo)
                end
			end else
				ErrorMsg  = 'Error in ':Service:' service. Failed to read record ':WONo:' from the WO_LOG table. '
				ErrorMsg := 'Error message: ':Error_Services('GetMessage')
			end
		end else
			ErrorMsg = 'Error in ':Service:' service. ':WONo:' does not exist in the WO_LOG table.'
		end
	end else
		ErrorMsg = 'Error in ':Service:' service. Null WONo passed into service'
	end
    
    LogData    = ''
    LogData<1> = LoggingDtm
    LogData<2> = WONo
    If ErrorMsg EQ '' then
    	LogResult   = 'Successfully updated WO_LOG record ':WONo:' field SHIP_QTY_STATIC '
    	LogResult  := 'with shipped quantity ':ShipQty:'.'
    	LogData<3>  = LogResult
    	Logging_Services('AppendLog', objUpShipLog, LogData, @RM, @FM)
    end else
    	LogResult   = 'Failed to update WO_LOG record ':WONo:' field SHIP_QTY_STATIC with shipped quantity '
    	LogResult  := ShipQty:'. Error message: ':ErrorMsg
    	LogData<3>  = LogResult
    	Logging_Services('AppendLog', objUpShipLog, LogData, @RM, @FM)
    	Error_Services('Add', ErrorMsg)
    end	
    
end service


Service UpdateScrappedQty(WONo)

	LogPath       = Environment_Services('GetApplicationRootPath') : '\LogFiles\WO_LOG'
	LogDate       = Oconv(Date(), 'D4/')
	LogTime       = Oconv(Time(), 'MTS')
	LogFileName   = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' Update Scrapped Qty Log.csv'
	Headers       = 'Logging DTM' : @FM : 'WONo' : @FM : 'Notes'
	objUpScrapLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$,',', Headers, '', False$, False$)
	LoggingDTM    = LogDate : ' ' : LogTime
	ScrapQty      = ''
	ErrorMsg      = ''
	If WONo NE '' then
		If RowExists('WO_LOG', WONo) then
			WORec = Database_Services('ReadDataRow', 'WO_LOG', WONo)
			If Error_Services('NoError') then
				EpiProFlag = Xlate('WO_LOG', WONo, 'EPI_PRO_FLAG', 'X')
				If EpiProFlag then
					 ScrapQty = Xlate('WO_STEP', WONo:'*1', 'TOT_REJ_TOTAL', 'X')
				end else
					 ScrapQty = Sum(Xlate('WO_LOG', WONo, 'WO_MAT_SAP_CONFIRM_SCRAP', 'X'))
				end					
                OrigScrapQty = WORec<WO_LOG_SCRAP_QTY_STATIC$>
                If OrigScrapQty NE ScrapQty then
                    Done = False$
                    For AttemptNo = 1 to NUM_ATTEMPTS$
                        If AttemptNo GT 1 then Delay(1)
                        HaveLock = Database_Services('GetKeyIDLock', 'WO_LOG', WONo)
                        If HaveLock then
                            Database_Services('WriteDataColumn', 'WO_LOG', WONo, WO_LOG_SCRAP_QTY_STATIC$, ScrapQty, True$, False$, False$)
                            If Error_Services('NoError') then
                                // Note: WriteDataColumn will have unlocked the record
                                Done = True$
                            end else
                                If AttemptNo GE NUM_ATTEMPTS$ then 
                                    ErrorMsg  = 'Error in ':Service:' service. Failed to write ':ScrapQty
                                    ErrorMsg := ' to the SCRAP_QTY_STATIC field of WO_LOG record ':WONo:'. '
                                    ErrorMsg := 'Error message: ':Error_Services('GetMessage')
                                    Database_Services('ReleaseKeyIDLock', 'WO_LOG', WONo)
                                end
                            end
                        end
                    Until Done
                    Next AttemptNo
                end
			end else
				ErrorMsg  = 'Error in ':Service:' service. Failed to read record ':WONo:' from the WO_LOG table. '
				ErrorMsg := 'Error message: ':Error_Services('GetMessage')
			end
		end else
			ErrorMsg = 'Error in ':Service:' service. ':WONo:' does not exist in the WO_LOG table.'
		end
	end else
		ErrorMsg = 'Error in ':Service:' service. Null WONo passed into service'
	end
    
    LogData    = ''
    LogData<1> = LoggingDtm
    LogData<2> = WONo
    If ErrorMsg EQ '' then
    	LogResult   = 'Successfully updated WO_LOG record ':WONo:' field SCRAP_QTY_STATIC '
    	LogResult  := 'with scrapped quantity ':ScrapQty:'.'
    	LogData<3>  = LogResult
    	Logging_Services('AppendLog', objUpScrapLog, LogData, @RM, @FM)
    end else
    	LogResult   = 'Failed to update WO_LOG record ':WONo:' field SCRAP_QTY_STATIC with scrapped quantity '
    	LogResult  := ScrapQty:'. Error message: ':ErrorMsg
    	LogData<3>  = LogResult
    	Logging_Services('AppendLog', objUpScrapLog, LogData, @RM, @FM)
    	Error_Services('Add', ErrorMsg)
    end		
	
end service


Service ConvertRecordToJSON(WONo, Record, ItemURL)
	
	ErrorMsg   = ''
    jsonRecord = ''
    IF WONo NE '' then
        If Record EQ '' then Record = Database_Services('ReadDataRow', 'WO_LOG', WONo)
        If Error_Services('NoError') then
            EpiPartNo  = Record<WO_LOG_EPI_PART_NO$>
            EpiPartRec = Database_Services('ReadDataRow', 'EPI_PART', EpiPartNo)
            If Error_Services('NoError') then
                SubSuppBy  = EpiPartRec<EPI_PART_SUB_SUPP_BY$>		;* L - EpiSvcs supplied, C - Customer Supplied
                ProdVerNo  = Record<WO_LOG_PROD_VER_NO$>
                ProdVerRec = Database_Services('ReadDataRow', 'PROD_VER', ProdVerNo)
                If Error_Services('NoError') then
                    SubPartNo = ProdVerRec<PROD_VER_SUB_PART_NO$>
                    // Create the JSON response object
                    objJSON = ''
                    If SRP_JSON(objJSON, 'New', 'Object') then
                        objWOLog = ''
                        If SRP_JSON(objWOLog, 'New', 'Object') then
                            SRP_JSON(objWOLog, 'SetValue', 'KeyId', WONo)
                            SRP_JSON(objWOLog, 'SetValue', 'ProdOrdNo', Record<WO_LOG_PROD_ORD_NO$>)
                            SRP_JSON(objWOLog, 'SetValue', 'PSN', Xlate('WO_LOG', WONo, 'PS_NO', 'X'))
                            SRP_JSON(objWOLog, 'SetValue', 'EpiPartNo', EpiPartNo)
                            SRP_JSON(objWOLog, 'SetValue', 'HotFlag', Record<WO_LOG_HOT_FLAG$>)
                            SRP_JSON(objWOLog, 'SetValue', 'WoQty', Record<WO_LOG_WO_QTY$>)
                            SRP_JSON(objWOLog, 'SetValue', 'RxQty', Record<WO_LOG_RX_QTY_STATIC$>)
                            SRP_JSON(objWOLog, 'SetValue', 'RelQty', Record<WO_LOG_REL_QTY_STATIC$>)
                            SRP_JSON(objWOLog, 'SetValue', 'UnRelQty', Record<WO_LOG_UNREL_QTY_STATIC$>)
                            SRP_JSON(objWOLog, 'SetValue', 'ShipQty', Record<WO_LOG_SHIP_QTY_STATIC$>)
                            SRP_JSON(objWOLog, 'SetValue', 'ScrapQty', Record<WO_LOG_SCRAP_QTY_STATIC$>)
                            SRP_JSON(objWOLog, 'SetValue', 'OpenQty', Record<WO_LOG_OPEN_QTY_STATIC$>)
                            SRP_JSON(objWOLog, 'SetValue', 'CustNo', Record<WO_LOG_CUST_NO$>)
                            SRP_JSON(objWOLog, 'SetValue', 'Customer', Xlate('WO_LOG', WONo, 'CUST_NAME', 'X'))

                            ExpectedVendorCd = Record<WO_LOG_EXP_VEND_CD$>
                            If ( (Len(ExpectedVendorCd) NE 2) or (SubSuppBy NE 'L') ) then ExpectedVendorCd = ''
                            SRP_JSON(objWOLog, 'SetValue', 'ExpectedVendorCd', ExpectedVendorCd)
                            SRP_JSON(objWOLog, 'SetValue', 'SubPartNo', SubPartNo)
                            
                            // Create a list of cassettes
                            CassIDs           = Record<WO_LOG_WO_MAT_KEY$>
                            objChildCassettes = ''
                            If SRP_JSON(objChildCassettes, 'New', 'Array') then
                                If CassIDs NE '' then
                                    for each Cass in CassIDs using @VM setting vPos
                                        WOMatRec         = Database_Services('ReadDataRow', 'WO_MAT', Cass)
                                        If Error_Services('NoError') then 
                                            // Add in each indv. cassette object
                                            objChildCassette = ''
                                            If SRP_JSON(objChildCassette, 'New', 'Object') then
                                                SRP_JSON(objChildCassette, 'SetValue', 'KeyId', Cass)
                                                SRP_JSON(objChildCassette, 'SetValue', 'CassNo', Field(Cass, '*', 2, 1))
                                                SRP_JSON(objChildCassette, 'SetValue', 'SAPBatchNo', WOMatRec<WO_MAT_SAP_BATCH_NO$>)
                                                SRP_JSON(objChildCassette, 'SetValue', 'SAPTXDtm', Date_Services('ConvertDateTimeToISO8601', WOMatRec<WO_MAT_SAP_TX_DTM$>))
                                                SRP_JSON(objChildCassette, 'SetValue', 'RDSNo', WOMatRec<WO_MAT_RDS_NO$>)
                                                SRP_JSON(objChildCassette, 'SetValue', 'ShipNo', WOMatRec<WO_MAT_SHIP_NO$>)
                                                SRP_JSON(objChildCassette, 'SetValue', 'LotNo', WOMatRec<WO_MAT_LOT_NO$>)
                                                SRP_JSON(objChildCassette, 'SetValue', 'Qty', WOMatRec<WO_MAT_WAFER_QTY$>)
                                                SRP_JSON(objChildCassette, 'SetValue', 'CustPartNo', WOMatRec<WO_MAT_CUST_PART_NO$>)
                                                SRP_JSON(objChildCassette, 'SetValue', 'SubPartNo', WOMatRec<WO_MAT_SUB_PART_NO$>)
                                                SRP_JSON(objChildCassette, 'SetValue', 'VendorCd', WOMatRec<WO_MAT_SUB_VEND_CD$>)
                                                SRP_JSON(objChildCassette, 'SetValue', 'RecDtm', Date_Services('ConvertDateTimeToISO8601', WOMatRec<WO_MAT_RX_DTM$>))
                                                SRP_JSON(objChildCassette, 'SetValue', 'RelDtm', Date_Services('ConvertDateTimeToISO8601', WOMatRec<WO_MAT_REL_DTM$>))
                                                SRP_JSON(objChildCassette, 'SetValue', 'RecUser', OConv(WOMatRec<WO_MAT_RX_BY$>, '[XLATE_CONV,LSL_USERS*FIRST_LAST]'))
                                                SRP_JSON(objChildCassette, 'SetValue', 'RelUser', OConv(WOMatRec<WO_MAT_REL_BY$>, '[XLATE_CONV,LSL_USERS*FIRST_LAST]' ))
                                                SRP_JSON(objChildCassette, 'SetValue', 'WMInKey', WOMatRec<WO_MAT_WMI_KEY$>)
                                                SRP_JSON(objChildCassette, 'SetValue', 'WMOutKey', WOMatRec<WO_MAT_WMO_KEY$>)
                                                SRP_JSON(objChildCassettes, 'Add', objChildCassette)
                                                SRP_JSON(objChildCassette, 'Release')
                                            end
                                        end else
                                            ErrorMsg = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
                                        end
                                    Next Cass
                                end else
                                    SRP_JSON(objWOLog, 'SetValue', 'ChildCassettes', '')
                                end
                                SRP_JSON(objWOLog, 'Set', 'ChildCassettes', objChildCassettes)
                                SRP_JSON(objChildCassettes, 'Release')
                            end
                            SRP_JSON(objJSON, 'Set', 'WO_Log', objWOLog)
                            SRP_JSON(objWOLog, '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.
                            objLinks = ''
                            If SRP_JSON(objLinks, 'New', 'Object') then
                                // Create a self link.
                                objLink = ''
                                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
                        ErrorMsg = 'Error in ':Service:' service. Unable to create JSON object'
                    end                    
                end else
                    ErrorMsg = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
                end
            end else
                ErrorMsg = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
            end
        end else
            ErrorMsg = 'Error in ':Service:' service. Error message: ':Error_Services('GetMessage')
        end
    end else
        ErrorMsg = 'Error in ':Service:' service. Null WONo passed into service.'
    end
    
    If ErrorMsg NE '' then Error_Services('Add', ErrorMsg)
    Response    = jsonRecord
    
end service


Service RemoveWoMatCassetteFromWO(WoMatKey, Username)
    
    ErrorMessage = ''
    If RowExists('WO_MAT', WoMatKey) then
        WoNo = Field(WoMatKey, '*', 1)
        WOLogRecord = Database_Services('ReadDataRow', 'WO_LOG', WoNo, True$, 0, False$)
        If Error_Services('NoError') then
            WoMatKeys = WOLogRecord<WO_LOG_WO_MAT_KEY$>
            Locate WoMatKey in WoMatKeys using @VM setting CassPos then
                WoLogRecord<WO_LOG_WO_MAT_KEY$> = Delete(WOLogRecord<WO_LOG_WO_MAT_KEY$>, 1, CassPos, 0)
                Database_Services('WriteDataRow', 'WO_LOG', WoNo, WoLogRecord)
                If Error_Services('NoError') then
                    Work_Order_Services('UpdateReceivedQty', WONo)
                    Work_Order_Services('UpdateReleasedQty', WONo)
                    Work_Order_Services('UpdateUnscheduledQuantities')
                end else
                    ErrorMessage = Error_Services('GetMessage')
                end
            end else
                ErrorMessage = "Unable to locate cass no " : WoMatKey : ' in the WO_LOG ' : WoNo : ' Record.'
            end
        end else
            ErrorMessage = Error_Services('GetMessage')
        end
    end else
        ErrorMessage = 'Invalid WoMat Key ' : WoMatKey : ' passed to RemoveWoMatCassetteFromWO routine.'
    end
    If ErrorMessage NE '' then
        Error_Services('Add', ErrorMessage)
    end
    
end service

Service CreateVoidedLotRecord(LotId, LegacyLotId, LegacyLotType, WoMatKey, UserId)
    //Todo: Move to Lot_Services
    ErrorMessage = ''
    
    If LotId NE '' OR LegacyLotId NE '' then
        If LotId EQ '' then
            //Determine NG Lot id if none was passed to routine.
            if LegacyLotType NE '' then
                if LegacyLotType EQ 'RDS' OR LegacyLotType EQ 'WM_IN' OR LegacyLotType EQ 'WM_OUT' then
                    LotId = Lot_Services('GetLotIdByLegacyLotIdAndType', LegacyLotId, LegacyLotType)
                    If Error_Services('HasError') then
                        ErrorMessage = Error_Services('GetMessage')
                    end
                end else
                    ErrorMessage = 'Invalid Legacy Lot Type passed to routine.' 
                end
            end else
                ErrorMessage = 'A Legacy lot ID was passed to the routine, but the legacy lot type was null.'
            end
        end
        
        If (ErrorMessage EQ '') then
            NewVoidedLotId                                 = RTI_CreateGUID()
            NewVoidedLotRec                                = ''
            NewVoidedLotRec<VOIDED_LOT_WORK_ORDER_LOG_ID$> = Field(WoMatKey, '*', 1)
            NewVoidedLotRec<VOIDED_LOT_VOID_BY$>           = UserId
            NewVoidedLotRec<VOIDED_LOT_VOID_DTM$>          = Datetime()
            NewVoidedLotRec<VOIDED_LOT_ENTITY_TYPE$>       = LegacyLotType
            NewVoidedLotRec<VOIDED_LOT_LOT_ID$>            = LotId
            NewVoidedLotRec<VOIDED_LOT_LEGACY_LOT_ID$>     = LegacyLotId
            NewVoidedLotRec<VOIDED_LOT_WO_MAT_KEY$>        = WoMatKey
            Database_Services('WriteDataRow', 'VOIDED_LOT', NewVoidedLotId, NewVoidedLotRec)
            If Error_Services('HasError') then
                ErrorMessage = Error_Services('GetMessage')
            end
        end
    end else
        ErrorMessage = 'Both the lot id and the legacy lot id were null.'
    end
    
    If ErrorMessage NE '' then
        Error_Services('Add', ErrorMessage)
    end
    
end service


Service IsLotVoided(LotId)
    
    //Todo: Move to Lot_Services
    ErrorMessage = ''
    VoidLotRecords = ''
    Open 'DICT.VOIDED_LOT' to hVoidTable then
        
        SearchString = 'LOT_ID':@VM:LotId:@FM
        Btree.Extract(SearchString, 'VOIDED_LOTS', hVoidTable, VoidLotRecord)
    end else
        ErrorMessage = 'Unable to open VOIDED_LOT table.'
    end
    
    If ErrorMessage NE '' then
        Error_Services('Add', ErrorMessage)
    end
    
    Response = VoidLotRecords
    
end service


Service SignVoidNonEpp(WOMatKeys, WONo, Username)
    
    ErrorMessage = ''
    
    WOLogRecord = Database_Services('ReadDataRow', 'WO_LOG', WONo, True$, 0, False$)
    if Error_Services('NoError') then
        for each WoMatKey in WOMatKeys using @VM
            if WoMatKey NE '' then
                CassNo = Field(WoMatKey, '*', 2)
                ReactType = XLATE('WO_LOG', WONo, WO_LOG_REACT_TYPE$, 'X')
                If ReactType EQ 'ASM' OR ReactType EQ 'ASM+' OR ReactType EQ 'HTR' then
                    RDSNo  = Database_Services('ReadDataColumn', 'WO_MAT', WoMatKey, WO_MAT_RDS_NO$, True$, 0, False$)
                    If Error_Services('NoError') then
                        If WoLogRecord<WO_LOG_WO_MAT_KEY$> NE '' then
                            Locate WoMatKey in WoLogRecord<WO_LOG_WO_MAT_KEY$> using @VM setting CassPos then
                                WoLogRecord<WO_LOG_WO_MAT_KEY$> = Delete(WOLogRecord<WO_LOG_WO_MAT_KEY$>, 1, CassPos, 0)
                                WO_Mat_Services('SetWoMatVoidFlag', WOMatKey, Username)
                                if Error_Services('NoError') then
                                    RDS_Services('DetachRDSFromWO', RDSNo)
                                    If Error_Services('NoError') then
                                        // Create a voided LOT record.
                                        Work_Order_Services('CreateVoidedLotRecord', '', RDSNo, 'RDS', WoMatKey, Username)
                                        LotId = Lot_Services('GetLotIdByLegacyLotIdAndType', RDSNo, 'RDS')
                                        // Create a LOT_EVENT record if this lot is in the new LOT system.
                                        If LotId NE '' then 
                                            Lot_Event_Services('CreateLotEvent', LotId, Datetime(), 'COMMENT', 'Lot voided by ' : Username, '', Username, True$, 'RDS')
                                            If Error_Services('HasError') then ErrorMessage = Error_Services('GetMessage')
                                        end
                                    end else
                                        ErrorMessage = Error_Services('GetMessage')
                                    end
                                end else
                                    ErrorMessage = Error_Services('GetMessage')
                                end
                            end else
                                ErrorMessage = 'WO_MAT key not found in WO_LOG record.'
                            end
                        end else
                            ErrorMessage = 'No WO_MAT keys exist in WO_LOG record.'
                        end
                    end else
                        ErrorMessage = Error_Services('GetMessage')
                    end
                end else
                    ErrorMessage = 'Cannot use this routine to void EpiPro lots.'
                end
            end
        Next WoMatKey
    end else
        ErrorMessage = Error_Services('GetMessage')
    end
    
    If ErrorMessage EQ '' then
        //Commit work order changes all at once here.
        Database_Services('WriteDataRow', 'WO_LOG', WONo, WoLogRecord)
        if Error_Services('NoError') then
            Work_Order_Services('UpdateReceivedQty', WONo)
            Work_Order_Services('UpdateReleasedQty', WONo)
            Work_Order_Services('UpdateUnscheduledQuantities')
        end else
            Error_Services('Add', ErrorMessage)
        end
    end else
        Error_Services('Add', ErrorMessage)
    end
    
end service


Service SignVoidWMI(WMInKeys, WONo, Username)
    
    ErrorMessage = ''
    
    WOLogRecord = Database_Services('ReadDataRow', 'WO_LOG', WONo, True$, 0, False$)
    if Error_Services('NoError') then
        for each WmInKey in WMInKeys using @VM
            if WmInKey NE '' then
                WONo   = Field(WMInKey, '*', 1)
                CassNo = Field(WMInKey, '*', 3)
                WoStepKey = Xlate('WO_LOG', WONo, WO_LOG_WO_STEP_KEY$, 'X')
                WoMatKey = WONo : '*' : CassNo
                ReactType = WOLogRecord<WO_LOG_REACT_TYPE$>
                If ReactType EQ 'EPP' then
                    WMInRec = Database_Services('ReadDataRow', 'WM_IN', WMInKey, True$, 0, False$)
                    IsVoided = WMInRec<WM_IN_VOID$>
                    If Not(IsVoided) then
                        IsOnHold = Hold_Services('CheckForHold', WoMatKey)
                        If Not(IsOnHold) then
                            WM_In_Services('SetVoidFlag', WmInKey, Username)
                            If Error_Services('NoError') then
                                Work_Order_Services('CreateVoidedLotRecord', '', WmInKey, 'WM_IN', WoMatKey, Username)
                                If Error_Services('NoError') then
                                    LotId = Lot_Services('GetLotIdByLegacyLotIdAndType', WmInKey, 'WM_IN')
                                    // Create a LOT_EVENT record if this lot is in the new LOT system.
                                    If LotId NE '' then 
                                        Lot_Event_Services('CreateLotEvent', WmInKey, DateTime(), 'COMMENT', 'Lot voided by ' : Username, '', Username, True$, 'WM_IN')
                                        If Error_Services('HasError') then ErrorMessage = Error_Services('GetMessage')
                                    end
                                    If ErrorMessage EQ '' then 
                                        //Check if WMO is also voided. If it is, remove it from the WO_MAT_KEY field in WO_LOG, and void the WO_MAT record
                                        WMOKey = WMInKey; //the paired WMO key is the same as the WMIKey
                                        IsWMOVoided = Database_Services('ReadDataColumn', 'WM_OUT', WMOKey, WM_OUT_VOID$)
                                        If IsWMOVoided then
                                            Locate WoMatKey in WoLogRecord<WO_LOG_WO_MAT_KEY$> using @VM setting CassPos then
                                                WoLogRecord<WO_LOG_WO_MAT_KEY$> = Delete(WOLogRecord<WO_LOG_WO_MAT_KEY$>, 1, CassPos, 0)
                                                WO_Mat_Services('SetWoMatVoidFlag', WoMatKey, Username)
                                            end else
                                                ErrorMessage = 'Unable to locate WO_MAT key in WO_LOG record.'
                                            end
                                        end
                                    end
                                end else
                                    ErrorMessage = Error_Services('GetMessage')
                                end
                            end else
                                ErrorMessage = Error_Services('GetMessage')
                            end 
                        end
                    end else
                        ErrorMessage = 'Unable to void lot as it is already voided.'
                    end
                end else
                    ErrorMessage = 'Cannot use this routine to void non EpiPro lots.'
                end
            end
        Next WoMatKey
    end else
        ErrorMessage = Error_Services('GetMessage')
    end
    
    If ErrorMessage EQ '' then
        //Commit work order changes all at once here.
        Database_Services('WriteDataRow', 'WO_LOG', WONo, WoLogRecord)
        if Error_Services('NoError') then
            Work_Order_Services('UpdateReceivedQty', WONo)
            Work_Order_Services('UpdateReleasedQty', WONo)
            Work_Order_Services('UpdateUnscheduledQuantities')
        end else
            Error_Services('Add', ErrorMessage)
        end
    end else
        Error_Services('Add', ErrorMessage)
    end
    
    If ErrorMessage NE '' then
        LogData    = ''
        LogData<1> = LoggingDTM
        LogData<2> = Username
        LogData<3> = WoNo
        LogData<4> = ErrorMessage
        Logging_Services('AppendLog', objReleaseLog, LogData, @RM, @FM, False$)
        Error_Services('Add', ErrorMessage)
    end else
        LogData    = ''
        LogData<1> = LoggingDTM
        LogData<2> = Username
        LogData<3> = WoNo
        LogData<4> = 'Void queued successfully.'
        Logging_Services('AppendLog', objReleaseLog, LogData, @RM, @FM, False$)
        Error_Services('Add', ErrorMessage)
    end
    
end service


Service SignVoidWMO(WMOutKeys, WONo, Username)
    
    ErrorMessage = ''
    
    WOLogRecord = Database_Services('ReadDataRow', 'WO_LOG', WONo, True$, 0, False$)
    If Error_Services('NoError') then
        for each WmOutKey in WMOutKeys using @VM
            if WmOutKey NE '' then
                WONo   = Field(WmOutKey, '*', 1)
                CassNo = Field(WmOutKey, '*', 3)
                WoStepKey = Xlate('WO_LOG', WONo, WO_LOG_WO_STEP_KEY$, 'X')
                WoMatKey = WONo : '*' : CassNo
                ReactType = WOLogRecord<WO_LOG_REACT_TYPE$>
                If ReactType EQ 'EPP' then
                    WMOutRec = Database_Services('ReadDataRow', 'WM_OUT', WmOutKey, True$, 0, False$)
                    IsVoided = WMInRec<WM_OUT_VOID$>
                    If Not(IsVoided) then
                        IsOnHold = Hold_Services('CheckForHold', WoMatKey)
                        If Not(IsOnHold) then
                            WM_Out_Services('SetVoidFlag', WmOutKey, Username)
                            If Error_Services('NoError') then
                                Work_Order_Services('CreateVoidedLotRecord', '', WmOutKey, 'WM_OUT', WoMatKey, Username)
                                If Error_Services('NoError') then
                                    LotId       = Lot_Services('GetLotIdByLegacyLotIdAndType', WmOutKey, 'WM_OUT')
                                    // Create a LOT_EVENT record if this lot is in the new LOT system.
                                    If LotId NE '' then 
                                        Lot_Event_Services('CreateLotEvent', WmOutKey, DateTime(), 'COMMENT', 'Lot voided by ' : Username, '', Username, True$, 'WM_OUT')
                                        If Error_Services('HasError') then ErrorMessage = Error_Services('GetMessage')
                                    end
                                    If ErrorMessage EQ '' then 
                                        //Check if WMI is also voided. If it is, remove it from the WO_MAT_KEY field in WO_LOG, and void the WO_MAT record
                                        WMIKey      = WmOutKey; //the paired WMO key is the same as the WMIKey
                                        IsWMIVoided = Database_Services('ReadDataColumn', 'WM_IN', WMOKey, WM_IN_VOID$)
                                        If IsWMIVoided then
                                            Locate WoMatKey in WoLogRecord<WO_LOG_WO_MAT_KEY$> using @VM setting CassPos then
                                                WoLogRecord<WO_LOG_WO_MAT_KEY$> = Delete(WOLogRecord<WO_LOG_WO_MAT_KEY$>, 1, CassPos, 0)
                                                WO_Mat_Services('SetWoMatVoidFlag', WoMatKey, Username)
                                            end else
                                                ErrorMessage = 'Unable to locate WO_MAT key in WO_LOG record.'
                                            end
                                        end
                                    end
                                end else
                                    ErrorMessage = Error_Services('GetMessage')
                                end
                            end else
                                ErrorMessage = Error_Services('GetMessage')
                            end
                        end
                    end else
                        ErrorMessage = 'Unable to void lot as it is already voided.'
                    end
                end else
                    ErrorMessage = 'Cannot use this routine to void non EpiPro lots.'
                end
            end
        Next WoMatKey
    end else
        ErrorMessage = Error_Services('GetMessage')
    end
    
    If ErrorMessage EQ '' then
        //Commit work order changes all at once here.
        Database_Services('WriteDataRow', 'WO_LOG', WONo, WoLogRecord)
        if Error_Services('NoError') then
            Work_Order_Services('UpdateReceivedQty', WONo)
            Work_Order_Services('UpdateReleasedQty', WONo)
            Work_Order_Services('UpdateUnscheduledQuantities')
        end else
            Error_Services('Add', ErrorMessage)
        end
    end else
        Error_Services('Add', ErrorMessage)
    end
    
    If ErrorMessage NE '' then
        LogData    = ''
        LogData<1> = LoggingDTM
        LogData<2> = Username
        LogData<3> = WONo
        LogData<4> = ErrorMessage
        Logging_Services('AppendLog', objReleaseLog, LogData, @RM, @FM, False$)
        Error_Services('Add', ErrorMessage)
    end else
        LogData    = ''
        LogData<1> = LoggingDTM
        LogData<2> = Username
        LogData<3> = ''
        LogData<4> = 'Void processed successfully.'
        Logging_Services('AppendLog', objReleaseLog, LogData, @RM, @FM, False$)
        Error_Services('Add', ErrorMessage)
    end
    
end service

Service GetEligiblePeelOffLotsByWOAndEntityType(WONo, EntityType)
    
    ErrorMessage = ''
    EligibleCassIds = ''
    
    If RowExists('WO_LOG', WONo) then
        Begin Case
            Case EntityType EQ 'RDS'
                CassIds = Database_Services('ReadDataColumn', 'WO_LOG', WONo, WO_LOG_WO_MAT_KEY$, True$, 0, False$)
                for each CassId in CassIds using @VM
                    RDSNo = XLATE('WO_MAT', CassId, WO_MAT_RDS_NO$, 'X')
                    Signatures = Signature_Services('GetSigProfile', CassId, 0, RDSNo)
                    Eligible = True$
                    for each Signature in Signatures<1> using @VM setting SigPos
                        SignatureDtm = Signatures<3, SigPos>
                        If SignatureDtm NE '' then
                            Eligible = False$
                        end
                    Until Eligible EQ False$
                    Next Signature
                    If Eligible then EligibleCassIds<1, -1> = CassId
                Next CassId
            Case EntityType EQ 'WM_OUT'
                //WM_OUTS can be voided when
                //1. There are no RDS Nos associated
                //2. There have never been any wafers in them
                WOStepKey = XLATE('WO_LOG', WONo, WO_LOG_WO_STEP_KEY$, 'X')
                CassIds = Database_Services('ReadDataColumn', 'WO_STEP', WOStepKey, WO_STEP_WM_OUT_KEYS$, True$, 0, False$)
                for each CassId in CassIds using @VM
                    WMORec = Database_Services('ReadDataRow', 'WM_OUT', CassId, True$, 0, False);//XLATE('WM_OUT',WMOutKeys<1,I>,'','X')
                    WMO_RdsNos		= WMORec<WM_OUT_RDS$>
                    WMO_NCRNos		= WMORec<WM_OUT_SLOT_NCR$>
                    WMO_UMWCassIDs	= WMORec<WM_OUT_UMW_CASS_ID$>
                    Eligible = True$
                    //Loop through RDS list to see if there is an RDS attached. If there are any RDS No's attached it is not eligible.
                    for each RDSNo in WMO_RDSNos using @VM
                        If RDSNo NE '' then
                            Eligible = False$
                        end
                    Until Eligible EQ False$    
                    Next RDSNo
                    //Loop through NCR list to see if there are any NCR's associated. If there are any NCR No's attached it is not eligible.
                    If Eligible then
                        For Each NCRNo in WMO_NCRNos using @VM
                            If NCRNo NE '' then
                                Eligible = False$
                            end
                        Until Eligible EQ False$
                        Next NCR
                    end
                    //Loop through MU Wafer Usage list to see if there are any MU wafer's taken from this lot. If there are any Makeup Wafers attached it is not eligible.
                    If Eligible then
                        For Each UMWCass in WMO_UMWCassIDs using @VM
                            If UMWCass NE '' then
                                Eligible = False$
                            end
                        Until Eligible EQ False$
                        Next UMWCass
                    end
                    If Eligible then EligibleCassIds<1, -1> = CassId
                Next CassId
            Case EntityType EQ 'WM_IN'
                WOStepKey = XLATE('WO_LOG', WONo, WO_LOG_WO_STEP_KEY$, 'X')
                CassIds = Database_Services('ReadDataColumn', 'WO_STEP', WOStepKey, WO_STEP_WM_IN_KEYS$, True$, 0, False$)
                for each CassId in CassIds using @VM
                    Eligible = True$
                    WMIRec = Database_Services('ReadDataRow', 'WM_IN', CassId, True$, 0, False);//XLATE('WM_OUT',WMOutKeys<1,I>,'','X')
                    WMI_RdsNos		= WMIRec<WM_IN_RDS_NO$>
                    WMI_NCRNos		= WMIRec<WM_IN_NCR_NO$>
                    Eligible = True$
                    //Loop through RDS list to see if there is an RDS attached. If there are any RDS No's attached it is not eligible.
                    for each RDSNo in WMI_RDSNos using @VM
                        If RDSNo NE '' then
                            Eligible = False$
                        end
                    Until Eligible EQ False$    
                    Next RDSNo
                    //Loop through NCR list to see if there are any NCR's associated. If there are any NCR No's attached it is not eligible.
                    If Eligible then
                        For Each NCRNo in WMI_NCRNos using @VM
                            If NCRNo NE '' then
                                Eligible = False$
                            end
                        Until Eligible EQ False$
                        Next NCR
                    end
                    If Eligible then EligibleCassIds<1, -1> = CassId
                Next CassId
            Case EntityType EQ ''
                ErrorMessage = 'Entity type parameter was invalid.'
            Case Otherwise$
                ErrorMessage = 'Entity type parameter was null.'
        End Case
    end else
        ErrorMessage = 'WO# ' : WONo : ' not found in WO_LOG table.'
    end
    If ErrorMessage NE '' then
        Error_Services('Add', 'Error in ' : Service : '. ' : ErrorMessage)
    end
    
    Response = EligibleCassIds
    
end service


Service UpdateOpenWorkOrderStatuses()
    
    hSysLists = Database_Services('GetTableHandle', 'SYSLISTS')
    Lock hSysLists, ServiceKeyID then
        
        StatusName = Service
        IsProd     = Environment_Services('IsProd')
        If IsProd then
            MonaResource = 'GRP_OPENINSIGHT_MES_OP_FE_SERVICE_MANAGER'
        end else
            MonaResource = 'GRP_OPENINSIGHT_MES_OP_FE_DEV_SERVICE_MANAGER'
        end	    
        MonaStatus = 'ok'
        
        LogPath               = Environment_Services('GetApplicationRootPath') : '\LogFiles\WO_LOG'
        LogDate               = Oconv(Date(), 'D4/')
        LogTime               = Oconv(Time(), 'MTS')
        LogFileName           = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' Update Work Order Status Log.csv'
        Headers               = 'Logging DTM':@FM:'Server':@FM:'WONo':@FM:'Result'
        objVerifyWOMatKeysLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, COMMA$, Headers, '', False$, False$)
        LoggingDTM            = LogDate : ' ' : LogTime   ; // Logging DTM    
        Server                = Environment_Services('GetServer')
        
        LogData    = ''
        LogData<1> = LoggingDtm
        LogData<2> = Server
        LogData<4> = 'Begin ':Service
        Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM)   			    
        
        ErrorMsg      = ''
        OpenWoLogKeys = ''
        ErrCode       = ''
        Extract_Si_Keys('WO_LOG', 'CLOSE_DATE', '', OpenWoLogKeys)
        If Not(Get_Status(ErrCode)) then 
            If OpenWoLogKeys NE '' then
                OpenWoLogKeys     = SRP_Array('SortSimpleList', OpenWoLogKeys, 'AscendingNumbers', @VM)
                LastOpenWoUpdated = ''
                ServiceKey        = UCase(Service)
                If RowExists('APP_INFO', ServiceKey) then
                    LastOpenWoUpdated = Database_Services('ReadDataRow', 'APP_INFO', ServiceKey)
                end
                If (LastOpenWoUpdated NE '') then 
                    Locate LastOpenWoUpdated in OpenWoLogKeys using @VM setting vPos then
                        vPos += 1
                    end else
                        vPos = 1
                    end
                end else
                    vPos = 1
                end
                NextOpenWoLogKey = OpenWoLogKeys<0, vPos>
                Database_Services('WriteDataRow', 'APP_INFO', ServiceKey, NextOpenWoLogKey, True$, False$, False$)            	
                If NextOpenWoLogKey NE '' then
                    Work_Order_Services('UpdateWorkOrderStatus', NextOpenWoLogKey)
                    If Error_Services('HasError') then
                        LogData<1> = OConv(Datetime(), 'DT/^S')
                        ErrorMsg   = 'Error calling UpdateWorkOrderStatus for WO_LOG ':NextOpenWoLogKey:'. ':Error_Services('GetMessage')
                        LogData<4> = ErrorMsg
                        Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM)
                        MonaStatus = 'critical'                        
                    end                    
                end
            end else
                LogData<1> = OConv(Datetime(), 'DT/^S')
                LogData<4> = 'No open work orders to update.'
                Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM)	
            end
        end else
            MonaStatus = 'critical'
            ErrorMsg   = 'Error calling Extract_Si_Keys. Error code: ':ErrCode:'.'
            LogData<1> = OConv(Datetime(), 'DT/^S')
            LogData<4> = ErrorMsg
            Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM)        
        end
        
        LogData<1> = OConv(Datetime(), 'DT/^S')
        LogData<4> = 'End ':Service
        Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM)
        
        Mona_Services('PostStatus', MonaResource, StatusName, MonaStatus)
        
        Unlock hSysLists, ServiceKeyID else Null
    end
    
end service


Service UpdateWorkOrderStatus(WOLogId)
    
    StatusName = Service
    IsProd     = Environment_Services('IsProd')
    If IsProd then
        MonaResource = 'GRP_OPENINSIGHT_MES_OP_FE_SERVICE_MANAGER'
    end else
        MonaResource = 'GRP_OPENINSIGHT_MES_OP_FE_DEV_SERVICE_MANAGER'
    end	    
    
    Server       = Environment_Services('GetServer')
    LogPath      = Environment_Services('GetApplicationRootPath') : '\LogFiles\WO_LOG'
    LogDate      = Oconv(Date(), 'D4/')
    LogTime      = Oconv(Time(), 'MTS')
    LogFileName  = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' Update Work Order Status Log.csv'
    Headers      = 'Logging DTM' : @FM : 'Work Order No' : @FM : 'Log'
    objStatusLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$,',', Headers, '', False$, False$)    
    
    LogData     = ''
    LogData<1>  = OConv(Datetime(), 'DT/^S')
    LogData<2>  = Server
    LogData<3>  = 'Begin ':Service
    Logging_Services('AppendLog', objStatusLog, LogData, @RM, @FM)	    
    
    ErrorMsg = ''
    If WOLogID NE '' then
        If RowExists('WO_LOG', WOLogId) then
            WoLogRec = Database_Services('ReadDataRow', 'WO_LOG', WOLogId)
            If Error_Services('NoError') then 
                CurrStatus   = WoLogRec<WO_LOG_CURR_STATUS_STATIC$>
                NewStatus    = obj_WO_Log('CurrStatus', WOLogId:@RM:WoLogRec)
                If CurrStatus NE NewStatus then
                    LogData<1> = OConv(Datetime(), 'DT/^S')
                    LogData<2> = Server
                    LogData<3> = 'Status change from ':CurrStatus:' to ':NewStatus:' for WO_LOG ':WOLogId:'.'
                    Logging_Services('AppendLog', objStatusLog, LogData, @RM, @FM)	                        
                    Transaction_Services('PostWriteFieldTransaction', 'WO_LOG', WOLogId, WO_LOG_CURR_STATUS_STATIC$, NewStatus)
                    If Error_Services('NoError') then
                        LogData<1> = OConv(Datetime(), 'DT/^S')
                        LogData<2> = Server
                        LogData<3> = 'Successfully posted status change for WO_LOG ':WOLogId:' to the TRANSACTION_QUEUE.'
                        Logging_Services('AppendLog', objStatusLog, LogData, @RM, @FM)                            
                    end else
                        ErrorMsg   = 'Error posting status change for WO_LOG ':WOLogId:' to the TRANSACTION_QUEUE.'
                        LogData<1> = OConv(Datetime(), 'DT/^S')
                        LogData<2> = Server
                        LogData<3> = ErrorMsg
                        Logging_Services('AppendLog', objStatusLog, LogData, @RM, @FM)                                    
                    end                    
                end else
                    LogData<1> = OConv(Datetime(), 'DT/^S')
                    LogData<2> = Server
                    LogData<3> = 'No status change for WO_LOG ':WOLogId:'.'
                    Logging_Services('AppendLog', objStatusLog, LogData, @RM, @FM)	                    
                end
            end else
                ErrorMsg   = 'Error in ':Service:' service. Error reading WO_LOG ':WOLogId:' record. Error message: ':Error_Services('GetMessage')
                LogData<1> = OConv(Datetime(), 'DT/^S')
                LogData<2> = Server
                LogData<3> = ErrorMsg
                Logging_Services('AppendLog', objStatusLog, LogData, @RM, @FM)                 
            end
        end else
            ErrorMsg   = 'Error in ':Service:' service. WO_LOG ':WOLogId:' does not exist.'
            LogData<1> = OConv(Datetime(), 'DT/^S')
            LogData<2> = Server
            LogData<3> = ErrorMsg
            Logging_Services('AppendLog', objStatusLog, LogData, @RM, @FM) 
        end
    end else
        ErrorMsg   = 'Error in ':Service:' service. Null WOLogId passed into service.'
        LogData<1> = OConv(Datetime(), 'DT/^S')
        LogData<2> = Server
        LogData<3> = ErrorMsg
        Logging_Services('AppendLog', objStatusLog, LogData, @RM, @FM) 
    end
    
    LogData    = ''
    LogData<1> = OConv(Datetime(), 'DT/^S')
    LogData<2> = Server
    LogData<3> = 'End ':Service
    Logging_Services('AppendLog', objStatusLog, LogData, @RM, @FM)
    
    If ErrorMsg EQ '' then
        MonaStatus = 'ok'
    end else
        MonaStatus = 'critical'
    end
    
    Mona_Services('PostStatus', MonaResource, StatusName, MonaStatus)    
    
    If ErrorMsg EQ '' then
        Response = True$
    end else
        Response = False$
        Error_Services('Add', ErrorMsg)
    end
    
end service


Service UpdateOpenWorkOrderData()

	hSysLists = Database_Services('GetTableHandle', 'SYSLISTS')
	Lock hSysLists, ServiceKeyID then
	    
	    StatusName = Service
        IsProd     = Environment_Services('IsProd')
        If IsProd then
            MonaResource = 'GRP_OPENINSIGHT_MES_OP_FE_SERVICE_MANAGER'
        end else
            MonaResource = 'GRP_OPENINSIGHT_MES_OP_FE_DEV_SERVICE_MANAGER'
        end	    
	    MonaStatus = 'ok'
	    
        LogPath               = Environment_Services('GetApplicationRootPath') : '\LogFiles\WO_LOG'
        LogDate               = Oconv(Date(), 'D4/')
        LogTime               = Oconv(Time(), 'MTS')
        LogFileName           = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' Update Work Order Data Log.csv'
        Headers               = 'Logging DTM':@FM:'Server':@FM:'WONo':@FM:'Result'
        objVerifyWOMatKeysLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, COMMA$, Headers, '', False$, False$)
        LoggingDTM            = LogDate : ' ' : LogTime   ; // Logging DTM    
        Server                = Environment_Services('GetServer')
        
        LogData    = ''
        LogData<1> = LoggingDtm
        LogData<2> = Server
        LogData<4> = 'Begin ':Service
        Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM)   			    
        
        ErrorMsg      = ''
        OpenWoLogKeys = ''
        ErrCode       = ''
        Extract_Si_Keys('WO_LOG', 'CLOSE_DATE', '', OpenWoLogKeys)
        If Not(Get_Status(ErrCode)) then 
            If OpenWoLogKeys NE '' then
            	OpenWoLogKeys     = SRP_Array('SortSimpleList', OpenWoLogKeys, 'AscendingNumbers', @VM)
            	LastOpenWoUpdated = ''
            	ServiceKey        = UCase(Service)
            	If RowExists('APP_INFO', ServiceKey) then
            		LastOpenWoUpdated = Database_Services('ReadDataRow', 'APP_INFO', ServiceKey)
            	end
				If (LastOpenWoUpdated NE '') then 
					Locate LastOpenWoUpdated in OpenWoLogKeys using @VM setting vPos then
						vPos += 1
					end else
						vPos = 1
					end
				end else
					vPos = 1
				end
				NextOpenWoLogKey = OpenWoLogKeys<0, vPos>
				Database_Services('WriteDataRow', 'APP_INFO', ServiceKey, NextOpenWoLogKey, True$, False$, False$)            	
            	If NextOpenWoLogKey NE '' then
            	    Work_Order_Services('UpdateWorkOrderData', NextOpenWoLogKey)
                    If Error_Services('HasError') then
                        LogData<1> = OConv(Datetime(), 'DT/^S')
                        ErrorMsg   = 'Error calling UpdateWorkOrderData for WO_LOG ':NextOpenWoLogKey:'. ':Error_Services('GetMessage')
                        LogData<4> = ErrorMsg
                        Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM)
                        MonaStatus = 'critical'                        
                    end
            	    Work_Order_Services('UpdateReceivedQty', NextOpenWoLogKey)
                    If Error_Services('HasError') then
                        LogData<1> = OConv(Datetime(), 'DT/^S')
                        ErrorMsg   = 'Error calling UpdateReceivedQty for WO_LOG ':NextOpenWoLogKey:'. ':Error_Services('GetMessage')
                        LogData<4> = ErrorMsg
                        Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM)
                        MonaStatus = 'critical'                        
                    end
            	    Work_Order_Services('UpdateReleasedQty', NextOpenWoLogKey)
                    If Error_Services('HasError') then
                        LogData<1> = OConv(Datetime(), 'DT/^S')
                        ErrorMsg   = 'Error calling UpdateReleasedQty for WO_LOG ':NextOpenWoLogKey:'. ':Error_Services('GetMessage')
                        LogData<4> = ErrorMsg
                        Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM)
                        MonaStatus = 'critical'                        
                    end
            	    Work_Order_Services('UpdateScrappedQty', NextOpenWoLogKey)
                    If Error_Services('HasError') then
                        LogData<1> = OConv(Datetime(), 'DT/^S')
                        ErrorMsg   = 'Error calling UpdateScrappedQty for WO_LOG ':NextOpenWoLogKey:'. ':Error_Services('GetMessage')
                        LogData<4> = ErrorMsg
                        Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM)
                        MonaStatus = 'critical'                        
                    end
            	    Work_Order_Services('UpdateShippedQty', NextOpenWoLogKey)
                    If Error_Services('HasError') then
                        LogData<1> = OConv(Datetime(), 'DT/^S')
                        ErrorMsg   = 'Error calling UpdateShippedQty for WO_LOG ':NextOpenWoLogKey:'. ':Error_Services('GetMessage')
                        LogData<4> = ErrorMsg
                        Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM)
                        MonaStatus = 'critical'                        
                    end                      
            	end
            end else
                LogData<1> = OConv(Datetime(), 'DT/^S')
                LogData<4> = 'No open work orders to update.'
                Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM)	
            end
        end else
            MonaStatus = 'critical'
            ErrorMsg   = 'Error calling Extract_Si_Keys. Error code: ':ErrCode:'.'
            LogData<1> = OConv(Datetime(), 'DT/^S')
            LogData<4> = ErrorMsg
            Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM)        
        end
        
        LogData<1> = OConv(Datetime(), 'DT/^S')
        LogData<4> = 'End ':Service
        Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM)
        
        Mona_Services('PostStatus', MonaResource, StatusName, MonaStatus)
        
        Unlock hSysLists, ServiceKeyID else Null
    end
    
end service


Service UpdateWorkOrderData(WONo)
    
    hSysLists = Database_Services('GetTableHandle', 'SYSLISTS')
    Lock hSysLists, ServiceKeyID:'*':WONo then
        
        StatusName = Service
        IsProd     = Environment_Services('IsProd')
        If IsProd then
            MonaResource = 'GRP_OPENINSIGHT_MES_OP_FE_SERVICE_MANAGER'
        end else
            MonaResource = 'GRP_OPENINSIGHT_MES_OP_FE_DEV_SERVICE_MANAGER'
        end	    
        MonaStatus = 'ok'
        
        LogPath               = Environment_Services('GetApplicationRootPath') : '\LogFiles\WO_MAT'
        LogDate               = Oconv(Date(), 'D4/')
        LogTime               = Oconv(Time(), 'MTS')
        LogFileName           = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : '-' : WONo : ' WO_MAT WO_LOG{WO_MAT_KEY} Log.csv'
        Headers               = 'Logging DTM':@FM:'Server':@FM:'WONo':@FM:'Result'
        objVerifyWOMatKeysLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, COMMA$, Headers, '', False$, False$)
        LoggingDTM            = LogDate : ' ' : LogTime   ; // Logging DTM    
        Server                = Environment_Services('GetServer')
        
        LogData    = ''
        LogData<1> = LoggingDtm
        LogData<2> = Server
        LogData<3> = WONo
        LogData<4> = 'Begin ':Service
        Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM)   			    
        
        Done               = False$
        ErrorMsg           = ''
        OpenWoLogKeys      = ''
        ErrCode            = ''
        EpiPro             = (Xlate('WO_LOG', WONo, 'PS_REACTOR_TYPE', 'X') _EQC 'EPP')
        OrigWOLogWOMatKeys = Database_Services('ReadDataColumn', 'WO_LOG', WONo, WO_LOG_WO_MAT_KEY$, True$, 0)
        If Error_Services('NoError') then
            NewWOLogWOMatKeys = ''
            For CassNo = 1 to MAX_NUM_CASS$
                WOMatKey    = WONo:'*':CassNo
                If RowExists('WO_MAT', WOMatKey) then
                    If EpiPro then 
                        WMIKey = Xlate('WO_MAT', WOMatKey, 'WMI_KEY', 'X')
                        If (WMIKey EQ '') then
                            If RowExists('WM_IN', WONo:'*1*':CassNo) then
                                WMIKey = WONo:'*1*':CassNo
                                WM_In_Services('VerifyWOMatWMIKeyIndex', WMIKey)
                            end
                        end
                        If (WMIKey NE '') then
                            WM_In_Services('VerifyWoStepWMIKeyIndex', WMIKey)
                            WM_In_Services('VerifyWOLogWMIKeyIndex', WMIKey)
                        end
                        WMOKey = Xlate('WO_MAT', WOMatKey, 'WMO_KEY', 'X')
                        If (WMOKey EQ '') then
                            If RowExists('WM_OUT', WONo:'*1*':CassNo) then
                                WMOKey = WONo:'*1*':CassNo
                                WM_Out_Services('VerifyWOMatWMOKeyIndex', WMOKey)
                            end
                        end
                        If (WMOKey NE '') then
                            WM_Out_Services('VerifyWoStepWMOKeyIndex', WMOKey)
                            WM_Out_Services('VerifyWOLogWMOKeyIndex', WMOKey)
                        end
                    end 
                    RDSNo  = Xlate('WO_MAT', WOMatKey, 'RDS_NO', 'X')
                    If (RDSNo NE '') then
                        If (DCount(RDSNo, @VM) GT 1) then
                            NewRDSNo = ''
                            NumRDS   = 0
                            For each RDSKey in RDSNo using @VM setting vPos
                                If RowExists('RDS', RDSKey) then
                                    NumRDS   += 1
                                    NewRDSNo  = RDSKey
                                    RDS_Services('VerifyWOLogRDSKeyIndex', RDSNo)
                                    RDS_Services('VerifyWOMatRDSNoIndex', RDSNo)
                                    RDS_Services('VerifyWOStepRDSKeyIndex', RDSNo)                					
                                end
                            Next RDSKey
                            If (NumRDS EQ 1) then 
                                Transaction_Services('PostWriteFieldTransaction', 'WO_MAT', WOMatKey, WO_MAT_RDS_NO$, NewRDSNo)
                            end else
                                MonaStatus = 'critical'
                                LogData<1> = OConv(Datetime(), 'DT/^S')
                                LogData<4> = 'Multiple RDS records associated with WO_MAT ':WOMatKey:'.'
                                Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM)                 				
                            end
                        end else
                            RDS_Services('VerifyWOLogRDSKeyIndex', RDSNo)
                            RDS_Services('VerifyWOMatRDSNoIndex', RDSNo)
                            RDS_Services('VerifyWOStepRDSKeyIndex', RDSNo) 							
                        end
                    end else
                        If Not(EpiPro) then
                            Query   = 'SELECT RDS WITH WO EQ ':WONo:' AND WITH CASS_NO EQ ':CassNo
                            RList(Query, TARGET_ACTIVELIST$, '', '', '')
                            ErrCode = ''
                            If Not(Get_Status(ErrCode)) then
                                ReadNext RDSNo then
                                    RDS_Services('VerifyWOLogRDSKeyIndex', RDSNo)
                                    RDS_Services('VerifyWOMatRDSNoIndex', RDSNo)
                                    RDS_Services('VerifyWOStepRDSKeyIndex', RDSNo)									
                                end else
                                    MonaStatus = 'critical'
                                    LogData<1> = OConv(Datetime(), 'DT/^S')
                                    LogData<4> = 'No RDS found for WO_MAT ':WOMatKey:'.'
                                    Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM) 
                                end
                            end else
                                MonaStatus = 'critical'
                                LogData<1> = OConv(Datetime(), 'DT/^S')
                                LogData<4> = 'Error calling RList to find RDSNo associated with WO_MAT ':WOMatKey:'. Error code: ':ErrCode
                                Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM) 								
                            end
                        end                		
                    end
                    Voided = Xlate('WO_MAT', WOMatKey, 'VOID', 'X')
                    If Not(Voided) then NewWOLogWOMatKeys<0, -1> = WOMatKey
                end else
                    Done = True$
                end
            Until Done
            Next CassNo
            NewWOLogWOMatKeys = SRP_Array('SortRows', NewWOLogWOMatKeys, 'AR2', 'LIST', @VM, '*')
            If OrigWOLogWOMatKeys NE NewWOLogWOMatKeys then
                LogData<1>        = OConv(Datetime(), 'DT/^S')
                LogData<4>        = 'WOMatKeys column out of date. Posting transaction to update WO_MAT_KEY column.'
                Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM)                             
                Transaction_Services('PostWriteFieldTransaction', 'WO_LOG', WONo, WO_LOG_WO_MAT_KEY$, NewWOLogWOMatKeys)
                If Error_Services('HasError') then ErrorMsg = Error_Services('GetMessage')
            end else
                LogData<1> = OConv(Datetime(), 'DT/^S')
                LogData<4> = 'WOMatKeys column accurate. Nothing to update.'
                Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM)             	
            end
        end else
            ErrorMsg   = 'Error in ':Service:' service. ':Error_Services('GetMessage')
        end
        
        If ErrorMsg NE '' then
            LogData<1> = OConv(Datetime(), 'DT/^S')
            LogData<4> = ErrorMsg
            Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM)
            MonaStatus = 'critical'
        end                      
        
        LogData<1> = OConv(Datetime(), 'DT/^S')
        LogData<2> = Server
        LogData<4> = 'End ':Service
        Logging_Services('AppendLog', objVerifyWOMatKeysLog, LogData, @RM, @FM)
        
        Mona_Services('PostStatus', MonaResource, StatusName, MonaStatus)
        
        Unlock hSysLists, ServiceKeyID:'*':WONo else Null
    end
    
end service


Service GetWoLogZpl(WoNo)
    
    ZPL        = ''
    ErrorMsg = ''
    If WoNo NE '' then
        If RowExists('WO_LOG', WoNo) then
            WOStepKeys = XLATE('WO_LOG',WONo,WO_LOG_WO_STEP_KEY$,'X')
            IF INDEX(WOStepKeys,@VM,1) THEN
                PrintWONo = WONo:'.':WOStep
            END ELSE
                PrintWONo = WONo
            END
            WORec = XLATE('WO_LOG',WONo,'','X')
            ReqShipDate	= OCONV(WORec<WO_LOG_PROMISE_SHIP_DT$>,'D2/')
            CustNo		= WORec<WO_LOG_CUST_NO$>
            CustName	= XLATE('COMPANY',CustNo,'ABBREV_OR_CO_NAME','X')
            ZPL := '^XA^CFD'
            ZPL := '^LH0,0'
            ZPL := '^PR2'			;* Print speed 2 inches per second
            ZPL := '^LL325' 		;* Label length in dots
            ZPL := '^MD15'			;* Media darkness
            ZPL := '^PQ2'			;* Print 2 labels for each pass through here
            ZPL := '^MMT':CRLF$		;* Media mode t=tear off mode
            IF INDEX(PrintWONo,'.',1) THEN
                ZPL := '^FO10,30^A0,220,210^FD':PrintWONo:'^FS':CRLF$
            END ELSE
                ZPL := '^FO70,30^A0,220,210^FD':PrintWONo:'^FS':CRLF$	 		
            END
            CharCnt      = Len(CustName)
            NameLength   = CharCnt*72
            WhiteSpace   = INT(((2103 - NameLength)/2103) * 806)
            StartingXPos = INT(WhiteSpace/2)
            ZPL:= '^FO':StartingXPos:',230^A0,75,72^FD':CustName:'^FS':CRLF$
            ZPL:= '^FO160,320^A080,50^FD':'Commit Date: ':OCONV( XLATE( 'WO_LOG', WONo, WO_LOG_COMMIT_DATE$, 'X' ), 'D2/' ):'^FS':CRLF$
            ZPL:= '^XZ'
        end else
            ErrorMsg = 'Error in ':Service:' service. WO_LOG ':WoNo:' does not exist.'
        end
    end else
        ErrorMsg = 'Error in ':Service:' service. Null WoNo passed in.'
    end
    
    If ErrorMsg EQ '' then
        Response = ZPL
    end else
        Error_Services('Add', ErrorMsg)
    end    
    
end service


Service GetVoidedWaferCount(WorkOrderNo)
	
	ErrorMsg         = ''
	VoidedWaferCount = 0
	If WorkOrderNo NE '' then
		If RowExists('WO_LOG', WorkOrderNo) then
			ReactorType = Xlate('WO_LOG', WorkOrderNo, 'PS_REACTOR_TYPE', 'X')
			If ReactorType EQ 'EPP' then
				Query = 'SELECT WM_IN WITH WO_NO EQ ':WorkOrderNo:' AND WITH VOID EQ ':True$
				Rlist(Query, TARGET_ACTIVELIST$, '', '', '')
				ErrCode = ''
				If Not(Get_Status(ErrCode)) then
					EOF = False$
					Loop
						Readnext WmInKey else EOF = True$
					Until EOF
						VoidedWaferCount += Xlate('WM_IN', WmInKey, 'WFR_CNT', 'X')
					Repeat
				end else
					ErrorMsg = 'Error in ':Service: 'service. Error calling RList. Error code: ':ErrCode
				end				
			end else
				Query = 'SELECT WO_MAT WITH WO_NO EQ ':WorkOrderNo:' AND WITH VOID EQ ':True$
				Rlist(Query, TARGET_ACTIVELIST$, '', '', '')
				ErrCode = ''
				If Not(Get_Status(ErrCode)) then
					EOF = False$
					Loop
						Readnext WoMatKey else EOF = True$
					Until EOF
						VoidedWaferCount += Xlate('WO_MAT', WoMatKey, 'WAFER_QTY', 'X')
					Repeat
				end else
					ErrorMsg = 'Error in ':Service: 'service. Error calling RList. Error code: ':ErrCode
				end				
			end
		end else
			ErrorMsg = 'Error in ':Service: 'service. WO_LOG ':WorkOrderNo:' does not exist.'
		end
	end else
		ErrorMsg = 'Error in ':Service: 'service. Null WorkOrderNo passed into service.'
	end
	
	If ErrorMsg NE '' then Error_Services('Add', ErrorMsg)
	Response = VoidedWaferCount
	
end service


Service GetClosedWOsToArchive(ArchiveYears)
    
    ErrorMsg = ''
    WONos = ''
    ArchiveThresholdDate = SRP_Datetime('AddYears', Date(), - ArchiveYears)
    Open 'DICT.WO_LOG' to hWOLogDict then
        SearchString = 'CLOSE_DATE':@VM:'#':@FM
        SearchString := 'CLOSE_DATE':@VM:'<':ArchiveThresholdDate:@FM
        SearchString := 'WO_START_DTM':@VM:'#':@FM
        Btree.Extract(SearchString, 'WO_LOG', hWOLogDict, WONos)
        If Get_Status(errCode) then
            ErrorMsg = 'Error querying the WO_LOG table.'
        end
    end else
        ErrorMsg = 'Error opening WO_LOG dictionary.'
    end
    
    If ErrorMsg NE '' then
        Error_Services('Add', ErrorMsg)
    end
    
    Response = WONos
    
end service

Service GetWOLogHierachy(WOLogId)
    
    ErrorMsg = ''
    HierarchyKeys = ''
    
    If WOLogId NE '' then
        If RowExists('WO_LOG', WOLogId) then
            WOLogRec = Database_Services('ReadDataRow', 'WO_LOG', WOLogId, True$, 0, False$)
            If Error_Services('NoError') then
                WOMatKeys = Wo_Mat_Services('GetWOMatKeys', WOLogId)
                If Error_Services('NoError') then
                    NCRKeys = ''
                    for each WOMatKey in WOMatKeys using @VM
                        NCRKeys<1, -1> = Database_Services('ReadDataColumn', 'WO_MAT', WOMatKey, WO_MAT_NCR_KEYS$, True$, 0, False$)
                    Next WOMatKey
                    WOMatQAKeys = WOMatKeys
                    WOStepKey = WOLogRec<WO_LOG_WO_STEP_KEY$>
                    WMInKeys = Database_Services('ReadDataColumn', 'WO_STEP', WOStepKey, WO_STEP_WM_IN_KEYS$, True$, 0, False$)
                    if Error_Services('NoError') then
                        WMOutKeys = Database_Services('ReadDataColumn', 'WO_STEP', WOStepKey, WO_STEP_WM_OUT_KEYS$, True$, 0, False$)
                        if Error_Services('NoError') then
                            RDSKeys = RDS_Services('GetRDSKeys', WOLogId)
                            if Error_Services('NoError') then
                                ReactRunKeys = RDSKeys
                                RDSLayerKeys = ''
                                CleanInspKeys = ''
                                for each ReactRunKey in ReactRunKeys using @VM setting iPos
                                    ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', ReactRunKey, True$, 0, False$)
                                    If Error_Services('NoError') then
                                        CleanInspKeys<1, -1> = ReactRunRec<REACT_RUN_CI_NO$>
                                        RDSLayerKeys<1, -1> = ReactRunRec<REACT_RUN_RDS_LAYER_KEYS$>
                                    end else
                                        ErrorMsg = Error_Services('GetMessage')
                                    end
                                Next ReactRunKey
                                RDSTestKeys = ''
                                If ErrorMsg EQ '' then
                                    For each RDSLayerKey in RDSLayerKeys using @VM
                                        RDSLayerRec = Database_Services('ReadDataRow', 'RDS_LAYER', RDSLayerKey, True$, 0, False$)
                                        //If Error_Services('NoError') then
                                            ThisLayerRDSTestKeys = RDSLayerRec<RDS_LAYER_RDS_TEST_KEYS$>
                                            for each LayerRDSTestKey in ThisLayerRDSTestKeys using @VM
                                                If LayerRDSTestKey NE '' then
                                                    RDSTestKeys<1, -1> = LayerRDSTestKey
                                                end
                                            Next LayerRDSTestKey
                                            
*                                        end else
*                                            ErrorMsg = Error_Services('GetMessage')
*                                        end
                                    Next RDSLayerKey
                                    TWUseKeys = ''
                                end
                                NCRKeys = ''
                                TWUseKeys = ''
                                If ErrorMsg EQ '' then
                                    for each RDSTestKey in RDSTestKeys using @VM
                                        if RDSTestKey NE '' then
                                            RDSTestRec = Database_Services('ReadDataRow', 'RDS_TEST', RDSTestKey, True$, 0, False$)
                                            If Error_Services('NoError') then
                                                TWUseKeys<1,-1> = RDSTestRec<RDS_TEST_TW_USE_ID$>
                                            end else
                                                ErrorMsg = Error_Services('GetMessage')
                                            end
                                        end
                                    Next RDSTestKey
                                end
                                If ErrorMsg EQ '' then
                                    //ArchiveRecord
                                    //WOLogId
                                    If RowExists('WO_LOG', WOLogId) then
                                        HierarchyKeys<1, -1> = WOLogId
                                        HierarchyKeys<2, -1>  = 'WO_LOG'
                                    end
                                    //WOStepKey
                                    If RowExists('WO_STEP', WOStepKey) then
                                        HierarchyKeys<1, -1> = WOStepKey
                                        HierarchyKeys<2, -1>  = 'WO_STEP'
                                    end
                                    //WOMatKeys
                                    for each WOMatKey in WOMatKeys using @VM
                                        If RowExists('WO_MAT', WOMatKey) then
                                            HierarchyKeys<1, -1> = WOMatKey
                                            HierarchyKeys<2, -1> = 'WO_MAT'
                                        end
                                    Next WOMatKey
                                    //WOMatQAKeys
                                    for each WOMatQAKey in WOMatQAKeys using @VM
                                        If RowExists('WO_MAT_QA', WOMatQAKey) then
                                            HierarchyKeys<1, -1> = WOMatQAKey
                                            HierarchyKeys<2, -1> = 'WO_MAT_QA'
                                        end
                                    Next WOMatQAKey
                                    //WMInKeys (EpiPro Specific)
                                    for each WMInKey in WMInKeys using @VM
                                        If RowExists('WM_IN', WMInKey) then
                                            HierarchyKeys<1, -1> = WMInKey
                                            HierarchyKeys<2, -1> = 'WM_IN'
                                        end
                                    Next WMInKey
                                    //WMOutKeys (EpiPro Specific)
                                    for each WMOutKey in WMOutKeys using @VM
                                        Archive_Services('VerifyRelationalIndexes', 'WM_OUT', WMOutKey)
                                        if Error_Services('NoError') then
                                            If RowExists('WM_OUT', WMOutKey) then
                                                HierarchyKeys<1, -1> = WMOutKey
                                                HierarchyKeys<2, -1> = 'WM_OUT'
                                            end
                                        end else
                                            ErrorMsg = Error_Services('GetMessage')
                                        end
                                    Next WMOutKey
                                    //RDSKeys
                                    for each RDSKey in RDSKeys using @VM
                                        If RowExists('RDS', RDSKey) then
                                            HierarchyKeys<1, -1> = RDSKey
                                            HierarchyKeys<2, -1> = 'RDS'
                                        end
                                    Next RDSKey
                                    //ReactRunKeys
                                    for each ReactRunKey in ReactRunKeys using @VM
                                        If RowExists('REACT_RUN', ReactRunKey) then
                                            HierarchyKeys<1, -1> = ReactRunKey
                                            HierarchyKeys<2, -1> = 'REACT_RUN'
                                        end
                                    Next ReactRunKey
                                    //RDSLayerKeys
                                    for each RDSLayerKey in RDSLayerKeys using @VM
                                        If RowExists('RDS_LAYER', RDSLayerKey) then
                                            HierarchyKeys<1, -1> = RDSLayerKey
                                            HierarchyKeys<2, -1> = 'RDS_LAYER'
                                        end
                                    Next RDSLayerKey
                                    //CleanInspKeys
                                    for each CleanInspKey in CleanInspKeys using @VM
                                        If RowExists('CLEAN_INSP', CleanInspKey) then
                                            HierarchyKeys<1, -1> = CleanInspKey
                                            HierarchyKeys<2, -1> = 'CLEAN_INSP'
                                        end
                                    Next CleanInspKey
                                    //RDSTestKeys
                                    for each RDSTestKey in RDSTestKeys using @VM
                                        If RowExists('RDS_TEST', RDSTestKey) then
                                            HierarchyKeys<1, -1> = RDSTestKey
                                            HierarchyKeys<2, -1> = 'RDS_TEST'
                                        end
                                    Next RDSTestKey
                                    //NCRKeys
                                    for each NCRKey in NCRKeys using @VM
                                        if NCRKey NE '' then
                                            If RowExists('NCR', NCRKey) then
                                                HierarchyKeys<1, -1> = NCRKey
                                                HierarchyKeys<2, -1> = 'NCR'
                                            end
                                        end
                                    Next NCRKey
                                    //TWUseKeys
                                    for each TWUseKey in TWUseKeys using @VM
                                        if TWUseKey NE '' then
                                            Archive_Services('VerifyRelationalIndexes', 'TW_USE', TWUseKey)
                                            if Error_Services('NoError') then
                                                If RowExists('TW_USE', TWUseKey) then
                                                    HierarchyKeys<1, -1> = TWUseKey
                                                    HierarchyKeys<2, -1> = 'TW_USE'
                                                end
                                            end else
                                                ErrorMsg = Error_Services('GetMessage')
                                            end 
                                            //end
                                        end
                                    Next TWUseKey
                                end
                            end else
                                ErrorMsg = Error_Services('GetMessage')
                            end
                        end else
                            ErrorMsg = Error_Services('GetMessage')
                        end
                    end else
                        ErrorMsg = Error_Services('GetMessage')
                    end
                end else
                    ErrorMsg = Error_Services('GetMessage')
                end
            end else
                ErrorMsg = Error_Services('GetMessage')
            end
        end else
            ErrorMsg = 'WO_LOG record not found in WO_LOG table.'
        end
    end else
        ErrorMsg = 'WO_LOG ID was null.'
    end
    
    If ErrorMsg EQ '' then
        Response = HierarchyKeys
    end else
        Error_Services('Add', ErrorMsg)
    end
    
end service

Service GetWOMetaData(WOLogId)
    
    ErrorMsg = ''
    WOMetaData = ''
    
    
    WOLogRec = Database_Services('ReadDataRow', 'WO_LOG', WOLogId, True$, 0, False$)
    If Error_Services('NoError') then
        // Meta Data field Variables
        CustNo = WOLogRec<WO_LOG_CUST_NO$>
        CustPartNo = WOLogRec<WO_LOG_CUST_PART_NO$>
        EpiPartNo = WOLogRec<WO_LOG_EPI_PART_NO$>
        ProdVerNo = WOLogRec<WO_LOG_PROD_VER_NO$>
        ProdSpecNo = XLATE('PROD_VER', ProdVerNo, PROD_VER_PROC_STEP_PSN$, 'X') 
        SubstratePartNo = WOLogRec<WO_LOG_ORD_SUB_PART_NO$>
        WOStartDate = WOLogRec<WO_LOG_ENTRY_DATE$>
        WOCloseDate = WOLogRec<WO_LOG_CLOSE_DATE$>
        ProdOrdNo = WOLogRec<WO_LOG_PROD_ORD_NO$>
        
        MetaDataFields = 'CUST_NO' : @VM : 'CUST_PART_NO' : @VM : 'EPI_PART_NO' : @VM : 'PROD_VER_NO' : @VM : 'PSN' : @VM : 'SUB_PART_NO' : @VM : 'ENTRY_DATE' : @VM : 'CLOSE_DATE' : @VM : 'PROD_ORD_NO'
        MetaDataTypes = 'string' : @VM : 'string' : @VM : 'string' : @VM : 'string' : @VM : 'string' : @VM : 'string' : @VM : 'DateTime' : @VM : 'DateTime' : @VM : 'string'
        MetaDataValues = CustNo : @VM :  CustPartNo : @VM :  EpiPartNo : @VM :  ProdVerNo : @VM :  ProdSpecNo : @VM :  SubstratePartNo : @VM :  WOStartDate : @VM :  WOCloseDate : @VM :  ProdOrdNo
        
        // Loop through the data and add only those fields that have a value.
        for i = 1 to DCount(MetaDataFields, @VM)
            if MetaDataValues<1, i> NE '' then
                WOMetaData<1, i> = MetaDataFields<1, i>
                WOMetaData<2, i> = MetaDataTypes<1, i>
                WOMetaData<3, i> = MetaDataValues<1, i>
            end
        Next i
        
    end else
        ErrorMsg = Error_Services('GetMessage')
    end
    
    
    If ErrorMsg EQ '' then
        Response = WOMetaData
    end else
        Error_Services('Add', ErrorMsg)
    end
    
end service

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Internal GoSubs
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

ClearCursors:
    
    For counter = 0 to 8
        ClearSelect counter
    Next counter
    
return




