---
title: Selling End to End
description: Open a session, park a cart, post an idempotent sale with DEMO10, tender cash, close out, and return part of it.
---

You run the full counter shift against the routes in `apps/server/src/features/sessions/route.ts`, `apps/server/src/features/parked/route.ts`, `apps/server/src/features/sales/route.ts`, and `apps/server/src/features/returns/route.ts`. You start by logging in and saving the token.

```bash
TOKEN=$(curl -s -X POST http://localhost:8000/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"pharm1@demo.pharmaflow","password":"Demo@1234"}' \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
```

Routes mount under the `/api/v1` prefix that `apps/server/src/app.ts` applies with `app.route("/api/v1", apiV1)`, so every path below sits at `http://localhost:8000/api/v1/...`.

### Open your cash session

You open one session per shift with `POST /sessions/open`. You pass your opening float and keep the returned session id for close-out.

```bash
curl -X POST http://localhost:8000/api/v1/sessions/open \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"opening_float":200}'
```

You check it any time with `GET /sessions/current`, and you review past shifts with `GET /sessions/history`.

### Park an interrupted cart

You park a cart when the customer walks away with `POST /parked`. You recall it later with `POST /parked/:parkedId/recall` and void it with `POST /parked/:parkedId/void`.

```bash
curl -X POST http://localhost:8000/api/v1/parked \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"items":[{"medicine_id":"MEDICINE_ID","quantity":1,"unit_price":20}],"customer_name":"Walk-in"}'
```

### Post the sale with DEMO10

You create the sale with `POST /sales` in `apps/server/src/features/sales/route.ts`. You send a fresh `X-Idempotency-Key` per new sale: replaying a key returns the stored response with 201 instead of charging twice, through `claimIdempotencyKey` and `readIdempotencyResponse` in `apps/server/src/features/sales/idempotency/queries.ts`. The `DEMO10` coupon applies 10 percent off and the server uppercases the code before lookup in `apps/server/src/features/sales/creation/service.ts`.

```bash
curl -X POST http://localhost:8000/api/v1/sales \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Idempotency-Key: sale-0001" \
  -d '{
    "items": [{"medicine_id":"MEDICINE_ID","quantity":2,"unit_price":12.5}],
    "payment_method": "cash",
    "coupon_code": "DEMO10",
    "customer_name": "Walk-in"
  }'
```

You save the returned sale `id` and `invoice_number`. Inside one transaction the server prices VAT, deducts batches FEFO, writes `OUT` stock movements, and allocates the invoice number plus ICV, in `apps/server/src/features/sales/creation/`.

In the register UI the same sale is built from `apps/register/components/sales/useSaleCart.ts` state in `apps/register/app/[locale]/pharmacist/sales/new/page.tsx`. When the checkout request fails with a timeout or a network error, `shouldQueueSaleLocally` in `apps/register/app/lib/offline-sale.ts` passes and `queueOfflineSale` in `apps/register/app/lib/tauri-sync.ts` queues the sale on the device, ready for the [offline-first flow](/guides/offline-first-flow).

### Declare tender and close

You declare the drawer cash with `POST /sessions/tender`, sending `declared_cash` so the server computes the difference against expected takings. Then you close the shift with `POST /sessions/close`, which stamps `closed_at` and flips the status to `CLOSED`.

```bash
curl -X POST http://localhost:8000/api/v1/sessions/tender \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"declared_cash":1250.5,"notes":"Evening close"}'
```

```bash
curl -X POST http://localhost:8000/api/v1/sessions/close \
  -H "Authorization: Bearer $TOKEN"
```

### Pull the day numbers

You reconcile with the sales and VAT reports in `apps/server/src/features/reports/route.ts`. Both `GET /reports/sales` and `GET /reports/vat` allow only `admin`, `branch_manager`, or `auditor` roles, so you repeat the login as `admin@demo.pharmaflow` and use that token:

```bash
ADMIN=$(curl -s -X POST http://localhost:8000/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"admin@demo.pharmaflow","password":"Demo@1234"}' \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")

curl "http://localhost:8000/api/v1/reports/sales?from_date=2026-01-01&to_date=2026-12-31" \
  -H "Authorization: Bearer $ADMIN"
```

```bash
curl "http://localhost:8000/api/v1/reports/vat?from_date=2026-01-01&to_date=2026-12-31" \
  -H "Authorization: Bearer $ADMIN"
```

The window defaults to the last thirty days when you omit the dates.

### Return part of the sale

You look up the invoice, then you post a partial return with `POST /returns`. You reference sale item ids with quantities, and the server restocks, writes the credit note, and records the refund.

```bash
curl http://localhost:8000/api/v1/returns/lookup/INV-001 \
  -H "Authorization: Bearer $TOKEN"
```

```bash
curl -X POST http://localhost:8000/api/v1/returns \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"sale_id":"SALE_ID","reason":"Customer changed mind","items":[{"sale_item_id":"ITEM_ID","quantity":1}]}'
```

### Next steps

Read the pricing internals in /sales/selling-at-the-counter, handle prescriptions in /dispensing/prescriptions, and trace every step in /platform/audit-log.
