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

    Name        :   WM_IN_Services

    Description :   Handler program for all WM_IN services.

    Notes       :   Application errors should be logged using the Error Services module. There are a few methodological
                    assumptions built into way errors are managed which are important to understand in order to properly
                    work with Error Services:

                    - The term 'top' refers to the originating procedure of a call stack and the term 'bottom' refers to
                      the last routine (or the current routine) within a call stack. Within the OpenInsight Debugger
                      this will appear backwards since the originating procedure always appears at the bottom of the
                      list and the current routine appears at the top of the list. We are using this orientation because
                      it is common to refer to the process of calling other procedures as 'drilling down'.

                    - The reason for defining the orientation of the call stack is because Error_Services allows for
                      multiple error conditions to be appended to an original error. In most cases this will happen when
                      a procedure at the bottom of the stack generates an error condition and then returns to its
                      calling procedure. This higher level procedure can optionally add more information relevant to
                      itself. This continues as the call stack 'bubbles' its way back to the top to where the
                      originating procedure is waiting.

                    - Native OpenInsight commands that handle errors (e.g., Set_Status, Set_FSError, Set_EventStatus)
                      preserve their error state until explicitly cleared. This can hinder the normal execution of code
                      since subsequent procedures (usually SSPs) will fail if a pre-existing error condition exists.
                      Our philosophy is that error conditions should automatically be cleared before a new procedure
                      is executed to avoid this problem. However, the nature of Basic+ does not make this easy to
                      automate for any given stored procedure. Therefore, if a stored procedure wants to conform to our
                      philosophy then it should include a call into the 'Clear' service request at the top of the
                      program. Alternatively this can be done through a common insert (see SERVICE_SETUP for example.)

                    - Service modules will use the SERVICE_SETUP insert and therefore automatically clear out any
                      error conditions that were set before.

    Parameters  :
        Service         [in] -- Name of the service being requested
        Param1-10   [in/out] -- Additional request parameter holders
        Response       [out] -- Response to be sent back to the Controller (MCP) or requesting procedure

    Metadata    :

    History     :   (Date, Initials, Notes)
        9/5/23    djm     Original programmer.

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

$Insert SERVICE_SETUP
$insert APP_INSERTS
$Insert UNIT_EQUATES
$Insert MSG_EQUATES
$Insert DICT_EQUATES
$Insert WM_IN_EQUATES
$Insert WO_LOG_EQUATES
$Insert PROD_SPEC_EQUATES
$Insert WO_MAT_EQUATES
$Insert CUST_EPI_PART_EQUATES
$Insert QUOTE_SPEC_EQU
$Insert WO_STEP_EQUATES

Equ NUM_ATTEMPTS$ to 60

EQU PI$LEFT		TO	1
EQU PI$TOP		TO  2
EQU PI$RIGHT	TO	3
EQU PI$BOTTOM	TO	4
EQU PI$WIDTH	TO 	5
EQU PI$HEIGHT	TO	6
EQU PI$SIZE		TO	7

EQU PS$TOOL		TO 1
EQU PS$TYPE		TO 2
EQU PS$RECIPE	TO 3
EQU PS$FREQ		TO 4
EQU PS$PATTERN	TO 11

Declare function   PSN_Services, SRP_Rotate_Array, Datetime, Database_Services, Environment_Services, Logging_Services
Declare function   obj_Install, SRP_Json
Declare subroutine Database_Services, Set_Status, obj_Wo_Mat_Log, Logging_Services, Extract_Si_Keys, Btree.Extract
Declare subroutine SRP_Json, Update_Index, Delay

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

Return Response else ''

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


//----------------------------------------------------------------------------------------------------------------------
// GetComments
//
// WMINNo - [Required]
//
// Returns a delimited array of all EPP_COMMENT_NOTE, EPP_COMMENT_USER, and EPP_COMMENT_DATE
// associated with a WM_IN record. Note: Dates are returned Oconv'd.
//----------------------------------------------------------------------------------------------------------------------
Service GetComments(WMINNo)
    
    CommentArray = ''
    WMInRow      = Database_Services('ReadDataRow', 'WM_IN', WMInNo)
	CommentDates = Oconv(WMInRow<WM_IN_EPP_COMMENT_DATE$>, 'DT')
    CommentUsers = WMInRow<WM_IN_EPP_COMMENT_USER$>
    Comments     = WMInRow<WM_IN_EPP_COMMENT_NOTE$>
    CommentList  = CommentDates :@FM: CommentUsers :@FM: Comments
    CommentArray = SRP_Rotate_Array(CommentList)
    Response     = CommentArray

End Service


//----------------------------------------------------------------------------------------------------------------------
// AddComment
//
// WMInNo - [Required]
// Comment - [Required]
//
// Adds  an EPP_COMMENT_NOTE, EPP_COMMENT_USER, and EPP_COMMENT_DATE
// to a WM_In record.
//----------------------------------------------------------------------------------------------------------------------
Service AddComment(WMInNo, Comment)
    
    WMInRow          = Database_Services('ReadDataRow', 'WM_IN', WMInNo)
    UserName		= @USER4
    CommentTime		= Datetime()

	OldDates = WMInRow<WM_IN_EPP_COMMENT_DATE$>
	OldUsers = WMInRow<WM_IN_EPP_COMMENT_USER$>
    OldComments = WMInRow<WM_IN_EPP_COMMENT_NOTE$>
       If (OldDates EQ '' AND OldUsers EQ '' AND OldComments EQ '') then
			WMInRow<WM_IN_EPP_COMMENT_DATE$> = CommentTime
			WMInRow<WM_IN_EPP_COMMENT_USER$> = UserName
			WMInRow<WM_IN_EPP_COMMENT_NOTE$> = Comment
       end else
       		WMInRow<WM_IN_EPP_COMMENT_DATE$> = CommentTime  :@VM: OldDates
			WMInRow<WM_IN_EPP_COMMENT_USER$> = UserName :@VM: OldUsers
			WMInRow<WM_IN_EPP_COMMENT_NOTE$> = Comment :@VM: OldComments
       end
	Database_Services('WriteDataRow', 'WM_IN', WMInNo, WMInRow, 1, 0, 0)
	
End Service


