================================================================================
MPUMALANGA PVC - SUPPLIER ORDERS MODULE
================================================================================
Last Updated: 2026-03-26
Path: /app/supplier_orders/
================================================================================

WHAT THIS SECTION DOES
-----------------------
Manages purchase orders placed with suppliers to fulfil client invoices.
Each supplier order is linked to a specific supplier and invoice.
Stock items are filtered to show only that supplier's products.
Supplier orders can be created manually or auto-generated from an invoice
via convert_to_supplier_orders.php (in the invoices module).

DATABASE TABLES
---------------
supplier_orders:
  record_id          int   - Primary key (NO AUTO_INCREMENT)
  supplier_order_no  text  - Auto-generated order number (e.g. EDPVC_3)
  supplier_id        int   - FK to suppliers.record_id
  invoice_id         int   - FK to invoices.record_id
  date_time          varchar - Timestamp

supplier_order_list:
  record_id           int
  stock_id            int   - FK to stock.record_id
  qty                 text
  price               text  - Currency string (cost price from stock)
  supplier_order_id   int   - FK to supplier_orders.record_id
  size_m              text
  pannels             text

================================================================================
FILES
================================================================================

home.php
--------
Lists all supplier orders with an Edit button.
Uses: new table("supplier_orders") + add_action_button("edit_supplier_orders.php")
Note: Title says "JOB CARDS".

--------------------------------------------------------------------------------
add_supplier_orders.php
-----------------------
What it does:
  Manual form to create a new supplier order. Fields: Supplier selector,
  auto-generated order number (read-only), Invoice selector, and line items.

How it works:
  On load:
    - Calls get_stock_datalist("stock_list") (loads all active stock).

  Supplier selection flow:
    1. User selects a supplier from the suppliers_list datalist.
    2. get_supplier_code(el) fires:
       - Splits value on ":" to extract supplier_id and code.
       - Sets hidden supplier_id and code inputs.
       - XHR POST to get_supplier_order.ajax.php with supplier_code.
       - Response is the next order number (e.g. EDPVC_4).
       - Sets supplier_order_no readonly field.
       - Clears all table rows and calls add_row() to start fresh.
       - Calls get_supplier_datalist() to filter stock to this supplier.
    3. get_supplier_datalist():
       - XHR POST to get_supplier_datalist.ajax.php with supplier_id.
       - Response is option tags for stock items from this supplier.
       - Populates suppliers_datalist (code-first format).
       - Also builds suppliers_desc_datalist (description-first format).

  Invoice selection:
    - Standard datalist with data-id extraction pattern.

  Line items:
    Two inputs per row:
    - Code input (lists from suppliers_datalist: CODE~Desc~UOM~CostPrice)
    - Description input (lists from suppliers_desc_datalist: Desc~CODE~UOM~CostPrice)

JS Functions:
  get_supplier_code(el)
    - Validates selection is from datalist. Alerts if not found.
    - Extracts supplier_id, name, code from "Name : id : CODE" format.
    - Fires AJAX to get next order number.
    - Resets table rows.

  get_supplier_datalist(index)
    - Loads stock items for selected supplier.
    - Builds two datalists (code-first and description-first).

  get_unit_of_measure(input, i)
    - Fires when code input changes.
    - Splits CODE~Desc~UOM~Price, populates UOM, code, description, price fields.
    - Special case: DISCOUNT code sets price to "-R 0.00" and qty to 1.

  get_unit_of_measure_from_desc(input, i)
    - Same as get_unit_of_measure but for description-first selection.
    - Format is Desc~CODE~UOM~Price so indexes are different.

  add_row()           - Adds new line item row. Increments index.
  delete_row(el)      - Removes row.
  calculateRow(input, i) - qty * price -> total_{i}
  calculateTotals()   - Sums totals, adds 15% VAT (supplier orders include VAT).
  change_to_currency(input) - Formats price as ZAR.
  save(url)           - Collects all inputs, submits POST form.

NOTE: Supplier orders DO include 15% VAT in totals (unlike quotes and invoices).

--------------------------------------------------------------------------------
save_supplier_orders.php
------------------------
What it does:
  Inserts supplier order header and all line items, redirects to home.php.

