# Wallet Payment Method & Refund-to-Wallet Design

## Goal
Allow customers to pay from their wallet, and ensure all refunds for real customers are credited to their wallet instead of mutating invoice balances.

## Constraints
- Touch existing code as little as possible.
- Keep the existing structure/style of `TransactionController` and `ReservationService`.
- Only real `Customer` reservations use the wallet. `PendingCustomer` reservations keep the old cash/bank refund behavior (mutate invoice `paid_amount`).
- `cash` and `bank` remain functionally identical from the system's point of view.

## Approach
Add `wallet` directly to the existing transaction payment-method column and request validation, then branch on the payment/refund type and customer type using plain string checks (no extra enum class) to keep the diff minimal.

## Data Model

### `customers.wallet`
Already exists (`decimal(10,2)`, default `0.00`). No migration needed.

### Transaction migration: `database/migrations/2026_06_08_175012_create_transactions_table.php`
Change the existing create-table line:
```php
$table->string('payment_method');
```
to:
```php
$table->enum('payment_method', ['cash', 'bank', 'wallet']);
```
This is done directly in the `create_transactions_table` migration, not in a separate `alter` migration.

### Validation: `TransactionRequest.php`
Change:
```php
'payment_method' => 'required|string|in:cash,bank',
```
to:
```php
'payment_method' => 'required|string|in:cash,bank,wallet',
```

## Behavior

### 1. Payments (`type === 'payment'`)

#### `cash` / `bank`
Unchanged from today:
1. Validate `amount <= invoice.remaining_amount`.
2. Create `Transaction` (`transaction_type='payment'`, `type='payment'`).
3. Increase `invoice.paid_amount`, decrease `invoice.remaining_amount`.
4. Mirror balances to the customer `Receipt` if present.
5. Update `reservation.payment_status`.

#### `wallet`
1. Verify the reservation's customer is a real `Customer` (`$reservation->customer_type === Customer::class`).
   - If it is a `PendingCustomer`, reject with 422 (wallet not available for pending customers).
2. Verify `$customer->wallet >= amount`.
3. Deduct `amount` from `$customer->wallet`.
4. Create `Transaction` (`transaction_type='payment'`, `type='payment'`, `payment_method='wallet'`).
5. Increase `invoice.paid_amount`, decrease `invoice.remaining_amount`.
6. Mirror to receipt and update reservation status as usual.

### 2. Refunds (`type === 'refund'`)

#### Real `Customer`
1. Validate that the requested refund amount does not exceed the effective paid balance (`sum(payment transactions) - sum(refund transactions)`).
2. Create `Transaction` (`transaction_type='payout'`, `type='refund'`, `payment_method='wallet'`).
3. Credit `amount` to `$customer->wallet`.
4. Do **not** change `invoice.paid_amount`, `invoice.remaining_amount`, receipt balances, or reservation status.

#### `PendingCustomer`
Keep the old behavior:
1. Validate that the requested refund amount does not exceed the effective paid balance (`sum(payment transactions) - sum(refund transactions)`).
2. Create `Transaction` (`transaction_type='payout'`, `type='refund'`, payment method as provided).
3. Decrease `invoice.paid_amount`, increase `invoice.remaining_amount`.
4. Mirror to receipt and update reservation status.

### 3. Automatic refund on date-shorten (`ReservationService::recalculateReservation`)

When dates are shortened and `paid_amount > net_price`:
1. Recalculate invoice totals (`price`, `quantity`, `total_price`, `discount`, `net_price`, `remaining_amount`).
2. Compute `refundAmount = paid_amount - net_price`.
3. Create refund `Transaction` with `payment_method='wallet'` for real `Customer` or `'cash'` for `PendingCustomer`.
4. Reset `invoice.paid_amount` to the new `netTotal` and `remaining_amount` to 0 for **both** customer types, and mirror the invoice to the receipt.
5. If the customer is a real `Customer`, credit `$customer->wallet` by `refundAmount`.

> **Rationale:** Manual real-customer refunds credit the wallet without reducing `invoice.paid_amount`. To prevent double-crediting when a date-shorten auto-refund follows a manual refund, the auto-refund uses transaction totals (`payments - refunds`) as the effective paid amount and then resets `paid_amount` to the new invoice total.
5. Update `reservation.payment_status` based on the final invoice state.

## Files to change

1. `database/migrations/2026_06_08_175012_create_transactions_table.php` — change `payment_method` to `enum('cash','bank','wallet')` directly in the create table.
2. `app/Http/Requests/TransactionRequest.php` — add `wallet` to `payment_method` rule.
3. `app/Http/Controllers/TransactionController.php` — branch on payment/refund type and customer type.
4. `app/Services/ReservationService.php` — use `wallet` payment method for auto-refund to real customers.

## Out of scope
- Changing unit-level `payment_method` enum (`cash`/`bank`) used for unit defaults.
- Adding wallet ledger/history beyond existing `transactions` table.
- UI/front-end changes.

## Tests to verify
- Wallet payment succeeds for real Customer and deducts wallet.
- Wallet payment fails for PendingCustomer.
- Wallet payment fails when balance is insufficient.
- Manual refund for real Customer credits wallet and leaves invoice balances untouched.
- Manual refund for PendingCustomer keeps old invoice-mutation behavior.
- Refund over the paid amount is rejected for both customer types.
- Date-shorten auto-refund credits wallet for real Customer and uses `payment_method='wallet'`.
