# Business rules — superbowl_daily (Superbowl Daily)

Evidence date: 2026-10-02.

Source: `git@bitbucket.org:overdrive/superbowl_daily.git`, `main` at `1ba6c4a` (2026-08-04).

Rules are taken from executable code. Comment history is labelled **Documented**. Rules derived from the code without an explicit business statement are labelled **Inferred**. Nothing here is a new product decision.

## Identity and access

BR-001. There is no application login. **Implemented.** The Windows identity that starts the executable is the security principal for files and, on the release connection strings, for SQL integrated security.

BR-002. Rows the batch writes stamp `strLastUser` from setting `UNAME`. Committed default is `Daily Update`. **Implemented.**

BR-003. Stock-control balance updates stamp `strLastUserUpdate = 'SD c14 Process'`. **Implemented.** This does not use `UNAME`.

BR-004. **Inferred.** Any person who can launch the exe and reach SQL and the shares can import, delete staging data, and send the log email. No role check exists in this repository.

## When the batch runs

BR-005. If `blnDebug` is false, `Run()` starts on form load. If true, it starts only from **ReRun** (or the other buttons for their own actions). Committed default is true. **Implemented.**

BR-006. A non-DEBUG build closes the window when `Run()` finishes, including after a fatal error. A DEBUG build does not. **Implemented.**

BR-007. No scheduler, service, or Windows Task definition is in the repository. How often production starts the exe is **Unknown / Requires Confirmation**.

## Fiscal calendar

BR-008. Periods are `yyyyMM` strings. `Duodecimal` stores month index as `year * 12 + month - 1`. **Implemented.**

BR-009. The company row used for fiscal start is `mstCompany.strCompany = 'RGBC'`, column `strFYStart`. **Implemented.** What `strFYStart` contains in production is **Unknown / Requires Confirmation**.

BR-010. If the latest period is fewer than 24 `Duodecimal` months ahead of the current year-month, the batch inserts the next six months. Fiscal year is the calendar year when the month number is below `strFYStart`, otherwise calendar year + 1. Fiscal month shifts by `strFYStart`. Fiscal quarter is `(fiscalMonth - 1) \ 3 + 1`. **Implemented.** The 24-month and six-month figures are not explained in comments. **Inferred** as operational lookahead, not a documented finance policy.

## Customers

BR-011. A Cognos 9 customer number longer than 7 characters is rejected. **Implemented.**

BR-012. The branch column (file column 12) must equal `mstBranch.strSAPCode`. Unknown branches fail the row. **Implemented.**

BR-013. A new customer is inserted active (`blnActive = 1`) and not a promotional account. **Implemented.**

BR-014. An existing customer's active flag is the inverse of file column 14: 0 becomes 1, any other integer becomes 0. **Implemented.** **Documented** in the v1.26 comment as `blnActive = !$blnActive`. If the old file has no column 14, the active flag is left unchanged.

BR-015. Liquor licence is written only when the file has a 16th or 17th column (index 15 or 16). Older 15-column files do not clear an existing licence on update. **Implemented.** **Documented** in `GetLiquorLicenseFromRow` and in `scripts/alter_mstCustomer_strLiquorLicense.sql`.

BR-016. The SQL script requires `strLiquorLicense VARCHAR(25) NULL` on `mstCustomer` and `dwdCustomer` before the v1.34 deploy. The application does not apply the script. **Documented.**

BR-017. After the file, every `mstCustomer` number missing from `dwdCustomer` is inserted as a key only. Full column sync is whatever `sysQuery` IDs 8 and 31 contain. Those statements are not in this repository. **Implemented** call. Query text **Unknown / Requires Confirmation**. The script comment says those queries must copy `strLiquorLicense`. `sql_update_Customer_dw()` would copy it, and `Run()` does not call that function.

## Sales documents

BR-018. Document type null, empty, `-`, or `C` reverses units, 9-litre case equivalent, nett, GSV, and nett-ex on `stgCognos14` before aggregation. **Implemented.** **Documented** as `c1. Reversals and doctype = C`.

