# Product requirements — superbowl_daily_2 (RGBC Superbowl Daily 2)

Evidence date: 2026-10-02.

Source: `git@bitbucket.org:overdrive/superbowl_daily_2.git`, `master` at `30eb35e` (2026-06-10).

Requirements below are taken from behaviour in the code, from comments in `frmMain.vb`, and from `Database_Scripts/README.md`. Each item is labelled:

- **Implemented** — the current code does this.
- **Documented** — a comment, README, or SQL script states it.
- **Inferred** — a reasonable reading of the code, not an explicit rule.
- **Not executed** — the code exists but the batch does not call it.
- **Unknown** — not established.

No separate PRD existed in the source repository or in Silicon Ark before this document.

## Product purpose

**Implemented.** RGBC Superbowl Daily 2 is a Windows desktop batch that:

1. Pulls daily survey, promo, call, vendor, GL, and call-cycle files or views into the Superbowl SQL Server database.
2. Pushes customer and product files, derived from `sysQuery`, onto a sales file share for AdvPDA / 2Way.
3. Pushes products, promotion activities, and customers to Repsly.
4. Pushes an active-customer outlet file to Track it All on a weekly schedule.
5. Emails an HTML log of the batch.

The form subtitle calls the product RGBC Superbowl Daily. Company context in period logic is the `mstCompany` row `strCompany = 'RGBC'`.

## Users and interactions

There are no user accounts inside the application.

| Actor | What they can do | Evidence | Provenance |
|---|---|---|---|
| Person who starts the executable | See the process list, toggle checkboxes that are enabled, press Run, Email Log, or Email Requisitions | `frmMain` | Implemented |
| Same person, when `My.Settings.Debug` is false | Does not get a chance to toggle. Load calls `Run()` and then `Close()` | `frmMain_Load` | Implemented |
| Same person, when `My.Settings.Debug` is true | Form stays open. Track it All preview checkbox is visible. It defaults to checked | `Panel19.Visible = My.Settings.Debug` | Implemented |
| Email recipients in `EmailRgbc` | Receive the batch log | `EmailLog` | Implemented |
| Email recipients in `TrackItAllFailureNotificationEmails` | Receive a mail only after a Thursday Track it All retry fails | `SendTrackItAllFailureNotification` | Implemented, setting undeclared |
| Recipient hardcoded in `EmailUnIinvoicedRequisitions` | Would receive the un-invoiced requisition list if that button is used on a Monday | Button handler | Not executed by the batch |

Who is supposed to start the executable, and whether a scheduler does it, is **Unknown / Requires Confirmation**.

`EmailBfbe` is configured and unused.

## Major workflow

`frmMain.Run` is the only batch. Order is fixed.

### Startup

1. `TRUNCATE TABLE stgSuperbowlLog`.
2. Insert a Batch Start row via `WriteLog`.
3. `CheckPeriods`.
4. `LogDWD` inserts one row into `dlyDataWarehouse` with counts of `dwdCustomer.strCustomerNo` and `dwdProduct.strProdCode`.
5. Monday backup of `dwfOffconPromo` and `dwfOnconPromo` is commented out.

### Imports

For each import whose checkbox is checked, `Run()` constructs the matching process and calls `Run()`.

File imports (off-con promo, off-con survey, on-con promo, on-con survey, rep calls, call cycle):

