# Technical documentation — snell-crondaily

Evidence date: 2026-10-03. Revision: `master` `60aeb26` (2023-11-17).

Credential values are omitted. They are present in git and should be treated as exposed.

## Architecture

One WinForms executable, `OutputType` WinExe, root namespace `SNELL_CronDaily`, assembly name `SNELL CronDaily`. Startup object `SNELL_CronDaily.My.MyApplication`. Main form `frmMain`.

`frmMain_Load` builds two in-memory lists. `Run` walks them. Customer and Product are subclasses of `Process`. Export is a separate class and is not added to its list.

There is no service layer, no web host, and no background worker. Work runs on the UI thread. `doQueryAsync` exists on `db` and has no caller in the import path.

Two switches are independent:

| Switch | Kind | Effect |
|---|---|---|
| Compile symbol `DEBUG` | `#If DEBUG` | Download directory, SQL connection string, SMTP client, archive copy vs move, and the (identical) `tblDwf` name |
| `My.Settings.Debug` | Application setting | Whether load calls `Run` and closes |

`App.config` sets `Debug` to true.

## Repository layout

```text
.gitignore
BuildProcessTemplates/          stock TFS 11 XAML (Upgrade, Lab, Default, Azure CD)
SNELL_CronDaily/
  SNELL_CronDaily.sln           VS 2017, TFS binding
  SNELL_CronDaily.vssscc
  SNELL_CronDaily/
    frmMain.vb                  batch
    frmMain.Designer.vb
    Process.vb                  shared import
    Export_Process.vb
    db.generic.vb               class db
    db.cls.vb                   empty class xdb
    App.config
    App_Code/Processes/         Customer_Process, Product_Process
    App_Code/VB/                ProjectFunctions, SystemFunctions, dataExport, Duodecimal
    My Project/                 application, assembly, settings, manifest
    bin/Debug/                  tracked build output, including exe.config
    obj/Debug/                  tracked compile caches
  .vs/                          tracked VS user options (v15 and v16 .suo)
```

No README, no test project, no `packages.config`, no pipeline file, no SQL project.

`.gitignore` is a generic template (Node, Java, Python, logs, `.exe`). It does not ignore `bin/`, `obj/`, or `.vs/`. Those directories are committed. The `.exe` rule would ignore a newly added executable. The existing `bin\Debug` listing tracked by git is config, manifests, pdb, and xml. The exe itself is not in `git ls-files`.

## Languages and frameworks

| Item | Value |
|---|---|
| Language | VB.NET. `Option Explicit` On, `Option Strict` Off, `Option Infer` On, `Option Compare` Binary |
| UI | Windows Forms |
| Runtime | .NET Framework 4.6.1 (`TargetFrameworkVersion`). `supportedRuntime` sku `.NETFramework,Version=v4.6.1` |
| IDE recorded in the solution | Visual Studio 15.0.26228.43 (VS 2017). A v16 `.suo` is also committed |
| Data | `System.Data.SqlClient` |
| Mail | `System.Net.Mail` |
| CSV | `Microsoft.VisualBasic.FileIO.TextFieldParser` |
| References | System, System.Data, System.Deployment, System.Drawing, System.Windows.Forms, System.Xml, System.Core, System.Xml.Linq, System.Data.DataSetExtensions |

ClickOnce bootstrapper entries in the project file are .NET Framework 4.5 (x86 and x64), .NET Framework 3.5 SP1 Client Profile, and .NET Framework 3.5 SP1. The compile target is 4.6.1. Those bootstrapper packages do not match the target.

`NoWarn` lists VB warnings `42016,41999,42017,42018,42019,42032,42036,42020,42021,42022`.

## Application structure

`Application.myapp`:

| Setting | Value |
|---|---|
| Main form | `frmMain` |
| Single instance | true |
| Shutdown mode | 0 (after main form closes) |
| Visual styles | true |
| Authentication mode | 0 (Windows) |
| Save My.Settings on exit | true |

`app.manifest` requests `asInvoker`. ClickOnce `TargetZone` is LocalIntranet. `GenerateManifests` is true. `SignManifests` is false. `SignAssembly` is false. `ManifestKeyFile` is `Superbowl Daily 2_TemporaryKey.pfx`, which is not in the tree.

