# Technical documentation — stock_system

Evidence date: 2026-10-02. Source: `git@bitbucket.org:overdrive/stock_system.git`, `main` at `02b62e87b9595ab3a8cfa8352809cc2173e92284`.

Secrets were present in the clone. This file names the files and the kinds of secret. It does not copy keys, passwords, tokens, or webhook URLs.

## Stack

| Item | Evidence | Provenance |
|---|---|---|
| Language | PHP. `composer.json` requires `^8.2`. `README.md` says 8.3. `docs/overview.md` says `^8.0` | **Implemented** constraint is `^8.2`. The other two figures disagree |
| Framework | `codeigniter4/framework` `^4.0`. Front controller `public/index.php`. CLI `spark` | **Implemented** |
| Composer package name | `codeigniter4/adminstarter`. Description "Admin Overdrive". Homepage `https://www.overdrive.co.za` | **Implemented** metadata |
| UI | Bootstrap 5, DataTables, Feather icons. `docs/index.md` | **Documented** |
| Database | MySQLi. Active settings in tracked `.env`: host `localhost`, database `annies`, user `root`. A commented block names a remote host and database `dms_test` | **Implemented** as config. Which database production uses is **Unknown** |
| Sessions | `app.sessionDriver` database handler, cookie `ci_session`, save path `sessions`, `sessionMatchIP` true | **Implemented** in `.env` |
| Tests | PHPUnit `^9.1` (`composer.json`), `phpunit.xml.dist`, `tests/` | **Implemented** |
| Code scan config | `sonar-project.properties`. Project key starts `overdrive_admin-overdrive-v2_`. Coverage analysis is disabled in that file | **Implemented** as config |

PHP extensions named in `README.md`: intl, libcurl if HTTP client is used, json, mbstring, mysqlnd, xml. DMS notes add fileinfo. Shop sync code does not add further extensions beyond what CodeIgniter and Composer already need (`ext-simplexml` is required).

## Application structure

```text
app/Config/Routes.php          HTTP map
app/Config/Filters.php         CSRF, login, menu grant, time access
app/Controllers/               Staff and integration controllers
app/Controllers/Storefront/    Public catalogue
app/Libraries/Shop/            Cart, catalogue, enquiry, sync log
app/Libraries/                 Mailbox, support, scheduler, OCR, TOTP, JWT, bulk email
app/Models/                    Including Models/Shop/
app/Database/Migrations/       63 migration files in this clone
app/Database/Seeds/            Roles, menus, demo catalogue
app/Views/                     Staff and storefront views
app/Commands/                  Spark commands listed below
public/                        Web root, plus deploy.php and info.php
docs/                          In-repo product and module notes
tests/                         Unit and a few feature tests
work_scripts/                  Local Trello seeding script for Annie's phase 1
```

`builds/` and `build/` exist as top-level names from the task's original scan. This clone's root listing showed `build` and `builds` as files or directories beside `app`. Their contents were not treated as the application. **Unknown** whether they are build artifacts that should be untracked.

## Auth

1. `Authentication` (`isLoggedIn`): session flag must be true, otherwise redirect to `login`.
2. `Authorization` (`isGranted`): first path segment must match a menu URL for the session role, and `UserModel::checkUserAccess` must pass.
3. `TimeAccessFilter`: for `documents*` and `mailbox*`, users with a time restriction must be inside the window.
4. CSRF on state-changing requests, except the support webhook and the shop sync log.
5. `SecurityHeaders` after every response.
6. Debug toolbar only when `ENVIRONMENT === 'development'` and `CI_DEBUG`.
7. `ShopSyncAuth` and `SupportWebhookAuth` for the two machine callbacks.
8. `EmailTrackingThrottle` on `email-tracking/*`.
9. Passwords: `password_verify` against the stored hash.
10. Email 2FA and TOTP are both present. See the PRD. They are separate code paths.
11. `JwtLibrary` exists. The public storefront does not use a bearer token. The commented route notes in `Routes.php` describe an organisations login token. That controller was not found.

`Filters.php` also aliases `throttle`, `honeypot`, and `Cors.php` exists as a class. Honeypot is commented out of globals. CORS is not in the alias list that was read. **Implemented** only where wired.

## Background work

| Command | Class | Role |
|---|---|---|
| `php spark scheduler:run` | `RunScheduler` | Due tasks. Flags documented: `--override`, `--task`, `--action` |
| `php spark mailbox:poll` | `MailboxPoll` | IMAP ingest |
| `php spark send:bulk-email-campaigns` | `SendBulkEmailCampaigns` | Due campaigns |
| `php spark monitor:system` | `MonitorSystemUsage` | CPU/memory monitor |
| `php spark shop:sync-demo` | `ShopSyncDemo` | Writes a demo sync log |

HTTP triggers outside login: `GET /cronJobs`, `GET /scheduler/run`.

Email queue tables go back to migration `2024-01-18-092523_SentQeue.php` (spelling as in the filename). Handlers `sendScheduledEmails` and `getAllUnsentEmails` are registered for the scheduler.

There is no queue broker (Redis, SQS, database queue worker framework) in `composer.json`. "Queue" here means the `sent_queue` table plus cron.

## Data

