VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "Account"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = True
Attribute VB_Description = "APE MTS Transaction Service (Account)"
Option Explicit

Implements APEInterfaces.IMTSAccount

Private Const E_NOTIMPL = &H80004001
Private Const mlSTARTINGBALANCE = 1000000     ' Starting

Public Sub Post(sConnect As String, eConnectOptions As ape_DbConnectionOptions, lAccountNo As Long, lAmount As Long)
    '-------------------------------------------------------------------------
    'Purpose:
    '   Provides an interface for late binding.  Late binding is only provided
    '   for test comparison.  Other custom services should only use the implemented
    '   interface.
    '-------------------------------------------------------------------------
    IMTSAccount_Post sConnect, eConnectOptions, lAccountNo, lAmount
End Sub

Private Sub IMTSAccount_Post(sConnect As String, eConnectOptions As APEInterfaces.ape_DbConnectionOptions, lAccountNo As Long, lAmount As Long)
    Dim lBalance As Long
    Dim sSQLUpdate As String
    Dim sSQLBalance As String
    
    ' Get our object context
    Dim ctxObject As ObjectContext
    Set ctxObject = GetObjectContext()
    
    sSQLUpdate = "UPDATE Account SET Balance = Balance + " + Str$(lAmount) + " WHERE AccountNo = " + Str$(lAccountNo)
    sSQLBalance = "SELECT Balance FROM Account WHERE AccountNo = " + Str$(lAccountNo)
    On Error GoTo PostError
    
    Select Case eConnectOptions
        Case ape_DbConnectionOptions.ape_idcADO
            If lAmount < 0 Then     ' If debit, then get balance
                PostADO sConnect, sSQLUpdate, sSQLBalance, lBalance
            Else
                PostADO sConnect, sSQLUpdate, sSQLBalance
            End If
        Case ape_DbConnectionOptions.ape_idcRDO
            If lAmount < 0 Then     ' If debit, then get balance
                PostRDO sConnect, sSQLUpdate, sSQLBalance, lBalance
            Else
                PostRDO sConnect, sSQLUpdate, sSQLBalance
            End If
        Case ape_DbConnectionOptions.ape_idcDAO
            If lAmount < 0 Then     ' If debit, then get balance
                PostDAO sConnect, sSQLUpdate, sSQLBalance, lBalance
            Else
                PostDAO sConnect, sSQLUpdate, sSQLBalance
            End If
        Case ape_DbConnectionOptions.ape_idcODBC
            If lAmount < 0 Then     ' If debit, then get balance
                PostODBCAPI sConnect, sSQLUpdate, sSQLBalance, lBalance
            Else
                PostODBCAPI sConnect, sSQLUpdate, sSQLBalance
            End If
        Case Else
            Err.Raise E_NOTIMPL
    End Select
    If lAmount < 0 And lBalance < 0 Then   ' If a debit and acount is overdrawn
        IMTSAccount_Post sConnect, eConnectOptions, lAccountNo, mlSTARTINGBALANCE         ' Give a new starting balance
    End If

    ctxObject.SetComplete          ' Transaction completed
    Exit Sub

PostError:
    Dim lErrorNumber As Long
    Dim sErrorDescription As String
    sErrorDescription = Err.Description
    lErrorNumber = Err.Number
    ctxObject.SetAbort              ' Transaction aborted
    ' Need to explicitly set the error source to AEMTSSvc
    Err.Raise lErrorNumber, "AEMTSSvc", LoadResString(ERROR_POSTING_TO_ACCOUNT) & " (" & sErrorDescription & ")"
End Sub

Private Sub PostADO(sConnect As String, sSQLUpdate As String, sSQLBalance As String, Optional lBalance As Long)
    ' If adoConnection throws an exception
    On Error GoTo PostadoError
    
    ' Obtain the ADO connection
    Dim adoConn As New ADODB.Connection
    adoConn.Open sConnect
    
    ' Update the balance
    adoConn.Execute sSQLUpdate
    If Not IsMissing(lBalance) Then
        ' Get resulting balance which may have been further updated via triggers
        Dim adoRS As ADODB.Recordset
        Set adoRS = adoConn.Execute(sSQLBalance)
        If Not adoRS.EOF Then
            lBalance = adoRS.Fields("Balance").Value
        Else
            Err.Raise errInvalidAccount
        End If
        adoRS.Close
    End If
    
    adoConn.Close
    Exit Sub

