# Corporate Phase 7 Credit Notes and Reversals Audit

## Scope

Reviewed the existing Phase 6 corporate invoicing implementation before adding:

- allocation reversal,
- credit notes,
- private corporate payment receipt downloads,
- receivables recalculation after financial corrections.

## Current Allocation Flow

- Allocation is implemented in `app/Domain/Corporate/Invoicing/CorporatePaymentAllocationService.php`.
- Only `CorporatePayment::STATUS_CONFIRMED` payments can be allocated.
- The service rejects cross-organization allocation, duplicate payment/invoice pairs, draft invoices, over-payment, and allocations above invoice balance.
- On allocation, the service:
  - creates `corporate_payment_allocations`,
  - increments `corporate_invoices.paid_dzd`,
  - decrements `corporate_invoices.balance_due_dzd`,
  - refreshes invoice status through `CorporateInvoiceTotalsCalculator`,
  - creates one idempotent `financial_transactions` row with `source_type=corporate_payment_allocation` and `source_id=allocation.id`.

## Financial Transaction Behavior

- Revenue is recognized only when a confirmed payment allocation is created.
- `financial_transactions` has a unique key on `(type, source_type, source_id)`.
- This is safe for positive allocations, but Phase 6 has no reversal source type.
- Phase 7 must add a new source type for negative correction transactions:
  - `corporate_payment_allocation_reversal`.

## Current Invoice Status Logic

- `CorporateInvoiceTotalsCalculator::refreshStatus()` derives statuses from `paid_dzd`, `balance_due_dzd`, and the current overdue state.
- It does not know about credit notes or reversed allocations yet.
- `CorporateReceivablesAgingService` sums `total_dzd`, `paid_dzd`, and `balance_due_dzd` directly from invoices.

## Missing Fields Required For Phase 7

### Allocation Reversal

`corporate_payment_allocations` currently has no:

- `reversed_at`,
- `reversed_by_user_id`,
- `reversal_reason`.

Without these fields, the UI and services cannot distinguish active allocations from reversed ones.

### Credit Notes

`corporate_invoices` currently has no:

- `credit_note_total_dzd`,
- `net_total_dzd`.

Phase 7 should preserve `total_dzd` as the gross issued invoice amount, add `credit_note_total_dzd`, and calculate `net_total_dzd = total_dzd - credit_note_total_dzd`.

## Receipt Downloads

- `corporate_payments.receipt_path` exists.
- There is no admin or org route for private receipt download yet.
- `CorporatePaymentPolicy` has no `downloadReceipt()` method.

## Risks

| Risk | Level | Required Fix |
| --- | --- | --- |
| Reversing by deleting allocation would erase audit history. | HIGH | Add `corporate_allocation_reversals` and mark allocation as reversed. |
| Revenue double counting after reversal/re-allocation. | HIGH | Create one negative `financial_transactions` row per reversal with a distinct source type and idempotent unique source. |
| `CorporatePayment::unallocatedAmount()` counting reversed allocations. | HIGH | Sum only active allocations where `reversed_at` is null. |
| Receivables not reflecting credit notes. | HIGH | Add credit note totals/net totals to invoice recalculation and aging. |
| Applying credit notes to already-paid revenue. | MEDIUM | MVP restricts credit note amount to current unpaid balance; no revenue reversal is generated for credit notes. |
| Receipt files becoming public. | HIGH | Add protected download controllers using private disk and policy checks. |

## Implementation Direction

- Keep original allocations and financial transactions immutable.
- Add reversal records and negative financial transactions.
- Add credit notes as auditable invoice adjustments, not silent invoice edits.
- Keep all receipt and credit note PDFs on the private disk.
- Update policies and permissions before exposing UI actions.