Service SetVoidFlag(WMInKey, Username)
    
	ErrorMessage = ''
	If RowExists('WM_IN', WMInKey) then
			WMInRec = Database_Services('ReadDataRow', 'WM_IN', WMInKey, True$, 0, False$)
			If Error_Services('NoError') then
				WMInRec<WM_IN_VOID$> = True$
				Database_Services('WriteDataRow', 'WM_IN', WMInKey, WMInRec, True$, False, False$)
				If Error_Services('NoError') then
				    Set_Status(0)
					WONo = Field(WMInKey, '*', 1)
                    CassNo = Field(WMInKey, '*', 3)
                    WOMLParms = ''
                    LogFile	= 'WO_MAT'					; WOMLParms  = LogFile:@RM
                    LogDTM	= OConv(Datetime(), 'DT')	; WOMLParms := LogDTM:@RM
                    Action	= 'WM_IN_VOID'				; WOMLParms := Action:@RM
                    WhCd	= 'CR'						; WOMLParms := WhCd:@RM
                    LocCd	= 'VOID'					; WOMLParms := LocCd:@RM
                    WONos	= WONo						; WOMLParms := WONos:@RM
                    CassNos	= CassNo					; WOMLParms := CassNos:@RM
                    UserID	= Username					; WOMLParms := UserID:@RM
                    Tags	= ''						; WOMLParms := Tags:@RM
                    ToolID	= ''						; WOMLParms := ToolID:@RM
                    WOMLParms := ''
                    obj_WO_Mat_Log('Create',WOMLParms)
                    IF Get_Status(errCode) THEN
                        ErrorMessage = 'Error writing inventory transactions'
                    end
				end else
				    ErrorMessage = 'Failed to write to the WM_IN record ' : WMInKey : ' while attempting to set void status.'
				end
			end else
				ErrorMessage = 'Failed to read WM_IN record ' : WMInKey : ' while attempting to set void status.'
			end
	end else
		ErrorMessage = 'Invalid WM_IN Key ' : WMInKey : ' passed to SetVoidFlag routine.'
	end
	If ErrorMessage NE '' then
		Error_Services('Add', ErrorMessage)
	end
	
end service


Service VerifyWoStepWMIKeyIndex(WMIKey)
    
    LogPath            = Environment_Services('GetApplicationRootPath') : '\LogFiles\WM_IN'
    LogDate            = Oconv(Date(), 'D4/')
    LogTime            = Oconv(Time(), 'MTS')
    LogFileName        = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' !WM_IN WO_STEP{WM_IN_KEY} Log.csv'
    Headers            = 'Logging DTM':@FM:'WMIKey':@FM:'WOStep':@FM:'Result'
    objVerifyWMIKeyLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, COMMA$, Headers, '', False$, False$)
    LoggingDTM         = LogDate : ' ' : LogTime   ; // Logging DTM    
    
    LogData    = ''
    LogData<1> = LoggingDtm
    LogData<2> = WMIKey
    LogData<4> = 'Begin ':Service
    Logging_Services('AppendLog', objVerifyWMIKeyLog, LogData, @RM, @FM)
    
    ErrorMsg = ''
    If WMIKey NE '' then
        If RowExists('WM_IN', WMIKey) then
            WOStepKey = Field(WMIKey, '*', 1, 2)
            If WOStepKey NE '' then
                WOStepWMIKeys = Xlate('WO_STEP', WOStepKey, 'WM_IN_KEYS', 'X')
                LogData<3>    = WOStepKey
                Logging_Services('AppendLog', objVerifyWMIKeyLog, LogData, @RM, @FM)
                Locate WMIKey in WOStepWMIKeys using @VM setting vPos else
                    LogData<4>          = 'WMIKey missing from WO_STEP record. Generating index transaction.'
                    Logging_Services('AppendLog', objVerifyWMIKeyLog, LogData, @RM, @FM)     
                    // Add index transaction to update WM_IN_KEYS relational index (target WO_STEP table)
                    IndexTransactionRow = 'WO_STEP*WM_IN_KEYS*AR':@FM:WMIKey:@FM:"":@FM:WOStepKey:@FM
                    Open "!WM_IN" to BangTable then
                        Done = False$
                        For AttemptNo = 1 to NUM_ATTEMPTS$
                            If AttemptNo GT 1 then Delay(1)                        
                            Lock BangTable, 0 then
                                Read PendingTrans from BangTable, 0 else PendingTrans = '0':@FM
                                PendingTrans := IndexTransactionRow
                                Write PendingTrans on BangTable, 0 then
                                    LogData<4> = 'Index transaction successfully added.'
                                    Logging_Services('AppendLog', objVerifyWMIKeyLog, LogData, @RM, @FM)
                                    Done       = True$
                                end else
                                    ErrorMsg = 'Unable to write index transaction to !WM_IN. ':WMIKey
                                end
                                Unlock BangTable, 0 else ErrorMsg = 'Unable to Open !WM_IN to add index transaction. ':WMIKey
                            end else
                                If AttemptNo GE NUM_ATTEMPTS$ then 
                                    ErrorMsg = 'Unable to Lock !WM_IN to add index transaction. ':WMIKey
                                end
                            end
                        Until Done or ErrorMsg
                        Next AttemptNo
                    end else
                        ErrorMsg = 'Unable to Open !WM_IN to add index transaction. ':WMIKey
                    end
                end
            end else
                LogData<4> = 'WO_STEP key for WM_IN ':WMIKey:' is null. Nothing to update.'
                Logging_Services('AppendLog', objVerifyWMIKeyLog, LogData, @RM, @FM)                    
            end
        end
    end
    
    If ErrorMsg NE '' then
        LogData<4> = ErrorMsg
        Logging_Services('AppendLog', objVerifyWMIKeyLog, LogData, @RM, @FM)
    end
    
    LogData<4> = 'End ':Service
    Logging_Services('AppendLog', objVerifyWMIKeyLog, LogData, @RM, @FM)
    
    If ErrorMsg NE '' then Error_Services('Add', ErrorMsg)    
    
end service


