# 01 — Current System Audit (TechTitans POS, Phase 1)

**Audit date:** 2026-11-03
**Scope:** `C:\wamp64\www\techpos` — copied from NH POS ("Cacart" restaurant POS SaaS).
**Mode:** read-only. No application code, schema or data was changed. No migrations, seeders, jobs, payment operations or test-database writes were run.

---

## 1. What was copied

| Part | Path | Stack | Git history |
|---|---|---|---|
| Backend API | `backend/` | Laravel **13.21.1**, PHP **8.5.12** (WAMP `C:\wamp64\bin\php\php8.5.12.1`) | 148 commits, latest "Phase 5B-D2: business customers, contact persons & customer relationships" |
| Frontend SPA | `frontend/` | React 19.2, Vite 8, TypeScript ~6.0, react-router 7, Vitest 4, Playwright 1.61 | 114 commits, latest "Phase 5B-D2: business customer UI…" |
| Database dump | `techpos.sql` | MySQL 8.4.7 phpMyAdmin dump of NH's `nh_pos_db` | — |
| WordPress plugin (NH original, **outside** this folder) | `C:\wamp64\www\nh\wp-content\plugins\vmwebs-commerce` | Standalone WP commerce plugin v0.3.0 | inspected read-only |

The project root `C:\wamp64\www\techpos` is **not** a git repository. `backend/` and `frontend/` are separate repos.

### Size

| Metric | Count |
|---|---|
| PHP files under `backend/app` | 909 |
| Eloquent models (`backend/app/Models`) | ~125 |
| Migrations (`backend/database/migrations`) | 194 (196 rows in the `migrations` table, batch 22) |
| Registered routes (`php artisan route:list`) | **479** (most under `api/v1/*`) |
| Backend test files | 436 (Feature + Unit) |
| Frontend unit test files | 164 files / 1,687 tests |
| Frontend e2e specs | 6 (`frontend/e2e/*.spec.ts`) |
| Database tables (imported into `techpos`) | 140, all InnoDB |

## 2. Architecture summary

- **Multi-tenant SaaS.** Every business table carries `tenant_id`. Integrity comes from composite `(tenant_id, id)` foreign keys plus a global `TenantScope` (`app/Models/Concerns/BelongsToTenant.php`, `app/Tenancy/*`). The `reject.tenant_id` middleware refuses any `tenant_id` sent by a client.
- **Location-scoped.** Locations, location access grants, and location-scoped roles live in `app/Access`, `app/Authorization/LocationScopeGuard.php`.
- **Domain-service layout.** Logic lives in `app/<Domain>/*Service.php` and is called from thin controllers under `app/Http/Controllers/Api/V1/*`.
- **Four auth stacks:**
  - tenant users (Sanctum tokens);
  - attendance-kiosk device tokens;
  - storefront-connection bearer tokens (WordPress);
  - platform (super-admin/support) tokens.
- **Write discipline.**
  - `X-Request-Id` idempotency (`app/Restaurant/RequestIdempotency.php`, `request_idempotency` table).
  - Row locks with deterministic lock order.
  - Append-only audit log (`app/Audit/DatabaseAuditRecorder.php`; a DB trigger blocks `UPDATE` on `audit_logs`).
  - Money in integer minor units, quantities via bcmath.
- **Status models.** Orders have four independent status axes (`status`, `payment_status`, `kitchen_status`, `pickup_status`), each protected by DB CHECK constraints.
- **Response envelope.** `{success, data, message, errors}` (frontend `src/lib/api/client.ts`).
- **Infrastructure dependencies** (from `backend/.env`):
  - MySQL;
  - **Redis** for cache, session and queue (`predis`);
  - Reverb for broadcasting (configured, but no broadcast events exist);
  - the `s3` filesystem disk (empty credentials);
  - mail driver `log`;
  - `barryvdh/laravel-dompdf` for invoices;
  - `giggsey/libphonenumber-for-php`.

## 3. Domain map (backend `app/`)

