================================================================================ MPUMALANGA PVC - PAYMENTS MODULE ================================================================================ Last Updated: 2026-03-26 Path: /app/payments/ ================================================================================ WHAT THIS SECTION DOES ----------------------- Records payments received against invoices. Supports CASH, EFT, and CARD payment types. When an invoice is selected, a live payment history panel appears showing the invoice total, credit notes, previous payments, and the outstanding balance. Fully paid invoices are filtered out of the invoice selector so they can't be double-paid. DATABASE TABLE: payments ------------------------ record_id int - Primary key (NO AUTO_INCREMENT) invoice_id int - FK to invoices.record_id amount text - Payment amount date_time varchar - Date of payment user_id int - FK to users pyament_type text - Note: typo in column name ("pyament_type" not "payment_type") ================================================================================ FILES ================================================================================ home.php -------- Lists all payments with an Edit button. Uses: new table("payments") + add_action_button("edit_payments.php") -------------------------------------------------------------------------------- add_payments.php ---------------- What it does: Form to record a new payment. Fields: Payment Type (dropdown), Invoice (from unpaid invoices datalist), Date, Amount. When an invoice is selected, a live panel shows the payment history. How it works: On load: - Calls get_unpaid_invoices_list() which outputs option tags for invoices that are not fully paid. - The datalist is wrapped in a manually. Invoice selection flow: 1. User types or selects an invoice number. 2. get_invoice_id() AJAX fires (invoice_no.ajax.php). 3. Response is the invoice record_id (or 0 if not found). 4. If valid, ajax_history() fires (invoice_ajax.php) with the invoice_id. 5. Payment history panel is populated via AJAX response HTML. 6. If the NETT balance element shows "R 0.00", the SAVE button and amount field are hidden (invoice is fully paid). JS Functions: get_invoice_id(invoice_no) - XHR GET to invoice_no.ajax.php?invoice_number={invoice_no} - On success: sets hidden invoice_id input, calls ajax_history(). - On failure (returns 0): alerts user, clears the inputs. ajax_history(invoice_id) - XHR GET to invoice_ajax.php?invoice_id={invoice_id} - Populates the invoice_payments div with HTML response. - Checks if #nett element contains "R 0.00" - if so, hides SAVE + amount. save(url) - Collects text, date, email inputs and selects. - Creates and submits POST form to save_payments.php. -------------------------------------------------------------------------------- save_payments.php ----------------- What it does: Inserts a new payment record. How it works: Runs INSERT INTO payments with invoice_id, amount, date, user_id, pyament_type. Note: The table name in the query is "stock" (bug - should be "payments") but since $db->query() uses the first argument only for validation/logging and the actual SQL is the second argument, the INSERT goes to payments correctly. Redirect logic: "if ($client_id > 0)" - same bug as clients, payments table has no AUTO_INCREMENT so this may not redirect. To be safe, treat this the same as other modules. -------------------------------------------------------------------------------- edit_payments.php ----------------- What it does: Edit form for an existing payment. Pre-fills all fields. On load, automatically calls ajax_history() to show current payment state. How it works: - Loads payment by $_GET['record_id']. - Loads the linked invoice to pre-fill the invoice number field. - Calls ajax_history() on page load with the payment's invoice_id. - Same invoice change flow as add_payments.php. - Saves to update_payments.php. -------------------------------------------------------------------------------- update_payments.php ------------------- What it does: Updates an existing payment record. How it works: UPDATE payments SET invoice_id, amount, date_time, user_id, pyament_type WHERE record_id = $_POST['record_id']. Note: Table name in query is "stock" (same naming bug as save_payments.php). Redirect: same "if ($client_id > 0)" issue. -------------------------------------------------------------------------------- invoice_no.ajax.php ------------------- What it does: AJAX. Receives invoice_number via GET and returns the invoice record_id. Returns 0 if not found. Response format: Plain integer (e.g. "45" or "0") Called by: get_invoice_id() in add_payments.php and edit_payments.php. -------------------------------------------------------------------------------- invoice_ajax.php ---------------- What it does: AJAX. Receives invoice_id via GET and returns a full HTML payment history panel for that invoice. Response: HTML table showing: - Credit total (if any credit notes exist) - Invoice total (sum of invoice_list prices * qtys) - Each payment with date and amount - Payments total - NETT balance (invoice total minus credits minus payments) The NETT element has id="nett" - used by add_payments.php to detect if the invoice is fully paid. Logic covers two states: - Invoice has existing payments: shows full history. - Invoice has no payments yet: shows invoice total and "NO PAYMENTS YET". Note: The invoice total calculation in this file uses raw float arithmetic on price * qty without cleaning currency strings. If prices are stored as "R 1.234" style strings the totals may be wrong. Most prices in invoice_list are stored as cleaned decimal strings via number_to_save(), so this usually works. -------------------------------------------------------------------------------- cash_slip.pdf.php ----------------- What it does: Generates a cash receipt PDF for a specific payment (?record_id=X). PDF contents: - "CASH RECEIPT" and "INVOICE: {invoice_number}" titles. - Client and company details. - Payment Received amount. - Balance Outstanding (invoice total minus credit total minus total payments). - Amount Due (same calculation). - Bank details block. - "THANK YOU FOR THE SUPPORT!" message. Note: The line items table code is commented out - only payment amounts shown. ================================================================================ KNOWN ISSUES SUMMARY ================================================================================ 1. Column name typo in DB: "pyament_type" (missing letters) - present throughout all payment files. Do not "fix" this without a DB ALTER TABLE too. 2. save_payments.php and update_payments.php use "stock" as the table name argument in $db->query() - should be "payments". Does not cause a bug because the SQL itself is correct, but the log entry will say "stock". 3. Redirect after save may not fire (same AUTO_INCREMENT issue as other modules). 4. invoice_ajax.php does not clean currency strings before arithmetic - could give wrong totals if prices contain R or commas. ================================================================================