Service VerifyWOLogWMIKeyIndex(WMIKey)
    
    LogPath                = Environment_Services('GetApplicationRootPath') : '\LogFiles\WM_IN'
    LogDate                = Oconv(Date(), 'D4/')
    LogTime                = Oconv(Time(), 'MTS')
    LogFileName            = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' !WM_IN WM_IN{WO_NO} Log.csv'
    Headers                = 'Logging DTM':@FM:'WMIKey':@FM:'WOStep':@FM:'Result'
    objVerifyWMIWoIndexLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, COMMA$, Headers, '', False$, False$)
    
    LogData    = ''
    LogData<1> = OConv(Datetime(), 'DT/^S')
    LogData<2> = WMIKey
    LogData<4> = 'Begin ':Service
    Logging_Services('AppendLog', objVerifyWMIWoIndexLog, LogData, @RM, @FM)
    
    ErrorMsg = ''
    If WMIKey NE '' then
        If RowExists('WM_IN', WMIKey) then
            WONo = Field(WMIKey, '*', 1, 1)
            If WONo NE '' then
                WOLogRDSKeys = ''
                Extract_Si_Keys('WM_IN', 'WO_NO', WONo, WOLogRDSKeys)
                LogData<1> = OConv(Datetime(), 'DT/^S')
                LogData<3>   = WONo
                Logging_Services('AppendLog', objVerifyWMIWoIndexLog, LogData, @RM, @FM)
                Locate WMIKey in WOLogRDSKeys using @VM setting vPos else
                    LogData<1> = OConv(Datetime(), 'DT/^S')
                    LogData<4>          = 'WMIKey missing from Btree index. Generating index transaction.'
                    Logging_Services('AppendLog', objVerifyWMIWoIndexLog, LogData, @RM, @FM)     
                    // Add index transaction to update WO_NO btree index
                    IndexTransactionRow = 'WO_NO':@FM:WMIKey:@FM:"":@FM:WONo:@FM
                    Open "!WM_IN" to BangTable then
                        Done = False$
                        For AttemptNo = 1 to NUM_ATTEMPTS$
                            If AttemptNo GT 1 then Delay(1)                        
                            Lock BangTable, 0 then
                                Read PendingTrans from BangTable, 0 else PendingTrans = '0':@FM
                                PendingTrans := IndexTransactionRow
                                Write PendingTrans on BangTable, 0 then
                                    LogData<1> = OConv(Datetime(), 'DT/^S')
                                    LogData<4> = 'Index transaction successfully added.'
                                    Logging_Services('AppendLog', objVerifyWMIWoIndexLog, LogData, @RM, @FM)
                                    Done       = True$
                                end else
                                    ErrorMsg = 'Error in ':Service:' service. Unable to write index transaction to !WM_IN. ':WMIKey
                                end
                                Unlock BangTable, 0 else ErrorMsg = 'Error in ':Service:' service. Unable to Open !WM_IN to add index transaction. ':WMIKey
                            end else
                                If AttemptNo GE NUM_ATTEMPTS$ then 
                                    ErrorMsg = 'Error in ':Service:' service. Unable to Lock !WM_IN to add index transaction. ':WMIKey
                                end
                            end
                        Until Done or ErrorMsg
                        Next AttemptNo
                    end else
                        ErrorMsg = 'Error in ':Service:' service. Unable to Open !WM_IN to add index transaction. ':WMIKey
                    end
                end
            end else
                LogData<1> = OConv(Datetime(), 'DT/^S')
                LogData<4> = 'WONo for WM_IN ':WMIKey:' is null. Nothing to update.'
                Logging_Services('AppendLog', objVerifyWMIWoIndexLog, LogData, @RM, @FM)                    
            end
        end
    end else
        ErrorMsg = 'Error in ':Service:' service. Null WMIKey passed in.'
    end
    
    If ErrorMsg NE '' then
        LogData<1> = OConv(Datetime(), 'DT/^S')
        LogData<4> = ErrorMsg
        Logging_Services('AppendLog', objVerifyWMIWoIndexLog, LogData, @RM, @FM)
    end
    
    LogData<1> = OConv(Datetime(), 'DT/^S')
    LogData<4> = 'End ':Service
    Logging_Services('AppendLog', objVerifyWMIWoIndexLog, LogData, @RM, @FM)
    
    If ErrorMsg NE '' then Error_Services('Add', ErrorMsg)
    
end service


Service VerifyWOMatWMIKeyIndex(WMIKey)
    
    LogPath           = Environment_Services('GetApplicationRootPath') : '\LogFiles\WM_IN'
    LogDate           = Oconv(Date(), 'D4/')
    LogTime           = Oconv(Time(), 'MTS')
    LogFileName       = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' !WM_IN WO_MAT{WMI_KEY} Log.csv'
    Headers           = 'Logging DTM':@FM:'WMIKey':@FM:'WOMatKey':@FM:'Result'
    objVerifyWoMatWmiKeyLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, COMMA$, Headers, '', False$, False$)
    
    LogData    = ''
    LogData<1> = OConv(Datetime(), 'DT/^S')
    LogData<2> = WMIKey
    LogData<4> = 'Begin ':Service
    Logging_Services('AppendLog', objVerifyWoMatWmiKeyLog, LogData, @RM, @FM)
    
    ErrorMsg = ''
    If WMIKey NE '' then
        If RowExists('WM_IN', WMIKey) then
            WOMatKey = Field(WMIKey, '*', 1, 1):'*':Field(WMIKey, '*', 3, 1)
            If WOMatKey NE '' then
                WOMatWMIKey = Xlate('WO_MAT', WOMatKey, 'WMI_KEY', 'X')
                LogData<1> = OConv(Datetime(), 'DT/^S')
                LogData<3> = WOMatKey
                Logging_Services('AppendLog', objVerifyWoMatWmiKeyLog, LogData, @RM, @FM)               
                If WOMatWMIKey EQ '' then
                    LogData<1>          = OConv(Datetime(), 'DT/^S')
                    LogData<4>          = 'WMIKey missing from WO_MAT record. Generating index transaction.'
                    Logging_Services('AppendLog', objVerifyWoMatWmiKeyLog, LogData, @RM, @FM)     
                    // Add index transaction to update WMI_KEY relational index (target WO_MAT table)
                    IndexTransactionRow = 'WO_MAT*WMI_KEY*TOP':@FM:WMIKey:@FM:"":@FM:WOMatKey:@FM
                    Open "!WM_IN" to BangTable then
                        Done = False$
                        For AttemptNo = 1 to NUM_ATTEMPTS$
                            If AttemptNo GT 1 then Delay(1)
                            Lock BangTable, 0 then
                                Read PendingTrans from BangTable, 0 else PendingTrans = '0':@FM
                                PendingTrans := IndexTransactionRow
                                Write PendingTrans on BangTable, 0 then
                                    LogData<1> = OConv(Datetime(), 'DT/^S')
                                    LogData<4> = 'Index transaction successfully added.'
                                    Logging_Services('AppendLog', objVerifyWoMatWmiKeyLog, LogData, @RM, @FM)
                                    Done       = True$
                                end else
                                    ErrorMsg = 'Unable to write index transaction to !WM_IN. ':WMIKey
                                end
                                Unlock BangTable, 0 else ErrorMsg = 'Unable to Open !WM_IN to add index transaction. ':WMIKey
                            end else
                                If AttemptNo GE NUM_ATTEMPTS$ then 
                                    ErrorMsg = 'Unable to Lock !WM_IN to add index transaction. ':WMIKey
                                end
                            end
                        Until Done or ErrorMsg
                        Next AttemptNo
                    end else
                        ErrorMsg = 'Unable to Open !WM_IN to add index transaction. ':WMIKey
                    end                        
                end
            end else
                LogData<1> = OConv(Datetime(), 'DT/^S')
                LogData<4> = 'WO_MAT key for WM_IN ':WMIKey:' is null. Nothing to update.'
                Logging_Services('AppendLog', objVerifyWoMatWmiKeyLog, LogData, @RM, @FM)                    
            end
        end
    end
    
    If ErrorMsg NE '' then
        LogData<1> = OConv(Datetime(), 'DT/^S')
        LogData<4> = ErrorMsg
        Logging_Services('AppendLog', objVerifyWoMatWmiKeyLog, LogData, @RM, @FM)
    end
    
    LogData<1> = OConv(Datetime(), 'DT/^S')
    LogData<4> = 'End ':Service
    Logging_Services('AppendLog', objVerifyWoMatWmiKeyLog, LogData, @RM, @FM)
    
    If ErrorMsg NE '' then Error_Services('Add', ErrorMsg)
    
