> ## Documentation Index
> Fetch the complete documentation index at: https://docs.laportenard.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Order submission

> How orders are submitted to Odoo — the useOrderSubmission hook, posReference format, and payment mapping.

Order submission is triggered from the payment flow when the balance due reaches zero and the user taps Done.

## Architecture

| Component                 | Path                                             | Purpose                                                                    |
| ------------------------- | ------------------------------------------------ | -------------------------------------------------------------------------- |
| `useOrderSubmission` hook | `src/features/sales/hooks/useOrderSubmission.ts` | Manages submission state, handles session errors, generates `posReference` |
| API client                | `src/api/orders/client.ts`                       | Typed wrapper around `postJson`, injects CSRF token                        |
| ID generator              | `src/domain/orders/posReference.ts`              | Generates unique `posReference`                                            |
| Backend linkage           | `src/domain/orders/models.ts` + `usePOS`         | Stores `posReference`, `odooOrderId`, `lineIdMap` on each order            |

## posReference format

```
NU/CFG{configId}/S{sessionId}/{timestamp}-{random}
```

Example: `NU/CFG1/S42/1709123456789-3f7a`

The `posReference` is generated once and persisted before submission. Retries use the same reference for idempotency — Odoo returns the existing order if the reference is already known.

## Submission flow

When hub sync is active, orders are finalized through the hub first. The hub calls `hub_finalize_snapshot` on Odoo, which runs the full fiscal pipeline (`create_from_ui`) inside a database savepoint. If the hub path fails for any reason, the frontend falls back to the direct API automatically.

<Steps>
  <Step title="Validate">
    Check that `bootstrapState` has valid `config_id` and `session_id`.
  </Step>

  <Step title="Choose path">
    If hub sync is active (`transport.mode === "hub"`) → **Hub finalization** via `transport.finalizeOrder()`. Otherwise → **Direct API** (`/orders/create` or `/orders/update`).
  </Step>

  <Step title="Map payload">
    * `product_id` strings → integers
    * `tax_ids` strings → integers
    * Payment lines include `statementId`, `journalId`, `accountId`, `amount`, and `name` (payment method name)
    * `amountPaid` (tendered) + `amountReturn` (cash change)
  </Step>

  <Step title="Submit">
    **Hub path**: Sends a full `OrderSnapshot` (lines, totals, payment lines) to the hub, which forwards to Odoo's `hub_finalize_snapshot`. On failure, falls back to the direct API automatically.

    **Direct path**: POST to `/orders/create` or `/orders/update`.
  </Step>

  <Step title="Handle result">
    * **Success**: local order marked paid, table freed, redirected to floor plan
    * **`no_pos_session`**: triggers global session guard (redirects/locks)
    * **Other error**: payment screen stays open, user can retry
  </Step>
</Steps>

### Hub finalization backend

The `hub_finalize_snapshot` method on `pos.order` wraps `create_from_ui` in a database savepoint. If any part of order creation fails (e.g., payment processing), the entire operation rolls back atomically — no phantom draft orders are left behind.

Key behaviors:

* **Default partner**: When no customer is selected, `config.default_partner_id` is used (typically "CLIENTE DE CONSUMO 1"), ensuring the receivable account is available for payment processing.
* **Idempotency**: If an order with the same `pos_reference` already exists and is paid, the existing order is returned.
* **Payment verification**: After `create_from_ui`, the method verifies that `statement_ids` were actually created. If not, the savepoint rolls back.
* **Fiscal pipeline**: The full NCF/DGII fiscal pipeline runs inside `create_from_ui`, so hub-finalized orders get the same fiscal treatment as direct API orders.

## Payment mapping

```
Cash tenders    → first cash statement in session
Non-cash tenders → first non-cash statement in session
Cash overpay    → amount_return field; tendered cash amount stays in statement_ids
```

## Usage

```typescript theme={null}
import { useOrderSubmission } from "@/features/sales/hooks/useOrderSubmission";

function PaymentScreen() {
  const { submitOrder, isSubmitting, error } = useOrderSubmission();
  const { state } = usePOS();

  const handlePay = async () => {
    const response = await submitOrder(state.activeOrder);
    // response.order_id contains the Odoo order ID
  };
}
```

## Backend mode

The `mode` field on the request defaults to `order` when omitted. Other values: `return` (refund) and `payment` (invoice collection).

See `src/api/orders/types.ts` for full request/response type definitions.
