# Production adoption of `ssgplatforms_deeq` — exact procedure

> **BLOCKED.** Nothing in this document may be executed until the owner has sent, in writing:
> **`PHASE 3 APPROVED — PROCEED`**
> The software enforces this too: `scripts/adopt-existing.sh` stops unless `KAABE_APPROVAL` is exactly that phrase, and
> `php artisan kaabe:adopt` refuses any database listed in `KAABE_PROTECTED_DATABASES` (default `ssgplatforms_deeq`,
> case-insensitive) unless `KAABE_ADOPT_PRODUCTION_APPROVED` is exactly that phrase. The phrase contains an **em dash (—)**,
> not a hyphen; a hyphen is refused (tested). Keep `KAABE_PROTECTED_DATABASES=ssgplatforms_deeq` in `platform/.env` at all times.

"Adoption" connects the existing PHP POS database to Kaabe **in place**. It is **additive only**: no business row
(products, customers, suppliers, employees, stock, sales, purchases, registers, history) is changed or deleted. It adds
two Kaabe tables, two indexes, a few settings rows, role templates and one platform API key. This was executed and
verified on a legacy copy (see `TESTING-REPORT.md` → *Existing POS adoption*): counts, totals and password hashes identical;
rollback restored 54/54 measures.

---

## 0. Preconditions (all required)

- [ ] Staging accepted (`STAGING-ACCEPTANCE-CHECKLIST.md` fully ticked).
- [ ] **Production** Kaabe installed on the production account exactly as in `INSTALLATION.md` / `DEPLOYMENT-INMOTION-CPANEL.md`
      (new central database, new admin subdomain; this does not touch `ssgplatforms_deeq`). Staging is **never** connected to it.
- [ ] Written approval `PHASE 3 APPROVED — PROCEED`.
- [ ] Agreed maintenance window with the business (plan 60–90 minutes; the rehearsal measures the real time).
- [ ] Decided: sub-domain (`<slug>`, e.g. `deeq`), the **owner** = the existing POS username that becomes business owner,
      plan (**Professional** as confirmed).
- [ ] Off-server place for backups (PC or remote storage).

## 1. Stop writes

1. Announce the window; all cashiers sign out of the old POS.
2. Put the old POS in maintenance (e.g. cPanel → Directory Privacy on its folder, or a maintenance `index.html`),
   so no sale is written during steps 2–6. Do not change DNS yet.

## 2. Full backup first — and verify it

In cPanel → Terminal (production account):

```bash
cd ~/kaabe
BACKUP_DB_PASSWORD='<current ssgplatforms_deeq db password>' \
  bash scripts/backup-database.sh --db-name=ssgplatforms_deeq --db-user=<its db user> --out=$HOME/kaabe-backups/pre-adoption
```

The script is read-only (single-transaction `mysqldump`) and exits 0 **only** when the dump is verified: gzip integrity,
SHA-256 checksum, "Dump completed" marker, and the number of tables in the dump equals the number in the database.
Also take a cPanel → **Backup** → *Download a MySQL Database Backup* of `ssgplatforms_deeq`.
**Copy both files off the server** and confirm they open. Do not continue without two good backups.

## 3. Rehearsal on a COPY (same window or the day before)

```bash
cd ~/kaabe/platform
php artisan kaabe:rehearse --dump=$HOME/kaabe-backups/pre-adoption/ssgplatforms_deeq_<time>.sql.gz --owner=<owner username>
```

It restores the dump into a temporary database `<prefix>rehearsal`, profiles it, adopts the **copy**, validates, runs
the rollback, validates again and drops the copy. **The live database is never opened.**
Read `platform/storage/app/rehearsal/<time>/ACCEPTANCE.md`: it must say *no FAIL*. Review *REQUIRES ACTION* items
(e.g. users without a usable password, limits exceeded) and `before-exceptions.md`. Stop here if anything fails.

## 4. Rotate the database password

The original POS source contained its database password in plain text, so it must be treated as exposed.
cPanel → MySQL Databases → *Change Password* for the `ssgplatforms_deeq` user → strong new password.
The old POS stops working from this moment (expected: it is in maintenance). The new password is given only to Kaabe
(step 5) and is stored encrypted.

## 5. Pre-adoption comparison, adoption, post-adoption comparison (one command)

```bash
cd ~/kaabe
KAABE_APPROVAL='PHASE 3 APPROVED — PROCEED' ADOPT_DB_PASSWORD='<new rotated password>' \
  bash scripts/adopt-existing.sh --db-name=ssgplatforms_deeq --db-user=<db user> --owner=<owner username> --slug=<slug>
```

What it does, in order (it stops at the first problem):

| Step | Action | Changes production? |
|---|---|---|
| 1a | `backup-all.sh` — platform (central) backup | no |
| 1b | `backup-database.sh` — **verified** backup of `ssgplatforms_deeq` into the adoption folder; adoption is not started if it cannot be verified | no (read-only) |
| 2 | `m7_profile.php` BEFORE — per-table row counts and checksums, sales/stock/balance totals, per-year and per-day history, password-hash inventory | no (read-only) |
| 3 | `kaabe:adopt --plan=professional --add-overrides` (**not activated**) — see below | additive only |
| 4 | `m7_profile.php` AFTER | no |
| 5 | `m7_compare.php` BEFORE vs AFTER → `validation.md`; exit ≠ 0 on any business-data difference | no |

