# Migration state — investigated before writing D0 / percent Requested by the user before any new migration is written. All findings are from **read-only** queries; nothing was reset and `_prisma_migrations` was not touched. ## The five questions ### 1. Is `20260917120000_customer_name_parts_and_percent` recorded as applied? **Yes — but as a no-op.** ``` migration_name 20260917120000_customer_name_parts_and_percent finished_at 2026-09-18T16:03:33.956Z rolled_back_at null applied_steps_count 0 <-- zero steps ran ``` `finished_at` is set and `rolled_back_at` is null, so **Prisma considers it applied and will never re-run it**. `applied_steps_count = 0` says no SQL statement was ever executed by it. ### 2. What is in its SQL file? **Comments only — no executable statement.** The file is a placeholder that explains itself: it says the columns were added "directly against the live DB" and that it was recreated as an empty no-op so `prisma migrate status` would stop erroring on a missing directory. ### 3. Does `schema.prisma` declare `firstName` / `lastName` / `productPercent`? **No.** `grep` over `prisma/schema.prisma` finds none of the three. The `Customer` model has `fullName` and nothing else name-related. ### 4. Do the columns exist in the database? **No.** Querying `information_schema.columns` for `Customer` and those names returns only `fullName`: ``` firstName absent lastName absent productPercent absent makingChargeOffsetPercent absent fullName present ``` **So the placeholder file's own comment is wrong.** It claims the columns "already exist live"; they do not, at least not in this database (`gold_banking` on localhost). Whatever database that comment described, it was not this one. **Consequence for the runtime:** none, and this is the good news. Prisma Client generates from `schema.prisma`, and the schema does not mention these fields either. Schema and database agree — both lack the columns. There is **no drift that could make Prisma Client throw at runtime**. The only thing that is wrong is the placeholder's comment. ### 5. `prisma migrate status` (read-only) ``` 9 migrations found in prisma/migrations Following migration have not yet been applied: 20260922000000_customer_otp ``` Note on that line: the OTP table **does exist** — stage 2 created it with `prisma db execute`, which runs SQL without writing a ledger row. So the schema is correct but the ledger entry is missing, and `migrate deploy` would try to run it again. The SQL is written with `CREATE TABLE IF NOT EXISTS`, the indexes with `IF NOT EXISTS`, and the FK inside a `DO $$ ... EXCEPTION WHEN duplicate_object` block, so a re-run is harmless. Still worth resolving cleanly — see the recommendation. ### 6. Where did the file come from? ``` b80fa9b Redesign product detail page and fix icon/cart reactivity bugs ``` A single commit, and the message has nothing to do with migrations — the placeholder was swept in as collateral to an unrelated frontend change. --- ## Recommendation **The placeholder is recorded as applied, so per the user's rule it must not be edited.** It stays exactly as it is. 1. **Leave `20260917120000_...` untouched.** Editing a migration Prisma has recorded would make its checksum mismatch and break `migrate status` for everyone. Its only sin is a misleading comment, and a follow-up migration makes that harmless. 2. **Write the D0 and `percent` work as a new, additive migration** with a fresh timestamp. It adds the columns the placeholder never added. Written to be idempotent (`IF NOT EXISTS`) so it is safe on a database where someone *did* apply the columns by hand. 3. **Resolve the OTP ledger entry** with `npx prisma migrate resolve --applied 20260922000000_customer_otp`. That writes the ledger row without running SQL, which is exactly right here since the table already exists. **Needs the user's approval before running**, as it writes to `_prisma_migrations`. 4. **Never** `migrate reset`, and never edit `_prisma_migrations` by hand. --- ## Naming: one field, not two The user asked for a single final name. **`productPercent` is dropped; `makingChargeOffsetPercent` is the name.** Reason: the reference has **three unrelated things** all called `percent`, and a vague name is how they got confused in the first place (the target currently reads the wrong one — CUST-02): | Reference `percent` | Where | What it is | |---|---|---| | `customers.percent` | `customer_add.php:32` | a **per-customer making-charge offset**, used only to shift catalogue filter bounds (`ApiMobileController.php:391-396,76-77`) | | `ConfigStatic['percent']` | `AdminController.php:903,994` | the **wallet cashback rate**: `total_weight × percent/100` | | `order_item.percent` | `MajorOrderController.php:220` | the **per-line making charge** on a wholesale order | `productPercent` would read as "a percent belonging to a product", which is the third meaning, not the first. `makingChargeOffsetPercent` says which of the three it is. It is mapped back to the legacy key `percent` in `toRow()`, so the admin panel's field name does not change. `productPercent` is not created, so there is no duplicate field.