# Restaurant Sale & Payment Integration v50

## Boundary
`restaurant.orders` remains the long-running operational document.
`sales.sales` remains the immutable commercial transaction.

v50 creates an explicit one-way conversion from a ready/served restaurant order into a Sale.

## Conversion
The conversion:
- locks the restaurant order,
- rejects cancelled/not-ready orders,
- is idempotent at the restaurant-order level,
- creates a synthetic converted Sales Cart for compatibility with the existing Sales model,
- creates immutable Sale and Sale Lines,
- preserves restaurant item/modifier snapshots,
- reserves a sale number through `sales.number_sequences`,
- posts sale inventory movements once,
- links `restaurant.orders.sale_id`,
- closes the restaurant order as finalized,
- writes an immutable restaurant order event.

## Restaurant modifiers
Sales has no dedicated modifier amount column.
The sale line therefore stores:
- gross = restaurant base gross + modifier amount
- discount = restaurant item discount
- net = restaurant item net
- modifier breakdown in price/metadata snapshots

This preserves the Sales database invariant `net = gross - discount`.

## Payments
The conversion endpoint may accept optional payment commands and delegates them to the existing `CollectPaymentAction`.
It does not create a second restaurant payment engine.

If payment collection fails after conversion, the Sale remains centrally available as pending/unpaid and can be settled through the standard Payments API. This avoids duplicating Sales/Payments truth.

## COD
COD remains operationally tracked by restaurant delivery (`expected`, `collected`, `settled`).
v50 deliberately does not auto-create a Sale cash payment from COD settlement because driver cash and register cash are not necessarily the same accounting location.

The COD status endpoint exposes the operational state and the linked Sale. The actual tender must be posted through the existing Payments/Cash contract.

## Inventory
Sale conversion posts `sale_issue` movements with:
- source_type = sale
- source_public_id = Sale public ULID
- source_line_public_id = SaleLine public ULID

This matches the existing Sales Inventory identity contract and makes retries naturally idempotent at source identity level.

## Deferred
- automatic SALE_RECIPE ingredient explosion during sale posting
- driver cash account settlement
- restaurant returns UX
- channel profitability read model
- fiscal receipt generation
- printing/local-agent routing
