# Business rules — magusEvents

Evidence date: 2026-10-09. Inspected revision: `master` at `bb6e583` (2019-08-16).

Source: `git@bitbucket.org:overdrive/magusevents.git`.

No business-rules document existed in the source repository or in Silicon Ark before this file. There is no product author statement of these rules. Each rule is **Inferred** from code unless the status says otherwise. A comment that states the rule is marked **Documented** only for that comment, and the behaviour is still **Implemented**.

Do not treat a rule as approved policy because the code does it. Where the code looks inconsistent, the inconsistency is written down.

## Actors

**BR-001 — Anyone at the PC can scan and change settings.**

There is no login, no role, and no PIN. The SETTINGS control opens setup immediately.

- Status: **Inferred** from the absence of a check, and **Implemented** as that absence.
- Evidence: `Scan.GoToSettings`, `Scanmeal.GoToSettings`, no password field in the project.

## Event selection

**BR-002 — One active event.**

Save sets `Active = 0` on every event with `AC > 0`, then sets `Active = 1` on the chosen event.

- Status: **Inferred**, **Implemented**.
- Evidence: `Setup.Save_Data`.

**BR-003 — The active event is the row read last.**

`GetAccreditationID` selects `Active = 1` with no `ORDER BY` and overwrites the id on every row. More than one active row is possible if a write failed part way.

- Status: **Inferred**, **Implemented**.
- Evidence: `Database.GetAccreditationID`.

**BR-004 — The event list comes from Footprint, and only while the probe succeeds.**

Offline, settings shows "SELECT EVENT" and does not read the local `Events` table (that call is commented out).

- Status: **Inferred**, **Implemented**.
- Evidence: `Setup.get_time`.

**BR-005 — Meal day and meal session are operator choices stored on the event.**

`DayNumber` and `SessionNumer` are the buttons selected in settings, not a date calculation. Meal scanning uses those stored integers. `MealDays` is copied from the API field `TotalDays`. `MealsPerDay` is copied from `MealsPerDay`.

- Status: **Inferred**, **Implemented**.
- Evidence: `Setup.Save_Data`, `Scanmeal` constructor.

**BR-006 — `IsMealZone` chooses the scanner only at process start.**

`1` opens `Scanmeal`. Any other value opens `Scan`. Saving, finishing an image download, or importing a CSV does not apply this flag to the next page. Those paths use `Setup.OPT` or hard-code `Scan`.

- Status: **Inferred**, **Implemented**. Whether the delayed switch is intentional is **Unknown / Requires Confirmation**.
- Evidence: `MainWindow.getbody`, `Setup.Save_Data`, `Setup.GetProfileImages`, `Setup.Import_CSV`.

## People data

**BR-007 — A downloaded person with no barcode is not saved.**

Comment in `Person.cs` (2019-07-18, maanie): "do not save profiles without a barcode". The JSON overload skips `Barcode` null or `""`.

- Status: **Documented** by that comment, **Implemented** in `save_to_db(string)`.
- The CSV overload does not apply this skip.
- Evidence: `Person.cs`.

**BR-008 — An existing person is updated in place; a new person is inserted only if the update affects no row.**

Match key on the JSON path is `AASID`. The INSERT statement that follows a failed update is malformed around the FirstName value (a comma sits inside the literal, then the Surname literal begins). `ExecuteData` swallows the error and returns false. The operator is not told.

- Status: **Implemented**. Whether new rows actually persist is **Unknown / Requires Confirmation**.
- Evidence: `Person.save_to_db` (both overloads), `Database.ExecuteData`.

**BR-009 — Quotes in zone lists are turned into spaces before save.**

`aZones` from JSON has `"` replaced with a space, then the zone screen strips `"`, `[`, and `]` again at scan time.

- Status: **Implemented**. The intended storage format is **Inferred**.
- Evidence: `Person.save_to_db(string)`, `Scan.UserProfile`.

**BR-010 — CSV rows are data, including the first line.**

No header is skipped. Column meaning is the array position documented in the PRD (FR-012), not a name in the file.

- Status: **Inferred**, **Implemented**.
- Evidence: `Setup.Import_CSV`.

**BR-011 — A profile image file means the photo is shown; the database only stores a flag.**

`User_pic` is `0`/`1` (or empty). The bytes live in `c:\temp_acc\{AAID}.txt`. The scan screen splits that file on comma and treats index 1 as base64. If the split has no index 1, the catch leaves the current image.