BR-019. Empty document type on the way in is stored as `R`. **Implemented.**

BR-020. Measure `BOT` or `ST` stores the product as `{code}S`. Measure `CS` stores `{code}F`. **Implemented.** Same suffix rule applies to ZMB, except `L` on product `05016`, which becomes `{code}F` and multiplies quantity by 9.

BR-021. A sales row needs a document number, a numeric customer number, a period of at least 8 characters, and a product code of at least 5 characters. **Implemented.**

BR-022. Staging sales, sales download, and sales data tables are deleted at the start of a sales file import. **Implemented.**

BR-023. Download rows with both units and net sales equal to 0 are deleted and excluded from the reported count. **Implemented.**

BR-024. 9-litre volume is `units * pack * size / 9000`. **Implemented.**

BR-025. For a promotional account (`blnPromotionalAccounts = 1`) whose order number differs from the customer number, the batch rewrites one staging row into three: bill-to value and free-stock negative, ship-to free-stock positive, and a bill-to document whose number is the first 8 characters of the original plus `FS` carrying the units. **Implemented.** **Documented** in the v1.17 and v1.31 comments, including the 2018-12-07 correction to use `ordernum` as the ship-to.

BR-026. `dwfSales` rows for the file's periods are deleted unless `refDocumentNo` contains `X` or `Y`, then reinserted from staging joined to product, customer, and period. **Implemented.** **Inferred** from the comment that `X` and `Y` documents are export documents that must not be wiped by the Cognos reload.

BR-027. Pink-slip on the Cognos row is copied to `dwfSales.strPSN` by document number, and `fsrRequisition.strInvoiceNumber` is set from the Cognos document where pink-slip numbers match. **Implemented.** **Documented** as added, commented out, then restored (v1.28).

BR-028. If the pink-slip insert throws, the row is still counted as inserted and the error is not logged. **Implemented.**

## Stock movements and balances

BR-029. ZMB receipts and exports are only movement types `101` and `102` at branches `XZB1` and `XZB3`. Positive quantity is a receipt. Negative quantity is an export (stored as a positive export amount). **Implemented.** **Documented** in the inline comment "Limit Plant to XZB1 and XZB3".

BR-030. Movement types `201` and `202` are repack. `701` and `702` are damages. **Implemented.**

BR-031. Before those updates, receipts, exports, damages, and repack for the file's periods are set to 0. Sales post-process sets `dblActual` to 0 for its periods before re-summing. **Implemented.** **Inferred.** A rerun replaces those measures for the periods in the file. It does not keep a delta.

BR-032. A product with no `refBrandID` is not posted to `stkProductPeriod`. The row fails with `Missing or incomplete product information`. **Implemented.**

BR-033. Flat quantities posted to stock control are rounded to 4 decimal places, midpoint away from zero. Open, close, and months of cover use the same rounding (months to 2 decimal places). **Implemented.**

BR-034. Closing stock for a past or current period with a non-zero actual is opening − actual + receipts − exports + damages + repack. **Implemented.**

BR-035. Closing stock for a future period, and for the current period when actual is 0, is opening − QF + orders − exports + damages − BMA + repack. **Implemented.**

BR-036. If `dblOpeningImports` is greater than 0, that value replaces opening for the row. **Implemented.** **Documented** as the v1.32 zero-opening fix.

BR-037. Months of cover consumes later `QF + BMA` of the same product and stops at 15. **Implemented.** The cap is not explained in comments.

BR-038. Daily stock (`cognos12`) replaces `dwfStock` for the date in `txtDate`, not for a date inside the file. Product codes are stored with an `F` suffix. **Implemented.**

## SALBA, exports, AdCheck, AdSpend

BR-039. SALBA delete key is year and month taken from the file name tokens, not from the spreadsheet period alone. **Implemented.** **Inferred** file-name shape `SALBA_{token}_{year}_{month}...` because the code uses split indexes 2 and 3.

