# Mobile API

## Stock Receipts API integration

Flutter uses authenticated endpoints to upload/list/reopen sessions, retrieve request candidates, link explicitly, start, count with a generated client UUID, adjust variance quantities, finish, and confirm. A retry with the same count UUID cannot increment twice. The source PDF is protected and never exposed by a public storage URL.

## Checkout product identity — 2026-08-16

Order item `product_id` is always the stable master `products.id`; `source_catalog_item_id` is the separate active-list membership reference. Flutter runs `POST /mobile/orders/preflight` before draft creation and surfaces structured affected SKU/name information without raw exception text.

## Stock Replenishment

`ApiService` integrates Stock Request CRUD, submit, operational actions, receiving, private attachment upload/streaming, and Request/Preparation/Receiving PDFs under `/v1/mobile/stock-requests`; Laravel is authoritative for permissions, transitions and stock posting.

`POST /stock-requests/{id}/attachments` accepts protected image/PDF evidence. Receiving discrepancy evidence includes request item, receipt, and receipt-item identifiers. `GET /stock-request-attachments/{attachment}` streams authorized evidence without a public storage URL. The two completion reports are `GET /stock-requests/{id}/preparation-pdf` and `GET /stock-requests/{id}/receiving-pdf`.

QA products/customers use explicit `is_test_data` and `qa_owner_user_id` fields. Catalog, barcode, customer, order and replenishment APIs enforce owner/admin visibility server-side. Public catalog routes always exclude QA products.

## Customer applications and invoice configuration

Flutter uses the customer-application draft/update/submit APIs, private document upload, allowed territories, warning-only duplicate detection and `/pricing-config`. Checkout sends `invoice_type`; final discount, Clearance classification, VAT and eligibility are calculated and persisted by Laravel.

## Returns production contract — 2026-08-09

- `POST /api/mobile/return-handovers` accepts `{ "item_ids": [1, 2] }`; the list is required and only those eligible items are batched.
- `POST /api/mobile/returns/opening-rma` atomically reclassifies existing physical van stock from sellable to RMA.
- A normal return requires a saved `customer_id`.
- Accounting review must submit every item belonging to the return and a complete settlement. Invoice settlement must reference an order for the same customer.

All protected endpoints use the mobile authentication middleware and require:

```http
Accept: application/json
Authorization: Bearer <token>
```

## Authentication

```text
POST /api/mobile/login
POST /api/mobile/logout
GET  /api/mobile/me
```

## Dashboard and van stock

```text
GET /api/mobile/dashboard
GET /api/mobile/van-stock
```

## Catalogs

```text
GET /api/mobile/catalogs/main
GET /api/mobile/catalogs/clearance
```

The exact catalog endpoints may include query parameters for search and pagination.

## Customers

```text
GET  /api/mobile/customers
POST /api/mobile/customers
GET  /api/mobile/customers/{customer}
```

Customer listing is scoped by the authenticated user.

## Orders

```text
GET  /api/mobile/orders
POST /api/mobile/orders
GET  /api/mobile/orders/{order}
GET  /api/mobile/orders/{order}/pdf
POST /api/mobile/orders/{order}/confirm
POST /api/mobile/orders/{order}/cancel
POST /api/mobile/orders/{order}/deliver
```

### Draft order request

```json
{
  "client_uuid": "uuid-v4",
  "customer_id": 123,
  "customer_name": null,
  "customer_phone": null,
  "discount_total": 0,
  "notes": null,
  "items": [
    {
      "product_id": 1214,
      "qty": 1,
      "unit_price": 33
    }
  ]
}
```

For a walk-in customer:

```json
{
  "client_uuid": "uuid-v4",
  "customer_id": null,
  "customer_name": "Walk-in Customer",
  "customer_phone": "0500000000",
  "discount_total": 0,
  "notes": null,
  "items": [
    {
      "product_id": 1214,
      "qty": 1,
      "unit_price": 33
    }
  ]
}
```

### Order lifecycle

```text
draft
→ confirmed
→ delivered
```

Alternative cancellation paths:

