Session State & Globals¶
The legacy app has no dependency injection and no per-request context. Instead, session
and configuration state live in module-level global variables declared in
INVENTORY/Module/VariablePublic.vb, set once at startup/login, and read directly from
anywhere in the codebase.
Understanding these globals is essential: half the code assumes they are already populated.
Startup: Sub Main¶
Entry point is Sub Main in VariablePublic.vb (MySubMain=true, MainForm=MenuUtama).
flowchart TD
A["Sub Main"] --> B["Resolve host IP → memIP"]
B --> C["SystemRegistry.GetRegistry()<br/>read server, db, uid, password, skin<br/>(all obfuscated)"]
C --> D["License / expiry gate<br/>REF_SYSTEM + license.smartsoft.co.id"]
D --> E["ConnStr = HAKDAC.GetConnectionString(...)"]
E --> F["ApplicationVersion.CheckingApplicationVersion()"]
F --> G["SynchroniseDateTime.SetDeviceTime()<br/>slave PC clock+locale to SQL Server"]
G --> H["ModiStructure.CheckingFileModiStructure()"]
H --> I["Show MenuUtama → login"]
style C fill:#fff3e0,stroke:#fb8c00
style G fill:#fff3e0,stroke:#fb8c00
Steps in order:
- Resolve IP —
Dns.GetHostByName(...)→memIP. - Load config from the Windows Registry —
SystemRegistry.GetRegistry()readsHKCU\Software\VB and VBA Program Settings\SmartSoft\SystemSetting\<AppName>:ServerName,DataBaseName,UID,Password,DataBaseNameBackup,Skin(plus host-to-hostH2H*values). Every value is obfuscated and decoded withPassToChar. - License / expiry gate — for non-POS apps, opens a test connection, reads the
"Expired Date" from
REF_SYSTEM, and if expired calls the cloud license APIhttps://license.smartsoft.co.id/license?client_id=<KodeCabang>. Connection failure → popsfrmSystemSetting. - Build
ConnStr—HAKDAC.GetConnectionString(server, db, uid, pwd)(SQL auth; credentials sit in the string in cleartext). - Version check —
ApplicationVersion.CheckingApplicationVersion()(uses the EXE file timestamp as the version). - Clock sync —
SynchroniseDateTime.SetDeviceTime(...)forces the workstation clock and date/number formats to match the SQL Server (see below). - Schema check —
ModiStructure.CheckingFileModiStructure(...)behind a splash.
Sub Main runs again from MenuUtama_Load
MenuUtama.vb re-invokes the bootstrap on load — re-reading branch code / IP from the
registry, loading parameters, applying skin/logo, and wiring the login panel. Don't be
surprised to see startup work happen in two places.
The global singletons¶
Declared Public … As New at module scope — effectively app-wide singletons.
| Global | Type | Holds |
|---|---|---|
HAKDAC |
General.DataAccessControl |
The primary data gateway — runs queries/non-queries, existence checks. |
HAKCVT |
General.Convertion |
Formatting + the password/registry cipher (PassToChar/CharToPass), numeric/string helpers. |
HAKFUNC |
MyLib.HAKFunction |
Login, station identity, access-menu building, image helpers, warehouse lookup. |
LocFunc |
MyFunction (app-local) |
App-specific helper functions. |
cPRSH |
Parameter.PERUSAHAAN |
Company / branch master. PRSH_KODE = branch code; PRSH_TGLAKTIF = system active date; name/address/PPN/HO/closing/logo. |
cPARAM / cPRM |
Parameter.tbTabel_Parameter / tv_Parameter |
Config parameters (PRM_CLOSINGHARIAN, PRM_MULTIGUDANG, PRM_MULTICABANG, colours/fonts). |
cSTOK |
BLL.STOCK |
Stock & costing business logic (GetAvgCost, recompute). |
cCOMP |
Parameter.tbMaster_Computer |
This workstation's record (keyed by IP + station); printer name, active user. |
The session variables¶
Set at login / post-login and read throughout.
| Variable | Meaning | Set where |
|---|---|---|
ConnStr |
Active SQL connection string | Sub Main |
memIP, memStation |
Machine identity | Sub Main / login |
memTanggal |
Business/system date (not always "now") | MenuUtama_Load |
memUserID, memUserName, memUserNIK |
Logged-in user | On successful login |
memUserPassword |
User's (deciphered) password | On login |
memUserLevel |
User level | User-admin only |
memLokasiGudang |
Active warehouse/location | Post-login via HAKFUNC.LokasiGudang(...) |
memServerName, memDataBaseName, memUID, memPassword |
Raw registry config | Sub Main |
There is no memKodeCabang
The branch code you'll look for is cPRSH.PRSH_KODE — the company/branch object
fills that role. (Some docs/specs abbreviate it as "memKodeCabang" conceptually.)
Business date vs wall-clock¶
memTanggal is not simply Now(). It is the reference date for every period filter,
recap, and closing check:
flowchart TD
A{"PRM_CLOSINGHARIAN = 'Y'?"} -->|No| B["memTanggal = Now.Date<br/>(wall clock)"]
A -->|Yes| C["memTanggal = cPRSH.PRSH_TGLAKTIF<br/>(DB system active date)"]
C --> D{"PC date < system date?"}
D -->|Yes| E["⚠️ Warn + End() the app"]
D -->|No| F["Proceed"]
style C fill:#fff3e0,stroke:#fb8c00
style E fill:#ffebee,stroke:#c62828
- If daily closing is enabled (
PRM_CLOSINGHARIAN = 'Y'), the business date is the database's active date (PRSH_TGLAKTIF), not the PC clock. This keeps all workstations on the same "today" as the server, even across a closing boundary. - If the PC's own date is earlier than the system active date, the app warns and terminates — to stop back-dated transactions from a mis-set clock.
SynchroniseDateTimegoes further: at startup it runsSELECT GetDate()and forces the Windows local clock and locale (Control Panel\International) to the DB server, so business-date and number formatting stay consistent fleet-wide.
Why this matters for you¶
Consequences of the globals design
- State is implicit. A function may depend on
memUserIDorcPRSH.PRSH_KODEwithout taking them as parameters. Grep the globals when a value "appears from nowhere." - Not thread-safe / not re-entrant. The design assumes a single interactive user per process. Don't add background threads that touch these globals.
- Startup order is load-bearing. Code assumes
Sub Mainalready ran. Launching a form in isolation without the bootstrap will hit null globals. - The app changes machine state at startup (system clock, locale registry keys, and
it can remap
LPT1viaNET USEfor receipt printing). Be aware on shared machines.