`AssemblyVersion` is `1.0.1.1`. `AssemblyFileVersion` is `1.0.0.0`. ClickOnce `ApplicationVersion` is `0.0.0.0`, `ApplicationRevision` 0. `Install` true, `InstallFrom` Disk, `UpdateEnabled` false, `CreateDesktopShortcut` true. `PublishUrl` is `C:\Users\pieter\Desktop\snell_cd\`. `ProductName` is `Superbowl Daily 2`.

The form `Text` is `Snell CronDaily`. Client size 781 by 555. Controls that matter: `chkCustomer`, `chkProduct`, `barCustomer`, `barProduct`, `btnRun`, `btnEmailLog`, `chkPartialImport`, three directory labels, `lblVersion`.

## Data access

Class `db` holds one shared `SqlConnection`, created at type initialization from the compile-specific setting.

| Setting | Server and catalog | Security |
|---|---|---|
| `SQLConnectionRelease` | `ESFSPHP02\SQLEXPRESS`, catalog `snell_2015`, timeout 60 | Integrated Security |
| `SQLConnectionDebug` | `SQL-SERVER`, catalog `snell_2015`, timeout 60 | SQL login `snell_fsr`. Password omitted |

`doQuery`:

- SQL whose trimmed text starts with `SELECT` uses a `SqlDataAdapter` and returns a `DataSet`. `intRows` becomes the first table's row count. Timeout error `-2146232060` returns the (empty) dataset. Other exceptions on the SELECT path call `Fill` a second time.
- Any other statement uses `SqlCommand.ExecuteNonQuery` with `CommandTimeout = 0`. The connection is closed if it was open, then opened, then closed in `Finally`. Exceptions propagate. The return value is the row count, stored in the shared field `intRows`.

`getRow` returns the first row, or `Nothing` on any failure.

`CleanString` is the only value escaping used by inserts.

`intRows` is `Shared`. `SetInactives` calls `xdb.doQuery`. `xdb` is an empty subclass of `db`, so that call uses the same shared connection and the same `intRows` field. Other methods `Dim xdb As New db`, which does not create a second connection because `conn` is shared on the base class.

`MergeDataSets`, `doQueryXTab`, `CleanData`, and `doQueryAsync` have no caller on the CronDaily path.

### Tables this code names

No `CREATE TABLE` is in the repository. Names below are from SQL strings.

| Name | How it is used |
|---|---|
| `stgCustomer` | Truncated and inserted by the customer import. Columns listed in `Customer_Process.sqlInsertStaging`. `LogDuplicates` also expects `ID` |
| `stgProduct` | Same for product. Columns in `Product_Process.sqlInsertStaging` |
| `dwdCustomer` | `blnActive`, `blnPromotionalAccounts`, `strRegionDesc2`. Uncalled `LogDWD` also reads `strCustomerNo`. Uncalled requisition SQL joins `strCustomerNo` and `strCustomerName` |
| `dwdProduct` | `blnActive`. Uncalled `LogDWD` reads `strProdCode` |
| `dwfCustomer`, `dwfProduct` | Name is built as `tblDwf` and is not used by the live `Run` SQL. `SetDeletes` and `DeleteDW` would use it. Both are uncalled |
| `mstCustomer`, `mstProduct` | Name is built as `tblMaster` and is not referenced by a live statement |
| `stgSuperbowlLog` | Truncated at batch start. `WriteLog` inserts `strStamp`, `dtLog`, `strType`, `strProcess`, `strAction`, `txtLog`, `blnEmail` |
| `sysQuery` | `ID`, `memQuery`. Live IDs 21, 22, 23, 24, 25, 43 |
| `logSuperbowl` | Only in a commented insert |
| `mstPeriod`, `mstCompany` | Uncalled `CheckPeriods` and fiscal helpers |
| `dlyDataWarehouse` | Uncalled `LogDWD` |
| `dwfOnconPromo` | Uncalled Monday backup |
| `fsrRequisition`, `fsrItem`, `fsrRequestType`, `mstUser` | Uncalled un-invoiced requisition mail |

`snell-cronjob` and `snell-freestock` document the same catalog name and several of the same table names (`dwdCustomer`, `dwdProduct`, `stgSuperbowlLog`, `sysQuery`, `fsrRequisition`). Their `sysQuery` IDs in Silicon Ark are 38–42, which do not overlap the IDs this program executes.

## File processing

`DownloadFile` uses `File.Copy` with overwrite. `CheckLocalFile` counts lines with a `StreamReader` and subtracts 1, including when the result would be negative for an empty file.

`ImportLocalFile` uses `TextFieldParser` with delimiter comma and `HasFieldsEnclosedInQuotes = true`. Header is `ReadFields` once. Each data row calls `sqlInsertStaging` and `db.doQuery`.

`ArchiveFile` destination:

```text
{dirImported}{Filename}.{yyyyMMdd_HHmmss}{extension}
```

`Filename` already includes `.txt`, and `extension` is appended again.

`setProgress` sets the bar maximum to `intRecordsTotal - 10` when the count is positive. `Progress` calls `PerformStep` and swallows exceptions by pinning the bar at its maximum.

Customer and product `Run` turn the bar `DarkOrchid` / continuous before warehouse work and back to blocks afterward. If `barProcess` was not found, `setProgress` throws on a null bar. `frmMain` looks up `bar` plus the process id (`barCustomer`, `barProduct`). Those controls exist.

## Mail

`EmailLog` builds HTML with the log text inserted into a table cell. `CleanString` has already turned quotes into SQL-escaped quotes at insert time, so the mail shows that escaped text. It does not HTML-encode `<`.

DEBUG client: `smtp.office365.com`, port 587, `EnableSsl` true, `UseDefaultCredentials` false, username `smtp@overdrive.co.za`. Password omitted. From `SNELL CronDaily <smtp@overdrive.co.za>`.

Release client: `SmtpClient(SMTPHostRelease)` which is `172.16.2.22`, `EnableSsl` false, `NetworkCredential(SMTP_Username, unsalt(SMTP_Password))`. Username `EDWARDSNELL\phpadmin`. `unsalt` replaces `ASP` with `php`. From `SNELL CronDaily <administrator@esnell.co.za>`.

`EmailUnIinvoicedRequisitions` builds a release client from `SMTPHostRelease` and does not assign credentials or SSL. It is not called.

## Environment configuration

Application settings (`App.config` and `Settings.settings`):

| Name | Scope | Role |
|---|---|---|
| `UNAME` | Application | Value `SNELL CronDaily`. No reader in the import path was found |
| `Debug` | Application | `True` in git. Gates auto-run |
| `SMTPHostRelease` | Application | `172.16.2.22` |
| `SMTP_Username` | Application | `EDWARDSNELL\phpadmin` |
| `SMTP_Password` | Application | Stored in git. Passed through `unsalt` on the release path. Value omitted here |
| `EmailRgbc` | Application | Log recipients. See the discrepancy with `exe.config` |
| `EmailBfbe` | Application | Unused |
| `SQLConnectionDebug` | Application | DEBUG compile connection |
| `SQLConnectionRelease` | Application | Release compile connection |

User settings, overwritten in `frmMain_Load` for the three directories:

| Name | Default in config | Runtime value |
|---|---|---|
| `dirImports` | `Imports\` | `{BaseDirectory}Imports\` |
| `dirImported` | `Imported\` | `{BaseDirectory}Imported\` |
| `dirExports` | `Exports\` | `{BaseDirectory}Exports\` |
| `SMTPHostDebug` | `192.168.100.6` | Not read by `EmailLog` |

`bin\Debug\SNELL CronDaily.exe.config` is a tracked build output. Its `EmailRgbc` and `EmailBfbe` still match the pre-`234cedd` values (`pj@overdrive.co.za` plus `maanie@overdrive.co.za`, and `EmailBfbe` `pj@overdrive.co.za`). `App.config` is the source a rebuild would compile in. The file beside the exe is what that particular binary reads.

## Build, development, and testing

Open `SNELL_CronDaily/SNELL_CronDaily.sln`. Configurations: Debug and Release, Any CPU. Debug output `bin\Debug\`. Release output `bin\Release\` (that folder is not committed).

No unit tests, no solution test settings, no CI definition. `BuildProcessTemplates` contains the standard TFS 2012/2013 templates `DefaultTemplate.11.1.xaml`, `UpgradeTemplate.xaml`, `LabDefaultTemplate.11.xaml`, and `AzureContinuousDeployment.11.xaml`. They are not wired to a project-specific build script in this repo.

The solution's Team Foundation binding is `http://tfs01:8080/tfs/vs_projects_2018`, project name `SNELL_CronDaily`. Whether builds still run there is **Unknown**.