- Status: **Inferred**, **Implemented**.
- Evidence: `Setup.DownloadPicFileCompleted`, `Scan.UserProfile`.

## Zone access

These rules are the same block in `Scan.UserProfile` and `Scanmeal.ViewProfile`.

**BR-012 — The barcode must match `Persons.Barcode` for the active event `AC`.**

Zone scan looks up the page field that still holds the wedge text. Meal scan looks up the method argument. NFC lookup uses `SCANID` and is not started.

- Status: **Implemented**.
- Evidence: `Person.GetRow`, `Person.GetByNFC`.

**BR-013 — Unknown barcode is denied.**

Zone screen: `AASID` must be greater than 0 or the labels become Invalid / N/A and access stays off. Meal screen: the test is `AAID > 0`. A row with `AAID` 0 and a positive `AASID` would pass one screen and fail the other.

- Status: **Implemented**. Whether that split is intended is **Unknown / Requires Confirmation**.
- Evidence: both `UserProfile` / `ViewProfile` methods.

**BR-014 — Zone id text `312` means full access.**

After bracket stripping, `string.Contains("312")` forces the all-access path and reloads zones without the lock filter. This is not an equality check on one id.

- Status: **Inferred**, **Implemented**. The business name of zone 312 is **Unknown / Requires Confirmation**.
- Evidence: both scan pages.

**BR-015 — A locked zone that is open right now restricts the match.**

`GetLockedCount` counts zones for the event where `Lock = 1`, `Active = 1`, and current `HHmm` is between `FromTime` and `ToTime` inclusive. If the count is greater than 0, and the person is not full access, only the person's zones that are `Lock = 1` and `Active = 1` are candidates. The time window is applied again in the page, not in that second query.

If the count is 0, candidates are the person's `Active = 1` zones, locked or not.

- Status: **Inferred**, **Implemented**.
- Evidence: `Database.GetLockedCount`, `GetActiveZonesByArrayLocked`, `GetActiveZonesByArray`.

**BR-016 — A zone with both times 0 is always in window. Any other zone must contain the current `HHmm`.**

Times are compared as integers after removing `:`. A value that is not numeric throws, and the scan message box shows the exception.

- Status: **Inferred**, **Implemented**.
- Evidence: the `FromTime_int` loop on both scan pages.

**BR-017 — Zone type 24 means all access.**

If a zone that survived BR-016 has `ZTID = 24`, the screen takes the all-access path and uses that zone's `Icon`.

- Status: **Inferred**, **Implemented**. The name of type 24 is **Unknown / Requires Confirmation**.
- Evidence: both scan pages.

**BR-018 — Grant and deny are visual, audible on the zone screen, and written to a text log.**

Zone grant plays `SystemSounds.Asterisk` and writes a `scan.txt` line ending `|1|0|;`. `images\ok.wav` is loaded and not played (`OK_Mplayer.Play` is commented out). Zone deny plays `images\Bazz.wav` and writes a shorter line (no `|1|0|`). Meal grant and deny change colour and the words GRANTED and DENIED only.

The integers in the log are not named. Treating `|1|0|` as granted-and-not-denied, and the meal file's later `|0|1|` as the opposite, is **Inferred** and not confirmed.

- Status: **Implemented** for the writes and the sounds. **Inferred** for the token meaning.
- Evidence: `Scan.UserProfile`, `Scanmeal.ViewProfile`.

**BR-019 — The result stays for 10 seconds, then the screen is rebuilt.**

- Status: **Inferred**, **Implemented**.
- Evidence: `DispatcherTimer` interval of 10 seconds, `reset` on both pages.

## Meals

**BR-020 — A person who fails the zone rules does not get a meal.**

No `Food` insert and no undo panel. A line is not added to `meal.txt` on that path.

- Status: **Inferred**, **Implemented**.
- Evidence: `Scanmeal.ViewProfile` (`Active` stays 0).

**BR-021 — One meal per person for the stored day and session.**

If `Food` already has that `AASID` + `Day` + `Season`, the scan is DENIED and no new row is inserted. Otherwise GRANTED and a row is inserted.

The lookup reads `Season` back from the `Day` column into the object. The SQL filter itself uses the `Season` column, and the decision uses `AASID`, so the mapping does not flip the allow/deny result. It does make the loaded `Season` property wrong.

- Status: **Inferred**, **Implemented**.
- Evidence: `Meals.GetRow`, `Database.GetFoodProfile`, `Scanmeal.ViewProfile`.