BR-040. Export staging rows are stored with batch ending `999090` and `blnProcessed = 0`. The post-step deletes previous unprocessed `mstExports` in those periods with that batch suffix, then inserts again. GSV becomes price times units when `mstProduct.dblPrice` is not null, else 0. **Implemented.**

BR-041. AdCheck replaces `dwfACLisp` for each audit period in the workbook. A new AdCheck product or store is logged and inserted into `mapACProduct` or `mapACStore` with only the external id. **Implemented.** Mapping those ids to Superbowl products and customers is not done by this batch. **Inferred** as a manual follow-up. **Unknown / Requires Confirmation** who does that follow-up.

BR-042. AdSpend is moved (not copied) from the BMI folder on every run, not only on the 13th. **Implemented.** **Documented** as the v1.24 change. There is no delete of existing `dwfAdSpend` rows before insert.

## eSales Charter (disabled on the daily copy)

BR-043. The daily run does not copy Mint / eSales Charter files from the share. **Implemented** (`And False`). **Documented** v1.27.

BR-044. If those files are imported anyway, rep-call, survey, and promo deletes are by `txtDate` or by periods inside the file, and SQL duplicate-key 2627 does not fail the row. Off-con promo and on-con promo do not require a rep id. **Implemented.** **Documented** rep-id removal v1.25.

BR-045. Sales Charter CSV generation is not part of `Run()`. **Implemented** (call commented). **Documented** v1.27. The **SCQ** button still runs it and still deletes `dwfQFSales` for the current fiscal year before rebuilding. **Implemented.**

## BFBE and mail

BR-046. BFBE files are produced at the end of every successful path through `Run()`, and also from **Mondayly**. **Implemented.**

BR-047. On the first day of the month the sales extract uses the previous `yyyyMM` and the file name `ZA_{yyyyMM}_INVW.txt`. On other days the file name includes the full `yyyyMMdd`. **Implemented.** **Documented** v1.22b: Snell sales are one day behind, so the first of the month must report the prior period.

BR-048. Customer BFBE file is always `ZA_{yyyyMMdd}_CUST.txt`, including on the first of the month. **Implemented.**

BR-049. If the named `sysQuery` row is missing, that export stops and logs `BFBE Export Skipped`. **Implemented.**

BR-050. `MailBrandon` is not part of `Run()`. **Implemented.** The button path still sends it. The message body is the literal `testing`. **Implemented.**

BR-051. Log email recipients are `Emails` split on semicolons. BFBE email recipients are `bfbeEmails` split on semicolons. Committed BFBE list is empty. **Implemented.**

BR-052. `Run()` marks log rows for email only when some step set `blnError`. `MailLog` then sends every row that already has `blnEmail = 1` and clears the flag, including when the send fails with a non-memory exception. **Implemented.** An out-of-memory send does not clear the flag in that catch. **Implemented.**

BR-053. Release mail from-address is `administrator@rgbc.co.za` through `SMTPHost`. **Implemented.**

## Data handling

BR-054. Text and Excel files are read with the Jet OLEDB text or Excel driver. For off-con survey the code inserts line by line, then `getKeys` deletes `dwfOffconSurvey` for `txtDate`, then the OleDb pass inserts again. **Implemented.** Rows whose survey date is not `txtDate` are not deleted and can exist twice.

BR-055. `CleanString` is the only quoting applied to text placed into SQL. **Implemented.** It is not a parameterised command.

BR-056. Import log inserts retry three times. **Implemented.**

## Rules explicitly not in force

BR-057. The **Fix SAP FUCKUP** button does not apply the commented credit-note reversal. **Implemented** as a no-op.

BR-058. Customer segmentation from the Excel sheet is marked in a comment "not being used / not 100%". The CSV type remains in the file list. **Documented** and **Implemented** as dormant unless the file is present and the disabled copy is bypassed.