PostadoError:
    Dim lErrorNumber As Long
    Dim sErrorDescription As String
    sErrorDescription = Err.Description
    lErrorNumber = Err.Number
    If Not adoRS Is Nothing Then
        adoRS.Close
    End If
    If Not adoConn Is Nothing Then
        adoConn.Close
    End If
    If lErrorNumber <> 0 Then
        Err.Raise lErrorNumber, , sErrorDescription
    End If
End Sub

Private Sub PostRDO(sConnect As String, sSQLUpdate As String, sSQLBalance As String, Optional lBalance As Long)
    ' If rdoConnection throws an exception
    On Error GoTo PostRDOError
    
    ' Obtain the RDO environment and connection
    Dim rdoConn As rdoConnection
    Set rdoConn = rdoEngine.rdoEnvironments(0).OpenConnection("", rdDriverNoPrompt, False, sConnect)

    ' Update the balance
    rdoConn.Execute sSQLUpdate
  
    If Not IsMissing(lBalance) Then
        ' Get resulting balance which may have been further updated via triggers
        Dim rdoRS As rdoResultset
        Set rdoRS = rdoConn.OpenResultset(sSQLBalance)
        If Not rdoRS.EOF Then
            lBalance = rdoRS.rdoColumns("Balance")
        Else
            Err.Raise errInvalidAccount
        End If
        rdoRS.Close
    End If
    
    rdoConn.Close
    Exit Sub

PostRDOError:
    Dim lErrorNumber As Long
    Dim sErrorDescription As String
    sErrorDescription = Err.Description
    lErrorNumber = Err.Number
    If Not rdoRS Is Nothing Then
        rdoRS.Close
    End If
    If Not rdoConn Is Nothing Then
        rdoConn.Close
    End If
    If lErrorNumber <> 0 Then
        Err.Raise lErrorNumber, , sErrorDescription
    End If
End Sub

Private Sub PostDAO(sConnect As String, sSQLUpdate As String, sSQLBalance As String, Optional lBalance As Long)
    ' If daoConnection throws an exception
    On Error GoTo PostDAOError
    
    ' Obtain the DAO workspace and connection
    Dim daoWorkspace As Workspace
    Dim daoConn As Connection
    Set daoWorkspace = CreateWorkspace("", "", "", dbUseODBC)
    Set daoConn = daoWorkspace.OpenConnection("", dbDriverNoPrompt, False, "ODBC;" & sConnect)

    ' Update the balance
    daoConn.Execute sSQLUpdate
  
    If Not IsMissing(lBalance) Then
        ' Get resulting balance which may have been further updated via triggers
        Dim daoRS As Recordset
        Set daoRS = daoConn.OpenRecordset(sSQLBalance)
        If Not daoRS.EOF Then
            lBalance = daoRS.Fields("Balance").Value
        Else
            Err.Raise errInvalidAccount
        End If
        daoRS.Close
    End If
    
    daoConn.Close
    Exit Sub

PostDAOError:
    Dim lErrorNumber As Long
    Dim sErrorDescription As String
    sErrorDescription = Err.Description
    lErrorNumber = Err.Number
    If Not daoRS Is Nothing Then
        daoRS.Close
    End If
    If Not daoConn Is Nothing Then
        daoConn.Close
    End If
    If lErrorNumber <> 0 Then
        Err.Raise lErrorNumber, , sErrorDescription
    End If
End Sub

