# WBMCZ — deploy notes · 06 Sep 2026 (third pass)
## The CSV now fits the shelf, and orders find their category

Two files. No new table, no new column — but **one data repair** to
`orders.category_id`, described in §2.

    admin/service_import.php   the sample and the parser follow the category
    admin/orders.php           the category tabs work, and the count matches the list

---

## 1 · THE BULK UPLOAD SHEET

The sample CSV was the same for every category, so downloading it from
**Government Portals** handed you six columns that must stay empty for ever:

    price   discount_price   seller_share_pct   referral_share_pct
    staff_share_pct   subscription_discount_pct

A portal is free. Those columns had nothing to say, and a sheet with six dead
columns on it is a sheet that gets filled in wrong.

### The sample now matches the category

The Bulk upload panel passes its category to the download, so you get the sheet
for the shelf you are standing on.

**Free category (Government Portals):**

    title, description, web_url, app_url, tutorial_url,
    icon_class, color_class, sub_category, status, required_kyc

**Paid category (Agency And Software and the rest):** the same, plus `price`,
`discount_price` and the four share columns — with a worked example that has real
numbers in it rather than a row of zeros.

The file is named after the category too, so a folder of downloads does not turn
into `sample (3).csv`.

### Which categories count as free

Read from the catalogue, not hard-coded. Government Portals always is; any other
category counts as free once it holds products and not one of them carries a
price. An **empty** category counts as paid, so the first product uploaded into
it can still be priced. Right now that gives:

    #1  Government Portals          free sheet
    #2–11                           paid sheet

### required_kyc — the column a portal actually needs

New, on both sheets. It says which customer details that portal asks for, comma
separated:

    personal.full_name, personal.phone_number, identity.aadhaar_number

Leave it blank to ask for nothing. Every key is checked against the same
catalogue the KYC Questions screen uses. A key that is not recognised is
**dropped with a note on that row** rather than failing it — one mistyped
document name should not lose the portal. The preview shows a **Customer data**
column so you can see exactly what was read before anything is written.

### An old sheet still imports

A free category ignores price, discount and the share columns even when the file
carries them — they are forced to zero rather than rejected. So the sheet you
already downloaded still works; it just has columns on it that do nothing.

The preview drops the **Price** column entirely on a free category, and the
column reference beside the upload box says, in a green box, that those columns
are not needed here.

---

## 2 · ORDERS: EMPTY CATEGORY TABS, AND 54 vs 45

You clicked **Agency And Software** and got *No orders found*, while All Orders
showed everything. Two separate faults, both now fixed.

### Fault one — the orders had no category

`orders.category_id` is written at checkout, but the column was added to the
table **after** the site had already been taking orders. So every order placed
before that had NULL in it. The tab filter runs `WHERE o.category_id = 5`, NULL
matches nothing, and every category read zero. All Orders has no filter, so it
showed the lot.

All 54 orders were affected.

`orders.php` now fills in what is missing when it loads — from the order's own
items first, and from the product it names second. It only ever touches rows that
are still empty, so it is a no-op from the second page load onwards.

**It has already run on your local database.** The result:

    Agency And Software     47
    no category              7
    ────────────────────────────
    total                   54

The 47 are exactly the ones you were looking for. The 7 are the oldest test
orders — #1 to #6 and #9, *PAN Agency Pro* and *WBMCZ SERVICES* — whose products
no longer exist, so there is nothing to read a category from.

Those 7 now get their own **No category** chip rather than vanishing, so the tab
figures always add up to the All Orders figure.

**On the live server** this happens by itself the first time an admin opens
Manage Orders. Nothing to run.

### Fault two — nine orders were being hidden

The header said **54** and the list said **45 orders shown**. The list ran

    JOIN users u ON o.user_id = u.id

— an inner join. Nine orders (#1, 2, 3, 4, 5, 6, 9, 33, 56) belong to accounts
that have since been deleted, so the join dropped them, while the count above
came straight from the `orders` table and still included them.

It is a `LEFT JOIN` now, and an order whose account is gone shows **Account
removed** in place of the name. An order is a record of money; it does not stop
existing because the account did.

Checked: the list query now returns 54 rows for 54 orders.

---

## 3 · CHECKED

* both files pass `php -l`
* the free sheet has no money column; the paid sheet has all six
* all 11 categories resolve to the sheet you would expect
* the backfill ran and put 47 orders in Agency And Software, 7 in No category,
  54 in total
* the list's join returns 54 rows for 54 orders

Needs your eyes, because it is behind the sign-in: download the sample from
**Government Portals → Bulk upload** and check the columns, then open Manage
Orders and click the Agency And Software tab.
