Function WO_LOG_Actions(Action, CalcColName, FSList, Handle, Name, FMC, Record, Status, OrigRecord, Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, Param9, Param10)
#pragma precomp SRP_PreCompiler

/***********************************************************************************************************************

    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 Infineon.

    Name        :   WO_LOG_Actions

    Description :   Handles calculated columns and MFS calls for the current table.

    Notes       :   This function uses @ID, @RECORD, and @DICT to make sure {ColumnName} references work correctly.
                    If called from outside of a calculated column these will need to be set and restored.

    Parameters  :
        Action          [in] -- Name of the action to be taken
        CalcColName     [in] -- Name of the calculated column that needs to be processed. Normally this should only be
                                populated when the CalcField action is being used.
        FSList          [in] -- The list of MFSs and the BFS name for the current file or volume. This is an @SVM
                                delimited array, with the current MFS name as the first value in the array, and the BFS
                                name as the last value. Normally set by a calling MFS.
        Handle          [in] -- The file handle of the file or media map being accessed. Note, this does contain the
                                entire handle structure that the Basic+ Open statement would provide. Normally set by a
                                calling MFS.
        Name            [in] -- The name (key) of the record or file being accessed. Normally set by a calling MFS.
        FMC             [in] -- Various functions. Normally set by a calling MFS.
        Record          [in] -- The entire record (for record-oriented functions) or a newly-created handle (for
                                "get handle" functions). Normally set by a calling MFS.
        Status      [in/out] -- Indicator of the success or failure of an action. Normally set by the calling MFS but
                                for some actions can be set by the action handler to indicate failure.
        OrigRecord      [in] -- Original content of the record being processed by the current action. This is
                                automatically being assigned by the WRITE_RECORD and DELETE_RECORD actions within
                                BASE_MFS.
        Param1-10   [in/out] -- Additional request parameter holders
        ActionFlow     [out] -- Used to control the action chain (see the ACTION_SETUP insert for more information.)
                                Can also be used to return a special value, such as the results of the CalcField
                                method.

    History     :   (Date, Initials, Notes)
        07/28/10    dmb     Original programmer.
        10/13/10    dmb     Fix logic to extract the file handle if file has an index
        03/26/11    dmb     Add logic to save and restore @FILE.ERROR

***********************************************************************************************************************/

$insert APP_INSERTS
$insert FILE.SYSTEM.EQUATES
$insert ACTION_SETUP
$insert WO_MAT_EQUATES
$insert WO_LOG_EQUATES
$insert WO_STEP_EQUATES
$insert RLIST_EQUATES
$insert SCHED_DET_NG_EQUATES

Equ Comma$ to ','

Declare function   Error_Services, Database_Services, Environment_Services, Logging_Services, obj_WO_Mat, Datetime
Declare function   GaN_Services, obj_WO_Log
Declare subroutine Error_Services, Database_Services, Environment_Services, Logging_Services, Obj_SAP
Declare subroutine Logging_Services, Set_Status, Schedule_Services, Work_Order_Services, obj_Notes
Declare subroutine Schedule_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] : ' Work Order Log.csv'
Headers     = 'Logging DTM' : @FM : 'WO No' : @FM : 'Notes'
objLog      = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, Comma$, Headers, '', False$, False$)
LoggingDTM  = LogDate : ' ' : LogTime   ; // Logging DTM

LogFileName  = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' Delete Attempt Log.csv'
Headers      = 'Logging DTM' : @FM : 'WO No' : @FM : 'Notes'
objDeleteLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, Comma$, Headers, '', False$, False$)

LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' WO_HOLD Log.csv'
Headers     = 'Logging DTM' : @FM : 'WO No' : @FM : 'User'
objHoldLog  = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, Comma$, Headers, '', False$, False$)

If KeyID then GoSub Initialize_System_Variables