Results are in `platform/storage/app/adoption/<time>/` (`adopt.txt`, `before.json`, `after.json`, `validation.md`, `backup/`).

### What `kaabe:adopt` does (step 3)

- **Kaabe migration (additive):** creates `phppos_kaabe_meta` and `phppos_kaabe_location_meta`; adds indexes
  `kaabe_sale_time` and `kaabe_last_modified` on `phppos_sales` (for sync speed); adds settings rows `kaabe_entitlements`
  and `kaabe_status`; adds Kaabe role templates; adds one hashed platform API key. Their ids are recorded in
  `phppos_kaabe_meta` so a rollback removes exactly these objects.
- **Existing users preserved:** every employee, permission, location assignment and password hash stays as it is. The
  chosen `--owner` user is recorded as business owner (`phppos_kaabe_meta.owner_person_id`).
- **Passwords preserved:** no hash is changed during adoption (verified by the AFTER profile).
- **MD5 → bcrypt:** at each user's **next successful sign-in**, a legacy MD5 hash is verified and immediately replaced by
  bcrypt. Users keep their current passwords. Users whose stored hash is empty or unusable cannot sign in; they are listed
  in `before-exceptions.md` → give each a temporary password in Super Admin → Business → Users (forced change at first sign-in).
- **Plan-limit overrides:** where existing data already exceeds the Professional plan (e.g. more users, branches or
  products than the plan allows), `--add-overrides` records a **limit exception** at the current usage, so nothing is
  blocked. They are listed in `adopt.txt` and in Super Admin → Business → Exceptions (review them with the business).
- **Registry:** the business is added to the tenant registry in status *draft* (the POS shows "not active" until step 7).

## 6. Validation (must all pass)

- [ ] `adopt-existing.sh` printed **PASS – business data unchanged**.
- [ ] `validation.md`: every business table and total **MATCH**; only Kaabe objects listed as added.
- [ ] `adopt.txt`: owner mapped; exceptions listed; no error.
- [ ] Super Admin → the business page: connection OK and entitlements delivered (no error shown).

If anything is not PASS → **do not activate**; go to §9 Rollback.

## 7. Activate and switch

1. Super Admin → the business → Status → **Active**.
2. Create `<slug>.<production domain>` (cPanel → Domains) with document root `~/kaabe/pos`, AutoSSL, Force HTTPS.
   Pointing the business's existing address to Kaabe is a **production DNS/domain change** — do it only in this window.
3. Super Admin → Sales → the business → **Sync now** (first sync reads the full history).

## 8. Post-adoption verification

- [ ] Owner signs in with the **existing** password; dashboard opens; `phppos_employees.password` for that user now starts with `$2y$`.
- [ ] Two other staff members sign in with their existing passwords.
- [ ] Old data visible: products, customers, suppliers, stock per location, last month's sales report and a register close-out report.
- [ ] One real sale and its receipt; stock decreases.
- [ ] Super Admin → Sales: totals for yesterday and last month equal the POS *Summary sales* report.
- [ ] Next nightly backup (`backup-all.sh`) contains the adopted database (it is now registered).
- [ ] Keep the pre-adoption backups for at least 30 days.

## 9. Rollback

**A. Kaabe objects only (fast, ~15 min) — use when business data is intact.**
1. Super Admin → the business → *Suspended* (or keep it draft).
2. phpMyAdmin → `ssgplatforms_deeq` → SQL (ids come from `phppos_kaabe_meta`):
   ```sql
   SELECT value FROM phppos_kaabe_meta WHERE `key` IN ('kaabe_templates_added', 'platform_key_sha1');
   -- with the template ids from the first row, e.g. 7,8,9,10,11:
   DELETE FROM phppos_permissions_template_actions WHERE template_id IN (7,8,9,10,11);
   DELETE FROM phppos_permissions_template WHERE template_id IN (7,8,9,10,11);
   DELETE FROM phppos_permissions_templates WHERE id IN (7,8,9,10,11);
   DELETE FROM phppos_keys WHERE `key` = '<platform_key_sha1 value>';
   DELETE FROM phppos_app_config WHERE `key` IN ('kaabe_entitlements', 'kaabe_status');
   DROP TABLE IF EXISTS phppos_kaabe_location_meta, phppos_kaabe_meta;
   ALTER TABLE phppos_sales DROP INDEX kaabe_sale_time, DROP INDEX kaabe_last_modified;
   ```
3. **Passwords:** hashes already upgraded to bcrypt work only with the Kaabe POS code. If the old POS must run again,
   restore the `phppos_employees.password` column from the step-2 backup (or use B).
4. Verify: `php migration/m7_profile.php … --label=rolledback --i-understand-this-is-read-only-production` then
   `php migration/m7_compare.php <adoption folder>/before.json <out>/rolledback.json` → must PASS.
   (This exact rollback was executed and verified by `kaabe:rehearse` on the copy.)

**B. Full restore (last resort).** Restore the verified step-2 dump into `ssgplatforms_deeq`
(`gzip -dc <file>.sql.gz | mysql -u <db user> -p ssgplatforms_deeq`), verify with `m7_profile.php` / `m7_compare.php`
against `before.json`, then restore the old POS and remove maintenance. Any sale made in Kaabe after activation would be
lost in B — which is why activation happens only after §6 passes.

Write down the rollback decision and time, and inform the business. (Status changes are recorded automatically in Super Admin → Audit.)