Shop tables and column ownership are specified in `docs/db_docs/shop_products.md` and `docs/shop_sync_contract.md`. Summary:

- `shop_products` match keys: unique `sqlexpress_product_id`, unique `code`.
- Images: `shop_product_images.product_id` cascades. Sync also fills `image_url_1/2/3`.
- `shop_order_items.order_id` cascades. `product_id` on the line is nullable and has no foreign key.
- `shop_sync_log` stores counts only. No foreign key to products.

Other table groups, by migration theme: users and roles, menus, settings, cronjob actions and logs, email templates and sent queue, login history, blocked users, two-factor and TOTP, documents and tags and categories and audit and user assignments, chat and embeddings, bulk email and tracking, mailbox tickets, support tickets, shop tables.

`app/Database/Migrations/2022-11-20-124016_Organisations.php` creates an organisations table. No organisations controller is in the tree.

## Integrations

See the PRD table. Technical notes:

- SMTP settings are read from the environment (`README.md` says this was changed so mail is not hard-coded). The tracked `.env` still contains an active SMTP block.
- OpenAI packages: `orhanerday/open-ai`, `neuron-core/neuron-ai`. Vector store type in `.env` is `mysql`, dimension 1536.
- PDF: `smalot/pdfparser`, `mpdf/mpdf`. Spreadsheets: `phpoffice/phpspreadsheet`. CSV: `league/csv`. AWS SDK is required (`aws/aws-sdk-php`) . Which AWS API the app calls was not traced for this pass. **Unknown** which AWS feature is still used.
- TOTP: `robthree/twofactorauth`. HTML cleaning: `ezyang/htmlpurifier`. Cron expressions: `dragonmantank/cron-expression`.
- WAHA base URL in the tracked env is `http://localhost:3000`. Webhook public URL path uses `admin_overdrive_v2`, while `app.baseURL` uses `annies_stock_system`.
- SOAP client targets PnP portal EDI pull-service WSDLs (test and production host `pnpportal.co.za`) inside `Soap.php`. TLS peer verification is disabled in that client. No caller was found.
- `Slack.php` and `public/deploy.php` embed Slack webhook URLs.

## Deployment and environment

`README.md` setup: copy `env` to `.env`, `composer install`, `php spark db:create`, `php spark migrate`, `php spark db:seed users`, `php spark key:generate`, `php spark serve`.

Additional seeds named in module docs: `DocumentsMenuSeeder`, `SupportMenuSeeder`, `ShopSyncMenuSeeder`, `SchedulerMenuSeeder`, `MailboxMenuSeeder`, `ShopCatalogueSeeder`, `UserRole`.

No `Dockerfile` or `docker-compose` file was in the root. No `bitbucket-pipelines.yml`.

`public/deploy.php` is a token-and-IP gated deploy script. The project string inside it is `SAWIS: Label`. It posts to Slack. It is excluded from Sonar (`sonar.exclusions` includes `public/deploy.php`).

`docs/testsprite_run.md` records a WAMP URL path `annies_stock_system`. That is a local test note, not a production host.

## Security findings (no values)

These are observations for human review. They are not a pentest.

| Item | Why it matters |
|---|---|
| `.env` is tracked (`git ls-files` lists it). `.gitignore` has no `.env` rule | OpenAI key, SMTP password, encryption key, WAHA dashboard password, and commented remote DB password are in git history as of this tip |
| `public/deploy.php` | Hard-coded Slack bot token, webhook, and deploy query token |
| `app/Controllers/Slack.php` | Hard-coded webhook. Route `GET slack` is outside the login filter |
| `public/info.php` | `phpinfo()` with no check in the file |
| `Soap.php` | `verify_peer` and `verify_peer_name` set false |
| Empty `shop.syncApiKey` | Sync log is open in the development environment |
| `test_cookies.txt` at the repo root | Present in the clone. Contents were not copied. Treat as possible session material |

## Development and testing

- Test command: Composer script `test` runs `phpunit`.
- Unit tests present for cart, catalogue, enquiry, product slug, support tickets, scheduler, mailbox parsing, bulk email, documents, health.
- Feature tests include mailbox auth and document tab preferences.
- `tests/database/ExampleDatabaseTest.php` and `tests/session/ExampleSessionTest.php` are framework examples.
- `docs/TESTING_SUMMARY.md`, `docs/TESTING_RESULTS.md`, and `docs/testsprite_run.md` are historical run notes. They were not re-executed for this document.
- In-repo task protocol: `docs/index.md` and `docs/tasks/TASK-*.md`. Latest task file found is TASK-015, status completed.
- `work_scripts/trello_annies_phase1/` seeds Trello cards. It is a local script, not the application.

## Monitoring

`ServerMonitoring` reads CPU, memory, and disk and compares them with settings named like `cpuUsageAlertThresholdDropdown`. Scheduler handler `checkServerLogs` is the scheduled path. There is no application-performance vendor (Sentry, Datadog) in `composer.json`. Sonar is a scan config only.

## What this document does not establish

- Production hostname, TLS, and who deploys.
- Whether migrations have been applied to database `annies`.
- Whether the .NET coordinator repository is a separate Bitbucket project.
- Full call graph for AWS SDK usage.
- Any commit before `02b62e87` (clone was the tip only).