Private Sub PostODBCAPI(sConnect As String, sSQLUpdate As String, sSQLBalance As String, Optional lBalance As Long)
    ' Handles for the ODBC API calls
    Dim hEnvironment As Long
    Dim hConnection As Long
    Dim hStatement As Long
    Dim iConnectLength As Integer

    On Error GoTo PostODBCAPIError
    If Not ODBCAPICallSuccessful(SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, hEnvironment)) Then
         Err.Raise ErrorAllocateHandle, , LoadResString(ErrorAllocateHandle)
    End If
    If Not ODBCAPICallSuccessful(SQLSetEnvAttrLong(hEnvironment, SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3, 0)) Then
        Err.Raise ErrorSetAttribute, , LoadResString(ErrorSetAttribute)
    End If
    If Not ODBCAPICallSuccessful(SQLAllocHandle(SQL_HANDLE_DBC, hEnvironment, hConnection)) Then
        Err.Raise ErrorAllocateHandle, , LoadResString(ErrorAllocateHandle)
    End If

    If Not ODBCAPICallSuccessful(SQLDriverConnect(hConnection, 0, sConnect, _
            LenB(sConnect), vbNullString, 0, iConnectLength, SQL_DRIVER_NOPROMPT)) Then
        Err.Raise ErrorConnectDriver, , LoadResString(ErrorConnectDriver)
    End If
    If Not ODBCAPICallSuccessful(SQLAllocHandle(SQL_HANDLE_STMT, hConnection, hStatement)) Then
        Err.Raise ErrorAllocateHandle, , LoadResString(ErrorAllocateHandle)
    End If
    If Not ODBCAPICallSuccessful(SQLSetStmtAttrLong(hStatement, SQL_CURSOR_TYPE, SQL_CURSOR_STATIC, 0)) Then
        Err.Raise ErrorSetAttribute, , LoadResString(ErrorSetAttribute)
    End If
    
    If Not ODBCAPICallSuccessful(SQLExecDirect(hStatement, sSQLUpdate, Len(sSQLUpdate))) Then
        ' See if the error is due to a resource deadlock
        Dim iRecNum As Integer, iTextLen As Integer
        Dim sSQLState As String * 5, sMsgText As String * 1
        Dim lNativeErrorPtr As Long
        iRecNum = 1
        Do While ODBCAPICallSuccessful(SQLGetDiagRec(SQL_HANDLE_STMT, hStatement, iRecNum, sSQLState, lNativeErrorPtr, _
                sMsgText, 0, iTextLen))
            If sSQLState = "40001" Then
                Err.Raise ErrorResourceDeadlock, , LoadResString(ErrorResourceDeadlock)
            End If
            iRecNum = iRecNum + 1
        Loop
        ' Else, raise generic error
        Err.Raise ErrorExecuteQuery, , LoadResString(ErrorExecuteQuery)
    End If
    
    If Not IsMissing(lBalance) Then
        ' Get resulting balance
        If Not ODBCAPICallSuccessful(SQLExecDirect(hStatement, sSQLBalance, Len(sSQLBalance))) Then
             Err.Raise ErrorExecuteQuery, , LoadResString(ErrorExecuteQuery)
        End If
        
        Select Case SQLFetchScroll(hStatement, SQL_FETCH_FIRST, 0)
            Case SQL_NO_DATA
                Err.Raise ErrorFetchRecord, , LoadResString(ErrorFetchRecord)
            Case SQL_SUCCESS, SQL_SUCCESS_WITH_INFO
                ' Do nothing
            Case Else
                 Err.Raise ErrorFetchRecord, , LoadResString(ErrorFetchRecord)
        End Select

        Dim lBalanceLength As Long
        If Not ODBCAPICallSuccessful(SQLGetDataLong(hStatement, 1, SQL_INTEGER, lBalance, 0, lBalanceLength)) Then
             Err.Raise ErrorGetData, , LoadResString(ErrorGetData)
        End If
    End If
        
PostODBCAPIError:
    Dim lErrorNumber As Long
    Dim sErrorDescription As String
    sErrorDescription = Err.Description
    lErrorNumber = Err.Number
    SQLCloseCursor hStatement
    SQLFreeHandle SQL_HANDLE_STMT, hStatement
    SQLDisconnect hConnection
    SQLFreeHandle SQL_HANDLE_DBC, hConnection
    SQLFreeHandle SQL_HANDLE_ENV, hEnvironment
    If lErrorNumber <> 0 Then
        Err.Raise lErrorNumber, , sErrorDescription
    End If
End Sub