Sonar files named in the onboarding issue are not in git history.

## Deployment

Evidence of a publish is the ClickOnce block: disk install, updates off, shortcut on, publish folder under a user desktop path `snell_cd`, product name Superbowl Daily 2. There is no publish profile beyond the vbproj properties, and the temporary key file is missing. This is historical project metadata, not proof of a current release process.

A production run, if it follows the comment on `Debug`, would be a Release compile with setting `Debug` false, drop files in `C:\SAPREPORTS\`, integrated security to `ESFSPHP02\SQLEXPRESS`, and SMTP to `172.16.2.22`. Whether that is how it is installed is **Unknown / Requires Confirmation**.

## Security notes from the tree

- SQL login password, SMTP password, and the DEBUG Office 365 password are committed in source (`App.config`, `Settings.settings`, `ProjectFunctions.vb`, and the tracked `exe.config`). They are not repeated in Silicon Ark. They should be rotated if they are still valid. **Requires human review.**
- SQL is concatenated. `CleanString` doubles single quotes and does not parameterise.
- HTML log mail inserts `txtLog` without encoding.
- `CommandTimeout = 0` on non-SELECT statements can hold a connection indefinitely.
- `SignManifests` and `SignAssembly` are false.
- `.vs` user-options binaries and `obj` caches are in git.
- The release SMTP connection disables SSL.
- `unsalt` is a string replace, not a secret store.

## Operations status

Last source change: 2023-11-17. No health check, no file log, no Windows service wrapper. Operational trace is `stgSuperbowlLog` plus the SMTP message. That table is truncated on the next `Run`.

## Related documents

- `project-plan.md` — scope, history, discrepancies, confirmation list.
- `prd.md` — workflows and FR-001–FR-017.
- `features.md` — what is on the batch path and what is only compiled.
- `business-rules.md` — rules the live path enforces, and rules that sit in uncalled methods.
- Silicon Ark `snell-cronjob/` and `snell-freestock/` — other programs that name catalog `snell_2015`. They are not this repository.
