# NH POS — Install & Upgrade Runbook (per-client independent installation)

VMWebs Commerce is deployed as a **separate, independent installation for each client**:
its own NH POS backend, frontend, database, and WordPress connector, with its own
business settings, catalogue, customer/order data, and payment credentials. There is no
shared database and no centralized tenant administration. This runbook stands up a new
client install and upgrades an existing one **without ever carrying development products,
test orders, sandbox credentials, or another client's data** into the new install.

> **Golden rule — never clone a dev or another client's database into a client install.**
> Each install starts from an EMPTY database + migrations + reference seeders +
> `cacart:provision-tenant`. The only data that ships is the permission/role catalogue
> (reference data). See "Clean-data guarantees" below.

---

## 1. Prerequisites (per install)

- PHP 8.x + Composer, Node 20+ + npm, MySQL 8 (a dedicated, empty database), Redis/Valkey
  (queues/cache). A queue worker and scheduler for background email/notifications.
- A dedicated DB user for this install (its own credentials).
- DNS + TLS for the API and the frontend.

## 2. Fresh install

1. **Get the master code** (do not modify the master to embed client details):
   ```
   git clone <master-repo> nhpos && cd nhpos/backend
   ```
2. **Configure the environment** — copy `.env.example` → `.env` and set per install:
   - `APP_NAME` (the install's app name), `APP_ENV=production`, `APP_DEBUG=false`,
     `APP_URL`, `FRONTEND_URL` (used to build owner activation/reset links).
   - `DB_*` — this install's dedicated empty database + user.
   - `MAIL_MAILER` + `MAIL_FROM_ADDRESS` / `MAIL_FROM_NAME` — the deliverability-bound
     envelope sender (SPF/DKIM). Customer order emails additionally brand the From
     **display name** + **Reply-To** from the business profile (set in-app), so this
     address just needs to be a domain the install is authorized to send from.
   - Redis, queue, Sanctum stateful domains, S3 (if used) — infra only.
   - **Do NOT** put currency/timezone/tax/business identity in env — those are per-tenant
     DB settings configured in-app or via `cacart:provision-tenant`.
3. **Install dependencies (production — no dev/test packages, so Faker/factories are not
   even autoloaded):**
   ```
   composer install --no-dev --optimize-autoloader
   ```
4. **Create the schema and seed reference data (permission catalogue only):**
   ```
   php artisan migrate --force
   php artisan db:seed --force      # DatabaseSeeder → PermissionSeeder (reference only)
   ```
5. **Provision the client tenant + first owner + primary location** (no demo data; prints
   a single-use owner activation link):
   ```
   php artisan cacart:provision-tenant \
     --name="Harbor Grill" --slug="harbor-grill" \
     --owner-email="owner@harborgrill.com" --owner-name="Sam Rivera" \
     --currency=USD --timezone="America/New_York" --country=US
   ```
   (Omit any option to be prompted. Re-running is safe and never clobbers an active owner.)
6. **Deliver the activation link** to the owner over a secure channel. They open it, set
   their password, and sign in as the Owner.
7. **Build + serve the frontend** with `VITE_API_BASE_URL` pointing at this install's API:
   ```
   cd ../frontend && npm ci && npm run build   # serve dist/ behind the web server
   ```
8. **Start workers**: a queue worker (`php artisan queue:work`) and the scheduler
   (`php artisan schedule:work` / cron) for emails, notifications, and reconciliation.

`composer setup` (from `backend/`) chains steps 3–4 + the frontend build for a quick
dev/staging bring-up; production should run the explicit steps above.

## 3. What the OWNER configures in-app after activation

Nothing client-specific is hard-coded; the owner configures each business in **Settings**
and **Menu** (all tenant/location-scoped, Owner-only where sensitive):

1. **Business info & branding** — Settings → Restaurant profile (name, legal name,
   contact, country, currency, timezone; logo).
2. **Locations & opening hours** — Locations (address, timezone, currency, business
   hours, receipt header/footer).
3. **Tax** — Settings → Tax (tax classes + per-location rates; the business tax
   registration number on the Operations tab for invoices).
4. **Pickup & delivery** — Settings → Delivery & pickup (zones, fees, free-over
   thresholds).
5. **Invoice & email identity** — Operations (invoice prefix, tax registration number);
   customer emails brand From-name/Reply-To from the restaurant profile automatically.
6. **Payment gateway** — Settings → Payment gateways (Bambora/Moneris; sandbox first,
   then production credentials — write-only, encrypted at rest).
7. **WordPress connection** — Settings → Website connections (issue a connection token to
   the client's WordPress/VMWebs connector).
8. **Catalogue** — Menu → Catalogues + import/activation; assign catalogues to the
   website connection to publish.

## 4. Upgrade an existing install

Upgrades are additive and idempotent. From `backend/`:
```
git pull                                   # master
composer install --no-dev --optimize-autoloader
php artisan migrate --force                # schema + reference back-fills (no demo data)
php artisan db:seed --force                # add any NEW catalogue permissions (idempotent)
php artisan cacart:roles-sync              # grant new catalogue permissions to each role
cd ../frontend && npm ci && npm run build  # rebuild the SPA
```
(`composer upgrade` from `backend/` runs the three artisan steps.) Then restart the queue
worker. `cacart:roles-sync` only **adds** missing permissions/role links — it never
removes a permission, rewrites a role, or touches a tenant's custom grants; owners must
re-login (re-fetch `/auth/context`) to pick up new grants.

## 5. Clean-data guarantees (why a client install carries no dev data)

- **Default seeder is reference-only.** `DatabaseSeeder` seeds **only** the permission
  catalogue (`PermissionSeeder`). No product/order/customer/employee/demo-tenant seeder
  exists.
- **No migration inserts demo data.** Data-touching migrations are permission back-fills
  or conditional reshapes that **no-op on an empty database** (e.g. the default-catalogue
  back-fill only runs for pre-existing tenants).
- **The demo bootstrap cannot run in production.** `cacart:dev-bootstrap` (the "Kings
  Biryani" demo tenant / `vmwebsent@gmail.com`) refuses to run outside `local`/`testing`.
  Use `cacart:provision-tenant` for real installs.
- **Factories are dev-only.** `fakerphp/faker` is a dev dependency, so `composer install
  --no-dev` doesn't even autoload the factories; no fake data can leak into production.
- **Per-install isolation.** Each install has its own DB and its own payment/connection
  credentials. Never restore a dev or another client's dump into a client install.

## 6. Optional — platform/operator console (support access)

Independent installs are **owner-operated** and do **not** require the platform/operator
console. If VMWebs wants scoped operator/support access for a specific install, seed the
platform catalogue and provision an operator **manually** (these are intentionally NOT in
the default seeder or `provision-tenant`, to keep basic installs owner-only):
```
php artisan db:seed --class="Database\\Seeders\\PlatformPermissionSeeder" --force
php artisan db:seed --class="Database\\Seeders\\PlatformRoleTemplateSeeder" --force
# then create a PlatformUser with the platform-super-admin role (tinker / a dedicated
# operator-provisioning step) and have them activate.
```
Skip this section entirely for a standard, owner-only client install.

## 7. Per-install configuration checklist (env vs in-app)

| Setting | Where | Notes |
|---|---|---|
| App name, URL, `APP_ENV=production`, debug off | env | per install |
| DB credentials | env | dedicated empty DB |
| `FRONTEND_URL` | env | builds activation/reset links |
| `MAIL_FROM_ADDRESS` / `MAIL_FROM_NAME` | env | deliverability-bound envelope sender |
| `VITE_API_BASE_URL` | frontend env | points the SPA at this API |
| Currency / timezone / country | `cacart:provision-tenant` + in-app | NEVER env |
| Business identity, tax, delivery, hours, branding | in-app (Owner) | tenant/location DB |
| Payment gateway credentials | in-app (Owner) | encrypted, write-only |
| WordPress connection token | in-app (Owner) | rotate on compromise |

## 8. Restrictions

Development changes only during build-out. No production deploy, no Git pushes, no live
payment activation, and no modifications to the original Cacart POS, are performed by this
tooling. Production installs, credential rotation, and go-live are approved operations
tasks performed by VMWebs.