How it works:
  - Gets invoice_id, supplier_order_no, supplier_id from POST.
  - Inserts into supplier_orders.
  - Loops over $_POST['stock_code'] (code strings).
    For each code, looks up stock by code to get stock_id.
    Inserts into supplier_order_list with stock_id, qty, price, size_m, pannels.
  - Redirects to home.php.

--------------------------------------------------------------------------------
edit_supplier_orders.php
------------------------
What it does:
  Pre-filled edit form for an existing supplier order. Same layout as add.

How it works:
  - Loads supplier_order by $_GET['record_id'].
  - Loads the linked supplier and invoice to pre-fill their fields.
  - Loads supplier_order_list rows and renders them.
  - PHP $index passed to JS for continued add_row() numbering.
  - calculateRow() called on load.
  - get_supplier_datalist() called immediately on load to populate the datalist
    for the current supplier.
  - Saves to update_supplier_orders.php.

--------------------------------------------------------------------------------
update_supplier_orders.php
--------------------------
What it does:
  Updates supplier order header, deletes all list rows, re-inserts from POST.

How it works:
  - Updates supplier_orders (supplier_order_no, supplier_id, invoice_id).
  - DELETEs all supplier_order_list rows.
  - Re-inserts all rows from $_POST['stock_code'].
  - Redirects to home.php.

--------------------------------------------------------------------------------
get_supplier_datalist.ajax.php
------------------------------
What it does:
  AJAX. Receives supplier_id via POST. Returns <option> tags for all stock
  items belonging to that supplier.

Response format (datalist options):
  <option>CODE~Name~UOM~retail_price</option>
  (Note: returns retail price, even though supplier orders use cost price.
   The page-level JS uses cost price from the order's existing data on edit.)

Called by: get_supplier_datalist() in add and edit pages.

--------------------------------------------------------------------------------
get_supplier_order.ajax.php
---------------------------
What it does:
  AJAX. Receives supplier_code via POST. Finds the last order number for that
  supplier and returns the next number in sequence.

How it works:
  - Queries supplier_orders WHERE supplier_order_no LIKE 'CODE_%' ORDER BY
    record_id DESC LIMIT 1.
  - Gets the last number by stripping the prefix and casting to int.
  - Returns: CODE_{N+1} (e.g. EDPVC_4).
  - If no orders exist for this supplier: returns CODE_1.

Format: SUPPLIER_CODE underscore NUMBER (e.g. EDPVC_1, EDPVC_2, EDPVC_3)

KNOWN ISSUE (fixed 2026-03):
  Original code used LIKE '%CODE-%' (dash) and returned CODE_N (underscore).
  The LIKE pattern never matched existing records (dash vs underscore mismatch),
  so the counter always returned _1 regardless of how many orders existed.
  Fixed: LIKE now uses CODE_% (underscore) to match the stored format correctly.
  Also changed from num_rows count (breaks on deletions) to last record increment.

NOTE ON INCONSISTENCY:
  Manual orders use underscore: EDPVC_1
  Auto-converted orders (from convert_to_supplier_orders.php) use dash: EDPVC-1
  These are separate numbering series and don't interfere, but can look
  inconsistent in the order list.

--------------------------------------------------------------------------------
supplier_order.pdf.php
----------------------
What it does:
  Generates a PDF purchase order for a supplier (?record_id=X).

PDF contents:
  - "ORDER NO: {supplier_order_no}" title.
  - Supplier details (left) and company details (right).
  - Date.
  - Line items: Code, Description, UOM, Qty, Size, Panels, Amount, Total.
  - Subtotal, VAT (15%), Net Total.

Note: The payment summary section (bank details, payments) that appears in
invoice PDFs is not included here.

================================================================================
KNOWN ISSUES SUMMARY
================================================================================
1. home.php title says "JOB CARDS".
2. Order number format is underscore (_) from manual adds and dash (-) from
   convert_to_supplier_orders.php - inconsistent.
3. get_supplier_datalist.ajax.php returns retail price but orders should
   use cost price - may cause price discrepancies on new rows.
4. save/update: stock_id lookup by code fails silently if code not found.
5. SQL injection throughout.

================================================================================