# POS Discount Imports & Payload Cleanup

**Commit:** `5b22842` – "Add POS discount imports and cleanup"
**Date:** 2026-08-28

This document explains, in plain terms, what changed and why.

---

## 1. The problem this solves

Two things happened with the POS (point-of-sale) data feed:

1. **The POS changed the names of some keys in its JSON payload.**
   As of the August 2026 "Neutron" payload change, the line-items key
   `SaleDetails` was renamed to `SalesDetails`. Old files still get
   re-read during "redo" imports, so **both spellings have to keep working.**

2. **Discounts (Senior Citizen / PWD) were never being saved.**
   The POS payload carries the discount ID number and name, but our
   importer ignored them. We now capture them into the datamart and,
   where possible, tag each one with the matching **SAP discount type**.

---

## 2. What was added

### a) `App\Helpers\PosPayloadHelper` (new file)

A small helper that reads the POS payload safely, no matter which key
spelling it uses. Three static methods:

| Method | What it returns | Notes |
|---|---|---|
| `saleDetails($item)` | The line items array | Accepts `SalesDetails` (new) **or** `SaleDetails` (old). Returns `[]` if neither is present. |
| `paymentDetails($item)` | The payments array | Returns `[]` when a transaction has no payments (e.g. zero-amount sales). |
| `discountDetails($item)` | The discount ID list | Each entry looks like `{ IDNo, IDName }`. Returns `[]` when the transaction has no discount. |

Everywhere the code used to do `$item['SaleDetails']` or
`$item['PaymentDetails']` directly, it now calls the helper instead.
This means **no more "undefined index" crashes** when a key is missing
or renamed.

### b) `discount_details` table (new migration)

`database/migrations/2026_08_28_000000_create_discount_details_table.php`

Creates a `discount_details` table on the **`mysql_datamart`** connection:

| Column | Type | Meaning |
|---|---|---|
| `id` | bigint (PK) | |
| `id_no` | string(50) | The Senior/PWD ID number from the POS |
| `id_name` | string(150) | The name on that ID |
| `discount_type` | string(50) | The raw discount type text from the POS payload |
| `sales_transaction_id` | bigint | Links back to the sales transaction (FK, cascades on delete/update) |
| `discount_mapping_id` | bigint, nullable | Which `discount_mapping` row matched (if any) |
| `sap_discount_type_id` | bigint, nullable | The resolved SAP discount type (if the mapping had one) |

**Important:** `discount_mapping` and `sap_discount_types` are shared
tables owned by the **SAP integration app**, not this repo. The
migration only attaches those foreign keys **if the tables actually
exist** in the target database. This keeps the migration safe to run
in environments where SAP tables aren't present.

### c) `App\Models\SQLModels\DiscountDetail` (new model)

Eloquent model for the new table. Uses the `mysql_datamart` connection,
no timestamps, and has a `sales_transaction()` belongs-to relationship.

### d) `SalesTransaction` model

Added the inverse relationship:

```php
public function discount_details() {
    return $this->hasMany(DiscountDetail::class, 'sales_transaction_id', 'id');
}
```

---

## 3. How discount import works

This logic was added to **all three import paths**:

- `ImportPosTransactionsToDatamart` (console command)
- `DatamartManagement::importTransactionsToDatamart()` (Livewire)
- `DatamartManagement::redoImportTransactionsToDatamart()` (Livewire redo)

Steps per import run:

1. **Load the discount mapping table once** at the start of the run:

   ```php
   $discount_mappings = DB::connection('mysql_datamart')->table('discount_mapping')
       ->where('status', 1)->orderBy('id')->get();
   ```

2. **Per transaction**, read `$item['DiscountType']` (defaults to `''`).

3. **Find the matching mapping** by checking whether the mapping's
   `like_name` appears anywhere inside the discount type text
   (case-insensitive `stripos`). First match wins.

4. **For each discount ID entry** in `PosPayloadHelper::discountDetails($item)`,
   create a `DiscountDetail` row with:
   - `id_no`, `id_name` from the payload
   - `discount_type` = the raw POS text
   - `discount_mapping_id` = matched mapping id, or `null`
   - `sap_discount_type_id` = the mapping's SAP type (only if it's numeric), or `null`

If there's no discount on the transaction, nothing is written.

---

## 4. Other small fixes

- **`TransactionViewer`**: now uses `PosPayloadHelper` for sale/payment
  details, and its `catch(Exception $e)` was widened to
  `catch(\Throwable $e)` so it also swallows PHP errors (not just
  exceptions) while parsing a bad file.

- **Repo cleanup**: removed committed local artifacts (`.env.bk`,
  `config/error_log`, `public/error_log`, `storage/logs/laravel.log`,
  compiled Blade views, cache/session files) and added `.gitignore`
  rules under `storage/` so these generated files stay out of git going
  forward.

---

## 5. To deploy

1. Run the migration:

   ```bash
   php artisan migrate
   ```

   (Creates `discount_details` on the datamart DB. FKs to SAP tables
   attach automatically only if those tables exist.)

2. No config changes needed. Next import run will start populating
   `discount_details`.

3. To backfill existing files, use the **redo import** in Datamart
   Management — it deletes and re-imports, so discounts get captured.
