Files
open-insight/LSL2/STPROC/WO_MAT_ACTIONS.txt
Mitchem Dakota (CSC FI SPS MESLEO External) 273b7f67a6 Merged PR 19143: Require 100 percent centerpoint on MU conversion.
Initial commit.

Create FQASignatureReady service in QA_SERVICES.

Create SignFQA service in SIGNATURE_SERVICES.

Commit remaining portion of project.

Implement changes requested in review meeting.

Fix typo. Add new MU logic to final entry point.

Restrict logic to only apply to 'THICK'
inspections.

Bypass new logic if Biorad 4 and 5 are down.
2025-06-18 22:23:57 +02:00

905 lines
40 KiB
Plaintext

Function WO_MAT_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_MAT_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 MAKEUP_WAFERS_EQUATES
$insert RLIST_EQUATES
Equ Comma$ to ','
Declare function Error_Services, Database_Services, Environment_Services, Logging_Services, obj_WO_Mat, Max
Declare function GaN_Services, Signature_Services, obj_WO_LOG, SRP_Array, MemberOf, Datetime
Declare subroutine Error_Services, Database_Services, Environment_Services, Logging_Services, Obj_SAP, obj_Notes
Declare subroutine SAP_Services, Material_Services, RList, Work_Order_Services, Service_Services, Set_Status
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] : ' Work Order Log.csv'
Headers = 'Logging DTM' : @FM : 'User' : @FM : 'WOMatKeyID' : @FM : 'HotLot' : @FM : 'HotLotOrig' : @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] : ' Work Order Material.csv'
Headers = 'Logging DTM' : @FM : 'User' : @FM : 'WOMatKeyID' : @FM : 'Notes'
objLog2 = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, Comma$, Headers, '', False$, False$)
LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' RDS Erase Attempt.csv'
Headers = 'Logging DTM' : @FM : 'User' : @FM : 'RDSNo' : @FM : 'WOMatKeyID' : @FM : 'RDS<WO_MAT_KEY>' : @FM : 'Error'
objLog3 = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, Comma$, Headers, '', False$, False$)
LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' NCR Slot List.csv'
Headers = 'Logging DTM' : @FM : 'User' : @FM : 'WOMatKeyID' : @FM : 'NCR by Slot' : @FM : 'Stack'
objNCRLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, Comma$, Headers, '', False$, False$)
LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' Slot List Error.csv'
Headers = 'Logging DTM' : @FM : 'User' : @FM : 'WOMatKeyID' : @FM : 'Last Slot' : @FM : 'Stack'
objSlotLog = 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 'CURR_LOCATION' ; GoSub CURR_LOCATION
Case CalcColName EQ 'HOT_LOT_FLAG' ; GoSub HOT_LOT_FLAG
Case CalcColName EQ 'LOAD_WFR_CNT' ; GoSub LOAD_WFR_CNT
Case CalcColName EQ 'SAP_YIELD' ; GoSub SAP_YIELD
Case CalcColName EQ 'WO_REACT_TYPE' ; GoSub WO_REACT_TYPE
Case CalcColName EQ 'FINAL_SIG_DT' ; GoSub FINAL_SIG_DT
Case CalcColName EQ 'FINAL_SIG_DTM' ; GoSub FINAL_SIG_DTM
End Case
return
CURR_LOCATION:
LastWH = @RECORD<6>[-1,'B':@VM]
LastLoc = @RECORD<7>[-1,'B':@VM]
Tmp = ''
IF @RECORD<13> NE '' THEN
Tmp = '' ;* Shipped
END ELSE
IF LastWH NE '' AND LastLoc NE '' THEN
Tmp = LastWH:'*':LastLoc
IF Tmp = 'SR*SB' THEN Tmp = ''
END
END
ActionFlow = Tmp
return
FINAL_SIG_DT:
ActionFlow = {FQA_DT}
return
FINAL_SIG_DTM:
ActionFlow = {FQA_DTM}
return
HOT_LOT_FLAG:
ActionFlow = Xlate('WO_LOG', {WO_NO}, 'HOT_FLAG', 'X')
return
LOAD_WFR_CNT:
ActionFlow = '' ; // Default description.
WOLogRow = Database_Services('ReadDataRow', 'WO_LOG', {WO_NO})
ReactType = WOLogRow<WO_LOG_REACT_TYPE$>
If ReactType _EQC 'EPP' OR ReactType _EQC 'EpiPro' then
WOStepKeyID = WOLogRow<WO_LOG_WO_STEP_KEY$>
WOStepRow = Database_Services('ReadDataRow', 'WO_STEP', WOStepKeyID)
Cassette = @ID[-1, 'B*']
RDSNo = WOStepRow<WO_STEP_RDS_KEY$, Cassette>
ActionFlow = Xlate('REACT_RUN', RDSNo, 'LOAD_WFR_CNT', 'X')
end else
ActionFlow = ''
end
return
SAP_YIELD:
If {REACTOR_TYPE} NE 'GAN' then
SAPData = obj_WO_Mat('GetGRProps',@ID:@RM:@RECORD)
* SAPData = GRWfrQty:@FM:ScrapQty:@FM:ProdTWQty:@FM:MUWfrQty
ActionFlow = SAPData<1> + SAPData<4>
end else
YieldInfo = Gan_Services('GetYieldInfo', {RDS_NO}, '')
GRWfrQty = YieldInfo<1>
ScrapQty = YieldInfo<2>
ProdTWQty = YieldInfo<3>
DummyQty = YieldInfo<4>
Yield = GRWfrQty - ScrapQty - ProdTWQty - DummyQty
ActionFlow = Yield
end
return
WO_REACT_TYPE:
WOLogRow = Database_Services('ReadDataRow', 'WO_LOG', {WO_NO})
ActionFlow = WOLogRow<WO_LOG_REACT_TYPE$>
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:
WOMatKeyID = Name
OrigRDSNo = OrigRecord<WO_MAT_RDS_NO$>
NewRDSNo = Record<WO_MAT_RDS_NO$>
If OrigRDSNo NE '' and NewRDSNo EQ '' and Not(MemberOf(@User4, 'OI_ADMIN')) then
Record<WO_MAT_RDS_NO$> = OrigRDSNo
SaveRecord = Record
LogData = ''
LogData<1> = LoggingDTM
LogData<2> = @USER4
LogData<3> = OrigRDSNo
LogData<4> = WOMatKeyID
If OrigRDSNo NE '' then
LogData<5> = Xlate('RDS', OrigRDSNo, 'WO_MAT_KEY', 'X')
end else
LogData<5> = ''
end
LogData<6> = 'RDS number cannot be removed. Setting RDS number back.'
Machine = Environment_Services('GetServer')
Logging_Services('AppendLog', ObjLog3, LogData, @RM, @FM, False$)
end
WaferQty = Record<WO_MAT_WAFER_QTY$>
If ( (WaferQty LT 0) or (WaferQty GT 25) ) then
// Erroneous wafer quantity save attempt. Fallback to default wafer quantity of 25.
Record<WO_MAT_WAFER_QTY$> = 25
SaveRecord = Record
end
If ({REACTOR_TYPE} EQ 'GAN') and ({FQA_DTM} NE {CASS_FINAL_SIG_DTM}) then
// Copy FQA date and datetime
DTM = {CASS_FINAL_SIG_DTM}
{FQA_DTM} = DTM
Date = {CASS_FINAL_SIG_DTM}[1, 'F.']
{FQA_DT} = Date
SaveRecord = @Record
end
// Sync up NCR signatures
NewNCRList = Record<WO_MAT_NCR_KEYS$>
OrigNCRList = OrigRecord<WO_MAT_NCR_KEYS$>
NewNCRSigs = Record<WO_MAT_NCR_FINAL_SIG$>
NewNCRSigDTMs = Record<WO_MAT_NCR_FINAL_SIG_DTM$>
// Check for deleted NCRs to remove final NCR signatures as needed.
For each OrigNCRNo in OrigNCRList using @VM setting vPos
NewNCRNo = NewNCRList<0, vPos>
If NewNCRNo EQ '' then
// An NCR has been deleted so we need to remove any final NCR signatures associate with it.
NewNCRSigs<0, vPos> = ''
NewNCRSigDTMs<0, vPos> = ''
end
Next OrigNCRNo
// Check for new NCRs to copy their final signature to the WO_MAT record.
For each NewNCRNo in NewNCRList using @VM setting vPos
// This is a new NCR, so copy the signature.
NewNCRSigs<0, vPos> = Xlate('NCR', NewNCRNo, 'AUTH_SHIP_SIG', 'X')
NewNCRSigDTMs<0, vPos> = Xlate('NCR', NewNCRNo, 'AUTH_SHIP_SIG_DTM', 'X')
Next NewNCRNo
// Trim trailing @VMs to avoid WO_MAT CURR_STATUS from erroneously calculating an open NCR is present.
NewNCRSigs = SRP_Array('Clean', NewNCRSigs, 'Trim', @VM)
NewNCRSigDTMs = SRP_Array('Clean', NewNCRSigDTMs, 'Trim', @VM)
Record<WO_MAT_NCR_FINAL_SIG$> = NewNCRSigs
Record<WO_MAT_NCR_FINAL_SIG_DTM$> = NewNCRSigDTMs
SaveRecord = Record
WONo = Field(Name, '*', 1)
ReactType = Xlate('WO_LOG', WONo, 'REACT_TYPE', 'X')
GaN = (ReactType EQ 'GAN')
If GaN then
// Sync QA signature with WO_MAT signature profile.
OrigCassFinalSig = OrigRecord<WO_MAT_CASS_FINAL_SIG$>
OrigCassFinalSigDTM = OrigRecord<WO_MAT_CASS_FINAL_SIG_DTM$>
CassFinalSig = Record<WO_MAT_CASS_FINAL_SIG$>
CassFinalSigDTM = Record<WO_MAT_CASS_FINAL_SIG_DTM$>
If ( (OrigCassFinalSig NE CassFinalSig) or (OrigCassFinalSigDTM NE CassFinalSigDTM) ) then
WOMatStage = '1G_FQA'
SigProf = Record<WO_MAT_SIG_PROFILE$>
Sigs = Record<WO_MAT_SIGNATURE$>
SigDTMs = Record<WO_MAT_SIG_DTM$>
Locate WOMatStage in SigProf using @VM setting vPos then
Sigs<0, vPos> = CassFinalSig
SigDTMs<0, vPos> = CassFinalSigDTM
Record<WO_MAT_SIGNATURE$> = Sigs
Record<WO_MAT_SIG_DTM$> = SigDTMs
SaveRecord = Record
end
end
end
Begin Case
Case {REACTOR_TYPE} EQ 'EPP'
// EpiPro Silicon - Populate OUT_SLOT_NCR column in WO_MAT record
NCRKeyChange = (OrigRecord<WO_MAT_NCR_KEYS$> NE Record<WO_MAT_NCR_KEYS$>)
NCRSlotChange = (OrigRecord<WO_MAT_EPOS_NCR$> NE Record<WO_MAT_EPOS_NCR$>)
WOMatSlotNCRs = {EPOS_NCR}
NCRKeys = {NCR_KEYS}
ChangeDetected = (NCRKeyChange or NCRSlotChange)
If ChangeDetected then
For each NCRKey in NCRKeys using @VM setting vPos
OutSlotNos = Xlate('NCR', NCRKey, 'OUT_SLOT_NO', 'X')
If OutSlotNos NE '' then
For each SlotNo in OutSlotNos using @VM setting sPos
WOMatSlotNCRs<0, SlotNo> = NCRKey
Next SlotNo
end
Next NCRKey
// Pad value marks if necessary
NumSlots = DCount(WOMatSlotNCRs, @VM)
If NumSlots LT 25 then WOMatSlotNCRs<0, 25> = ''
Record<WO_MAT_EPOS_NCR$> = WOMatSlotNCRs
SaveRecord = Record
end
Case {REACTOR_TYPE} EQ 'GAN'
// Galiumn Nitride - Currently no issues with NCR keys getting erased, so nothing to do here.
Null
Case Otherwise$
// Non-EpiPro Silicon - Populate SLOT_NCR column in WO_MAT record
NCRKeyChange = (OrigRecord<WO_MAT_NCR_KEYS$> NE Record<WO_MAT_NCR_KEYS$>)
NCRSlotChange = (OrigRecord<WO_MAT_SLOT_NCR$> NE Record<WO_MAT_SLOT_NCR$>)
ChangeDetected = (NCRKeyChange or NCRSlotChange)
If ChangeDetected then
SlotNCRs = {SLOT_NCR}
NCRKeys = {NCR_KEYS}
For each NCRKey in NCRKeys using @VM setting vPos
NCRSlotNos = Xlate('NCR', NCRKey, 'SLOT_NO', 'X')
If NCRSlotNos NE '' then
For each SlotNo in NCRSlotNos using @VM setting Dummy
SlotNCRs<0, SlotNo> = NCRKey
Next SlotNo
end
Next NCRKey
Record<WO_MAT_SLOT_NCR$> = SlotNCRs
SaveRecord = Record
end
NewMUFlag = Record<WO_MAT_MAKEUP_BOX$>
OrigMUFlag = OrigRecord<WO_MAT_MAKEUP_BOX$>
UnloadCheck = Signature_Services('GetStageSummary', WOMatKeyID, 'UNLOAD')<2>
If (NewMUFlag EQ True$) AND (OrigMUFlag NE True$) then
If UnloadCheck NE True$ then
OrigFileError = 104:': Cassette ineligible to be converted to makeup wafer until unload is signed.'
Status = 0
Record = ''
ActionFlow = ACTION_STOP$
end
end
End Case
TWChangeDetected = (OrigRecord<WO_MAT_SLOT_MET_NO$> NE Record<WO_MAT_SLOT_MET_NO$>)
If TWChangeDetected then
Begin Case
Case {REACTOR_TYPE} EQ 'EPP'
// EpiPro silicon -> build list of RDS_TEST keys and select those with TW_USE key(s).
RDSNos = Xlate('WM_IN', {WMI_KEY}, 'RDS_NO', 'X')
RDSNos = SRP_Array('Clean', RDSNos, 'TrimAndMakeUnique', @VM)
If RDSNos NE '' then
Query = 'SELECT RDS_TEST '
For each RDSNo in RDSNos using @VM setting vPos
If vPos EQ 1 then
Query := 'WITH RDS_NO EQ ':RDSNo:' '
end else
Query := 'OR WITH RDS_NO EQ ':RDSNo:' '
end
Next RDSNo
EOF = False$
RecordCopy = Record
RList(Query, TARGET_LATENTLIST$, '', '', '')
Query = 'SELECT RDS_TEST WITH TW_USE_ID NE ""'
RList(Query, TARGET_LATENTLIST$, '', '', '')
RDSTestKeys = ''
Loop
ReadNext KeyID else EOF = True$
Until EOF
RDSTestKeys<0, -1> = KeyID
Repeat
TWUseKeys = Xlate('RDS_TEST', RDSTestKeys, 'TW_USE_ID', 'X')
WaferIDs = Xlate('TW_USE', TWUseKeys, 'WAFER_ID', 'X')
SlotMetNos = ''
For each WaferID in WaferIDs using @VM setting vPos
SlotNo = Field(WaferID, '*', 3)
SlotMetNos<0, SlotNo> = TWUseKeys<0, vPos>
Next WaferID
Done = False$
NumVMs = DCount(SlotMetNos, @VM)
// Pad column with @VMs
If NumVMs LT 25 then SlotMetNos<0, 25> = ''
Record = RecordCopy
Record<WO_MAT_SLOT_MET_NO$> = SlotMetNos
SaveRecord = Record
end
Case {REACTOR_TYPE} EQ 'GAN'
// Gallium nitride -> TW_USE records do not apply, so nothing to do.
Null
Case Otherwise$
// Non-EpiPro silicon -> lookup RDS_TEST record and interrogate it for TW_USE key(s).
RDSNo = Record<WO_MAT_RDS_NO$>
If RDSNo NE '' then
Query = 'SELECT RDS_TEST WITH RDS_NO EQ ':RDSNo
EOF = False$
RecordCopy = Record
RList(Query, TARGET_LATENTLIST$, '', '', '')
Query = 'SELECT RDS_TEST WITH TW_USE_ID NE ""'
RList(Query, TARGET_LATENTLIST$, '', '', '')
RDSTestKeys = ''
Loop
ReadNext KeyID else EOF = True$
Until EOF
RDSTestKeys<0, -1> = KeyID
Repeat
TWUseKeys = Xlate('RDS_TEST', RDSTestKeys, 'TW_USE_ID', 'X')
// Rebuild list
SlotMetNos = ''
For each TWUseKey in TWUseKeys using @VM setting tPos
WaferIDs = Xlate('TW_USE', TWUseKey, 'WAFER_ID', 'X')
For each WaferID in WaferIDs using @VM setting vPos
SlotNo = Field(WaferID, '*', 3)
SlotMetNos<0, SlotNo> = TWUseKey
Next WaferID
Next TWUseKey
NumVMs = DCount(SlotMetNos, @VM)
// Pad column with @VMs
If NumVMs LT 25 then SlotMetNos<0, 25> = ''
Record = RecordCopy
Record<WO_MAT_SLOT_MET_NO$> = SlotMetNos
SaveRecord = Record
end
End Case
end
// Verify slot list does not exceed 25
NewSlotList = Record<WO_MAT_SLOT_NO$>
NumSlots = DCount(NewSlotList, @VM)
LastSlot = NewSlotList[-1, 'B':@VM]
If ( (NumSlots GT 25) or (LastSlot GT 25) ) then
LogCount = Max(NumSlots, LastSlot)
// Correct the slot column
NewSlotList = ''
For Slot = 1 to 25
NewSlotList<0, Slot> = Slot
Next Slot
Record<WO_MAT_SLOT_NO$> = NewSlotList
SaveRecord = Record
// Log the error
Stack = RetStack()
Swap @FM with CRLF$ in Stack
LogData = ''
LogData<1> = LoggingDTM
LogData<2> = @User4
LogData<3> = Name
LogData<4> = LogCount
LogData<5> = CRLF$:Stack
Logging_Services('AppendLog', objSlotLog, LogData, @RM, @FM)
// Send internal LSL message to OI admins
Recipients = XLATE('SEC_GROUPS', 'OI_ADMIN', 'USER', 'X')
SentFrom = @USER4
Subject = 'Slot count exceeds 25!'
Message = 'WO_MAT key ':Name
AttachWindow = 'WO_MAT'
AttachKey = Name
SendToGroup = ''
Parms = Recipients:@RM:SentFrom:@RM:Subject:@RM:Message:@RM:AttachWindow:@RM:AttachKey:@RM:SendToGroup
obj_Notes('Create',Parms)
end
If {REACTOR_TYPE} EQ 'EPP' then
WMKey = Field(Name, '*', 1):'*1*':Field(Name, '*', 2)
// Only set fields if the records exist, otherwise WO_MAT curr status can be calculated incorrectly.
If RowExists('WM_IN', WMKey) then Record<WO_MAT_WMI_KEY$> = WMKey
If RowExists('WM_OUT', WMKey) then Record<WO_MAT_WMO_KEY$> = WMKey
SaveRecord = Record
end
SigProf = Record<WO_MAT_SIG_PROFILE$>
Sigs = Record<WO_MAT_SIGNATURE$>
SigDtms = Record<WO_MAT_SIG_DTM$>
NumSteps = DCount(SigProf, @VM)
Record<WO_MAT_SIGNATURE$> = Field(Sigs, @VM, 1, NumSteps)
Record<WO_MAT_SIG_DTM$> = Field(SigDtms, @VM, 1, NumSteps)
SaveRecord = Record
NewMUFlag = Record<WO_MAT_MAKEUP_BOX$>
OrigMUFlag = OrigRecord<WO_MAT_MAKEUP_BOX$>
If NewMUFlag NE OrigMUFlag then
SAPBatchNo = Record<WO_MAT_SAP_BATCH_NO$>
SAPTXDtm = Record<WO_MAT_SAP_TX_DTM$>
AwaitingBatchNo = ( (SAPTXDtm NE '') and (SAPBatchNo EQ '') )
FullBoxReject = (SAPBatchNo[-1, 1] = 'R')
Begin Case
Case AwaitingBatchNo
Error_Services('Add', 'Cassette ineligible to be converted as it is awaiting a batch number from SAP.')
OrigFileError = 104:': Cassette ineligible to be converted as it is awaiting a batch number from SAP.'
Status = 0
Record = ''
ActionFlow = ACTION_STOP$
Case FullBoxReject
Error_Services('Add', 'Cassette ineligible to be converted as it is a full box reject.')
OrigFileError = 104:': Cassette ineligible to be converted as it is a full box reject.'
Status = 0
Record = ''
ActionFlow = ACTION_STOP$
Case Otherwise$
Null
End Case
end
return
WRITE_RECORD:
WONo = Field(Name, '*', 1)
CassNo = Field(Name, '*', 2)
WOMatKeyID = Name
If {REACTOR_TYPE} NE 'EPP' then
If {MAKEUP_BOX} then
CurrWaferCount = obj_WO_Mat('CurrWaferCnt', WOMatKeyID)
CurrStatus = {CURR_STATUS}
If ( (CurrWaferCount GT 0) and (CurrStatus NE 'VOID') ) then
// Populate MAKEUP_WAFERS table
// Add/update cassette data to the MAKEUP_WAFERS table
If RowExists('MAKEUP_WAFERS', WOMatKeyID) then
MUWfrRec = Database_Services('ReadDataRow', 'MAKEUP_WAFERS', WOMatKeyID)
end else
MUWfrRec = ''
end
MUWfrRec<MAKEUP_WAFERS.SAP_BATCH_NO$> = {SAP_BATCH_NO}
MUWfrRec<MAKEUP_WAFERS.PS_NO$> = Xlate('WO_LOG', WONo, 'PS_NO', 'X')
MUWfrRec<MAKEUP_WAFERS.CUST_NO$> = {CUST_NO}
MUWfrRec<MAKEUP_WAFERS.PROD_ORD_NO$> = {PROD_ORD_NO}
MUWfrRec<MAKEUP_WAFERS.WAFER_SIZE$> = {WAFER_SIZE}
MUWfrRec<MAKEUP_WAFERS.EPI_PART_NO$> = {EPI_PART_NO}
MUWfrRec<MAKEUP_WAFERS.RDS_NO$> = {RDS_NO}
MUWfrRec<MAKEUP_WAFERS.WM_OUT_NO$> = '' ; // Only applies to EPP
MUWfrRec<MAKEUP_WAFERS.PROD_VER_NO$> = {PROD_VER_NO}
MUWfrRec<MAKEUP_WAFERS.CUST_PART_NO$> = {CUST_PART_NO}
MUWfrRec<MAKEUP_WAFERS.REACT_TYPE$> = {REACTOR_TYPE}
MUWfrRec<MAKEUP_WAFERS.CURR_STATUS_STATIC$> = CurrStatus
MUWfrRec<MAKEUP_WAFERS.WFR_QTY$> = CurrWaferCount
DateOut = Xlate('RDS', {RDS_NO}, 'DATE_OUT', 'X')
TimeOut = Xlate('RDS', {RDS_NO}, 'TIME_OUT', 'X') / 86400
TimeOut[1, 2] = ''
TimeOut = TimeOut[1, 5]
UnloadDTM = DateOut:'.':TimeOut
MUWfrRec<MAKEUP_WAFERS.UNLOAD_DTM$> = UnloadDTM
Database_Services('WriteDataRow', 'MAKEUP_WAFERS', WOMatKeyID, MUWfrRec, True$, False$, True$)
end else
// Remove cassette data from MAKEUP_WAFERS table if it is present
If RowExists('MAKEUP_WAFERS', WOMatKeyID) then
Database_Services('DeleteDataRow', 'MAKEUP_WAFERS', WOMatKeyID, True$, False$)
end
end
end else
// Remove cassette data from MAKEUP_WAFERS table if it is present
If RowExists('MAKEUP_WAFERS', WOMatKeyID) then
Database_Services('DeleteDataRow', 'MAKEUP_WAFERS', WOMatKeyID, True$, False$)
end
end
end
OrigSlotNos = OrigRecord<WO_MAT_SLOT_NO$>
OrigSlotNCRs = OrigRecord<WO_MAT_SLOT_NCR$>
OrigSlotMetNos = OrigRecord<WO_MAT_SLOT_MET_NO$>
OrigSlotMovedTos = OrigRecord<WO_MAT_SLOT_MOVED_TO$>
OrigSlotRepWaferIDs = OrigRecord<WO_MAT_SLOT_REP_WAFER_ID$>
OrigVoid = OrigRecord<WO_MAT_VOID$>
OrigShipNo = OrigRecord<WO_MAT_SHIP_NO$>
OrigInvLoc = OrigRecord<WO_MAT_INV_LOCATION$>
OrigInvWH = OrigRecord<WO_MAT_INV_WH$>
OrigWMICurrStatus = OrigRecord<WO_MAT_WMI_CURR_STATUS$>
OrigWMOCurrStatus = OrigRecord<WO_MAT_WMO_CURR_STATUS$>
OrigHoldEntity = OrigRecord<WO_MAT_HOLD_ENTITY$>
OrigSigs = OrigRecord<WO_MAT_SIGNATURE$>
OrigCancelled = OrigRecord<WO_MAT_CANCELLED$>
OrigSubSupplBy = OrigRecord<WO_MAT_SUB_SUPPL_BY$>
OrigRetRejects = OrigRecord<WO_MAT_RET_REJECTS$>
OrigMakeupBox = OrigRecord<WO_MAT_MAKEUP_BOX$>
OrigNCRKeys = OrigRecord<WO_MAT_NCR_KEYS$>
OrigNCRFinalSig = OrigRecord<WO_MAT_NCR_FINAL_SIG$>
OrigSlotNo = OrigRecord<WO_MAT_SLOT_NO$>
OrigSlotNCR = OrigRecord<WO_MAT_SLOT_NCR$>
OrigSlotMetNo = OrigRecord<WO_MAT_SLOT_MET_NO$>
OrigSlotMovedTo = OrigRecord<WO_MAT_SLOT_MOVED_TO$>
OrigSlotRepWaferID = OrigRecord<WO_MAT_SLOT_REP_WAFER_ID$>
OrigSAPBatchNo = OrigRecord<WO_MAT_SAP_BATCH_NO$>
OrigQty = OrigRecord<WO_MAT_WAFER_QTY$>
NewSlotNos = Record<WO_MAT_SLOT_NO$>
NewSlotNCRs = Record<WO_MAT_SLOT_NCR$>
NewSlotMetNos = Record<WO_MAT_SLOT_MET_NO$>
NewSlotMovedTos = Record<WO_MAT_SLOT_MOVED_TO$>
NewSlotRepWaferIDs = Record<WO_MAT_SLOT_REP_WAFER_ID$>
NewVoid = Record<WO_MAT_VOID$>
NewShipNo = Record<WO_MAT_SHIP_NO$>
NewInvLoc = Record<WO_MAT_INV_LOCATION$>
NewInvWH = Record<WO_MAT_INV_WH$>
NewWMICurrStatus = Record<WO_MAT_WMI_CURR_STATUS$>
NewWMOCurrStatus = Record<WO_MAT_WMO_CURR_STATUS$>
NewHoldEntity = Record<WO_MAT_HOLD_ENTITY$>
NewSigs = Record<WO_MAT_SIGNATURE$>
NewCancelled = Record<WO_MAT_CANCELLED$>
NewSubSupplBy = Record<WO_MAT_SUB_SUPPL_BY$>
NewRetRejects = Record<WO_MAT_RET_REJECTS$>
NewMakeupBox = Record<WO_MAT_MAKEUP_BOX$>
NewNCRKeys = Record<WO_MAT_NCR_KEYS$>
NewNCRFinalSig = Record<WO_MAT_NCR_FINAL_SIG$>
NewSlotNo = Record<WO_MAT_SLOT_NO$>
NewSlotNCR = Record<WO_MAT_SLOT_NCR$>
NewSlotMetNo = Record<WO_MAT_SLOT_MET_NO$>
NewSlotMovedTo = Record<WO_MAT_SLOT_MOVED_TO$>
NewSlotRepWaferID = Record<WO_MAT_SLOT_REP_WAFER_ID$>
NewSAPBatchNo = Record<WO_MAT_SAP_BATCH_NO$>
NewQty = Record<WO_MAT_WAFER_QTY$>
** NCR log for troubleshooting ****************************
LogData = ''
LogData<1> = LoggingDTM
LogData<2> = @User4
LogData<3> = WOMatKeyID
LogData<4> = NewSlotNCRs
LogData<5> = RetStack()
Logging_Services('AppendLog', objNCRLog, LogData, @RM, @FM)
***********************************************************
// Check for ship quantity changes and update WO_LOG<WO_LOG_STATIC_SHIP_QTY$>
ShipQtyChange = ( (OrigSlotNos NE NewSlotNos) or (OrigSlotNCRs NE NewSlotNCRs) or (OrigSlotMetNos NE NewSlotMetNos) or (OrigSlotMovedTos NE NewSlotMovedTos) or (OrigSlotRepWaferIDs NE NewSlotRepWaferIDs) )
If ShipQtyChange then
WOLogRec = Database_Services('ReadDataRow', 'WO_LOG', WONo)
OrigShipQty = WOLogRec<WO_LOG_STATIC_SHIP_QTY$>
NewShipQty = obj_WO_Log('ShipQty',WONo:@RM:WOLogRec)
If OrigShipQty NE NewShipQty then
WOLogRec<WO_LOG_STATIC_SHIP_QTY$> = NewShipQty
Database_Services('WriteDataRow', 'WO_LOG', WONo, WOLogRec, True$, False$, True$)
end
end
// Original MU wafer transaction
IF OrigSAPBatchNo EQ '' AND NewSAPBatchNo NE '' then
MULotFlag = False$
If {REACTOR_TYPE} NE 'EPP' then
MULotFlag = Record<WO_MAT_MAKEUP_BOX$>
end else
WMOKey = Record<WO_MAT_WMO_KEY$>
IF WMOKey NE '' then
MULotFlag = XLATE('WM_OUT', WMOKey, 'MAKEUP_BOX', 'X')
If MULotFlag EQ '' then MULotFlag = False$
end
end
Sap_Services('SendUnTransMU', WOMatKeyID, NewSAPBatchNo, MULotFlag)
end
// SAP transactions
MakeupBox = Record<WO_MAT_MAKEUP_BOX$>
SAPBatchNo = Trim(Record<WO_MAT_SAP_BATCH_NO$>)
MakeupBoxOrig = OrigRecord<WO_MAT_MAKEUP_BOX$>
Begin Case
Case ( (MakeupBox EQ True$) and ( (MakeupBoxOrig EQ False$) or (MakeupBoxOrig EQ '') ) and (SAPBatchNo EQ '') )
// Intial WIP to MU conversion -> Send CASS_COMP SAP transaction to get a batch number.
SAP_Services('AddCassCompTransaction', WOMatKeyID)
Case ( (MakeupBox EQ True$) and ( (MakeupBoxOrig EQ False$) or (MakeupBoxOrig EQ '') ) and (SAPBatchNo NE '') )
// Converting finished goods cassette into MU cassette -> Send BATCH_CONV transaction to SAP.
SAP_Services('AddBatchConvTransaction', WOMatKeyID)
Case ( ( (MakeupBox EQ False$) or (MakeupBox EQ '') ) and (MakeupBoxOrig EQ True$) and (SAPBatchNo NE '') )
// Converting MU cassette into finished goods cassette -> Send BATCH_CONV transaction to SAP.
SAP_Services('AddBatchConvTransaction', WOMatKeyID)
End Case
SAPTestFlag = Xlate('APP_INFO', 'SAP_TEST_FLAG', 1, 'X')
If SAPTestFlag then
// If makeup wafers added/removed after CASS_COMP and if not shipped -> send to SAP
IF ({SAP_BATCH_NO} NE '') THEN
MakeupBox = {MAKEUP_BOX}
OrigMUWfrIDs = OrigRecord<WO_MAT_SLOT_MOVED_FROM$>
NewMUWfrIDs = Record<WO_MAT_SLOT_MOVED_FROM$>
SAPSLocFrom = ''
SAPSLocTo = ''
If (OrigMUWfrIDs NE NewMUWfrIDs) then
Material = Xlate('WO_LOG', WONo, 'EPI_PART_NO', 'X')
For WfrIndex = 1 to 25
BatchFrom = ''
BatchTo = ''
OrigMUWfrID = OrigMUWfrIDs<0, WfrIndex>
NewMUWfrID = NewMUWfrIDs<0, WfrIndex>
Begin Case
Case ( (OrigMUWfrID EQ '') and (NewMUWfrID NE '') )
// MU wafer added
If MakeupBox EQ True$ then
// Current box is a makeup box and new box is a makeup box. We want to update
// the batch number in SAP.
SAPSLocFrom = '0400'
SAPSLocTo = '0400'
end else
SAPSLocFrom = '0400'
SAPSLocTo = '0500'
end
// Add transaction
NewMUCassID = Field(NewMUWfrID, '.', 1, 2)
Convert '.' to '*' in NewMUCassID
BatchFrom = Xlate('WO_MAT', NewMUCassID, 'SAP_BATCH_NO', 'X')
BatchTo = {SAP_BATCH_NO}
If ( (BatchFrom NE '') and (BatchTo NE '') ) then
TransQty = 1
obj_SAP('AddTransaction','BATCHMOVE_IN':@RM:Material:@RM:BatchFrom:@RM:BatchTo:@RM:SAPSLocFrom:@RM:SAPSLocTo:@RM:TransQty)
end
Case ( (OrigMUWfrID NE '') and (NewMUWfrID EQ '') )
// MU wafer removed
If MakeupBox EQ True$ then
// Current box is a makeup box and new box is a makeup box. We want to update
// the batch number in SAP.
SAPSLocFrom = '0400'
SAPSLocTo = '0400'
end else
SAPSLocFrom = '0500'
SAPSLocTo = '0400'
end
// Add transaction
OrigMUCassID = Field(OrigMUWfrID, '.', 1, 2)
Convert '.' to '*' in OrigMUCassID
BatchFrom = {SAP_BATCH_NO}
BatchTo = Xlate('WO_MAT', OrigMUCassID, 'SAP_BATCH_NO', 'X')
If ( (BatchTo NE '') and (BatchFrom NE '') ) then
TransQty = 1
obj_SAP('AddTransaction','BATCHMOVE_IN':@RM:Material:@RM:BatchFrom:@RM:BatchTo:@RM:SAPSLocFrom:@RM:SAPSLocTo:@RM:TransQty)
end
Case Otherwise$
// No change
Null
End Case
Next WfrIndex
end
end
end
PSNo = {WO_STEP_PS_NO}
If {REACTOR_TYPE} EQ 'EPP' then
NewEppMUFlag = Record<WO_MAT_EPO_MAKEUP_BOX$>
OrigEppMUFlag = OrigRecord<WO_MAT_EPO_MAKEUP_BOX$>
NewInvActions = Record<WO_MAT_INV_ACTION$>
OrigInvActions = OrigRecord<WO_MAT_INV_ACTION$>
If ( (NewEppMUFlag NE OrigEppMUFlag) or (NewInvActions NE OrigInvActions) ) then
// Need to trigger an update WMO Curr Status field because WM_MFS is not
// attached to the WO_MAT table and the WMO status may have changed.
WMOKey = {WMO_KEY}
WMORec = Database_Services('ReadDataRow', 'WM_OUT', WMOKey)
If Error_Services('NoError') then
Database_Services('WriteDataRow', 'WM_OUT', WMOKey, WMORec, True$, False$, True$)
end
end
end
If OrigRecord<WO_MAT_WAFER_QTY$> NE Record<WO_MAT_WAFER_QTY$> then
Work_Order_Services('UpdateReceivedQty', WONo)
Work_Order_Services('UpdateReleasedQty', WONo)
end
CurrWaferCount = obj_WO_Mat('CurrWaferCnt', WOMatKeyID)
If CurrWaferCount EQ 0 then
// This should catch cases where the entire cassette is "peeled off", NCR'ed, or used for destructive testing.
Service_Services('PostProcedure', 'SCHEDULE_SERVICES', 'MarkCassProcessed':@VM:WONo:@VM:CassNo:@VM:Datetime())
end
return
DELETE_RECORD_PRE:
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