Excel desktop POC: direct SQL via VBA, safe mode launcher
- GLAccountsExcelApi.bas: RefreshGLApi reads Setup!B6:B9 directly, calls PostgreSQL via ADODB without sheet.Evaluate or live UDF formulas - Connection string pointed to vps107.geneva.hosting / postgres - GL_Accounts_Client_Template.xlsx: fixed formula text cell refs (B6 not B7) - Ouvrir_Excel_SafeMode.bat: launches Excel /safe with template to bypass print spooler freeze (splwow64 deadlock on edit mode entry) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,28 +1,36 @@
|
|||||||
Attribute VB_Name = "GLAccountsExcelApi"
|
Attribute VB_Name = "GLAccountsExcelApi"
|
||||||
Option Explicit
|
Option Explicit
|
||||||
|
|
||||||
' Finaccount-like Excel API POC for GL ACCOUNTS FUNCTIONS.
|
' Excel API POC for GL ACCOUNTS FUNCTIONS.
|
||||||
'
|
'
|
||||||
' Setup:
|
' Setup:
|
||||||
' 1. Install PostgreSQL ODBC driver.
|
' 1. Install PostgreSQL ODBC driver.
|
||||||
' 2. Apply notes/accounting/excel_api/gl_accounts_sql_poc.sql.
|
' 2. Apply notes/accounting/excel_api/gl_accounts_sql_poc.sql.
|
||||||
' 3. In Excel: Developer > Visual Basic > File > Import File...
|
' 3. In Excel: Developer > Visual Basic > File > Import File...
|
||||||
' 4. Import this .bas module.
|
' 4. Import this .bas module.
|
||||||
' 5. Edit GL_API_CONNECTION_STRING below.
|
' 5. Edit GL_API_CONNECTION_STRING below (or use a named DSN).
|
||||||
'
|
'
|
||||||
' Example:
|
' Usage:
|
||||||
' =GetAccountAmount("COMPANY01","400000","USD",DATE(2026,5,31))
|
' - On the Setup sheet, fill in B6 (company), B7 (account), B8 (currency), B9 (date).
|
||||||
|
' - Run RefreshGLApi to query the database and populate the Functions sheet.
|
||||||
|
' - Excel stays in manual calculation mode after refresh; edit Setup freely.
|
||||||
|
'
|
||||||
|
' DSN example:
|
||||||
|
' "DSN=TradonGLApi;Uid=tradon_readonly;Pwd=change_me;"
|
||||||
|
|
||||||
Private Const GL_API_CONNECTION_STRING As String = _
|
Private Const GL_API_CONNECTION_STRING As String = _
|
||||||
"Driver={PostgreSQL Unicode(x64)};" & _
|
"Driver={PostgreSQL Unicode(x64)};" & _
|
||||||
"Server=localhost;" & _
|
"Server=vps107.geneva.hosting;" & _
|
||||||
"Port=5432;" & _
|
"Port=5432;" & _
|
||||||
"Database=tradon;" & _
|
"Database=tradon;" & _
|
||||||
"Uid=tradon_readonly;" & _
|
"Uid=postgres;" & _
|
||||||
"Pwd=change_me;"
|
"Pwd=dsproject;"
|
||||||
|
|
||||||
Private gConnection As Object
|
Private gConnection As Object
|
||||||
Private gCache As Object
|
Private gCache As Object
|
||||||
|
Private gApiEnabled As Boolean
|
||||||
|
|
||||||
|
' ---- Connection helpers ----
|
||||||
|
|
||||||
Private Function ApiCache() As Object
|
Private Function ApiCache() As Object
|
||||||
If gCache Is Nothing Then
|
If gCache Is Nothing Then
|
||||||
@@ -35,6 +43,8 @@ Private Function DbConnection() As Object
|
|||||||
If gConnection Is Nothing Then
|
If gConnection Is Nothing Then
|
||||||
Set gConnection = CreateObject("ADODB.Connection")
|
Set gConnection = CreateObject("ADODB.Connection")
|
||||||
gConnection.ConnectionString = GL_API_CONNECTION_STRING
|
gConnection.ConnectionString = GL_API_CONNECTION_STRING
|
||||||
|
gConnection.ConnectionTimeout = 5
|
||||||
|
gConnection.CommandTimeout = 30
|
||||||
gConnection.Open
|
gConnection.Open
|
||||||
ElseIf gConnection.State = 0 Then
|
ElseIf gConnection.State = 0 Then
|
||||||
gConnection.Open
|
gConnection.Open
|
||||||
@@ -62,15 +72,87 @@ Public Sub CloseGLApiConnection()
|
|||||||
Set gConnection = Nothing
|
Set gConnection = Nothing
|
||||||
End Sub
|
End Sub
|
||||||
|
|
||||||
Private Function SqlDate(ByVal valueDate As Variant) As String
|
' ---- Main macro ----
|
||||||
SqlDate = Format$(CDate(valueDate), "yyyy-mm-dd")
|
|
||||||
End Function
|
|
||||||
|
|
||||||
Private Function SqlText(ByVal value As Variant) As String
|
' Reads Setup!B6:B9, queries the database directly, writes plain values to Functions!C6:C13.
|
||||||
SqlText = Replace(CStr(value), "'", "''")
|
' Excel stays in xlCalculationManual after this runs -- edit Setup cells freely,
|
||||||
End Function
|
' then run RefreshGLApi again when you want fresh results.
|
||||||
|
Public Sub RefreshGLApi()
|
||||||
|
Dim wsSetup As Worksheet
|
||||||
|
Dim wsFunctions As Worksheet
|
||||||
|
|
||||||
Private Function QueryScalar( _
|
On Error Resume Next
|
||||||
|
Set wsSetup = ThisWorkbook.Worksheets("Setup")
|
||||||
|
Set wsFunctions = ThisWorkbook.Worksheets("Functions")
|
||||||
|
On Error GoTo 0
|
||||||
|
|
||||||
|
If wsSetup Is Nothing Then
|
||||||
|
MsgBox "Sheet 'Setup' not found.", vbExclamation
|
||||||
|
Exit Sub
|
||||||
|
End If
|
||||||
|
If wsFunctions Is Nothing Then
|
||||||
|
MsgBox "Sheet 'Functions' not found.", vbExclamation
|
||||||
|
Exit Sub
|
||||||
|
End If
|
||||||
|
|
||||||
|
Application.EnableEvents = False
|
||||||
|
Application.ScreenUpdating = False
|
||||||
|
Application.Calculation = xlCalculationManual
|
||||||
|
|
||||||
|
Dim company As String
|
||||||
|
Dim account As String
|
||||||
|
Dim currency As String
|
||||||
|
Dim valueDate As Variant
|
||||||
|
|
||||||
|
company = CStr(wsSetup.Range("B6").Value)
|
||||||
|
account = CStr(wsSetup.Range("B7").Value)
|
||||||
|
currency = CStr(wsSetup.Range("B8").Value)
|
||||||
|
valueDate = wsSetup.Range("B9").Value
|
||||||
|
|
||||||
|
ClearGLApiCache
|
||||||
|
|
||||||
|
On Error GoTo RefreshError
|
||||||
|
|
||||||
|
wsFunctions.Range("C6").Value = RunQuery("get_account_base_amount", company, account, currency, valueDate)
|
||||||
|
wsFunctions.Range("C7").Value = RunQuery("get_account_real_base_amount", company, account, currency, valueDate)
|
||||||
|
wsFunctions.Range("C8").Value = RunQuery("get_account_amount", company, account, currency, valueDate)
|
||||||
|
wsFunctions.Range("C9").Value = RunQuery("get_account_abbr", company, account, currency, valueDate)
|
||||||
|
wsFunctions.Range("C10").Value = RunQuery("get_account_name", company, account, currency, valueDate)
|
||||||
|
wsFunctions.Range("C11").Value = RunQuery("get_account_contact", company, account, currency, valueDate)
|
||||||
|
wsFunctions.Range("C12").Value = RunQuery("get_account_contact_code", company, account, currency, valueDate)
|
||||||
|
wsFunctions.Range("C13").Value = RunQuery("get_account_contact_name", company, account, currency, valueDate)
|
||||||
|
|
||||||
|
CloseGLApiConnection
|
||||||
|
GoTo RefreshDone
|
||||||
|
|
||||||
|
RefreshError:
|
||||||
|
MsgBox "RefreshGLApi error: " & Err.Description, vbExclamation
|
||||||
|
|
||||||
|
RefreshDone:
|
||||||
|
Application.ScreenUpdating = True
|
||||||
|
Application.EnableEvents = True
|
||||||
|
' Intentionally keep xlCalculationManual -- prevents any live recalculation.
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
' Resets Functions!C6:C13 to placeholder text.
|
||||||
|
Public Sub ClearGLApiResults()
|
||||||
|
Dim wsFunctions As Worksheet
|
||||||
|
On Error Resume Next
|
||||||
|
Set wsFunctions = ThisWorkbook.Worksheets("Functions")
|
||||||
|
On Error GoTo 0
|
||||||
|
If wsFunctions Is Nothing Then Exit Sub
|
||||||
|
|
||||||
|
Application.EnableEvents = False
|
||||||
|
Dim r As Long
|
||||||
|
For r = 6 To 13
|
||||||
|
wsFunctions.Cells(r, 3).Value = "-"
|
||||||
|
Next r
|
||||||
|
Application.EnableEvents = True
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
' ---- Core SQL runner ----
|
||||||
|
|
||||||
|
Private Function RunQuery( _
|
||||||
ByVal functionName As String, _
|
ByVal functionName As String, _
|
||||||
ByVal companyKey As Variant, _
|
ByVal companyKey As Variant, _
|
||||||
ByVal accountCode As Variant, _
|
ByVal accountCode As Variant, _
|
||||||
@@ -84,7 +166,7 @@ Private Function QueryScalar( _
|
|||||||
& "|" & CStr(currencyIso) & "|" & SqlDate(valueDate)
|
& "|" & CStr(currencyIso) & "|" & SqlDate(valueDate)
|
||||||
|
|
||||||
If ApiCache.Exists(key) Then
|
If ApiCache.Exists(key) Then
|
||||||
QueryScalar = ApiCache(key)
|
RunQuery = ApiCache(key)
|
||||||
Exit Function
|
Exit Function
|
||||||
End If
|
End If
|
||||||
|
|
||||||
@@ -102,13 +184,13 @@ RetryQuery:
|
|||||||
rs.Open sql, DbConnection, 0, 1
|
rs.Open sql, DbConnection, 0, 1
|
||||||
|
|
||||||
If rs.EOF Or IsNull(rs.Fields(0).Value) Then
|
If rs.EOF Or IsNull(rs.Fields(0).Value) Then
|
||||||
QueryScalar = Empty
|
RunQuery = Empty
|
||||||
Else
|
Else
|
||||||
QueryScalar = rs.Fields(0).Value
|
RunQuery = rs.Fields(0).Value
|
||||||
End If
|
End If
|
||||||
rs.Close
|
rs.Close
|
||||||
|
|
||||||
ApiCache.Add key, QueryScalar
|
ApiCache.Add key, RunQuery
|
||||||
Exit Function
|
Exit Function
|
||||||
|
|
||||||
ErrorHandler:
|
ErrorHandler:
|
||||||
@@ -120,16 +202,41 @@ ErrorHandler:
|
|||||||
ResetDbConnection
|
ResetDbConnection
|
||||||
Resume RetryQuery
|
Resume RetryQuery
|
||||||
End If
|
End If
|
||||||
QueryScalar = "#ERR " & Err.Number & ": " & Err.Description
|
RunQuery = "#ERR " & Err.Number & ": " & Err.Description
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
|
Private Function SqlDate(ByVal valueDate As Variant) As String
|
||||||
|
SqlDate = Format$(CDate(valueDate), "yyyy-mm-dd")
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Private Function SqlText(ByVal value As Variant) As String
|
||||||
|
SqlText = Replace(CStr(value), "'", "''")
|
||||||
|
End Function
|
||||||
|
|
||||||
|
' ---- UDF functions (optional, for manual formula use) ----
|
||||||
|
'
|
||||||
|
' These functions are NOT called by RefreshGLApi.
|
||||||
|
' They are available if you want to type a live formula directly in a cell,
|
||||||
|
' but you must first run EnableGLApi and keep Excel in manual calculation mode.
|
||||||
|
' Warning: live UDF formulas in cells can freeze Excel on recalculation.
|
||||||
|
|
||||||
|
Public Sub EnableGLApi()
|
||||||
|
gApiEnabled = True
|
||||||
|
Application.Calculation = xlCalculationManual
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Public Sub DisableGLApi()
|
||||||
|
gApiEnabled = False
|
||||||
|
CloseGLApiConnection
|
||||||
|
End Sub
|
||||||
|
|
||||||
Public Function GetAccountBaseAmount( _
|
Public Function GetAccountBaseAmount( _
|
||||||
ByVal companyKey As Variant, _
|
ByVal companyKey As Variant, _
|
||||||
ByVal accountCode As Variant, _
|
ByVal accountCode As Variant, _
|
||||||
ByVal currencyIso As Variant, _
|
ByVal currencyIso As Variant, _
|
||||||
ByVal valueDate As Variant) As Variant
|
ByVal valueDate As Variant) As Variant
|
||||||
GetAccountBaseAmount = QueryScalar("get_account_base_amount", _
|
If Not gApiEnabled Then GetAccountBaseAmount = "#GLAPI_OFF": Exit Function
|
||||||
companyKey, accountCode, currencyIso, valueDate)
|
GetAccountBaseAmount = RunQuery("get_account_base_amount", companyKey, accountCode, currencyIso, valueDate)
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Public Function GetAccountRealBaseAmount( _
|
Public Function GetAccountRealBaseAmount( _
|
||||||
@@ -137,8 +244,8 @@ Public Function GetAccountRealBaseAmount( _
|
|||||||
ByVal accountCode As Variant, _
|
ByVal accountCode As Variant, _
|
||||||
ByVal currencyIso As Variant, _
|
ByVal currencyIso As Variant, _
|
||||||
ByVal valueDate As Variant) As Variant
|
ByVal valueDate As Variant) As Variant
|
||||||
GetAccountRealBaseAmount = QueryScalar("get_account_real_base_amount", _
|
If Not gApiEnabled Then GetAccountRealBaseAmount = "#GLAPI_OFF": Exit Function
|
||||||
companyKey, accountCode, currencyIso, valueDate)
|
GetAccountRealBaseAmount = RunQuery("get_account_real_base_amount", companyKey, accountCode, currencyIso, valueDate)
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Public Function GetAccountAmount( _
|
Public Function GetAccountAmount( _
|
||||||
@@ -146,8 +253,8 @@ Public Function GetAccountAmount( _
|
|||||||
ByVal accountCode As Variant, _
|
ByVal accountCode As Variant, _
|
||||||
ByVal currencyIso As Variant, _
|
ByVal currencyIso As Variant, _
|
||||||
ByVal valueDate As Variant) As Variant
|
ByVal valueDate As Variant) As Variant
|
||||||
GetAccountAmount = QueryScalar("get_account_amount", _
|
If Not gApiEnabled Then GetAccountAmount = "#GLAPI_OFF": Exit Function
|
||||||
companyKey, accountCode, currencyIso, valueDate)
|
GetAccountAmount = RunQuery("get_account_amount", companyKey, accountCode, currencyIso, valueDate)
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Public Function GetAccountAbbr( _
|
Public Function GetAccountAbbr( _
|
||||||
@@ -155,8 +262,8 @@ Public Function GetAccountAbbr( _
|
|||||||
ByVal accountCode As Variant, _
|
ByVal accountCode As Variant, _
|
||||||
ByVal currencyIso As Variant, _
|
ByVal currencyIso As Variant, _
|
||||||
ByVal valueDate As Variant) As Variant
|
ByVal valueDate As Variant) As Variant
|
||||||
GetAccountAbbr = QueryScalar("get_account_abbr", _
|
If Not gApiEnabled Then GetAccountAbbr = "#GLAPI_OFF": Exit Function
|
||||||
companyKey, accountCode, currencyIso, valueDate)
|
GetAccountAbbr = RunQuery("get_account_abbr", companyKey, accountCode, currencyIso, valueDate)
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Public Function GetAccountName( _
|
Public Function GetAccountName( _
|
||||||
@@ -164,8 +271,8 @@ Public Function GetAccountName( _
|
|||||||
ByVal accountCode As Variant, _
|
ByVal accountCode As Variant, _
|
||||||
ByVal currencyIso As Variant, _
|
ByVal currencyIso As Variant, _
|
||||||
ByVal valueDate As Variant) As Variant
|
ByVal valueDate As Variant) As Variant
|
||||||
GetAccountName = QueryScalar("get_account_name", _
|
If Not gApiEnabled Then GetAccountName = "#GLAPI_OFF": Exit Function
|
||||||
companyKey, accountCode, currencyIso, valueDate)
|
GetAccountName = RunQuery("get_account_name", companyKey, accountCode, currencyIso, valueDate)
|
||||||
End Function
|
End Function
|
||||||
|
|
||||||
Public Function GetAccountContact( _
|
Public Function GetAccountContact( _
|
||||||
@@ -173,6 +280,24 @@ Public Function GetAccountContact( _
|
|||||||
ByVal accountCode As Variant, _
|
ByVal accountCode As Variant, _
|
||||||
ByVal currencyIso As Variant, _
|
ByVal currencyIso As Variant, _
|
||||||
ByVal valueDate As Variant) As Variant
|
ByVal valueDate As Variant) As Variant
|
||||||
GetAccountContact = QueryScalar("get_account_contact", _
|
If Not gApiEnabled Then GetAccountContact = "#GLAPI_OFF": Exit Function
|
||||||
companyKey, accountCode, currencyIso, valueDate)
|
GetAccountContact = RunQuery("get_account_contact", companyKey, accountCode, currencyIso, valueDate)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function GetAccountContactCode( _
|
||||||
|
ByVal companyKey As Variant, _
|
||||||
|
ByVal accountCode As Variant, _
|
||||||
|
ByVal currencyIso As Variant, _
|
||||||
|
ByVal valueDate As Variant) As Variant
|
||||||
|
If Not gApiEnabled Then GetAccountContactCode = "#GLAPI_OFF": Exit Function
|
||||||
|
GetAccountContactCode = RunQuery("get_account_contact_code", companyKey, accountCode, currencyIso, valueDate)
|
||||||
|
End Function
|
||||||
|
|
||||||
|
Public Function GetAccountContactName( _
|
||||||
|
ByVal companyKey As Variant, _
|
||||||
|
ByVal accountCode As Variant, _
|
||||||
|
ByVal currencyIso As Variant, _
|
||||||
|
ByVal valueDate As Variant) As Variant
|
||||||
|
If Not gApiEnabled Then GetAccountContactName = "#GLAPI_OFF": Exit Function
|
||||||
|
GetAccountContactName = RunQuery("get_account_contact_name", companyKey, accountCode, currencyIso, valueDate)
|
||||||
End Function
|
End Function
|
||||||
|
|||||||
BIN
notes/accounting/excel_api/GL_Accounts_Client_Template.xlsx
Normal file
BIN
notes/accounting/excel_api/GL_Accounts_Client_Template.xlsx
Normal file
Binary file not shown.
2
notes/accounting/excel_api/Ouvrir_Excel_SafeMode.bat
Normal file
2
notes/accounting/excel_api/Ouvrir_Excel_SafeMode.bat
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
@echo off
|
||||||
|
start "" "C:\Program Files\Microsoft Office\root\Office16\EXCEL.EXE" /safe "%~dp0GL_Accounts_Client_Template.xlsx"
|
||||||
@@ -7,6 +7,17 @@ This POC covers only the PDF section **GL ACCOUNTS FUNCTIONS**.
|
|||||||
- `gl_accounts_sql_poc.sql`: PostgreSQL schema/view/functions.
|
- `gl_accounts_sql_poc.sql`: PostgreSQL schema/view/functions.
|
||||||
- `GLAccountsExcelApi.bas`: VBA module exposing Excel formulas.
|
- `GLAccountsExcelApi.bas`: VBA module exposing Excel formulas.
|
||||||
- `GL_Accounts_POC.xlsx`: sample workbook with formulas wired to the VBA UDFs.
|
- `GL_Accounts_POC.xlsx`: sample workbook with formulas wired to the VBA UDFs.
|
||||||
|
- `GL_Accounts_POC_client_safe.xlsm`: macro-enabled workbook copy configured
|
||||||
|
with manual calculation and no forced recalculation on open.
|
||||||
|
- `GL_Accounts_POC_client_formula_text.xlsx`: client-safe workbook template
|
||||||
|
with formulas stored as text, no VBA project, and no active UDF formulas.
|
||||||
|
- `GL_Accounts_Client_Template.xlsx`: recommended client delivery workbook
|
||||||
|
with setup and functions sheets, formulas stored as text, no VBA project,
|
||||||
|
and no active UDF formulas.
|
||||||
|
- `CLIENT_DELIVERY_README.md`: recommended client packaging and setup flow.
|
||||||
|
- `GL_Accounts_Functions_Documentation.md`: user-facing function reference.
|
||||||
|
- `GL_Accounts_Functions_Documentation.pdf`: PDF export of the function
|
||||||
|
reference.
|
||||||
|
|
||||||
## SQL Objects
|
## SQL Objects
|
||||||
|
|
||||||
@@ -20,6 +31,8 @@ The SQL creates:
|
|||||||
- `excel_api.get_account_abbr(company, account, currency, date)`
|
- `excel_api.get_account_abbr(company, account, currency, date)`
|
||||||
- `excel_api.get_account_name(company, account, currency, date)`
|
- `excel_api.get_account_name(company, account, currency, date)`
|
||||||
- `excel_api.get_account_contact(company, account, currency, date)`
|
- `excel_api.get_account_contact(company, account, currency, date)`
|
||||||
|
- `excel_api.get_account_contact_code(company, account, currency, date)`
|
||||||
|
- `excel_api.get_account_contact_name(company, account, currency, date)`
|
||||||
|
|
||||||
## Excel Formulas
|
## Excel Formulas
|
||||||
|
|
||||||
@@ -32,6 +45,8 @@ After importing the VBA module:
|
|||||||
=GetAccountAbbr("COMPANY01","400000","USD",DATE(2026,5,31))
|
=GetAccountAbbr("COMPANY01","400000","USD",DATE(2026,5,31))
|
||||||
=GetAccountName("COMPANY01","400000","USD",DATE(2026,5,31))
|
=GetAccountName("COMPANY01","400000","USD",DATE(2026,5,31))
|
||||||
=GetAccountContact("COMPANY01","400000","USD",DATE(2026,5,31))
|
=GetAccountContact("COMPANY01","400000","USD",DATE(2026,5,31))
|
||||||
|
=GetAccountContactCode("COMPANY01","400000","USD",DATE(2026,5,31))
|
||||||
|
=GetAccountContactName("COMPANY01","400000","USD",DATE(2026,5,31))
|
||||||
```
|
```
|
||||||
|
|
||||||
The sample workbook already contains these formulas in the `GL Accounts POC`
|
The sample workbook already contains these formulas in the `GL Accounts POC`
|
||||||
@@ -46,11 +61,36 @@ sheet. Save it as `.xlsm` after importing the VBA module.
|
|||||||
currency id, `currency_currency.code`, `name`, `symbol`, or `numeric_code`.
|
currency id, `currency_currency.code`, `name`, `symbol`, or `numeric_code`.
|
||||||
- `GetAccountAbbr` returns `account_account.code` because no separate GL
|
- `GetAccountAbbr` returns `account_account.code` because no separate GL
|
||||||
abbreviation field exists in the current schema.
|
abbreviation field exists in the current schema.
|
||||||
- `GetAccountContact` returns a comma-separated list of parties found on
|
- `GetAccountContact` is kept for backward compatibility and returns the
|
||||||
posted GL lines for the account/currency/date.
|
contact code when available, otherwise the contact name.
|
||||||
|
- `GetAccountContactCode` returns contact codes when available, otherwise
|
||||||
|
names.
|
||||||
|
- `GetAccountContactName` returns contact names when available, otherwise
|
||||||
|
codes.
|
||||||
- `GetAccountBaseAmount` revalues the transaction amount at `value_date` using
|
- `GetAccountBaseAmount` revalues the transaction amount at `value_date` using
|
||||||
`currency_currency_rate`.
|
`currency_currency_rate`.
|
||||||
|
|
||||||
|
## Client-Safe Workbook Packaging
|
||||||
|
|
||||||
|
For external delivery, prefer `GL_Accounts_Client_Template.xlsx` as the stable
|
||||||
|
starting point. It contains no VBA project and no active UDF formulas, so Excel
|
||||||
|
cannot open an ODBC connection during workbook startup.
|
||||||
|
|
||||||
|
The previous `.xlsm` workbooks can block at `Opening ... 0%` because they
|
||||||
|
combine an embedded VBA project with active `GetAccount...` formulas. Even
|
||||||
|
with manual calculation, Microsoft 365 may inspect or recalculate these UDF
|
||||||
|
cells while loading the workbook.
|
||||||
|
|
||||||
|
Recommended delivery workflow:
|
||||||
|
|
||||||
|
1. Send the `.xlsx` template with formulas stored as text.
|
||||||
|
2. Send/import `GLAccountsExcelApi.bas` separately where macros are allowed.
|
||||||
|
3. On the `Functions` sheet, run `InstallGLApiFormulas` instead of editing
|
||||||
|
formula text manually. It prepares result cells without writing live
|
||||||
|
worksheet formulas.
|
||||||
|
4. Use `RefreshGLApi` to call the database and write plain result values
|
||||||
|
instead of relying on edit-time, open-time, or paste-time recalculation.
|
||||||
|
|
||||||
## Session Notes - 2026-05-26
|
## Session Notes - 2026-05-26
|
||||||
|
|
||||||
Validated manually with Excel Desktop in safe mode after Excel initially hung
|
Validated manually with Excel Desktop in safe mode after Excel initially hung
|
||||||
@@ -98,11 +138,13 @@ Next session:
|
|||||||
`ClearGLApiCache` / `CloseGLApiConnection`.
|
`ClearGLApiCache` / `CloseGLApiConnection`.
|
||||||
- Consider moving ODBC connection settings to a hidden/settings worksheet
|
- Consider moving ODBC connection settings to a hidden/settings worksheet
|
||||||
instead of hardcoding `GL_API_CONNECTION_STRING`.
|
instead of hardcoding `GL_API_CONNECTION_STRING`.
|
||||||
- Review whether contact output should be party codes, names, or both.
|
- Import the new VBA wrappers if a workbook needs distinct contact code/name
|
||||||
|
formulas.
|
||||||
|
|
||||||
## Recommended Production Shape
|
## Recommended Production Shape
|
||||||
|
|
||||||
For production, prefer a readonly DB user and an ODBC DSN. The VBA module is a
|
For production, prefer a readonly DB user and a named ODBC System DSN, for
|
||||||
|
example `DSN=TradonGLApi;Uid=tradon_readonly;Pwd=...;`. The VBA module is a
|
||||||
minimal proof of concept; a production workbook should add:
|
minimal proof of concept; a production workbook should add:
|
||||||
|
|
||||||
- a settings sheet for connection parameters;
|
- a settings sheet for connection parameters;
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
-- - GetAccountAbbr
|
-- - GetAccountAbbr
|
||||||
-- - GetAccountName
|
-- - GetAccountName
|
||||||
-- - GetAccountContact
|
-- - GetAccountContact
|
||||||
|
-- - GetAccountContactCode
|
||||||
|
-- - GetAccountContactName
|
||||||
--
|
--
|
||||||
-- Design notes:
|
-- Design notes:
|
||||||
-- - PostgreSQL lowercases function names; VBA calls these SQL functions.
|
-- - PostgreSQL lowercases function names; VBA calls these SQL functions.
|
||||||
@@ -233,3 +235,43 @@ as $$
|
|||||||
and gl.posting_status = 'posted'
|
and gl.posting_status = 'posted'
|
||||||
and coalesce(gl.party_code, gl.party_name) is not null
|
and coalesce(gl.party_code, gl.party_name) is not null
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
|
create or replace function excel_api.get_account_contact_code(
|
||||||
|
p_company_key text,
|
||||||
|
p_account_code text,
|
||||||
|
p_currency_iso text,
|
||||||
|
p_value_date date)
|
||||||
|
returns text
|
||||||
|
language sql
|
||||||
|
stable
|
||||||
|
as $$
|
||||||
|
select nullif(string_agg(distinct coalesce(gl.party_code, gl.party_name),
|
||||||
|
', ' order by coalesce(gl.party_code, gl.party_name)), '')
|
||||||
|
from excel_api.gl_account_lines gl
|
||||||
|
where excel_api.company_matches(gl.company_key, p_company_key)
|
||||||
|
and gl.account_code = p_account_code
|
||||||
|
and excel_api.currency_matches(gl.transaction_currency_id, p_currency_iso)
|
||||||
|
and gl.posting_date <= p_value_date
|
||||||
|
and gl.posting_status = 'posted'
|
||||||
|
and coalesce(gl.party_code, gl.party_name) is not null
|
||||||
|
$$;
|
||||||
|
|
||||||
|
create or replace function excel_api.get_account_contact_name(
|
||||||
|
p_company_key text,
|
||||||
|
p_account_code text,
|
||||||
|
p_currency_iso text,
|
||||||
|
p_value_date date)
|
||||||
|
returns text
|
||||||
|
language sql
|
||||||
|
stable
|
||||||
|
as $$
|
||||||
|
select nullif(string_agg(distinct coalesce(gl.party_name, gl.party_code),
|
||||||
|
', ' order by coalesce(gl.party_name, gl.party_code)), '')
|
||||||
|
from excel_api.gl_account_lines gl
|
||||||
|
where excel_api.company_matches(gl.company_key, p_company_key)
|
||||||
|
and gl.account_code = p_account_code
|
||||||
|
and excel_api.currency_matches(gl.transaction_currency_id, p_currency_iso)
|
||||||
|
and gl.posting_date <= p_value_date
|
||||||
|
and gl.posting_status = 'posted'
|
||||||
|
and coalesce(gl.party_name, gl.party_code) is not null
|
||||||
|
$$;
|
||||||
|
|||||||
Reference in New Issue
Block a user