Begin Case

    Case Action _EQC 'CalculateColumn'      ;   GoSub CalculateColumn
    Case Action _EQC 'READ_RECORD_PRE'      ;   GoSub READ_RECORD_PRE
    Case Action _EQC 'READ_RECORD'          ;   GoSub READ_RECORD
    Case Action _EQC 'READONLY_RECORD_PRE'  ;   GoSub READONLY_RECORD_PRE
    Case Action _EQC 'READONLY_RECORD'      ;   GoSub READONLY_RECORD
    Case Action _EQC 'WRITE_RECORD_PRE'     ;   GoSub WRITE_RECORD_PRE
    Case Action _EQC 'WRITE_RECORD'         ;   GoSub WRITE_RECORD
    Case Action _EQC 'DELETE_RECORD_PRE'    ;   GoSub DELETE_RECORD_PRE
    Case Action _EQC 'DELETE_RECORD'        ;   GoSub DELETE_RECORD
    Case Otherwise$                         ;   Status = 'Invalid Action'

End Case

If KeyID then GoSub Restore_System_Variables

If Assigned(ActionFlow) else ActionFlow = ACTION_CONTINUE$

Return ActionFlow


// ----- Calculated Columns --------------------------------------------------------------------------------------------
//
// The typical structure of a calculated column will look like this:
//
// Declare function TableName_Actions
//
// A = {COL1} ; * Reference as many data columns in this way to ensure the dictionary dependency is generated.
//
// @ANS = TableName_Actions('CalcField', 'CalcColName')
//
// ---------------------------------------------------------------------------------------------------------------------

CalculateColumn:

    // Make sure the ActionFlow return variable is cleared in case nothing is calculated.
    ActionFlow = ''

    Begin Case
        Case CalcColName EQ 'GAN_COST_TYPE'              ; GoSub GAN_COST_TYPE
        Case CalcColName EQ 'SAP_YIELD_QTY'              ; GoSub SAP_YIELD_QTY

    End Case

return


GAN_COST_TYPE:
    
    EpiStruct = {EPI_STRUCTURE}
    Begin Case
        Case ( (EpiStruct EQ 'HV') or (IndexC(EpiStruct, 'HEKLA', 1) GT 0) )
            ActionFlow = 'High Voltage'
        Case EpiStruct EQ 'MV'
            ActionFlow = 'Mid Voltage'
        Case Otherwise$
            ActionFlow = ''
    End Case
    
return


SAP_YIELD_QTY:

    If {REACTOR_TYPE} NE 'GAN' then
        ActionFlow = SUM({WO_MAT_SAP_YIELD})
    end else
        WOStepRec = Database_Services('ReadDataRow', 'WO_STEP', {WO_STEP_KEY})
        WOYield   = 0
        
        RDSKeys = WOStepRec<WO_STEP_RDS_KEY$>
        For each RDSKey in RDSKeys using @VM
            RDSYieldInfo = GaN_Services('GetYieldInfo', RDSKey)
            GRWfrQty  = RDSYieldInfo<1>
            ScrapQty  = RDSYieldInfo<2>
            ProdTWQty = RDSYieldInfo<3>
            DummyQty  = RDSYieldInfo<4>
            RDSYield  = GRWfrQty - ScrapQty - ProdTWQty - DummyQty
            WOYield   += RDSYield
        Next RDSKey
    end
    ActionFlow = WOYield
    
return


// ----- MFS calls -----------------------------------------------------------------------------------------------------

READ_RECORD_PRE:
    // In order to stop a record from being read in this action these lines of code must be used:
    //
    //    OrigFileError   = 100 : @FM : KeyID
    //    Status          = 0
    //    Record          = ''
    //    ActionFlow      = ACTION_STOP$
return


READ_RECORD:
    // In order to stop a record from being read in this action these lines of code must be used:
    //
    //    OrigFileError   = 100 : @FM : KeyID
    //    Status          = 0
    //    Record          = ''
return

