================================================================================
MPUMALANGA PVC - INVOICES MODULE
================================================================================
Last Updated: 2026-03-26
Path: /app/invoices/
================================================================================

WHAT THIS SECTION DOES
-----------------------
Manages tax invoices issued to clients. An invoice is either created manually
(add_invoices.php) or converted from a quote (convert_to_invoice.php).
Invoices can be further actioned: converted to a work schedule, converted to
supplier orders, or exported as a PDF. Credit notes linked to an invoice are
visible inline on the edit page.

DATABASE TABLES
---------------
invoices:
  record_id                  int   - Primary key (NO AUTO_INCREMENT)
  client_id                  int   - FK to clients
  order_type                 text  - SUPPLY / SUPPLY & INSTALL / SUPPLY & DELIVERY
  user_id                    int   - FK to users
  status                     text  - e.g. PENDING PAYMENT, OPENED
  date_time_created          varchar(50) - Timestamp
  terms                      text
  subject                    text
  notes                      text
  area                       text
  quote_id                   int   - FK to quotes (0 if created directly)
  invoice_number             int   - Incrementing number
  additional_delivery_details text

invoice_list:
  record_id   int
  stock_id    int   - FK to stock
  qty         text
  price       text  - Currency string
  invoice_id  int   - FK to invoices
  size_m      text
  pannels     text

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

home.php
--------
Lists all invoices with an Edit button per row.
Uses: new table("invoices") + add_action_button("edit_invoices.php")
Note: Title says "JOB CARDS" - minor bug.

--------------------------------------------------------------------------------
add_invoices.php
----------------
What it does:
  Manual invoice creation form. Same structure as add_quotes.php but creates
  an invoice directly (not from a quote).

How it works:
  - Loads the last invoice to calculate the next invoice_number.
  - get_stock_datalist("stock_list") and get_clients_datalist("clients_list").
  - Notes and terms fields pre-fill with standard invoice text.
  - Same dynamic line items table as quotes.
  - Saves to save_invoices.php.

JS Functions: Same as add_quotes.php (get_client_name, add_row, delete_row,
  get_unit_of_measure, calculateRow, calculateTotals, save, change_to_currency).

Note: VAT row is hidden (display:none) but code still calculates it. Net total
effectively equals subtotal because VAT multiplier is 0 in calculateTotals.

--------------------------------------------------------------------------------
save_invoices.php
-----------------
What it does:
  Inserts a new invoice header and all line items, redirects to home.php.

How it works:
  - Sanitises subject, note, terms, area (strips quotes and double-quotes).
  - Gets last invoice_number + 1.
  - Inserts into invoices with quote_id=0 (not from a quote).
  - Loops over $_POST['stock_code'], looks up each by code, inserts invoice_list.
  - Redirects to home.php.

--------------------------------------------------------------------------------
edit_invoices.php
-----------------
What it does:
  Full edit form for an existing invoice. Shows all fields, line items,
  and any credit notes linked to this invoice.

How it works:
  - Loads invoice by $_GET['record_id'].
  - If invoice has a linked quote (quote_id != 0), shows a button linking
    back to the quote's edit page.
  - Loads all invoice_list rows and renders them as editable table rows.
  - After the invoice line items, loads any credit_notes linked to this invoice
    and renders each as a read-only row showing the credit note total.
    Each credit note row has an OPEN button to go to edit_c_notes.php
    with a from_invoice=1 flag so SAVE returns here.
  - Has a USER CREATED dropdown and DATE CREATED readonly field.
  - Saves to update_invoices.php.
  - Shows SAVE button.

  Additional functions available (called from quick bar or invoice detail actions):
    go_to(url)  - Simple window.location.href redirect.

JS Functions (same as add_invoices.php plus calculateRow on load for all rows).

--------------------------------------------------------------------------------
update_invoices.php
-------------------
What it does:
  Updates invoice header, deletes all invoice_list rows, re-inserts from POST.

How it works:
  - Sanitises text fields.
  - Updates invoices table.
  - DELETEs all invoice_list rows for this invoice_id.
  - Loops over $_POST['stock_code'], skips empty entries, inserts back.
  - Redirects to home.php.

--------------------------------------------------------------------------------
get_unit_of_measure.ajax.php
----------------------------
AJAX endpoint. Receives stock record_id via POST.
Returns:  "unit_of_measure,retail_price"
Used by:  edit_invoices.php and edit_quotes.php when switching stock items.