1. Log process start.
2. If the source file exists, copy it into `{exe}\Imports\`. Missing file: log a warning. `isValid` stays false, so staging is skipped.
3. Accept only `.csv`. Other extensions log an error.
4. Count lines, truncate `stg{ProcessId}`, parse comma-separated quoted CSV, skip a header row, insert each data row with the child `sqlInsertStaging`.
5. Invalid cast, malformed line, or SQL error on a data row: log a warning and continue. Other exceptions: log an error and continue.
6. Archive: DEBUG compile copies the file into `Imported\` with a timestamp. Otherwise it moves the file.
7. Log duplicate primary keys (`GROUP BY` pk `HAVING COUNT(ID) > 1`). Duplicates are logged. They are not removed.
8. If Partial Import is unchecked, mark existing `dwf*` rows for the periods present in staging (`blnDelete = 1`), then later delete those rows. If Partial Import is checked, `SetDeletes` returns immediately. The checkbox defaults to checked.
9. Run update then insert SQL from `sysQuery`.
10. Delete `dwf*` rows with `blnDelete = 1` when step 8 ran.
11. Log process completed.

Vendor and GL do not use a file. They truncate staging, run an insert from a view on `db_trgbc` (`vwAPVendors`, `vwGLAccounts`), then apply `sysQuery` into master tables. `db_trgbc` uses `SQLConnectionDebug` in a DEBUG compile and `SQLConnectionTRGBCRelease` otherwise. Catalog name in the release connection is `TRGBC`.

Call cycle download and import follow the file pattern. `SetDeletes` in that process is commented out, so partial-import does not apply to it.

### Exports that are SQL files

For AdvPDA CRM, CRM inactive, exposure CRM, product, and planned promotions, when checked:

1. Load SQL from `sysQuery.memQuery` at startup (IDs 35, 60, 37, 36, 59). If that row is missing, load throws. That failure mode is **Implemented** as an unhandled exception on form load.
2. Count rows, write a CSV under `{exe}\Exports\`, copy that CSV onto the process path (the Hennessy or debug folder) under the process filename.

The SQL text is not in the repository. Column lists and filters are **Unknown** until `sysQuery` is read from the database.

### Repsly exports

When checked, each Repsly process:

1. Refreshes its queue (new, changed, and failed rows).
2. Selects rows in status `Pending` or `Updated`.
3. If none, logs and returns.
4. Writes an XLSX under `Exports\`.
5. Uploads that XLSX by FTP. FTP failure is logged and the API push still runs.
6. POSTs JSON in batches of 200.
7. `response.Code = 0` marks the batch `Completed` and stores `ImportJobID` parsed from a message containing `ID:`. Any other code marks the batch `Failed` with the message in `ErrorLog`.
8. The client does not call `GetImportStatus` after the POST.

### Track it All

When checked, see business rules for the Wednesday / Thursday gate. Data is a full snapshot of active `mstCustomer` rows. Upload is multipart to `/rgbc/updateOutlets` with form field `mode` of `sync` or `preview`.

### Finish

1. Log Batch Completed.
2. Copy `stgSuperbowlLog` into `logSuperbowl` (columns `strStamp, dtLog, strType, strProcess, strAction, txtLog` only; `blnEmail` is not copied).
3. `EmailLog` reads `logSuperbowl` where `blnEmail = 0`.

The comment in `Run()` that lists customer-dimension updates, sales-charter recalculation, and BFBE export email is **Documented** as a comment and **Not executed**.

## Functional requirements

### FR-001 — Unattended run when Debug is false

**Implemented.**

- Trigger: form load.
- Condition: `My.Settings.Debug = False`.
- Behaviour: `Run()` then `Me.Close()`.
- When Debug is true: no automatic run.

The checked-in default of `Debug` is `True`, so a build that uses that default does not auto-run.

### FR-002 — Process selection

**Implemented.**

A checked box runs that process. An unchecked box skips it. Default checked state is in `frmMain.Designer.vb`. Rep calls is unchecked. Customer segmentation is disabled. See `features.md`.

### FR-003 — Period calendar

**Implemented.**

- Trigger: every batch, before imports.
- Read: latest `mstPeriod.strPeriod`, and `mstCompany` where `strCompany = 'RGBC'`.
- If the latest period is fewer than 30 months ahead of the current `yyyyMM`, insert the next 6 periods.
- Fiscal year: if `maxPeriod.Month < strFYStart` then fiscal year is the calendar year, otherwise calendar year + 1.
- Fiscal month shifts by `strFYStart`. Fiscal quarter is `((fiscalMonth - 1) \ 3) + 1`.
- Failure to insert one period: log type `error`, action `Check mstPeriod`, and continue the loop.
- If `mstCompany` has no RGBC row, `dr!strFYStart` throws. That path is not caught inside the company lookup. **Implemented** as a possible batch failure. Whether the row exists is **Unknown**.

### FR-004 — Warehouse row counts

**Implemented.** Each batch inserts `dlyDataWarehouse` (`dtStamp`, `strDate` `yyyyMMdd`, customer count, product count). Failure logs action `Log DWD`.

### FR-005 — Off-consumption promo import

**Implemented.**

- UI label: `2Way Offcon Promo`. Process name: `Offcon Promo`.
- File: `OffPremisePromoSurvey.csv` from the 2Way folder.
- Primary key passed to the process: `strRecordID`.
- Staging table name from the process id: `stgOffconPromo`. Fact table: `dwfOffconPromo`.
- Warehouse update/insert: `sysQuery` ID 25 then 26.
- Period delete key: `LEFT(strPromoDate,6)`, subject to Partial Import.
- Default: checked.

Older filenames `OffconPromos.csv` and `OffPromoSurvey.csv` are comments only.

### FR-006 — Off-consumption survey import

**Implemented.**

- UI: `Adv Offcon Survey`. File: `OffPremiseSurvey.csv` from the ADM folder.
- Primary key: `strCustomerNo,strProdCode,strDateSurveyed`.
- `sysQuery` 21 then 22.
- Period delete: `LEFT(strDateSurveyed,6)`.
- Staging insert includes distribution, RSP, display, facing, location, pallet, and PDU columns. See `OffconSurvey.process.vb`.
- Default: checked.

### FR-007 — On-consumption promo import

**Implemented.**

- UI: `Adv Oncon Promo`. File: `OnPromoSurvey.csv` from the ADM folder.
- Primary key: `strRecordID`.
- `sysQuery` 27 then 28.
- Period delete: `LEFT(strDatePromo,6)`.
- Default: checked.

### FR-008 — On-consumption survey import

**Implemented.**

- UI: `Adv Oncon Survey`. File: `OnPremiseSurvey.csv` from the ADM folder.
- Primary key: `strCustomerNo,strBrand,strDateSurveyed`.
- `sysQuery` 23 then 24.
- Period delete: `LEFT(strDateSurveyed,6)`.
- Current staging insert includes `blnBobVisibility` and permanent/temporary visibility. Older column lists remain as comments.
- Default: checked.

### FR-009 — Rep calls import

**Implemented.**

- UI: `Mint Rep Calls`. File: `eSalesCharter_CallsAndOrders.csv` from the ADM folder.
- Primary key: `strDate,RepID`.
- `sysQuery` 29 then 30.
- Period delete: `LEFT(strDate,6)`.
- Default: unchecked, so a normal Run skips it until someone checks it. An unattended run with Debug false cannot check it.

### FR-010 — Vendor import

**Implemented.**

- UI: `Por Vendor Import`. Source view: `vwAPVendors` via `db_trgbc`. Primary key: `VendorID`.
- Staging insert of vendor id, name, address, contact, phone, email, cell uses `sysQuery` ID 53 for the view select.
- Master update/insert: `sysQuery` 44 then 45.
- Default: checked.
- No file archive and no period delete.

### FR-011 — GL code import

**Implemented.**

- UI: `Por GL Code Import`. View: `vwGLAccounts`. Primary key: `GLCode`.
- `sysQuery` 52 (view select), 50 (update), 51 (insert).
- Staging columns in code: `MainFullGLCode`, `strMainFullGLCode`.
- Default: checked.

### FR-012 — Call cycle data import

**Implemented.**

- UI: `Call Cycle Data Import`. File: `Call Cycle Data Report.csv` from the ADM folder.
- Primary key: `strProvince,strUserName,intWeeks,intStoreID`.
- Staging: `stgCallCycleDataReport` columns province, user, weeks, days, store id, store code, store name.
- `sysQuery` 82 then 83.
- Period delete is commented out.
- Default: checked.
- Changelog attributes this process to Dirk, 2023-01-20.

### FR-013 — Share CSV exports

**Implemented.**

| UI label | Process name | File | `sysQuery` ID | Default |
|---|---|---|---|---|
| 2Way CRM Export | AdvPDA CRM | `pda_crm.csv` | 35 | Checked |
| 2Way CRM Inactive Export | AdvPDA CRM INACTIVE | `pda_crm_inactive.csv` | 60 | Checked |
| Adv Exposure CRM Export | AdvPDA ExpCRM | `exposure_pda_crm.csv` | 37 | Checked |
| Adv PROD Export | AdvPDA PROD | `pda_prod.csv` | 36 | Checked |
| Planned Promotions Export | Planned Promotions | `PlannedPromotions.csv` | 59 | Checked |

Destination folder is the ADM share (Release) or `C:\Work\RGBC\` (DEBUG compile). Query text is in the database, not in Git.

### FR-014 — Repsly product export

**Implemented.**

- UI: `Repsly Products Export`. File: `RepslyProducts.xlsx` under `Exports\`.
- Queue: `sysCronJobProducts`.
- New queue rows: `mstProduct` where `blnDataTransfer = 1` and the product code is not already queued. Status `Pending`.
- Completed rows are set to `Updated` when `mstProduct.dtStamp` (12-digit `YYYYMMDDHHMM`) converts to a datetime later than `LastProcessed`, and `blnDataTransfer = 1`.
- Completed rows are also set to `Updated` when `blnLastSentDataTransfer` is null or differs from current `blnDataTransfer`. If that column is missing, the code logs a warning and uses a legacy rule: re-queue when `blnDataTransfer = 0`.
- Failed rows are reset to `Pending` when `blnDataTransfer = 1`, or when it is `0` and `LastProcessed` is not null.
- Rows sent: status `Pending` or `Updated`, and (`blnDataTransfer = 1` or (`blnDataTransfer = 0` and `LastProcessed` is not null)).
- Repsly `Active` is `blnDataTransfer`, not `mstProduct.blnActive`. Header comment in the process file states this.
- Name is long description, or `strProductDesc`, plus ` {pack}x{size}` when pack or size is greater than 0.
- Endpoint: `POST /import/productList` body `{"Products":[...]}`. Batch size 200. Comment in code calls 200 a Repsly API limit. The limit was not verified against Repsly documentation in this repo.
- Optional tag: `strTags` with `|` replaced by `, `.
- Custom attributes Size, Package, CBarcode, GBarcode are mapped in the SQL. Type, Type2, Short Code, and PriceSeg are comments saying ignore.
- On success, queue update stores the payload in `RequestPayload` when that column exists. If the column is missing, the payload is stored in `ErrorLog` and a warning names script `05_Add_RequestPayload_Column.sql`. Product success also writes `blnLastSentDataTransfer` when that column exists.
- Default: checked.

### FR-015 — Repsly promotion export

**Implemented.**

- UI: `Repsly Promotions Export`. File: `RepslyPromotions.xlsx`.
- Source is `promoActivity`, not `dwfPromo`. Queue table is `sysCronJobPromotionActivities`, one row per `ActivityID`.
- New activities not in the queue are inserted as `Pending`. There is no `blnActive` filter.
- Completed rows become `Updated` when `promoActivity.dtStamp` is later than `LastProcessed` (same 12-digit conversion as products).
- Failed rows that still exist on `promoActivity` are reset to `Pending`.
- Code sent to Repsly: `PR` plus `ActivityID` zero-padded to 4 digits (`PR0290`, `PR0005`).
- Name: `strActivityName`. Product group code: `refBrandID`. Product group name: `mstBrand.strBrand`. Active: `promoActivity.blnActive`. Unit price: 0. EAN and barcodes: empty. Size and package: 0. Tag: `strTags` with `|` replaced by `, `.
- Endpoint and batch size match products: `POST /import/productList`, 200 per request.
- Both active and inactive activities are eligible. **Implemented.** The cursor plan file describes this as the target. The code matches that target.
- Default: checked.

### FR-016 — Repsly customer export

**Implemented.**

- UI: `Repsly Customers Export`. File: `RepslyCustomers.xlsx`.
- Queue: `sysCronJobCustomers`. New rows from `mstCustomer` with `CustomerType = 'RGBC'`, status `Pending`. Active and inactive customers are both inserted. `blnActive` is not a filter.
- Completed rows become `Updated` when `mstCustomer.dtStamp` is later than `LastProcessed`.
- Completed rows with `LastProcessed` set become `Updated` when `strLastSentTerritory` is null or differs from the current rep manager name (`mstRepManager.strRepManager` via `mstRep.refSalesManagerID`). If the column is missing, a warning names script `16_Add_LastSentTerritory_To_Customer_Queue.sql` and that resync is skipped.
- All `Failed` rows are reset to `Pending`.
- Payload fields: Code `strCustomerNo`, Name `strCustomerName`, Active `blnActive`, RepresentativeCode `refRepID`, RepresentativeName `strRepName`, Territory = rep manager name, State = `mstBranch.strRegionDesc2`, Country `strCountry`, AccountCode / AccountName from key account, Tag from channel-segment fields plus `strTags`.
- Endpoint: `POST /import/clientList` body `{"Clients":[...]}`. Batch size 200.
- Success handling matches products (`Code = 0`, `ImportJobID` from `ID:`).
- Default: checked.

Non-RGBC customers are **Documented** in SQL script 13 and **Not executed** in the VB code at this revision.

### FR-017 — Track it All outlet sync

**Implemented.**

- UI: `Track it All Outlets Export`. File: `TrackItAllOutlets.xlsx`.
- Production path (`Debug` false, or preview checkbox not the cause, and `TrackItAllAllowTestRun` false):
  - Not Wednesday and not Thursday: log skip and return.
  - Thursday: read `sysTrackItAllOutletSyncState` where `StateID = 1`. If `PendingRetry` is missing or 0, log skip and return.
  - Wednesday failure, or a non-Thursday failure: set `PendingRetry = 1`, `LastError`, `LastRunDate`.
  - Thursday failure: clear `PendingRetry`, send `SendTrackItAllFailureNotification`.
  - Success: clear `PendingRetry`.
- Preview path: `My.Settings.Debug` is true and the preview checkbox is checked. `mode=preview`. No weekday gate, no retry flag, no failure email. Response body is logged and written to `Exports\TrackItAllPreviewResult.json` when the write succeeds.
- Test-run path: `TrackItAllAllowTestRun` true. `mode=sync` on any day. No retry flag and no failure email. The log tells the operator to set the flag false when finished.
- A comment in `Run()` says "Debug=False: always sync". The code does not do that. Non-debug runs still use the Wednesday / Thursday gate. The comment disagrees with the code. **Requires Confirmation** only if someone treats the comment as the rule. The requirement here follows the code.
- Data: `mstCustomer` where `blnActive = 1`, ordered by `strCustomerNo`. Columns are listed in `TrackItAllOutletExport.process.vb` `GetOutletData`. `Superbowl Cust` is the constant `1`. Null rep name becomes `-`. Null rep email becomes `n/a`. Null sales manager becomes `-`. Null key account becomes `-`.
- The SQL aliases segment fields as `Channel` and channel fields as `Segment`. That swap is **Implemented**. Whether it is intentional is **Unknown / Requires Confirmation**.
- Upload: `POST {TrackItAllAPIBaseUrl}/rgbc/updateOutlets`, header `X-API-KEY`, multipart fields `file` and `mode`. Timeout 120 seconds.
- HTTP success is any success status code. The body is not parsed for a business error.
- Zero active customers: log, and on a production run clear pending retry.
- Default: export checked, and preview checked. Preview only applies when `Debug` is true. When `Debug` is false the preview panel is hidden and preview is not used, even though the checkbox default is checked (`PreviewOnly = My.Settings.Debug AndAlso chkTrackItAllPreviewOnly.Checked`).

### FR-018 — Batch log email

**Implemented.**

- Trigger: end of `Run()`, and the Email Log button.
- Source after the batch insert: `logSuperbowl` where `blnEmail = 0`.
- Rows included: type not `warning` or `debug`, except action `ArchiveFile` is included even as a warning. Warnings that are not `ArchiveFile` are collapsed to one row per process and action, with `txtLog` equal to the count.
- HTML table. Colours: error `FFC2B7`, warning `FFDBB7`, debug `B7FFBC`, info from `getHexColourString(process)`, else grey.
- Empty result: one row `- No Log items found -`.
- After the table is built, `blnEmail` is set to 1 for rows still 0, then one message per non-empty address in `EmailRgbc` split on `;`.
- Subject: `Superbowl Daily 2 Log [{universal time}]`. From address: DEBUG compile `Superbowl Daily 2 <smtp@overdrive.co.za>` via Office 365 port 587 with SSL. Otherwise `Superbowl Daily 2 <administrator@rgbc.co.za>` via `SMTPHost` (no SSL block in that branch).
- Send failure: set `blnEmail = -1` on rows still 0, and log an error into `logSuperbowl`.
- Out of memory: log a warning and try to send whatever body was built, then mark emailed up to the last rendered `dtLog`.

`blnEmail` is not copied from staging into `logSuperbowl`, so new history rows arrive with the column default. The default is **Unknown** (not in this repository). If the default is not 0, the email query returns nothing. **Requires Confirmation.**

### FR-019 — Un-invoiced requisitions email

**Not executed** by `Run()`. **Implemented** on the Email Requisitions button.

- Runs only when today is Monday (`DayOfWeek` string `Monday`).
- Selects `fsrRequisition` joined to user, request type, item totals, and bill-to / ship-to `dwdCustomer`.
- Filters: request type `blnActive = 1`, `strStatus = 'Approved'`, invoice number null or empty, `dtDate` earlier than 14 days ago.
- Sends HTML to a hardcoded To and two hardcoded CC addresses.
- From: `FreeStock Requisition <...>` using the same DEBUG vs live SMTP split as the log.
- No rows: no mail. Non-Monday: no mail.

The 2016 changelog comment says the daily email was cancelled. The button was left in place.

### FR-020 — Logging

**Implemented.**

`WriteLog` inserts into `stgSuperbowlLog` by default (`strStamp` = `Date.Now.Ticks`, `dtLog` universal format, `strType`, `strProcess`, `strAction`, `txtLog`, `blnEmail`). Quotes in the text are doubled. The log text is still concatenated into SQL.

## Integrations

| System | Direction | Trigger | Auth in code | Failure |
|---|---|---|---|---|
| SQL Server `Superbowl` | Read/write | Every process | Connection string. Debug compile: `SQLConnectionDebug`. Else `SQLConnectionRelease` | Many SQL failures are logged; some `doQuery` paths swallow exceptions. See technical documentation |
| SQL Server `TRGBC` | Read views | Vendor and GL import, non-DEBUG compile | `SQLConnectionTRGBCRelease` (integrated security in the checked-in string) | Import error log |
| File share Hennessy `Sales\ADM` and `Sales\2WaySystem` | Read CSV, write CSV | File imports and `sysQuery` exports | None in code (Windows identity of the process) | Missing source: warning, process skipped. Copy failure: error log |
| Repsly | Outbound JSON | Three exports | HTTP Basic from settings | Non-success HTTP returns a response object; `Code <> 0` fails the batch. Transport exception returns `Code = -1` |
| FTP | Outbound XLSX | Before each Repsly POST | Username/password, passive, binary, 60s | Log error; API push continues |
| Track it All | Outbound XLSX | Outlet export | Header `X-API-KEY` | Production: retry next Thursday once, then email |
| SMTP | Outbound mail | End of batch; requisition button; Thursday Track it All failure | See FR-018 | Log email sets `blnEmail = -1`. Track it All mail failure is logged and not retried |

Repsly base URL and Track it All base URL values are **Unknown** in the committed settings file. The code concatenates `baseUrl & endpoint`.

## Constraints

- CSV only for file import. Delimiter comma. Quotes honoured.
- Repsly batches are 200 rows. A failed batch fails every row in that batch.
- Repsly success is `Code = 0` on the POST response. Later import-job failure at Repsly would still leave the queue `Completed`, because status is not polled. **Inferred** from the absence of a `GetImportStatus` call.
- Track it All production sync does not run Friday through Tuesday.
- Partial Import default suppresses fact-table deletes for the file imports that call `SetDeletes`.
- `sysQuery` text is an external dependency. IDs are **Implemented**. SQL text is **Unknown**.
- Application settings required by Repsly, FTP, and Track it All are **not** in `Settings.settings`. See technical documentation.
- Secrets are stored in the repository. This PRD does not repeat them.

## Error and empty states

| Situation | Behaviour | Provenance |
|---|---|---|
| Import file missing | Warning, process skipped, batch continues | Implemented |
| Unsupported extension | Error, `isValid` false | Implemented |
| Duplicate staging key | Warning per duplicate key, import continues | Implemented |
| Repsly queue empty | Info "No products/promotions/customers to process" | Implemented |
| Track it All no active customers | Info, clear pending retry on a production run | Implemented |
| Track it All wrong weekday | Info skip | Implemented |
| FTP upload fails | Error, Repsly POST still attempted | Implemented |
| XLSX generation fails | Error, Repsly process returns without POST. Track it All sets retry or sends the Thursday mail | Implemented |
| Log email has no rows | HTML says no log items, and still sends | Implemented |
| `sysQuery` row missing at startup | Form load throws on `dr!memQuery` | Implemented |

## Planned behaviour

The cursor plan under `.cursor/plans/` is a design note for the promotion-as-product export. That design is already in `RepslyPromotionExport.process.vb`. No other planned feature was found.

`Database_Scripts/README.md` status `Inactive` and `Processing` are **Documented**. Current export code sets `Pending`, `Updated`, `Completed`, and `Failed`. It does not set `Inactive` or `Processing`. Script `06_Fix_Stuck_Processing_Records.sql` updates rows already in `Processing` on `sysCronJobProducts` and `sysCronJobPromotions`.