READONLY_RECORD_PRE:
    // In order to stop a record from being read in this action these lines of code must be used:
    //
    //    OrigFileError   = 100 : @FM : KeyID
    //    Status          = 0
    //    Record          = ''
    //    ActionFlow      = ACTION_STOP$
return

READONLY_RECORD:
    // In order to stop a record from being read in this action these lines of code must be used:
    //
    //    OrigFileError   = 100 : @FM : KeyID
    //    Status          = 0
    //    Record          = ''
return

WRITE_RECORD_PRE:

    OrigQty      = OrigRecord<WO_LOG_QTY$>
    NewQty       = Record<WO_LOG_QTY$>
    OrigCassList = OrigRecord<WO_LOG_WO_MAT_KEY$>
    NewCassList  = Record<WO_LOG_WO_MAT_KEY$>
    If ( (OrigQty NE NewQty) or (OrigCassList NE NewCassList) ) then
        // Update released, unreleased, and received quantities
        RelQty                         = obj_WO_Log('RelQty', Name:@RM:Record)
        Record<WO_LOG_REL_QTY_STATIC$> = RelQty
        If RelQty GT NewQty then
            UnRelQty = 0
        end else
            UnRelQty = NewQty - RelQty
        end
        Record<WO_LOG_UNREL_QTY_STATIC$> = UnRelQty
        
        RXQty                         = obj_WO_Log('RxQty', Name:@RM:Record)
        Record<WO_LOG_RX_QTY_STATIC$> = RXQty
        SaveRecord                    = Record
    end
    
return