--------------------------------------------------------------------------------
invoice.pdf.php
---------------
What it does:
  Generates a full Tax Invoice PDF for a given invoice (?record_id=X).

PDF contents:
  - Company logo (left) and secondary logo (right).
  - "TAX INVOICE {invoice_number}" title.
  - Client details (left) and company details (right).
  - Date, order type.
  - Subject block.
  - Area.
  - Line items table: Code, Description, UOM, Qty, Size, Panels, Amount, Total.
  - Any credit notes linked to this invoice are shown as deduction rows.
  - Subtotal and Net Total.
  - Payments Received, Balance Outstanding, Amount Due.
  - Bank details block.
  - Notes section.

Note: VAT is 0 (commented out). Balance outstanding shown as R 0.00 (hardcoded
bug - it should show invoice total minus payments, but instead shows 0).
The "Amount Due" line does correctly calculate net_total - payments.

--------------------------------------------------------------------------------
delivery.pdf.php
----------------
What it does:
  Generates a delivery note PDF directly from the invoice_list items.
  This is separate from the delivery_note module's PDF (which uses
  delivery_note_list). This one is a quick delivery note based purely on what
  is on the invoice.

PDF contents:
  - "DELIVERY NOTE" title (not a tax invoice).
  - Client and company details.
  - Invoice number reference.
  - Delivery details block (if filled).
  - Line items table: Code, Description, Size, Panels.
    NOTE: Skips items with code "DLVR".
  - "RECEIVED IN GOOD CONDITION" statement with signature lines.

--------------------------------------------------------------------------------
convert_to_order.php
--------------------
What it does:
  Converts an invoice into a work schedule (order) record, then redirects to
  the work schedule edit page.
  Called via: convert_to_order.php?record_id={invoice_id}

How it works:
  Step 1 - Check if order already exists:
    Queries orders WHERE invoice_id = record_id.
    If found, uses existing order_id.

  Step 2 - Create order if not found:
    Inserts into orders with date_created=NOW() and the invoice_id.
    Because orders table has no AUTO_INCREMENT, immediately SELECTs back:
      SELECT record_id FROM orders WHERE invoice_id = X ORDER BY record_id DESC LIMIT 1
    This gives the real order_id.

  Step 3 - Rebuild order_list:
    DELETEs all existing order_list rows for this order.
    Re-inserts from invoice_list.

  Step 4 - Redirect:
    Goes to ../work_schedule/edit_work_schedule.php?record_id={order_id}

KNOWN ISSUE (fixed 2026-03):
  Originally redirected to ../orders/edit_orders.php which does not exist.
  Fixed to go to the correct work_schedule path.

--------------------------------------------------------------------------------
convert_to_supplier_orders.php
-------------------------------
What it does:
  Converts an invoice into one or more supplier orders - one order per
  supplier of items on the invoice. Redirects to supplier_orders/home.php.
  Called via: convert_to_supplier_orders.php?record_id={invoice_id}

How it works:
  Step 1 - Get distinct suppliers:
    Queries invoice_list JOIN stock to get DISTINCT supplier_id values.

  Step 2 - For each supplier:
    Checks if a supplier_order already exists for this invoice + supplier combo.
    If yes: uses the existing supplier_order_id.
    If no:
      Gets last order for this supplier and increments the number.
      Format: SUPPLIERCODE-N (e.g. EDPVC-3)
      Inserts into supplier_orders.

  Step 3 - Rebuild supplier_order_list:
    DELETEs existing list rows for this supplier_order.
    Re-inserts from invoice_list JOIN stock, filtered to this supplier,
    using stock.cost as the price.

  Step 4 - Redirect to supplier_orders/home.php.

KNOWN BUG (fixed 2026-03):
  Original code used str_replace() + 1 on the order number string, which fails
  in PHP 8 because str_replace returns a string and string + int throws a
  TypeError. Fixed with (int) cast.

NOTE ON SEPARATOR: This file uses dash (-) for new orders. The
get_supplier_order.ajax.php file (used by the manual add form) uses underscore (_).
This is an inconsistency - orders created via convert use dashes, orders created
manually use underscores.

================================================================================
KNOWN ISSUES SUMMARY
================================================================================
1. home.php title says "JOB CARDS".
2. invoice.pdf.php has hardcoded "R 0.00" for Balance Outstanding.
3. convert_to_supplier_orders uses dash (-) but manual add uses underscore (_)
   for order number format - inconsistency that can cause numbering to break.
4. No invoice status update flow (status stays as whatever it was set to).
5. SQL injection throughout.

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