```text
draft → cancelled
confirmed → cancelled with stock reversal
```

### PDF response

```http
Content-Type: application/pdf
```

The PDF route only renders the order. It does not confirm the order and does not change stock.

## Boxes

```text
GET /api/mobile/boxes
GET /api/mobile/boxes/{box}
GET /api/mobile/boxes/{box}/items
```

## Barcode lookup

```text
GET /api/mobile/products/barcode/{barcode}
```

## Inventory check

```text
GET  /api/mobile/inventory-check
POST /api/mobile/inventory-check/start
POST /api/mobile/inventory-check/mark
POST /api/mobile/inventory-check/finish
```

## Smart Daily Sales Report

```text
GET  /api/mobile/reports/daily-sales?month=2026-08
POST /api/mobile/reports/daily-sales/entry
POST /api/mobile/reports/daily-sales/submit
GET  /api/mobile/reports/daily-sales/pdf?month=2026-08
```

All financial fields are currently entered manually. Creating and editing use the
same idempotent entry endpoint, keyed by the authenticated salesman and `date`.

```json
{
  "date": "2026-08-02",
  "wiwu_sales": 1500,
  "sales_return": 50,
  "cash_collection": 900,
  "bank_transfer": 400,
  "cheque": 100,
  "notes": "Optional note"
}
```

Submission request:

```json
{
  "month": "2026-08"
}
```

The report response should include `status` as `draft` or `submitted`. Laravel
must reject entry changes after submission unless a future reopen permission is
implemented. The mobile app queues entry writes locally while offline and retries
them before loading or submitting the report.

<!-- SNYT_RETURNS_RMA_FLUTTER_V1_START -->
## Returns / RMA v1

Status: **Implemented in source; device QA required**

### Navigation and UI

- `Returns` is a top-level bottom-navigation destination for salesman and manager/admin layouts.
- Team chat remains available from the More page.
- The global app theme now uses the approved SNYT dark visual system: deep navy/black backgrounds, cyan primary actions, teal success states, amber accounting states, red RMA states, subtle borders, rounded enterprise cards, and compact mobile-first spacing.
- Existing business-specific report sheets keep their required company document appearance while app chrome follows the dark SNYT system.

### Returns Screens

- Returns list with search and filters.
- Create Return with mandatory saved customer.
- Customer Return / Resellable and RMA / Defective type selection. Types cannot be mixed in one document.
- Partial SKU/name/barcode search and barcode scanner.
- Multiple items per return.
- Optional photos.
- Direct exchange from sellable van stock on RMA items.
- Return details with separate operational and financial status.
- Accounting Review for admin/supervisor accounts.
- RMA warehouse handover list/create/details.
- Dispatch to warehouse.
- Full / partial / rejected warehouse confirmation.
- Print A4 handover PDF.
- Customer profile includes Sales / Returns / Payments tabs.

### Stock Behavior

- Resellable items do not enter sellable stock until accountant approval.
- RMA/Defective items enter a separate non-sellable RMA stock bucket.
- Direct exchange shows current sellable VAN availability, adds the defective unit to RMA, and deducts the replacement from sellable van stock.
- Flutter never performs authoritative stock math locally; Laravel confirms all stock-changing operations.

### API

Returns APIs are exposed under the existing mobile API base and include returns, product search, accountant review, opening RMA reclassification, handover batches, warehouse confirmation, and handover PDF.
<!-- SNYT_RETURNS_RMA_FLUTTER_V1_END -->
## Explicit Return state endpoints

- `POST /api/mobile/returns/{return}/submit`
- `POST /api/mobile/returns/{return}/approve`
- `POST /api/mobile/returns/{return}/reject` with required `rejection_reason`

The legacy accounting-review endpoint remains available for compatibility, while Flutter uses the explicit approve endpoint.
## RMA Stock

`GET /api/mobile/rma-stock?search=...&salesman_id=...`

Salesmen always receive only their own stock. Admin/supervisor users may view all rows or filter by salesman. Returned rows distinguish `rma_qty`, `ready_qty`, `prepared_qty`, `in_transit_qty`, and `total_assigned_qty`.