| Domain folder | Purpose | Retail relevance |
|---|---|---|
| `Menu/` | Catalogue: categories, products, variants (option matrix), SKU policy, pricing (base / location / online / sale), tax classes, media, catalogues, CSV import, combos, modifiers, kitchen routing | **High** — the generic retail catalogue is here, under restaurant naming |
| `Inventory/` | Stock items, per-location stock, movement ledger, alerts, availability, order deduction | **High** — foundation for TechTitans inventory |
| `Orders/` | POS and online orders, pricing, workflow, cancel/restock, reorder, tracking | **High** — retail POS core (restaurant flavoured) |
| `Payments/` | Gateways (Sandbox, Bambora/Shift4, Moneris), offline payments, payment links, ledger, reconciliation | **High** |
| `Invoices/` | PDF invoice/proforma, numbering | **High** |
| `Checkout/`, `Shipping/`, `Storefront/` | Server-side quote, shipping zones, WordPress storefront API | **High** — online selling |
| `Customers/` | CRM: contacts, addresses, groups, tags, custom fields, business profiles, relationships, financial summary | **High** — base for the unified CRM |
| `Auth/`, `Authorization/`, `Access/`, `Tenancy/`, `Audit/`, `Platform/` | Identity, RBAC (75 permission keys), tenancy, audit, platform admin | **High** |
| `Workforce/` | Employees, scheduling, time clock, attendance kiosk, attendance intelligence | Medium (useful staff tooling) |
| `AI/` | AI feature framework; only `FakeAiProvider` | Low |
| `Kds/`, `Printers/`, `Stations/`, `Channels/` | Kitchen display, printer config, prep stations, sales channels | Restaurant-specific (printers reusable later for receipts/labels) |
| `Restaurant/` | Tenant/location settings, business hours, logo | Generic settings under restaurant naming |

## 4. Safe checks performed and results

| Check | Command | Result |
|---|---|---|
| PHP / Laravel versions | `php -v`, `php artisan --version` | PHP 8.5.12, Laravel 13.21.1 ✅ |
| Route registration (boots the app, no DB) | `php artisan route:list --json` | **479 routes** registered ✅ |
| Static analysis | `vendor/bin/phpstan analyse` (level 5, `app/`) | **No errors** ✅ |
| Code style | `vendor/bin/pint --test` | ⚠️ **Not completed.** Still running after more than an hour on the slow WAMP filesystem, so stopped. Re-run later. |
| Frontend typecheck | `tsc -p tsconfig.app.json --noEmit` | **0 errors** ✅ |
| Frontend lint | `eslint .` | **0 errors, 3 warnings** (`react-hooks/exhaustive-deps`, e.g. `src/pages/settings/WebsiteConnectionsPage.tsx:34`) ✅ |
| Frontend unit tests | `vitest run` | **1,685 / 1,687 passed.** 2 failed in `src/pages/settings/RestaurantProfilePage.test.tsx`. That file passes **18/18 when re-run alone**, so these are timeout failures under machine load, not defects. ✅ |
| Backend PHPUnit suite | `php artisan test` | ❌ **Not run (deliberately).** See §5. |
| Frontend e2e (Playwright) | `playwright test` | ❌ **Not run.** The specs log in to a live backend as the NH tenant `kings-biryani` with hard-coded NH credentials and write data. |
| WordPress plugin | — | Inspected read-only, not executed. |

## 5. Why the backend test suite was not run

`backend/phpunit.xml` sets `DB_DATABASE=nh_pos_test`, the **NH project's test database**. The suite uses `RefreshDatabase` (drop, re-migrate, write), and `tests/Support/TestDatabaseGuard.php` only checks that the name ends in `_test`. Running it from this copy would wipe and rewrite NH's test database. That breaks the isolation rule.

**To unblock:**
1. Change `phpunit.xml` to `DB_DATABASE=techpos_test`.
2. Create that empty database.
3. Run `scripts\test.cmd` (or `php artisan test`).

This needs owner approval because it creates and migrates a new database.

## 6. Database state (`techpos` database)

- 140 InnoDB tables imported from NH's `nh_pos_db` dump. The original dump left the engine unspecified on some tables; this was fixed during import so every table is InnoDB.
- It contains **NH business data**:
  - one tenant, `Kings Biryani` (slug `kings-biryani`);
  - one user, `nhuser`;
  - 37 orders, 25 customers with real-looking emails and phones, 348 audit-log rows;
  - 6 storefront connections pointing at `http://localhost/NHweb` with valid token hashes;
  - 1 sandbox payment gateway config with **encrypted credentials**;
  - 93 personal access tokens.
- Encrypted columns were encrypted with **NH's `APP_KEY`**: `payment_gateway_configs.credentials`, `customer_business_profiles.tax_identifiers`.
  - The TechTitans `.env` has an **empty `APP_KEY`**, so these values cannot be decrypted here.
  - Once a new key is generated, they stay unreadable for good. That is intended for isolation, but the rows must be removed or reset.

## 7. Blockers and unverified items (summary)

See `02` for per-module detail and `04` for WordPress.

