From 7da99b3bf723b55ec9216c2c970d1ebb1141b6ff Mon Sep 17 00:00:00 2001 From: laurentbarontini Date: Fri, 29 May 2026 01:25:15 +0200 Subject: [PATCH] Live UDF formulas in template, remove gApiEnabled guard - GL_Accounts_Client_Template.xlsx: C6:C13 now have real formulas =GetAccountAmount(Setup!$B$6,...) instead of text placeholders - GLAccountsExcelApi.bas: removed gApiEnabled flag entirely, UDFs call RunQuery directly; add SetManualCalculation helper macro - Works in Excel safe mode + manual calc: F9 triggers DB queries Co-Authored-By: Claude Sonnet 4.6 --- .../excel_api/GLAccountsExcelApi.bas | 101 ++++++------------ .../GL_Accounts_Client_Template.xlsx | Bin 11021 -> 8724 bytes 2 files changed, 33 insertions(+), 68 deletions(-) diff --git a/notes/accounting/excel_api/GLAccountsExcelApi.bas b/notes/accounting/excel_api/GLAccountsExcelApi.bas index dc323d3..461b21e 100644 --- a/notes/accounting/excel_api/GLAccountsExcelApi.bas +++ b/notes/accounting/excel_api/GLAccountsExcelApi.bas @@ -6,17 +6,14 @@ Option Explicit ' Setup: ' 1. Install PostgreSQL ODBC driver. ' 2. Apply notes/accounting/excel_api/gl_accounts_sql_poc.sql. -' 3. In Excel: Developer > Visual Basic > File > Import File... -' 4. Import this .bas module. -' 5. Edit GL_API_CONNECTION_STRING below (or use a named DSN). +' 3. Open Excel in safe mode: double-clic sur Ouvrir_Excel_SafeMode.bat +' 4. Developer > Visual Basic > File > Import File > importe ce .bas. +' 5. Run SetManualCalculation once (sets xlCalculationManual). ' -' Usage: -' - 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;" +' Usage (2 modes au choix): +' Mode formule : tape =GetAccountAmount("ICT TRADING","20011","USD","2026-05-13") +' dans n'importe quelle cellule, puis F9 pour recalculer. +' Mode macro : remplis Setup!B6:B9, lance RefreshGLApi. Private Const GL_API_CONNECTION_STRING As String = _ "Driver={PostgreSQL Unicode(x64)};" & _ @@ -28,7 +25,6 @@ Private Const GL_API_CONNECTION_STRING As String = _ Private gConnection As Object Private gCache As Object -Private gApiEnabled As Boolean ' ---- Connection helpers ---- @@ -72,11 +68,18 @@ Public Sub CloseGLApiConnection() Set gConnection = Nothing End Sub -' ---- Main macro ---- +' ---- Setup ---- -' Reads Setup!B6:B9, queries the database directly, writes plain values to Functions!C6:C13. -' Excel stays in xlCalculationManual after this runs -- edit Setup cells freely, -' then run RefreshGLApi again when you want fresh results. +' A lancer une seule fois apres import du module. +' Passe Excel en calcul manuel : les formules ne se recalculent que sur F9. +Public Sub SetManualCalculation() + Application.Calculation = xlCalculationManual + MsgBox "Calcul manuel actif. F9 = recalculer toutes les formules GL.", vbInformation +End Sub + +' ---- Mode macro : RefreshGLApi ---- + +' Lit Setup!B6:B9, interroge la DB, ecrit les valeurs dans Functions!C6:C13. Public Sub RefreshGLApi() Dim wsSetup As Worksheet Dim wsFunctions As Worksheet @@ -87,11 +90,11 @@ Public Sub RefreshGLApi() On Error GoTo 0 If wsSetup Is Nothing Then - MsgBox "Sheet 'Setup' not found.", vbExclamation + MsgBox "Feuille 'Setup' introuvable.", vbExclamation Exit Sub End If If wsFunctions Is Nothing Then - MsgBox "Sheet 'Functions' not found.", vbExclamation + MsgBox "Feuille 'Functions' introuvable.", vbExclamation Exit Sub End If @@ -101,26 +104,26 @@ Public Sub RefreshGLApi() Dim company As String Dim account As String - Dim currency As String + Dim currencyIso As String Dim valueDate As Variant company = CStr(wsSetup.Range("B6").Value) account = CStr(wsSetup.Range("B7").Value) - currency = CStr(wsSetup.Range("B8").Value) + currencyIso = 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) + wsFunctions.Range("C6").Value = RunQuery("get_account_base_amount", company, account, currencyIso, valueDate) + wsFunctions.Range("C7").Value = RunQuery("get_account_real_base_amount", company, account, currencyIso, valueDate) + wsFunctions.Range("C8").Value = RunQuery("get_account_amount", company, account, currencyIso, valueDate) + wsFunctions.Range("C9").Value = RunQuery("get_account_abbr", company, account, currencyIso, valueDate) + wsFunctions.Range("C10").Value = RunQuery("get_account_name", company, account, currencyIso, valueDate) + wsFunctions.Range("C11").Value = RunQuery("get_account_contact", company, account, currencyIso, valueDate) + wsFunctions.Range("C12").Value = RunQuery("get_account_contact_code", company, account, currencyIso, valueDate) + wsFunctions.Range("C13").Value = RunQuery("get_account_contact_name", company, account, currencyIso, valueDate) CloseGLApiConnection GoTo RefreshDone @@ -131,23 +134,6 @@ RefreshError: 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 ---- @@ -213,29 +199,15 @@ 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 +' ---- UDF formulas (mode formule) ---- +' Utilisation : =GetAccountAmount("ICT TRADING", B7, B8, B9) +' Puis F9 pour recalculer. Le cache evite les appels DB redondants. Public Function GetAccountBaseAmount( _ ByVal companyKey As Variant, _ ByVal accountCode As Variant, _ ByVal currencyIso As Variant, _ ByVal valueDate As Variant) As Variant - If Not gApiEnabled Then GetAccountBaseAmount = "#GLAPI_OFF": Exit Function GetAccountBaseAmount = RunQuery("get_account_base_amount", companyKey, accountCode, currencyIso, valueDate) End Function @@ -244,7 +216,6 @@ Public Function GetAccountRealBaseAmount( _ ByVal accountCode As Variant, _ ByVal currencyIso As Variant, _ ByVal valueDate As Variant) As Variant - If Not gApiEnabled Then GetAccountRealBaseAmount = "#GLAPI_OFF": Exit Function GetAccountRealBaseAmount = RunQuery("get_account_real_base_amount", companyKey, accountCode, currencyIso, valueDate) End Function @@ -253,7 +224,6 @@ Public Function GetAccountAmount( _ ByVal accountCode As Variant, _ ByVal currencyIso As Variant, _ ByVal valueDate As Variant) As Variant - If Not gApiEnabled Then GetAccountAmount = "#GLAPI_OFF": Exit Function GetAccountAmount = RunQuery("get_account_amount", companyKey, accountCode, currencyIso, valueDate) End Function @@ -262,7 +232,6 @@ Public Function GetAccountAbbr( _ ByVal accountCode As Variant, _ ByVal currencyIso As Variant, _ ByVal valueDate As Variant) As Variant - If Not gApiEnabled Then GetAccountAbbr = "#GLAPI_OFF": Exit Function GetAccountAbbr = RunQuery("get_account_abbr", companyKey, accountCode, currencyIso, valueDate) End Function @@ -271,7 +240,6 @@ Public Function GetAccountName( _ ByVal accountCode As Variant, _ ByVal currencyIso As Variant, _ ByVal valueDate As Variant) As Variant - If Not gApiEnabled Then GetAccountName = "#GLAPI_OFF": Exit Function GetAccountName = RunQuery("get_account_name", companyKey, accountCode, currencyIso, valueDate) End Function @@ -280,7 +248,6 @@ Public Function GetAccountContact( _ ByVal accountCode As Variant, _ ByVal currencyIso As Variant, _ ByVal valueDate As Variant) As Variant - If Not gApiEnabled Then GetAccountContact = "#GLAPI_OFF": Exit Function GetAccountContact = RunQuery("get_account_contact", companyKey, accountCode, currencyIso, valueDate) End Function @@ -289,7 +256,6 @@ Public Function GetAccountContactCode( _ 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 @@ -298,6 +264,5 @@ Public Function GetAccountContactName( _ 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 diff --git a/notes/accounting/excel_api/GL_Accounts_Client_Template.xlsx b/notes/accounting/excel_api/GL_Accounts_Client_Template.xlsx index efca947cf9755e745821bb1b713c057dd88c4854..81f0a15c1673a6343df8dc9f40c4dff5bf55e460 100644 GIT binary patch literal 8724 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 literal 11021 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 NgJ1{1O8eWl{{yFFNGJdR