================================================================================ MPUMALANGA PVC - CREDIT NOTES (c_notes) MODULE ================================================================================ Last Updated: 2026-03-26 Path: /app/c_notes/ ================================================================================ WHAT THIS SECTION DOES ----------------------- Manages credit notes issued against invoices. A credit note reduces what a client owes on an invoice (e.g. for returned goods, adjustments, or errors). Credit notes appear inline on the invoice edit page and are deducted in the payment history, invoice PDF, and cash receipt PDF. DATABASE TABLES --------------- credit_notes: record_id int - Primary key (NO AUTO_INCREMENT) invoice_id int - FK to invoices.record_id client_banking_details text - Free text for the client's bank details date_time varchar - Timestamp (column name: date_time, not date_time_created) IMPORTANT: The column is called date_time, NOT date_time_created. If filtering credit notes by date use cn.date_time, not cn.date_time_created (the dashboard had a bug caused by this - fixed 2026-03). credit_notes_list: record_id int credit_note_id int - FK to credit_notes.record_id name text - Description of the credit item amount decimal - Amount for this item ================================================================================ FILES ================================================================================ home.php -------- Lists all credit notes with an Edit button. Uses: new table("credit_notes") + add_action_button("edit_c_notes.php") Page title is set to "CREDIT" (correct). -------------------------------------------------------------------------------- add_c_notes.php --------------- What it does: Form to create a new credit note. Fields: Invoice selector, line items (description + amount each), Client Banking Details textarea. How it works: On load: - Queries credit_notes to calculate next credit note number (display only). - NOTE: The number calculation uses num_rows + 1, not the last record_id + 1. If any credit notes have been deleted the number will be wrong. This is display-only - the actual record_id is assigned by the DB. - Calls get_invoices_list_as_record_id('invoice_list') for invoice selector. Invoice selection: - User selects an invoice from the datalist. - The change listener extracts the data-id attribute to get the record_id. - Stores it in hidden input invoice_id. Line items: - No stock lookup - just free text description and a numeric amount. - add_row() adds new rows. - calculateTotals() sums all amount_{i} inputs. save(url) - Validates invoice_id is set. Alerts if not. - Collects all inputs including [] arrays. - Submits POST to save_c_notes.php. JS Functions: add_row() - Inserts a new description + amount row. delete_row(el) - Removes the parent . calculateTotals() - Sums all [id^='amount_'] inputs and displays in #total. - Formats as South African Rand. -------------------------------------------------------------------------------- save_c_notes.php ---------------- What it does: Inserts a new credit note header and all line items, redirects to home.php. How it works: - Sanitises the note field (strips quotes). - Inserts into credit_notes (invoice_id, client_banking_details). - Gets back the credit_note_id. - Loops over $_POST['descriptions'] and inserts each into credit_notes_list with the matching amount. - Redirects to home.php. -------------------------------------------------------------------------------- edit_c_notes.php ---------------- What it does: Pre-filled edit form for an existing credit note. Supports being opened from the invoice page (from_invoice=1 in URL). How it works: - Loads credit note by $_GET['record_id']. - Loads all credit_notes_list rows for this credit note. - Renders each row with existing description and amount values. - PHP $index counter passed to JS for add_row(). - calculateTotals() called on window.onload. - If ?from_invoice=1: SAVE button posts to update_c_notes.php?send_to_invoice=1. Otherwise: posts to update_c_notes.php. get_invoice_no($credit_note['invoice_id']) - Uses the app_features helper to pre-fill the invoice name field. JS Functions: Same as add_c_notes.php plus calculateTotals() called on load. -------------------------------------------------------------------------------- update_c_notes.php ------------------ What it does: Updates the credit note header, deletes all list rows, re-inserts from POST. How it works: - Sanitises the note field. - Updates credit_notes table (invoice_id, client_banking_details). - DELETEs all credit_notes_list rows for this credit_note_id. - Re-inserts all description/amount rows. - If $_GET['send_to_invoice'] == 1: Redirects to ../invoices/edit_invoices.php?record_id={invoice_id} (sends you back to the invoice you came from). - Otherwise: redirects to home.php. ================================================================================ HOW CREDIT NOTES AFFECT OTHER PARTS OF THE SYSTEM ================================================================================ Credit notes are not standalone - they affect totals in other modules: invoice_ajax.php (payments module) - Sums all credit note amounts for an invoice. - Deducts from invoice total to show correct outstanding balance. invoice.pdf.php - Shows each linked credit note as a deduction row in the line items table. - Deducts credit total from subtotal before showing Net Total. cash_slip.pdf.php - Deducts credit total when calculating Balance Outstanding. edit_invoices.php - Shows each linked credit note as a read-only row with an OPEN button. dashboard (app/home.php) - Queries credit_notes using cn.date_time (not cn.date_time_created). ================================================================================ KNOWN ISSUES SUMMARY ================================================================================ 1. Credit note display number uses num_rows + 1 - breaks if any notes deleted. 2. No PDF for credit notes. 3. No validation that the amount fields contain valid numbers. 4. SQL injection throughout. ================================================================================