1. **Isolation blockers.** These must be fixed before any write, migration or test run (detail in §8).
2. **Backend tests unverified in this copy.** They were not run, for the reason in §5.
3. **The v0.4.0 WordPress connector is missing.**
   - Audit logs show requests from user agent `VMWebs-Commerce/0.4.0` against `http://localhost/NHweb`.
   - That build is **not present** on this machine. The local `nh` plugin is v0.3.0 and contains no POS connector code.
   - The real connector source must be supplied before the WordPress sync can be audited.
4. **MySQL service state.** At the time of the audit, the WAMP MySQL service had been stopped after a `my.ini` edit and needs restarting from the WAMP tray, which requires admin rights. Database-dependent checks were done earlier while it was running.
5. **Pint not completed** (see §4).

## 8. Isolation findings (Task 3)

Secrets are not reproduced here.

| Area | Finding | Risk | Action |
|---|---|---|---|
| Database credentials | `backend/.env` → `DB_DATABASE=techpos`, root, local | ✅ isolated | Create a dedicated, non-root MySQL user |
| Test database | `phpunit.xml` → `nh_pos_test` (NH's) | 🔴 Tests would wipe NH test data | Change to `techpos_test` |
| Git remotes | `backend` → `bitbucket.org/vmwebs-softwares/nh-backend.git`; `frontend` → `nh-frontend.git` | 🔴 A push would land in the NH repositories | Re-point to new TechTitans repos (`git remote set-url`) |
| Redis cache / session / queue | `config/database.php:116`, `config/cache.php:115`, `config/session.php:132`: prefixes come from `APP_NAME` = "Restaurant POS SaaS API", the same as NH. Redis was **not listening** on 6379 during the audit | 🔴 With a shared Redis, TechTitans and NH would share queues (jobs run by the wrong app), caches and sessions | Set `APP_NAME=TechTitans POS` and explicit `REDIS_PREFIX`, `CACHE_PREFIX`, `SESSION_COOKIE`, or use a separate Redis DB index (`REDIS_DB`/`REDIS_CACHE_DB`) |
| APP_KEY | Empty in `.env` | 🟠 App cannot encrypt or decrypt; imported NH ciphertext cannot be read | `php artisan key:generate` (new key), then purge or re-enter encrypted rows |
| App / frontend URLs | `APP_URL=http://localhost`, `FRONTEND_URL=http://localhost:5173`; frontend `VITE_API_BASE_URL=http://localhost:8000/api/v1` | 🟠 Same default ports as NH, so both cannot run at once | Assign distinct ports or vhosts (e.g. `techpos.local`) |
| Storage | `FILESYSTEM_DISK=s3` with empty credentials; `storage/app` is empty (no NH files were copied) | 🟠 Product images in the DB point at paths that do not exist here | Use the local disk for dev, or a dedicated TechTitans bucket |
| Mail | `MAIL_MAILER=log`, `no-reply@example.com` | ✅ No outbound email | Configure TechTitans sender later |
| Payments | Stripe keys empty; one **sandbox** gateway row (NH, encrypted); Bambora/Moneris adapters coded for sandbox | 🟠 No live NH payment credentials in env, but the DB row belongs to NH | Delete or replace the NH gateway row |
| WordPress webhooks / connections | 6 `storefront_connections` (NH sites `localhost/NHweb`, `nhdream.ca`, test sites) with active token hashes | 🔴 Any NH WordPress install still holding those tokens can call this TechTitans API if both run on localhost | Revoke all NH connection tokens / delete NH connections before running the API |
| Scheduled jobs | `routes/console.php`: only `cacart:attendance-evaluate` every 15 minutes | 🟢 Harmless, but runs against NH data | Do not start `schedule:work` until the data is cleaned |
| Branding | Backend `APP_NAME` "Restaurant POS SaaS API"; command namespace `cacart:*`; `DevBootstrap` seeds "Kings Biryani"; frontend "Cacart" (title, logos, login copy), `cacart.*` localStorage keys, "NH POS" in some UI copy | 🟡 Cosmetic, but user-visible | Rebrand pass (see `05`) |
| Dev scripts | `backend/scripts/_resolve-php.cmd` uses an `NH_POS_PHP` override variable | 🟡 | Rename later |
| NH tenant data | Kings Biryani tenant, NH customers (PII), orders, tokens | 🔴 Personal data and credentials from another business | Start TechTitans from a **clean** database (fresh `migrate` + provisioning) instead of the NH dump, after approval |

**Conclusion:** the TechTitans environment is **not yet independent**. The red items must be resolved, with owner approval, before any migration, seeding, test run, queue worker or scheduler is started.
