# POS Return-Only Design

## Problem

The POS receipt-return workflow can add returned receipt lines without adding a replacement product. Those lines are submitted as `exchange_returns`, while normal sale lines are submitted as `products`. `SellPosController::store()` currently performs all return processing inside the `products` branch, so a return-only request reaches the generic “Something went wrong” response.

## Approved Behavior

- A finalized POS request with an original receipt and at least one returned item must complete even when there are no replacement products.
- It creates only a sell-return transaction; it must not create a zero-value replacement sale.
- The complete return value is refunded using the existing payment-modal choice:
  - `cash` records a refund payment and cash-register outflow.
  - `store_credit` records non-cash exchange credit and increases the named customer’s available credit.
- Store credit is rejected for the walk-in customer with a clear message.
- Returned quantity, price, serial validation, restocking, payment status, and receipt generation use the existing sell-return utilities.
- Sub-unit quantities are normalized consistently in receipt selection, return-only refunds, replacement exchanges, and serial release counts.
- The refund prorates the original invoice's stored discount and recorded tax amount, so later tax-rate changes cannot alter a historical return.
- The original sale, return quantities, and store-credit customer balance are locked while the refund is finalized; retry UUIDs prevent duplicate refunds.
- Receipt lookup is restricted to the current register location unless the cashier has all-location access.
- Exchanges containing replacement products continue through the existing sale-plus-return workflow unchanged.

## Design

Detect return-only input before the current `products` gate. Delegate it to a focused controller method that validates permission, original finalized sale, remaining return quantities, and refund customer; creates the sell return and its full refund inside one database transaction; runs the standard after-return hook; and returns a POS-compatible receipt response.

The existing POS receipt builder will render `sell_return.receipt` when its transaction is a sell return. No payment-modal changes are needed because the current exchange refund selector already posts `exchange_refund_method` and correctly calculates a zero customer-payable amount for return-only rows.

## Error Handling

Validation and business-rule failures roll back the transaction and return their actionable message. Unexpected exceptions remain logged and use the existing safe POS error resolver.

## Verification

- Regression tests prove the return-only branch is before the normal products gate and cover refund, customer-credit, register, payment-status, receipt, sub-unit, location, historical-tax, and retry behavior.
- Existing exchange serial tests and the full PHP test suite remain green.
- Deployment copies are hash-compared with the workspace files.