end service


Service GetWMInKeys(WOLogId)
    
    ErrorMsg = ''
    WMInKeys = ''
    
    If WOLogId NE '' then
        Open 'DICT.WM_IN' to hWmInDict then
            SearchString = ''
            SearchString := 'WO_NO':@VM:WOLogId:@FM
            Btree.Extract(SearchString, 'WM_IN', hWmInDict, WMInKeys, 0, 0)
        end else
            ErrorMsg = 'Error opening WM_IN dictionary.'
        end
    end else
        ErrorMsg = 'WOLogId was null'
    end
    
    If ErrorMsg NE '' then
        Error_Services('Add', ErrorMsg)
    end
    
    Response = WMInKeys
    
end service


Service GetWmInZpl(WmInKey)
    
    ZPL        = ''
    ErrorMsg = ''
    If WmInKey NE '' then
        Swap '-' with '*' in WmInKey
        Swap '.' with '*' in WmInKey
        If RowExists('WM_IN', WmInKey) then
            WONo       = Field(WmInKey, '*', 1, 1)
            WOStep     = Field(WmInKey, '*', 2, 1)
            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')
            Captive		= XLATE('COMPANY',CustNo,'CAPTIVE','X')
            PONo		= WORec<WO_LOG_CUST_PO_NO$>
            PSNo	= XLATE('WO_STEP',WONo:'*':WOStep,WO_STEP_PROD_SPEC_ID$,'X')
            PSRec	= XLATE('PROD_SPEC',PSNo,'','X')
            CustSpecNo = ''
            IF Captive THEN
                CustSpecNos		= PSRec<PROD_SPEC_SPEC_NUM$>
                CustSpecDescs	= PSRec<PROD_SPEC_SPEC_DESC$>
                CustSpecRevNos	= PSRec<PROD_SPEC_REV_NUM$>
                CustSpecRevDts	= PSRec<PROD_SPEC_REV_DATE$>
                SpecCnt = COUNT(CustSpecNos,@VM) + (CustSpecNos NE '')
                FOR I = 1 TO SpecCnt
                    IF CustSpecDescs<1,I> _EQC 'GENERAL' THEN
                        CustSpecNo		= CustSpecNos<1,I>
                        CustSpecDesc	= CustSpecDescS<1,I>
                        CustSpecRevNo	= CustSpecRevNos<1,I>
                        CustSpecRevDt	= OCONV(CustSpecRevDts<1,I>,'D4/')
                    END
                UNTIL CustSpecNo NE ''
                NEXT I
            END
            EpiPartNo         = WORec<WO_LOG_EPI_PART_NO$>
            CustEpiPartRec    = XLATE('CUST_EPI_PART',CustNo:'*':EpiPartNo,'','X')
            ShipBagReq        = CustEpiPartRec<CUST_EPI_PART_SHIP_BAG_REQ$>
            PreSurfscan       = PSRec<PROD_SPEC_PRE_SURFSCAN$>
            FirstSurfscan     = PSRec<PROD_SPEC_FIRST_SURFSCAN$>
            PostCleanSurfScan = PSRec<PROD_SPEC_POST_CLEAN_SURFSCAN$>
            SubPreClean       = XLATE( 'PROD_SPEC', PSNo, 'SUB_PRE_CLEAN', 'X' )
            SubPostClean      = XLATE( 'PROD_SPEC', PSNo, 'SUB_POST_CLEAN', 'X' )
            PreCleanTool      = SubPreClean					;* Temporary for 2x4 -> 4x4 conversion
            PostCleanTool     = SubPostClean
            PreAkrionRecipe   = XLATE( 'PROD_SPEC', PSNo, 'PRE_AKRION_RECIPE', 'X' )   
            PostAkrionRecipe  = XLATE( 'PROD_SPEC', PSNo, 'POST_AKRION_RECIPE', 'X' )
            RecipeNo          = XLATE( 'PROD_SPEC', PSNo, 'RECIPE_NO_L1', 'X' )
            RecipeName        = XLATE( 'PROD_SPEC', PSNo, 'RECIPE_NAME_L1', 'X' )
            SpecType          = OCONV( XLATE( 'PROD_SPEC', PSNo, 'SPEC_TYPE', 'X' ), '[SPEC_TYPE_CONV]' )
            SubOxide          = OCONV( XLATE( 'PROD_SPEC', PSNo, 'SUB_OXIDE_STRIP', 'X' ), 'B' )
            ThickTarget       = OCONV( XLATE( 'PROD_SPEC', PSNo, 'THICK_TARGET_ALL', 'X' ), 'MD3' )
            ResTarget         = OCONV( XLATE( 'PROD_SPEC', PSNo, 'RES_TARGET_ALL', 'X' ), 'MD3' )
            ThickUnit         = XLATE( 'PROD_SPEC', PSNo, 'THICK_UNITS_ALL', 'X' )
            ResUnit           = XLATE( 'PROD_SPEC', PSNo , 'RES_UNITS_ALL', 'X' )
            Dopant            = XLATE( 'PROD_SPEC', PSNo, 'DOPANT_L1', 'X' )
            RecipeNo          = XLATE( 'PROD_SPEC', PSNo, 'RECIPE_NO', 'X' )	
            RecipeInfo        = XLATE( 'RECIPE', RecipeNo, 'RECIPE_NAME_NO', 'X' )
            CleaningReqs      = ''
            ThickCnt          = FIELDCOUNT( ThickTarget<1>, @VM )
            PrintThickTargets = ''
            FOR J  = 1 TO ThickCnt
                PrintThickTargets<1,J> = ThickTarget<1,J>:ThickUnit<1,J>
            NEXT J 
            ResCnt = FIELDCOUNT( ResTarget<1>, @VM )
            PrintResTargets = ''
            FOR J  = 1 TO ResCnt
                IF ResTarget<1,J>[1,1] = '.' THEN
                    TargetVal = '0':ResTarget<1,J>
                END ELSE
                    TargetVal = ResTarget<1,J>
                END
                PrintResTargets<1,J> = TargetVal:ResUnit<1,J>
            NEXT J 
            APreRec  = ''
            APostRec = ''
            IF ( PreAkrionRecipe<1> <> '' ) THEN
                APreRec = ' ':PreAkrionRecipe:' '
                SubOxide = 'No' 					;* If Akrion then no oxide strip
            END
            IF ( PostAkrionRecipe<1> <> '' ) THEN
                APostRec = ' ':PostAkrionRecipe
            END
            PrintCleaningReqs	= TRIM( 'Strip:':SubOxide:' Pre:':SubPreClean:APreRec:' Post:':SubPostClean:APostRec )
            swap UNIT_MICROMETER$ with 'um' in PrintThickTargets
            swap UNIT_OHM_CM$     with 'ohm.cm' in PrintThickTargets
            swap UNIT_OHM_PER_SQ$ with 'ohm/sq' in PrintThickTargets
            swap UNIT_A$ with 'A' in PrintThickTargets  
            swap UNIT_MICROMETER$ with 'um' in PrintResTargets
            swap UNIT_OHM_CM$     with 'ohm.cm' in PrintResTargets
            swap UNIT_OHM_PER_SQ$ with 'ohm/sq' in PrintResTargets
            swap UNIT_A$ with 'A' in PrintResTargets

            PrintWMInKey = WMInKey
            CONVERT '*' TO '.' IN PrintWMInKey
            CassNo       = FIELD(WMInKey,'*',3)
            WOMatKey     = WONo:'*':CassNo
            WOMatRec     = XLATE('WO_MAT',WOMatKey,'','X')
            LotNo        = WOMatRec<WO_MAT_LOT_NO$>
            WfrQty       = WOMatRec<WO_MAT_WAFER_QTY$>
            CustPartNo   = WOMatRec<WO_MAT_CUST_PART_NO$>
            SubPartNo    = WOMatRec<WO_MAT_SUB_PART_NO$>
            OrderItem    = WOMatRec<WO_MAT_ORDER_ITEM$>
            Vendor       = WOMatRec<WO_MAT_SUB_VEND_CD$>
            OrderDetKey  = WORec<WO_LOG_ORDER_NO$>:'*':OrderItem
            LOCATE CustPartNo IN CustEpiPartRec<CUST_EPI_PART_CUST_PART_NO$> USING @VM SETTING Pos THEN
                CustPartDesc = CustEpiPartRec<CUST_EPI_PART_CUST_PART_DESC$,Pos>
            END ELSE
                CustPartDesc = ''
            END
            PartNo      = CustPartNo				
            PartDesc    = CustPartDesc			
            SuppCd      = TRIM(SubPartNo[-1,'B-'])
            IF LEN(SuppCd) NE '2' THEN SuppCd =''
            If CassNo EQ 1 then
                SpecEpi		= PSRec<prod_spec_spec_epi$>
                SpecSub		= PSRec<PROD_SPEC_SPEC_SUBSTRATE$>
                ThkSmile	= PSRec<PROD_SPEC_THICK_SMILE_REQ$>
                ThkShift	= Field(PSRec, @FM, PROD_SPEC_THICK_LIMIT_SHIFT$)
                ResShift	= Field(PSRec, @FM, PROD_SPEC_RES_LIMIT_SHIFT$)
                SSRecipe	= XLATE('PRS_STAGE', PSNo:'*LWI', 5, 'X')
                PCRecipe	= XLATE('PRS_STAGE', PSNo, 'POST_CLEAN_SURFSCAN_RECIPE', 'X')
                WaferSize	= Field(SpecSub, @VM, QSSubWafersize$)
                convert char(248) to @fm in SpecEpi ;* char(248) is used to separate layers
                layerCount  = fieldcount( SpecEpi, @fm )
                layerInfo   =  SpecEpi<layerCount>
                // If this is just 1 layer, we're returning that info, if 3 layers, we're returning layer 3 (combo)
                ThkData		= Field(layerInfo, @VM, QSEpiThickMeasure$)
                ResData		= Field(layerInfo, @VM, QSEpiResMeasure$)
                ConData		= Field(layerInfo, @VM, QSEpiConcMeasure$)
                CResData	= Field(layerInfo, @VM, QSEpiCresMeasure$)
                ThkMin		= OCONV(Field(layerInfo, @VM, QSEpiMinThick$), 'MD3')
                ThkMax		= OCONV(Field(layerInfo, @VM, QSEpiMaxThick$), 'MD3')
                ThkTarg		= ''
                If ThkMin NE '' Then ThkTarg = oConv(iconv((ThkMin+ThkMax)/2, 'MD3'), 'MD3')
                ResMin		= OCONV(Field(layerInfo, @VM, QSEpiResMinThick$), 'MD3')
                ResMax		= OCONV(Field(layerInfo, @VM, QSEpiResMaxThick$), 'MD3')
                ResTarg		= ''
                If ResMin NE '' Then ResTarg = oConv(iconv((ResMin+ResMax)/2, 'MD3'), 'MD3')
                ConMin		= OCONV(Field(layerInfo, @VM, QSEpiConcMinThick$), 'MD3')
                ConMax		= OCONV(Field(layerInfo, @VM, QSEpiConcMaxThick$), 'MD3')
                ConTarg		= ''
                If ConMin NE '' Then ConTarg = oConv(iconv((ConMin+ConMax)/2, 'MD3'), 'MD3')
                CResMin		= OCONV(Field(layerInfo, @VM, QSEpiCresMin$), 'MD3')
                CResMax		= OCONV(Field(layerInfo, @VM, QSEpiCresMax$), 'MD3')
                CResTarg	= ''
                If CResMin NE '' Then CResTarg = oConv(iconv((CResMin+CResMax)/2, 'MD3'), 'MD3')
                ThkTool		= Field(ThkData, '~', PS$TOOL)
                ThkType		= Field(ThkData, '~', PS$TYPE)
                ThkRecipe	= Field(ThkData, '~', PS$RECIPE)
                ThkFreq		= Field(ThkData, '~', PS$FREQ)
                ThkPattern	= Field(ThkData, '~', PS$PATTERN)
                ResTool		= Field(ResData, '~', PS$TOOL)
                ResType		= Field(ResData, '~', PS$TYPE)
                ResRecipe	= Field(ResData, '~', PS$RECIPE)
                ResFreq		= Field(ResData, '~', PS$FREQ)
                ResPattern	= Field(ResData, '~', PS$PATTERN)
                ConTool		= Field(ConData, '~', PS$TOOL)
                ConType		= Field(ConData, '~', PS$TYPE)
                ConRecipe	= Field(ConData, '~', PS$RECIPE)
                ConFreq		= Field(ConData, '~', PS$FREQ)
                ConPattern	= Field(ConData, '~', PS$PATTERN)
                CResTool	= Field(CResData, '~', PS$TOOL)
                CResType	= Field(CResData, '~', PS$TYPE)
                CResRecipe	= Field(CResData, '~', PS$RECIPE)
                CResFreq	= Field(CResData, '~', PS$FREQ)
                CResPattern	= Field(CResData, '~', PS$PATTERN)
                IF ThkMin		EQ '' THEN ThkMin		= '--'
                IF ThkMax		EQ '' THEN ThkMax		= '--'
                IF ThkTarg		EQ '' THEN ThkTarg		= '--'
                IF ThkTool		EQ '' THEN ThkTool		= '--'
                IF ThkType		EQ '' THEN ThkType		= '--'
                IF ThkRecipe	EQ '' THEN ThkRecipe	= '--'
                IF ThkPattern	EQ '' THEN ThkPattern	= '--'
                IF ThkFreq		EQ '' THEN ThkFreq		= '--'
                IF ResMin		EQ '' THEN ResMin		= '--'
                IF ResMax		EQ '' THEN ResMax		= '--'
                IF ResTarg		EQ '' THEN ResTarg		= '--'
                IF ResTool		EQ '' THEN ResTool		= '--'
                IF ResType		EQ '' THEN ResType		= '--'
                IF ResRecipe	EQ '' THEN ResRecipe	= '--'
                IF ResPattern	EQ '' THEN ResPattern	= '--'
                IF ResFreq		EQ '' THEN ResFreq		= '--'
                IF ConMin		EQ '' THEN ConMin		= '--'
                IF ConMax		EQ '' THEN ConMax		= '--'
                IF ConTarg		EQ '' THEN ConTarg		= '--'
                IF ConTool		EQ '' THEN ConTool		= '--'
                IF ConType		EQ '' THEN ConType		= '--'
                IF ConRecipe	EQ '' THEN ConRecipe	= '--'
                IF ConPattern	EQ '' THEN ConPattern	= '--'
                IF ConFreq		EQ '' THEN ConFreq		= '--'
                IF CResMin		EQ '' THEN CResMin		= '--'
                IF CResMax		EQ '' THEN CResMax		= '--'
                IF CResTarg		EQ '' THEN CResTarg		= '--'
                IF CResTool		EQ '' THEN CResTool		= '--'
                IF CResType		EQ '' THEN CResType		= '--'
                IF CResRecipe	EQ '' THEN CResRecipe	= '--'
                IF CResPattern	EQ '' THEN CResPattern	= '--'
                IF CResFreq		EQ '' THEN CResFreq		= '--'
                Swap @VM with ' / ' in SSRecipe
                IF PCRecipe EQ '' Then PCRecipe = '--'
                If ThkSmile EQ '' OR ThkSmile EQ '0' then
                    ThkSmile = 'N'
                end else
                    ThkSmile = 'Y'
                end
                If ThkShift EQ '' OR ThkShift EQ '0' then
                    ThkShift = 'N'
                end else
                    ThkShift = 'Y'
                end
                If ResShift EQ '' OR ResShift EQ '0' then
                    ResShift = 'N'
                end else
                    ResShift = 'Y'
                end
                if Len(ThkMin) > 6 then
                    ThkMin = ThkMin[1,6]
                end
                if Len(ThkMax) > 6 then
                    ThkMax = ThkMax[1,6]
                end	
                if Len(ThkTarg) > 6 then
                    ThkTarg = ThkTarg[1,6]
                end
                if Len(ResMin) > 6 then
                    ResMin = ResMin[1,6]
                end
                if Len(ResMax) > 6 then
                    ResMax = ResMax[1,6]
                end	
                if Len(ResTarg) > 6 then
                    ResTarg = ResTarg[1,6]
                end
                if Len(ConMin) > 6 then
                    ConMin = ConMin[1,6]
                end
                if Len(ConMax) > 6 then
                    ConMax = ConMax[1,6]
                end	
                if Len(ConTarg) > 6 then
                    ConTarg = ConTarg[1,6]
                end	
                if Len(CResMin) > 6 then
                    CResMin = CResMin[1,6]
                end
                if Len(CResMax) > 6 then
                    CResMax = CResMax[1,6]
                end	
                if Len(CResTarg) > 6 then
                    CResTarg = CResTarg[1,6]
                end	
                ****** Begin ZPL
                ZPL := '^XA'
                ZPL := '^LH10,0'
                ZPL := '^PR2'		;* Print speed 2 inches per second
                ZPL := '^LL406'		;* Label length @203 dpi
                ZPL := '^PW900'
                ZPL := '^MD0'		;* Media darkness							
                ZPL := '^MMT'		;* Media mode t=tear off mode
                ****** 1st/2nd Line 
                ZPL := '^FO30,20^A0,,40^FDWO ':WONo:'^FS'
                ZPL := '^FO590,10^A050,30^FDPSN ':PSNo:'^FS'
                ZPL := '^FO555,40^A050,30^FDDopant ':Dopant:'^FS'	
                ****** 3rd Line - headers
                ZPL := '^FO85,100^A050,23^FDMin^FS'
                ZPL := '^FO145,100^A050,23^FDTarg^FS'
                ZPL := '^FO210,100^A050,23^FDMax^FS'
                ZPL := '^FO280,100^A050,23^FDTool^FS'
                ZPL := '^FO330,100^A050,23^FDType^FS'
                ZPL := '^FO470,100^A050,23^FDRecipe^FS'
                ZPL := '^FO600,100^A050,23^FDPattern^FS'
                ZPL := '^FO740,100^A050,23^FDFreq^FS'
                ****** 4th Line - Thickness (Thk)
                ZPL := '^FO70,130^GB720,0,3^FS'
                ZPL := '^FO70,130^GB0,160,3^FS'
                ZPL := '^FO10,140^A050,23^FDThk:^FS'
                ZPL := '^FO80,140^A050,23^FD':ThkMin:'^FS'
                ZPL := '^FO145,140^A050,23^FD':ThkTarg:'^FS'
                ZPL := '^FO210,140^A050,23^FD':ThkMax:'^FS'
                ZPL := '^FO280,140^A050,23^FD':ThkTool[1,5]:'^FS'
                ZPL := '^FO330,140^A050,23^FD':ThkType[1,11]:'^FS'
                ZPL := '^FO470,140^A050,23^FD':ThkRecipe[1,11]:'^FS'
                ZPL := '^FO600,140^A050,23^FD':ThkPattern[1,11]:'^FS'
                ZPL := '^FO750,140^A050,23^FD':ThkFreq:'^FS'
                ****** 5th Line - Resistivity (Res)
                ZPL := '^FO10,170^GB780,0,3^FS'
                ZPL := '^FO10,180^A050,23^FDRes:^FS'
                ZPL := '^FO80,180^A050,23^FD':ResMin:'^FS'
                ZPL := '^FO145,180^A050,23^FD':ResTarg:'^FS'
                ZPL := '^FO210,180^A050,23^FD':ResMax:'^FS'
                ZPL := '^FO280,180^A050,23^FD':ResTool[1,5]:'^FS'
                ZPL := '^FO330,180^A050,23^FD':ResType[1,11]:'^FS'
                ZPL := '^FO470,180^A050,23^FD':ResRecipe[1,11]:'^FS'
                ZPL := '^FO600,180^A050,23^FD':ResPattern[1,11]:'^FS'
                ZPL := '^FO750,180^A050,23^FD':ResFreq:'^FS'
                ****** 6th Line - Contact (Con)
                ZPL := '^FX***LINE 4'
                ZPL := '^FO10,210^GB780,0,3^FS'
                ZPL := '^FO10,220^A050,23^FDCon:^FS'
                ZPL := '^FO80,220^A050,23^FD':ConMin:'^FS'
                ZPL := '^FO145,220^A050,23^FD':ConTarg:'^FS'
                ZPL := '^FO210,220^A050,23^FD':ConMax:'^FS'
                ZPL := '^FO280,220^A050,23^FD':ConTool[1,5]:'^FS'
                ZPL := '^FO330,220^A050,23^FD':ConType[1,11]:'^FS'
                ZPL := '^FO470,220^A050,23^FD':ConRecipe[1,11]:'^FS'
                ZPL := '^FO600,220^A050,23^FD':ConPattern[1,11]:'^FS'
                ZPL := '^FO750,220^A050,23^FD':ConFreq:'^FS'
                ****** 7th Line - Contact Resistance (CRes)
                ZPL := '^FO10,250^GB780,0,3^FS'
                ZPL := '^FO10,260^A050,23^FDCRes:^FS'
                ZPL := '^FO80,260^A050,23^FD':CResMin:'^FS'
                ZPL := '^FO145,260^A050,23^FD':CResTarg:'^FS'
                ZPL := '^FO210,260^A050,23^FD':CResMax:'^FS'
                ZPL := '^FO280,260^A050,23^FD':CResTool[1,5]:'^FS'
                ZPL := '^FO330,260^A050,23^FD':CResType[1,11]:'^FS'
                ZPL := '^FO470,260^A050,23^FD':CResRecipe[1,11]:'^FS'
                ZPL := '^FO600,260^A050,23^FD':CResPattern[1,11]:'^FS'
                ZPL := '^FO750,260^A050,23^FD':CResFreq:'^FS'
                ZPL := '^FO10,290^GB780,0,3^FS'
                ****** 8th Line - Special Instructions
                ZPL := '^FO10,310^A050,23^FDSurfScan: ':SSRecipe:'^FS'
                ZPL := '^FO10,340^A050,23^FDPostClean: ':PCRecipe:'^FS'
                ZPL := '^FO10,370^A050,23^FDWafer Size: ':WaferSize:'^FS'
                ZPL := '^FO500,310^A050,23^FDSmile Required: ':ThkSmile:'^FS'
                ZPL := '^FO500,340^A050,23^FDSPC Thk Shift: ':ThkShift:'^FS'
                ZPL := '^FO500,370^A050,23^FDSPC Res Shift: ':ResShift:'^FS'
                ZPL := '^XZ':CRLF$
                ****** End Cheet Sheet ZPL            
            end
            For cnt = 1 to 2
                ZPL := '^XA'
                ZPL := '^LH0,0'
                ZPL := '^PR2'		;* Print speed 2 inches per second
                ZPL := '^LL406'		;* Label length @203 dpi
                ZPL := '^PW900'
                ZPL := '^MD8'		;* Media darkness
                ZPL := '^MMT'		;* Media mode t=tear off mode
                ZPL := '^FO15,15^GB795,398,3,B^FS'				;* Border
                ZPL:= '^FO680,25^A0,36^FDWM In^FS'
                ****** First Line
                Company = 'IFX Epi Services'
                ZPL := '^FO30,25^A0,,36^FD':Company:'^FS'
                ZPL := '^FO290,25^AC,18^FDWO No:^FS':CRLF$					
                ZPL := '^FO370,25^A045,36^FD':PrintWONo:'^FS':CRLF$			
                IF CassNo = 1 THEN
                    ZPL := '^FO535,20^GB120,40,40,,3^FS':CRLF$
                    ZPL := '^FO540,25^AC,18^FR^FDCass:^FS':CRLF$			
                    ZPL := '^FO600,25^A045,36^FR^FD':CassNo:'^FS':CRLF$		
                END ELSE
                    ZPL := '^FO540,25^AC,18^FDCass:^FS':CRLF$				
                    ZPL := '^FO600,25^A045,36^FD':CassNo:'^FS':CRLF$		
                END
                ****** Second Line
                ZPL := '^FO30,70^AC,18^FDWM In:^FS':CRLF$
                ZPL := '^FO120,70^A050,30^FD':PrintWMInKey:'^FS':CRLF$
                ZPL := '^BY2,2.0':CRLF$
                ZPL := '^FO300,60^B3,,37,N^FD':'I':PrintWMInKey:'^FS':CRLF$		
                ZPL := '^BY2,3.0':CRLF$
                ZPL := '^FO700,70^AC,18^FDQty:^FS':CRLF$						
                ZPL := '^FO760,70^A045,25^FD':WfrQty:'^FS':CRLF$				
                ****** Third Line
                ZPL := '^FO30,105^AC,18^FDShip Dt:^FS'	
                ZPL := '^FO140,105^A0,,25^FD':ReqShipDate:'^FS':CRLF$		
                ZPL := '^FO250,105^AC,18^FDCust:^FS':CRLF$					
                ZPL := '^FO320,105^A045,25^FD':CustName:'^FS':CRLF$			
                ZPL := '^FO560,105^AC,18^FDPO:^FS':CRLF$
                ZPL := '^FO600,105^A045,25^FD':PONo:'^FS':CRLF$
                ****** Separator bar
                ZPL := '^FO15,128^GB795,1^FS'
                ****** Fourth Line
                ZPL := '^FO30,135^AC,18^FDLot No:^FS':CRLF$
                ZPL := '^FO120,135^A0,20^FD':LotNo:'^FS':CRLF$
                ZPL := '^FO410,135^AC,18^FDPart No:^FS':CRLF$
                ZPL := '^FO510,135^A0,20^FD':PartNo:'^FS':CRLF$
                ****** Fifth Line
                ZPL := '^FO30,160^AC,18^FDSub PN:^FS':CRLF$
                ZPL := '^FO120,160^A0,20^FD':SubPartNo:'^FS':CRLF$
                IF SuppCd NE '' THEN
                    ZPL := '^FO410,160^AC,18^FDSupp Cd:^FS':CRLF$			
                    ZPL := '^FO510,160^A0,20^FD':SuppCd:'^FS':CRLF$		
                    ZPL := '^BY2,2.0':CRLF$
                    ZPL := '^FO610,160^A040,20^B3,,23,N^FD':SuppCd:'^FS':CRLF$
                    ZPL := '^BY2,3.0':CRLF$
                END
                ****** Separator bar
                ZPL := '^FO15,183^GB795,1^FS'
                ****** Seventh Line
                ZPL := '^FO30,210^AC,18^FDPre:^FS':CRLF$
                ZPL := '^FO90,210^A0,20^FD':SubPreClean:'^FS':CRLF$
                IF PreAkrionRecipe NE '' THEN
                    ZPL := '^FO210,210^AC,18^FDAkrion:^FS':CRLF$
                    ZPL := '^FO300,210^A0,20^FD':PreAkrionRecipe:'^FS':CRLF$
                    ZPL := '^BY2,2.0':CRLF$
                    ZPL := '^FO450,210^A040,20^B3,,23,N^FD':PreAkrionRecipe:'^FS':CRLF$
                    ZPL := '^BY2,3.0':CRLF$
                END
                ****** Eighth Line
                ZPL := '^FO30,235^AC,18^FDRecipe:^FS':CRLF$
                ZPL := '^FO120,235^A0,20^FD':RecipeInfo:'^FS':CRLF$
                ZPL := '^FO410,235^AC,18^FDEpi Dopant:^FS':CRLF$
                ZPL := '^FO560,235^A040,20^FD':Dopant:'^FS':CRLF$
                ****** Tenth, Eleventh and Twelfth Lines
                ZPL := '^FO30,285^AC,18^FDThk Spec:^FS':CRLF$
                FOR M = 1 TO COUNT(PrintThickTargets,@VM) + (PrintThickTargets NE '')
                    BaseLine = 285 + (M-1)*20
                    ZPL := '^FO140,':BaseLine:'^A040,20^FD':PrintThickTargets<1,M>:'^FS':CRLF$
                NEXT M
                ZPL := '^FO410,285^AC,18^FDRes Spec:^FS':CRLF$
                FOR M = 1 TO COUNT(PrintResTargets,@VM) + (PrintResTargets NE '')
                    BaseLine = 285 + (M-1)*20
                    ZPL := '^FO520,':BaseLine:'^A040,20^FD':PrintResTargets<1,M>:'^FS':CRLF$
                NEXT M
                ****** Separator bar
                ZPL := '^FO15,373^GB795,1^FS'
                ****** Fourteenth Line
                ZPL := '^FO30,385^AC,18^FDProd Spec No:^FS':CRLF$			
                ZPL := '^FO190,385^A0,25^FD':PSNo:'^FS':CRLF$				
                IF SpecType = 'Production' THEN
                    SpecType = 'Prod'
                end else IF SpecType = 'Pre-Production' THEN
                    SpecType = 'Pre'
                end
                ZPL := '^FO275,385^AC,18^FDSpec Type:^FS':CRLF$				
                ZPL := '^FO400,385^A0,25^FD':SpecType:'^FS':CRLF$
                ****** Data Matrix barcode
                ZPL	:= '^FO720,280^CI28':CRLF$
                ZPL	:= '^BXN,2,200^FDP':PartNo:'|S':SubPartNo:'|1T':PrintWMInKey:'|2T':LotNo:'|':PSNo:'|Q':WfrQty:'|1V':Vendor:'|SEQ':Cnt:'^FS':CRLF$
                ZPL:= '^XZ'
            Next
            * Check to see if they get the monitor wafers based on the PSN
            MonitorWafer = CustEpiPartRec<CUST_EPI_PART_MONITOR_WAFER_LABEL$>
            IF MonitorWafer THEN
                RDSIds  = ''
                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    := '^MMT':CRLF$										;* Media mode t=tear off mode
                ZPL    := '^FO30,30^A060,40^FDMonitor Wafer^FS':CRLF$
                ZPL    := '^FO550,30^A060,40^FDReactor#_____^FS':CRLF$
                ZPL    := '^FO30,70^A060,40^FDDate_____________^FS':CRLF$
                ZPL    := '^FO30,130^A060,40^FDCustomer Name: ':CustName<I>:'^FS':CRLF$
                ZPL    := '^FO30,190^A060,40^FDRDS#: ':RDSIds<I>:'^FS':CRLF$
                ZPL    := '^FO400,190^A060,40^FDLot#: ':LotNo<I>:'^FS':CRLF$
                ZPL    := '^FO30,250^A060,40^FDThick Avg__________^FS':CRLF$
                ZPL    := '^FO400,250^A060,40^FDWafer Type__________^FS':CRLF$		
                ZPL    := '^FO30,280^A060,40^FDRes   Avg____________^FS':CRLF$
                ZPL    := '^FO400,280^A060,40^FDWafer Type__________^FS':CRLF$
                ZPL    := '^XZ'
            END
        end else
            ErrorMsg = 'Error in ':Service:' service. WM_IN ':WmInKey:' does not exist.'
        end
    end else
        ErrorMsg = 'Error in ':Service:' service. Null WmInKey passed in.'
    end
    
    If ErrorMsg EQ '' then
        Response = ZPL
    end else
        Error_Services('Add', ErrorMsg)
    end
    
end service


Service ConvertRecordToJSON(KeyID, Record, ItemURL)
    
    jsonRecord    = ''

    If KeyID NE '' then

        If Record EQ '' then Record = Database_Services('ReadDataRow', 'WM_IN', KeyID)
        If Error_Services('NoError') then
            @DICT   = Database_Services('GetTableHandle', 'DICT.WM_IN')
            @ID     = KeyID
            @RECORD = Record
            objJSON = ''
            If SRP_JSON(objJSON, 'New', 'Object') then
                objWmIn = ''
                If SRP_JSON(objWmIn, 'New', 'Object') then 
                    SRP_JSON(objWmIn, 'SetValue', 'keyId', @ID)
                    SRP_JSON(objJSON, 'Set', 'wmIn', objWmIn)
                    SRP_JSON(objWmIn, '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
                Error_Services('Add', 'Unable to create JSON representation in the ' : Service : ' service.')
            end
        end
    end else
        Error_Services('Add', 'KeyID argument was missing in the ' : Service : ' service.')
    end

    Response    = jsonRecord
    
End Service

