# Sales / Server-side Cart Foundation v17

The cart is server-side business state.

Clients such as:
- desktop POS
- PWA
- Android/iOS mobile POS
- waiter interface

do not own authoritative pricing, promotion calculation, or cart totals.

## Flow

Scan barcode
-> Catalog barcode resolution
-> Variant / unit
-> Pricing resolution
-> Promotion eligibility and line discount
-> Persist line snapshots
-> Recalculate cart totals
-> Return authoritative cart

## Tables

- sales.carts
- sales.cart_lines
- sales.cart_operations

## Concurrency

Every cart has an integer version.

Mutations include `expected_version`.
A stale version returns HTTP 409.

This prevents two devices from silently overwriting the same cart state.

## Mobile idempotency

Mutations include `client_operation_id`.

`(tenant_id, client_operation_id)` is unique.

Retrying a mobile scan after a network timeout does not double-add the item.

## Money

Money and quantity use PostgreSQL NUMERIC / Laravel DECIMAL.
No float is used.

## Snapshotting

Each cart line stores:
- resolved unit price
- price source snapshot
- applied promotion snapshot
- gross
- discount
- net

Final immutable sale snapshots will be created during Checkout in a later increment.

## Promotions

v17 applies:
- percentage discounts
- fixed discounts

Exclusive campaign precedence is deterministic by lowest numeric priority.

Buy-X-Get-Y eligibility is preserved in the snapshot, but free-item materialization is deferred
to the reward-allocation/checkout increment.

## APIs

- POST /api/v1/sales/carts
- GET /api/v1/sales/carts/{cartId}
- POST /api/v1/sales/carts/{cartId}/scan
- PATCH /api/v1/sales/carts/{cartId}/lines/{lineId}

## Permissions

- sales.cart.view
- sales.cart.manage

## Next

Checkout foundation:
- immutable sales
- idempotent checkout command
- payment orchestration boundary
- unknown-result resolution
- cart -> sale conversion