WRITE_RECORD:

    Machine = Environment_Services('GetServer')
    
    If OrigRecord<WO_LOG_WO_HOLD$> NE Record<WO_LOG_WO_HOLD$> then
        // Log the user changing this flag.
        LogData    = ''
        LogData<1> = LoggingDTM
        LogData<2> = Name
        LogData<3> = @User4
        Message    = @User4:' changed WO_HOLD flag for work order ':Name
        Logging_Services('AppendLog', objHoldLog, LogData, @RM, @FM)
    end

    If ( (OrigRecord<WO_LOG_LAST_CHAR_RUN$> NE '') and (Record<WO_LOG_LAST_CHAR_RUN$> EQ '') and (Machine _NEC 'MESSA01EC') ) then
        Stack = RetStack()
        LogData = ''
        LogData<1> = LoggingDTM
        LogData<2> = Name
        LogData<3> = Stack
        Convert @FM to CRLF$ in Stack
        Message = 'Last char run erased on work order ':Name:'.':CRLF$:CRLF$:'Call Stack':CRLF$:Stack
        Logging_Services('AppendLog', objLog, LogData, @RM, @FM, '', '', '')
    end

    OrigQty       = OrigRecord<WO_LOG_QTY$>
    NewQty        = Record<WO_LOG_QTY$>
    ModifyQty     = NewQty - OrigQty
    WONo          = Name
    SchedDetNGKey = ''

    Begin Case
        Case ModifyQty GT 0
            
            LogData    = ''
            LogData<1> = OConv(Datetime(), 'DT2/^H')
            LogData<2> = 'WO_LOG_ACTIONS'
            LogData<3> = 'Work order ':WONo:' quantity increased from ':OrigQty:' to ':NewQty:'.'
            Schedule_Services('LogActivity', '', LogData, False$)
            
            LogData<1> = OConv(Datetime(), 'DT2/^H')
            LogData<3> = 'Searching for a scheduled event to increase the quantity.'
            Schedule_Services('LogActivity', '', LogData, False$)
            
            // Search schedule for most recent event and increase that event quantity by the difference.
            Query  = 'SELECT SCHED_DET_NG WITH WO_NO EQ ':WONo:' BY-DSND STOP_DTM'                  
            GoSub ClearCursors
            Set_Status(0)
            RList(Query, TARGET_ACTIVELIST$, '', '', False$)
            errCode       = ''
            If Get_Status(errCode) then
                
                ErrorMsg = 'Error retrieving event list in WO_LOG_Actions. RList error code ':errCode:'.'
                
                LogData<1> = OConv(Datetime(), 'DT2/^H')
                LogData<3> = ErrorMsg
                Schedule_Services('LogActivity', '', LogData, False$)
                
                Error_Services('Add', ErrorMsg)
                
            end else
                
                EOF = False$
                
                LogData<1> = OConv(Datetime(), 'DT2/^H')
                LogData<3> = 'Query successful. Number of keys found: ':@RecCount:'.'
                Schedule_Services('LogActivity', '', LogData, False$)
                
                ReadNext SchedDetNGKey else EOF = True$
                If SchedDetNGKey NE '' then
                    EventRec    = Database_Services('ReadDataRow', 'SCHED_DET_NG', SchedDetNGKey)
                    StopDTM     = EventRec<SCHED_DET_NG.STOP_DTM$>
                    
                    LogData<1> = OConv(Datetime(), 'DT2/^H')
                    LogData<3> = 'Work order record ':WONo:' quantity increased from ':OrigQty:' to ':NewQty:'.'
                    Schedule_Services('LogActivity', EventRec<SCHED_DET_NG.REACT_NO$>, LogData, False$)
                    
                    LogData<1> = OConv(Datetime(), 'DT2/^H')
                    LogData<3> = 'Scheduled event ':SchedDetNGKey:' found.'
                    Schedule_Services('LogActivity', EventRec<SCHED_DET_NG.REACT_NO$>, LogData, False$)
                    
                    // Only modify the event if it is still running or is scheduled in the future.
                    If StopDTM GT Datetime() then
                        
                        LogData<1> = OConv(Datetime(), 'DT2/^H')
                        LogData<3> = 'Event stop DTM ':OConv(StopDtm, 'DT2/^H'):' less than current DTM, so increasing event quantity.'
                        Schedule_Services('LogActivity', EventRec<SCHED_DET_NG.REACT_NO$>, LogData, False$)

                        ReactNo      = EventRec<SCHED_DET_NG.REACT_NO$>
                        WONo         = EventRec<SCHED_DET_NG.WO_NO$>
                        StartDTM     = EventRec<SCHED_DET_NG.START_DTM$>
                        Desc         = EventRec<SCHED_DET_NG.DESC$>
                        EventQty     = EventRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$>
                        NewEventQty  = EventQty + ModifyQty
                        Schedule_Services('ModifySchedEvent', SchedDetNGKey, ReactNo, WONo, StartDTM, StopDTM, Desc, NewEventQty)
                        // Adjust reactor events as needed.
                        Schedule_Services('AdjustScheduleEvents', ReactNo)
                        
                    end else
                        
                        LogData<1> = OConv(Datetime(), 'DT2/^H')
                        LogData<3> = 'Event stop DTM ':OConv(StopDtm, 'DT2/^H'):' greater than current DTM.'
                        Schedule_Services('LogActivity', EventRec<SCHED_DET_NG.REACT_NO$>, LogData, False$)
                        
                        LogData<1> = OConv(Datetime(), 'DT2/^H')
                        LogData<3> = 'Event quantity not increased.'
                        Schedule_Services('LogActivity', EventRec<SCHED_DET_NG.REACT_NO$>, LogData, False$)
                        
                    end
                end
            end
            GoSub ClearCursors
        Case ModifyQty LT 0
            
            LogData    = ''
            LogData<1> = OConv(Datetime(), 'DT2/^H')
            LogData<2> = 'WO_LOG_ACTIONS'
            LogData<3> = 'Work order ':WONo:' quantity decreased from ':OrigQty:' to ':NewQty:'.'
            Schedule_Services('LogActivity', '', LogData, False$)
            
            LogData<1> = OConv(Datetime(), 'DT2/^H')
            LogData<3> = 'Searching for a scheduled event to decrease the quantity.'
            Schedule_Services('LogActivity', '', LogData, False$)
            
            // Search schedule for most recent events and decrease/delete those events as needed.
            Query  = 'SELECT SCHED_DET_NG WITH WO_NO EQ ':WONo:' BY-DSND STOP_DTM'                  
            GoSub ClearCursors
            Set_Status(0)
            RList(Query, TARGET_ACTIVELIST$, '', '', False$)
            errCode       = ''
            
            If Get_Status(errCode) then
                
                ErrorMsg = 'Error retrieving event list in WO_LOG_Actions. RList error code ':errCode:'.'
                
                LogData<1> = OConv(Datetime(), 'DT2/^H')
                LogData<3> = ErrorMsg
                Schedule_Services('LogActivity', '', LogData, False$)
                
                Error_Services('Add', ErrorMsg)
                
            end else
                
                LogData<1> = OConv(Datetime(), 'DT2/^H')
                LogData<3> = 'Query successful. Number of keys found: ':@RecCount:'.'
                Schedule_Services('LogActivity', '', LogData, False$)
                
                EOF       = False$
                Done      = False$
                UpdateReq = False$
                Loop
                ReadNext SchedDetNGKey else EOF = True$
                Until EOF EQ True$
                    If SchedDetNGKey NE '' then
                        EventRec    = Database_Services('ReadDataRow', 'SCHED_DET_NG', SchedDetNGKey)
                        StopDTM   = EventRec<SCHED_DET_NG.STOP_DTM$>
                        
                        LogData<1> = OConv(Datetime(), 'DT2/^H')
                        LogData<3> = 'Work order record ':WONo:' quantity decreased from ':OrigQty:' to ':NewQty:'.'
                        Schedule_Services('LogActivity', EventRec<SCHED_DET_NG.REACT_NO$>, LogData, False$)
                        
                        LogData<1> = OConv(Datetime(), 'DT2/^H')
                        LogData<3> = 'Scheduled event ':SchedDetNGKey:' found.'
                        Schedule_Services('LogActivity', EventRec<SCHED_DET_NG.REACT_NO$>, LogData, False$)
                        
                        // Only modify the event if it is still running or is scheduled in the future.
                        If StopDTM GT Datetime() then
                            
                            LogData<1> = OConv(Datetime(), 'DT2/^H')
                            LogData<3> = 'Event stop DTM ':OConv(StopDtm, 'DT2/^H'):' less than current DTM, so decreasing event quantity.'
                            Schedule_Services('LogActivity', EventRec<SCHED_DET_NG.REACT_NO$>, LogData, False$)
                            
                            UpdateReq = True$
                            ReactNo   = EventRec<SCHED_DET_NG.REACT_NO$>
                            WONo      = EventRec<SCHED_DET_NG.WO_NO$>
                            StartDTM  = EventRec<SCHED_DET_NG.START_DTM$>
                            Desc      = EventRec<SCHED_DET_NG.DESC$>
                            EventQty  = EventRec<SCHED_DET_NG.EVENT_TOTAL_WFRS$>
                            If EventQty GT Abs(ModifyQty) then
                                
                                // This event has enough wafers, so just reduce the quantity.
                                LogData<1> = OConv(Datetime(), 'DT2/^H')
                                LogData<3> = 'Event quantity ':EventQty:' greater than quantity reduction ':ModifyQty:', so decreasing event quantity.'
                                Schedule_Services('LogActivity', EventRec<SCHED_DET_NG.REACT_NO$>, LogData, False$)
                                
                                Done        = True$
                                NewEventQty = EventQty + ModifyQty
                                Schedule_Services('ModifySchedEvent', SchedDetNGKey, ReactNo, WONo, StartDTM, StopDTM, Desc, NewEventQty)
                                
                            end else
                                
                                // This event has fewer wafers than are being removed. Delete this event and continue
                                // onto the next event.
                                LogData<1> = OConv(Datetime(), 'DT2/^H')
                                LogData<3> = 'Event quantity ':EventQty:' less than quantity reduction ':ModifyQty:', so canceling event.'
                                Schedule_Services('LogActivity', EventRec<SCHED_DET_NG.REACT_NO$>, LogData, False$)
                                
                                Schedule_Services('CancelScheduleEvent', SchedDetNGKey, True$)
                                ModifyQty += EventQty
                                
                            end
                        end else
                            
                            LogData<1> = OConv(Datetime(), 'DT2/^H')
                            LogData<3> = 'Event stop DTM ':OConv(StopDtm, 'DT2/^H'):' greater than current DTM.'
                            Schedule_Services('LogActivity', EventRec<SCHED_DET_NG.REACT_NO$>, LogData, False$)
                            
                            LogData<1> = OConv(Datetime(), 'DT2/^H')
                            LogData<3> = 'Event quantity not decreased.'
                            Schedule_Services('LogActivity', EventRec<SCHED_DET_NG.REACT_NO$>, LogData, False$)
                            
                        end
                    end
                Until Done EQ True$
                Repeat
                If UpdateReq then
                    // Adjust reactor events as needed.
                    Schedule_Services('AdjustScheduleEvents', ReactNo)
                end
            end
            GoSub ClearCursors
            
        Case Otherwise$
            Null
    End Case   
    