**BR-022 — Every zone-cleared meal scan writes a `|1|0|` line before the allow/deny branch.**

A first meal then also writes a `|0|1|` line. A repeat meal does not write the second line. So a granted meal produces two lines, and a repeat produces one line that uses the same `|1|0|` shape as the first half of a grant.

- Status: **Implemented**. Reading `|1|0|` as "denied" would be wrong for this file, because it is written before the decision. **Requires confirmation** if any downstream report treats the first line as the outcome.
- Evidence: `Scanmeal.ViewProfile`.

**BR-023 — An undo barcode deletes one meal row.**

The text must be `UNDO`, then `|`, then AASID, then `|day|`, then the day number, then `|season|`, then the session number. Delete success (a row changed) reloads the meal page. Delete of nothing leaves the page as it is.

- Status: **Inferred** from the parser and the commented QR string, **Implemented**.
- Evidence: `Scanmeal.ScanninAcc`.

## Online behaviour

**BR-024 — The process starts online.**

`WorkMode = "ON"` is set in `MainWindow` and is not restored from disk. A previous offline choice is forgotten on the next launch.

- Status: **Inferred**, **Implemented**.
- Evidence: `MainWindow` constructor.

**BR-025 — Offline mode stops only the zone-screen timers.**

It does not block a manual Download Data or event-list load. Those still call the network when the operator is on settings. The meal screen never starts the timers.

- Status: **Inferred**, **Implemented**.
- Evidence: `Scan` constructor, `Setup.GetNewData`, `Scanmeal` constructor.

**BR-026 — While online, images refresh every 120 seconds and applicant updates every 240 seconds.**

Both wait until `DownloadingImg` is 0 and the connectivity probe succeeds. The update uses `c:\temp_acc\LastUpdate.txt`, default text `2018:09:29 08:00` when the file is missing. After each response the file becomes `yyyy-MM-dd HH:mm` in local time, even when `data` is empty.

- Status: **Implemented**. The default timestamp is **Implemented** and is not a valid `yyyy-MM-dd` value. Whether the API accepts it is **Unknown / Requires Confirmation**.
- Evidence: `Scan.xaml.cs`, `Webservices.CheckUpdates`. The intervals were shortened in `43bc412` (15s and 20s) and restored to 120s and 240s in `bb6e583`.

**BR-027 — "Online" means the tournament URL answered.**

The probe is `https://www.footprintapp.net/api/tourn/`, not the accreditation URLs used for data. A failure there is treated as offline even if the accreditation host would have answered, and the reverse is also possible.

- Status: **Inferred**, **Implemented**.
- Evidence: `Webservices.CheckForInternetConnection`.

## Data that is not enforced

**BR-028 — Barcode length is stored as a column and never checked.**

`Events.BarcodeLength` is created and `GetBarcodeLength` can read it. No screen calls that method. `Save_Data` does not write the column.

- Status: **Implemented** as unused. No rule is enforced.

**BR-029 — Scan and meal logs are not sent anywhere.**

Nothing in the live path uploads `scan.txt` or `meal.txt`. `Save_Report` is unused.

- Status: **Inferred** from the absence of a call.

**BR-030 — There is no audit of who pressed Save, Download, or Import.**

- Status: **Inferred** from the absence of a user id.

## Conflicts inside the implementation

| Topic | What the code does | What needs a human |
|---|---|---|
| New applicant insert | UPDATE is valid SQL. INSERT in `Person.save_to_db` is not, around FirstName. Errors are hidden. | Confirm whether new people appear after Download Data or CSV import. |
| Clear on download | Comment says delete applicants. The DELETE is passed to a method that uses `ExecuteReader`. | Confirm the table is emptied before a download. |
| Meal screen after save | Checkbox is stored. The next page ignores it until restart. Image download and CSV import always open `Scan`. | Confirm the intended screen. |
| Found-person test | Zone screen uses `AASID > 0`. Meal screen uses `AAID > 0`. | Confirm both identifiers are always set together. |
| Meal log tokens | `|1|0|` is written before the meal decision. `|0|1|` is written only on a new meal. | Confirm any report that reads `meal.txt`. |
| `312` and `ZTID` 24 | Both force all access. Names are not in the repo. | Confirm the zone catalogue. |
| NFC | Types and a UID length of 11 are implemented. Startup is commented out. | Confirm NFC was dropped on purpose by 2019-08-16. |
| Settings protection | This app has none. `maguseventsxamarin` has a fixed four-digit code. | Confirm the desktop app was meant to stay open. |