return

DELETE_RECORD_PRE:

    // Log which user and computer station attempted to delete the record
    LogData    = ''
    LogData<1> = LoggingDTM
    LogData<2> = Name
    LogData<3> = @User4
    Logging_Services('AppendLog', objDeleteLog, LogData, @RM, @FM)
    
    // Send an LSL message to FI admins to alert them
    Recipients   = Xlate('SEC_GROUPS', 'OI_ADMIN', 'USER', 'X')
    SentFrom     = 'SYSTEM'
    Subject      = 'WO_LOG Delete Attempt'
    Message      = 'An attempt to delete WO_LOG record ':Name:' was made by ':@User4:'.'
    AttachWindow = 'NDW_WO_LOG'
    AttachKey    = Name
    SendToGroup  = ''

    Parms = Recipients:@RM:SentFrom:@RM:Subject:@RM:Message:@RM:AttachWindow:@RM:AttachKey:@RM:SendToGroup
    obj_Notes('Create',Parms)
    
    // Stop the system from deleting the record
    ActionFlow = ACTION_STOP$
        
return

DELETE_RECORD:
return


// ----- Internal Methods ----------------------------------------------------------------------------------------------

Initialize_System_Variables:
    // Save these for restoration later
    SaveDict        = @DICT
    SaveID          = @ID
    SaveRecord      = @RECORD
    OrigFileError   = @FILE.ERROR

    // Now make sure @DICT, ID, and @RECORD are populated
    CurrentDictName = ''
    If @DICT then
        DictHandle = @DICT<1, 2>
        Locate DictHandle in @TABLES(5) Using @FM Setting fPos then
            CurrentDictName = Field(@TABLES(0), @FM, fPos, 1)
        end
    end

    If CurrentDictName NE DictName then
         Open DictName to @DICT else Status = 'Unable to initialize @DICT'
    end    

    @ID = KeyID
    If Record else
        // Record might not have been passed in. Read the record from the database table just to make sure.
        @FILE.ERROR     = ''
        Open TableName to hTable then
            FullFSList  = hTable[1, 'F' : @VM]
            BFS         = FullFSList[-1, 'B' : @SVM]
            LastHandle  = hTable[-1, 'B' : \0D\]
            FileHandle  = \0D\ : LastHandle[1, @VM]

            Call @BFS(READO.RECORD, BFS, FileHandle, KeyID, FMC, Record, ReadOStatus)
        end
    end
    @RECORD = Record
return

Restore_System_Variables:
    Transfer SaveDict   to @DICT
    Transfer SaveID     to @ID
    Transfer SaveRecord to @RECORD
    @FILE.ERROR = OrigFileError
return

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


