orders - BevvyTech/BrewskiDocs GitHub Wiki

Orders

Method Path Description
GET /orders Paginated team orders with payment summaries and paid/overdue flags.
GET /orders/summary Aggregate counts (all, due, draft, confirmed, paid, cancelled) for a team.
GET /orders/:id Fetch a single order with line items and payment ledger.
GET /orders/:id/payments Retrieve the payment/refund ledger and summary for an order.
POST /orders Create a new order for a team (supports compliance metadata).
POST /orders/:id/items Add a line item to an order (beer, multipack, or custom charge).
POST /orders/:id/payments Record a payment or refund against an order.
PATCH /orders/:id Update order status, dates, notes, or compliance fields.
PATCH /orders/:id/dispatch Update the delivery status for an order.
POST /orders/:id/invoice Generate the latest invoice PDF, upload it to Spaces, and record it in documents.
POST /orders/:id/delivery-note Generate the latest delivery note PDF, upload it to Spaces, and record it in documents.

GET /orders/summary

  • Query Parameters: teamId (uuid, required)
  • Requires the caller to belong to the referenced team.
  • Returns a single object so dashboard cards can fetch total counts without looping through the paginated list.
  • Response 200:
    {
      "teamId": "d43c046a-10a1-4f52-bd0a-9bf16f828ab7",
      "summary": {
        "all": 26,
        "due": 3,
        "draft": 5,
        "confirmed": 15,
        "cancelled": 6,
        "paid": 9
      }
    }
  • due only includes confirmed orders with a past-due date and an outstanding balance. paid is true when the net remaining balance is zero (payments minus refunds) regardless of status.
  • Errors: 401 unauthenticated, 403 user not on the team, 404 team not found.

Document Invalidation

  • Any mutating order endpoint (recording payments, updating order metadata, adjusting dispatch, or adding/editing/removing line items) deletes existing invoice and delivery note PDFs before the change is applied. The API removes the documents row and the corresponding DigitalOcean Spaces object, so clients must regenerate fresh paperwork on demand after each update.

POST /orders/:id/invoice

  • Requires the caller to be a member of the order’s team.
  • Generates a fresh invoice PDF using current order data, team billing settings, client addresses, payment ledger, and embeds the latest team logo (when available) plus a QR code linking to https://indiebrewer.com.
  • Uploads the file to DigitalOcean Spaces at documents/orders/<teamId>/<orderId>/invoice.pdf, replaces any previous invoice document row, and returns the stored metadata.
  • Response 200 (Spaces configured):
    {
      "document": {
        "id": "5f4d...",
        "relatedType": "invoice",
        "relatedId": "709f31fc-9371-4e7b-a41c-0eb4e7d5d7fa",
        "paperSize": "A4",
        "fileUrl": "https://cdn.example.com/documents/orders/.../invoice.pdf",
        "format": "pdf",
        "metadata": {
          "invoiceNumber": "INV-2025-001",
          "generatedAt": "2025-03-04T10:15:33.123Z",
          "outstandingMinor": 74000
        },
        "createdAt": "2025-03-04T10:15:33.456Z",
        "updatedAt": "2025-03-04T10:15:33.456Z"
      }
    }
  • Response 200 (Spaces not configured): returns { "document": null, "pdf": "<base64>", "mimeType": "application/pdf" }, allowing clients to render the blob directly.
  • Errors:
    • 401 unauthenticated
    • 403 caller not on the team
    • 404 order not found
    • 502 when Spaces deletion/upload fails (the generator logs the error)

POST /orders/:id/delivery-note

  • Requires the caller to be a member of the order’s team.
  • Builds a delivery note PDF (team logo, billing + delivery addresses, line items without monetary values, dispatch status, QR link to https://indiebrewer.com), uploads it to Spaces, and stores the metadata in documents.
  • Response 200 (Spaces configured): identical shape to the invoice endpoint but with relatedType: "delivery_note".
  • Response 200 (Spaces disabled): returns { "document": null, "pdf": "<base64>", "mimeType": "application/pdf" }.
  • Errors mirror the invoice endpoint.

GET /orders

  • Query Parameters:
    • teamId (uuid, required)
    • state (draft | confirmed | cancelled | due | paid)
    • status (draft | confirmed | cancelled)
    • dispatchStatus (pending | packed | dispatched | in_transit | delivered)
    • groupId (uuid)
    • groupClaimStatus (unclaimed | claimed | collating | completed)
    • sort (issueDate | dueDate | createdAt, default issueDate)
  • direction (asc | desc, default desc)
  • page, pageSize (≤ 200)
  • Response 200:
    {
      "teamId": "d43c046a-10a1-4f52-bd0a-9bf16f828ab7",
      "page": 1,
      "pageSize": 25,
      "total": 1,
      "pages": 1,
      "sort": "issueDate",
      "direction": "desc",
      "state": null,
      "status": null,
      "dispatchStatus": null,
      "groupId": null,
      "groupClaimStatus": null,
      "results": [
        {
          "id": "ord-1001...",
          "number": "INV-2025-001",
          "status": "confirmed",
          "dispatchStatus": "dispatched",
          "issueDate": "2025-01-25",
          "dueDate": "2025-02-08",
          "subtotalMinor": 185000,
          "taxTotalMinor": 37000,
          "totalMinor": 222000,
          "totalPaidMinor": 148000,
          "remainingMinor": 74000,
          "currencySymbol": "£",
          "dutySuspense": false,
          "awrsNumber": "XAW000000123",
          "clientAwrsNumber": "XAW000000123",
          "createdAt": "2025-01-25T10:02:33.000Z",
          "updatedAt": "2025-01-30T14:45:10.000Z",
          "clientId": "5e7f...",
          "clientName": "Lantern Taproom",
          "groupId": "6f84c0d5-9a3d-4a67-9fc7-3df1e6d079a4",
          "group": {
            "id": "6f84c0d5-9a3d-4a67-9fc7-3df1e6d079a4",
            "slug": "southwest-brewers",
            "name": "Southwest Brewers Cooperative",
            "status": "active",
            "shopVisible": true,
            "regionLabel": "Bristol & Somerset"
          },
          "groupClaimStatus": "collating",
          "groupClaimTeamId": "d43c046a-10a1-4f52-bd0a-9bf16f828ab7",
          "groupClaimTeamName": "Lantern Brewery",
          "groupEarliestDeliveryAt": "2025-03-10T09:00:00.000Z",
          "groupLatestDeliveryAt": "2025-03-13T17:00:00.000Z",
          "flags": {
            "due": false,
            "paid": false,
            "partial": true,
            "refunded": false
          },
          "payments": {
            "summary": {
              "totalPaymentsMinor": 148000,
              "totalRefundsMinor": 0,
              "netPaidMinor": 148000,
              "remainingMinor": 74000
            }
          },
          "deposits": {
            "balanceMinor": -2500,
            "creditMinor": 2500,
            "liabilityMinor": 0
          }
        }
      ]

}


### `GET /group-orders`

Fetches only the orders associated with a specific brewery group—useful for group dashboards without pulling the full orders list.

- `teamId` *(uuid, required)*
- `groupId` *(uuid, required)*
- `sort` *(issueDate \| dueDate \| createdAt, default createdAt)*
- `direction` *(asc \| desc, default desc)*
- `page`, `pageSize` *(≤ 200)*
- `search` *(optional)*
- `groupClaimStatus` *(optional)*

The response mirrors `GET /orders` but is filtered to the provided `groupId`.
- **Notes**:
  - The list response includes payment **summaries** only; fetch line items and the full ledger from `GET /orders/:id` or `GET /orders/:id/payments` when needed.
  - `flags.paid` indicates the order is fully settled, `flags.partial` shows part-payment, and `flags.refunded` indicates net refunds exceed collected payments.
  - **Deposits**: `deposits.balanceMinor` reports the running ledger for team + client (positive = outstanding liability, negative = available credit). `deposits.creditMinor` exposes the credit that can be applied to future invoices. See [`Docs/api/Deposits.md`](Deposits.md) for ledger semantics.
  - **Duty suspense**: `dutySuspense` is a boolean flag (default `false`) indicating whether the order should be treated as duty suspended for Excise reporting.
  - **AWRS numbers**: `awrsNumber` stores the registration number captured for the order, while `clientAwrsNumber` mirrors the current value on the linked client record so historic paperwork retains its original context.
  - **Group shipping**: `groupId`, `group`, claim fields, and delivery window timestamps are only populated when the order is linked to a brewery group workflow; otherwise they return `null`.

## `GET /orders/:id`
- **Path Parameters**: `id` *(uuid, required)*
- **Response 200**:
  ```json
  {
    "order": {
      "id": "709f31fc-9371-4e7b-a41c-0eb4e7d5d7fa",
      "teamId": "d43c046a-10a1-4f52-bd0a-9bf16f828ab7",
      "clientId": "5e7f...",
      "clientName": "Lantern Taproom",
      "number": "INV-2025-001",
      "status": "confirmed",
      "dispatchStatus": "packed",
      "issueDate": "2025-01-25",
      "dueDate": "2025-02-08",
      "groupId": "6f84c0d5-9a3d-4a67-9fc7-3df1e6d079a4",
      "group": {
        "id": "6f84c0d5-9a3d-4a67-9fc7-3df1e6d079a4",
        "slug": "southwest-brewers",
        "name": "Southwest Brewers Cooperative",
        "status": "active",
        "shopVisible": true,
        "regionLabel": "Bristol & Somerset"
      },
      "groupClaimStatus": "collating",
      "groupClaimTeamId": "d43c046a-10a1-4f52-bd0a-9bf16f828ab7",
      "groupClaimTeamName": "Lantern Brewery",
      "groupEarliestDeliveryAt": "2025-03-10T09:00:00.000Z",
      "groupLatestDeliveryAt": "2025-03-13T17:00:00.000Z",
      "subtotalMinor": 185000,
      "taxTotalMinor": 37000,
      "totalMinor": 222000,
      "totalPaidMinor": 148000,
      "remainingMinor": 74000,
      "currencySymbol": "£",
      "dutySuspense": false,
      "awrsNumber": "XAW000000123",
      "clientAwrsNumber": "XAW000000123",
      "shipping": {
        "mode": "auto",
        "ruleId": "c003...",
        "ruleName": "Standard pallet",
        "amountMinor": 5500,
        "notes": null
      },
      "payments": {
        "summary": {
          "totalPaymentsMinor": 148000,
          "totalRefundsMinor": 0,
          "netPaidMinor": 148000,
          "runningBalanceMinor": 148000,
          "remainingMinor": 74000
        },
        "items": [
          {
            "id": "pay_01H...",
            "amountMinor": 120000,
            "kind": "payment",
            "paymentMethod": "bank_transfer",
            "paymentReference": "INV-2025-001",
            "paidAt": "2025-01-28T11:32:00.000Z",
            "createdAt": "2025-01-28T11:32:05.000Z",
            "runningBalanceMinor": 120000
          },
          {
            "id": "pay_01I...",
            "amountMinor": 28000,
            "kind": "payment",
            "paymentMethod": "card",
            "paymentReference": null,
            "paidAt": "2025-02-03T15:09:00.000Z",
            "createdAt": "2025-02-03T15:09:04.000Z",
            "runningBalanceMinor": 148000
          }
        ]
      },
      "deposits": {
        "balanceMinor": -2500,
        "creditMinor": 2500,
        "liabilityMinor": 0
      },
      "deposits": {
        "balanceMinor": -2500,
        "creditMinor": 2500,
        "liabilityMinor": 0,
        "transactions": [
          {
            "id": "dep_01R...",
            "orderId": "709f31fc-9371-4e7b-a41c-0eb4e7d5d7fa",
            "containerReturnId": "ret_01R...",
            "source": "return",
            "amountMinor": -2500,
            "createdAt": "2025-02-12T10:02:33.000Z",
            "updatedAt": "2025-02-12T10:02:33.000Z"
          }
        ]
      },
      "notes": "Leave with cellar team",
      "createdAt": "2025-01-25T10:02:33.000Z",
      "updatedAt": "2025-01-31T09:12:00.000Z",
      "flags": {
        "due": false,
        "paid": false,
        "partial": true,
        "refunded": false
      }
    },
    "items": [
      {
        "id": "item_01H...",
        "orderId": "709f31fc-9371-4e7b-a41c-0eb4e7d5d7fa",
        "beerId": "82f6...",
        "beerName": "Lantern Pils",
        "multipackId": null,
        "multipackName": null,
        "packSize": null,
        "componentContainerId": "cont_30l",
        "componentContainerName": "30L Keg",
        "componentContainerType": "keg",
        "description": "Lantern Pils — 30L Keg",
        "quantity": 6,
        "unit": "30L Keg",
        "unitPriceMinor": 30000,
        "taxRate": 20,
        "discountType": null,
        "discountValue": null,
        "discountMinor": 0,
        "lineTotalMinor": 216000,
        "createdAt": "2025-01-25T10:02:33.000Z",
        "updatedAt": "2025-01-25T10:02:33.000Z"
      }
    ]
  }
  • Notes: Payment ledger entries are returned in chronological order and include a runningBalanceMinor column showing the net paid amount after each entry.
  • Deposits: deposits.transactions (when present) list all ledger entries associated with the order (charges, returns, adjustments). Each item exposes the signed amountMinor (returns are negative) and source (charge, return, adjustment).
  • Deposits: deposits.transactions list all ledger entries associated with the order (charges, returns, adjustments) so UIs can display how credit was earned or applied.

GET /orders/:id/payments

  • Path Parameters: id (uuid, required – the order id)
  • Response 200:
    {
      "orderId": "709f31fc-9371-4e7b-a41c-0eb4e7d5d7fa",
      "teamId": "d43c046a-10a1-4f52-bd0a-9bf16f828ab7",
      "totalMinor": 222000,
      "currencySymbol": "£",
      "summary": {
        "totalPaymentsMinor": 148000,
        "totalRefundsMinor": 0,
        "netPaidMinor": 148000,
        "runningBalanceMinor": 148000,
        "remainingMinor": 74000
      },
      "payments": [
        {
          "id": "pay_01H...",
          "amountMinor": 120000,
          "kind": "payment",
          "paymentMethod": "bank_transfer",
          "paymentReference": "INV-2025-001",
          "paidAt": "2025-01-28T11:32:00.000Z",
          "createdAt": "2025-01-28T11:32:05.000Z",
          "runningBalanceMinor": 120000
        },
        {
          "id": "pay_01I...",
          "amountMinor": 28000,
          "kind": "payment",
          "paymentMethod": "card",
          "paymentReference": null,
          "paidAt": "2025-02-03T15:09:00.000Z",
          "createdAt": "2025-02-03T15:09:04.000Z",
          "runningBalanceMinor": 148000
        }
      ],
      "deposits": {
        "balanceMinor": -2500,
        "creditMinor": 2500,
        "liabilityMinor": 0
      }
    }
  • Notes: Use this endpoint when only the ledger is required—the order summary is omitted for brevity.

POST /orders

  • Body:
    {
      "teamId": "d43c046a-10a1-4f52-bd0a-9bf16f828ab7",
      "clientId": "5e7f...",
      "status": "draft",
      "issueDate": "2025-01-25",
      "dueDate": "2025-02-08",
      "dutySuspense": false,
      "awrsNumber": "XAW000000123"
    }
  • Response 201:
    {
      "order": {
        "id": "709f31fc-9371-4e7b-a41c-0eb4e7d5d7fa",
        "teamId": "d43c046a-10a1-4f52-bd0a-9bf16f828ab7",
        "status": "draft",
        "deposits": {
          "balanceMinor": 0,
          "creditMinor": 0,
          "liabilityMinor": 0
        },
        "payments": { "summary": { "totalPaymentsMinor": 0, "totalRefundsMinor": 0, "netPaidMinor": 0, "runningBalanceMinor": 0, "remainingMinor": 0 } }
      },
      "items": []
    }
  • Notes:
    • awrsNumber is optional; when omitted the API snapshots the AWRS number stored on the client (if available). Pass null to leave the field empty even when the client carries a value.
    • Duty suspense defaults to false and may be toggled on creation to ensure compliance reports include the flag from the outset.
    • The response includes the current deposit ledger summary under order.deposits.
    • Order numbers are generated automatically. Teams with obscureOrderNumber enabled receive random codes such as ORD-20250206-1A3B; otherwise orders follow the sequential ORD-00001 format.
    • Optional group linkage fields (groupId, groupClaimTeamId, groupClaimStatus, groupEarliestDeliveryAt, groupLatestDeliveryAt) allow the order to participate in a brewery group workflow. The creating team must be an active member of the referenced group; claim teams must also be active members, and non-unclaimed statuses require a claim team. Delivery window timestamps must represent a valid range. Supplying groupId: null (or omitting the field) ignores all group metadata.

POST /orders/:id/items

  • Requires the caller to belong to the order’s team.

  • Recalculates order totals and removes cached invoice/delivery note PDFs before returning.

  • Supports adding standard beer or multipack items as well as custom charges (delivery fees, glassware, etc.).

  • Body:

    {
      "kind": "custom",
      "description": "Pallet delivery",
      "unit": "Charge",
      "quantity": 1,
      "unitPriceMinor": 2500,
      "taxRate": 20,
      "discountType": "fixed",
      "discountValue": 5
    }
    • kind (string, optional — standard or custom, default standard) — determines whether the item references stock or records a standalone charge.
    • multipackId (uuid, optional — only for standard items) — when supplied, beerId and containerId must be omitted and pricing defaults to the multipack or pricebook override.
    • beerId (uuid, optional — standard items only) — required with containerId when multipackId is absent.
    • containerId (uuid, optional — standard items only) — required alongside beerId to identify the packaged format.
    • description (string ≤ 500) — defaults to “Beer – Container” for standard items and is required for custom items.
    • unit (string ≤ 40, optional) — printed unit label (e.g. “30L Keg”, “Charge”).
    • quantity (decimal > 0) — number of packs/units added to the order.
    • unitPriceMinor (integer ≥ 0, optional — required for custom items) — price in the order’s minor currency; standard items may omit it to fall back to pricebook values.
    • taxRate (number 0–100, optional) — applied tax percentage.
    • discountType (string, optional — percentage or fixed) and discountValue (number ≥ 0, optional) — must be provided together to apply a per-line discount.
  • Response 201:

    {
      "item": {
        "id": "item_01X...",
        "orderId": "709f31fc-9371-4e7b-a41c-0eb4e7d5d7fa",
        "beerId": null,
        "multipackId": null,
        "componentContainerId": null,
        "description": "Pallet delivery",
        "quantity": 1,
        "unit": "Charge",
        "unitPriceMinor": 2500,
        "taxRate": 20,
        "discountType": "fixed",
        "discountValue": 5,
        "discountMinor": 500,
        "lineTotalMinor": 2450,
        "createdAt": "2025-02-18T10:12:33.000Z",
        "updatedAt": "2025-02-18T10:12:33.000Z"
      },
      "order": { "...": "updated order summary with recalculated totals and deposit balances" },
      "items": [
        { "id": "item_01X...", "description": "Pallet delivery", "lineTotalMinor": 2450 },
        { "...": "existing line items in ascending creation order" }
      ]
    }
  • Error Codes:

    Status Body Example When It Happens
    400 Bad Request { "message": "beerId and containerId are required when multipackId is not supplied" } Standard item missing identifiers.
    400 Bad Request { "message": "unitPriceMinor is required for custom items" } Custom item submitted without a price.
    401 Unauthorized { "message": "unauthorized" } Missing/invalid token.
    403 Forbidden { "message": "forbidden" } Caller is not on the order’s team.
    404 Not Found { "message": "Order not found" } Order does not exist or is not visible to the caller.
  • Side Effects & Notes:

    • Existing invoice and delivery note PDFs are deleted; regenerate them if paperwork is needed after the change.
    • The order totals (subtotal, tax, shipping thresholds) are recalculated in the same transaction.
    • Manual inventory adjustments run for beers flagged with manual tracking when standard items are added.

POST /orders/:id/payments

  • Body:
    {
      "amountMinor": 28000,
      "kind": "payment",
      "paymentMethod": "card",
      "paymentReference": "AUTH-9821",
      "paidAt": "2025-02-03T15:09:00.000Z",
      "depositCreditMinor": 2500
    }
  • Response 201:
    {
      "order": { "...": "order payload with updated payment ledger and deposits summary" },
      "payment": {
        "id": "pay_01I...",
        "amountMinor": 28000,
        "kind": "payment",
        "paymentMethod": "card",
        "paymentReference": "AUTH-9821",
        "paidAt": "2025-02-03T15:09:00.000Z",
        "createdAt": "2025-02-03T15:09:04.000Z",
        "runningBalanceMinor": 148000
      },
      "depositTransaction": {
        "id": "dep_01A...",
        "orderId": "709f31fc-9371-4e7b-a41c-0eb4e7d5d7fa",
        "source": "adjustment",
        "amountMinor": 2500,
        "createdAt": "2025-02-03T15:09:04.000Z",
        "updatedAt": "2025-02-03T15:09:04.000Z"
      }
    }
  • When depositCreditMinor is greater than zero, the response includes depositTransaction describing the ledger adjustment applied to consume the credit.
  • Errors:
    • 400: Attempted to record a payment against a cancelled order.
    • 401/403: User is not authenticated or not on the order’s team.
    • 404: Order not found.
  • Notes:
    • kind defaults to "payment"; set "refund" to record money returned to the customer.
    • amountMinor is always positive and expressed in the order’s minor currency unit (pence/cents).
    • Supported paymentMethod values: cash, card, bank_transfer, cheque, other.
    • depositCreditMinor applies available deposit credit (max the client’s current credit balance); a corresponding depositTransaction is returned when credit is applied.
    • When credit is applied the deposit ledger emits an adjustment row, which also appears under order.deposits.transactions on subsequent fetches.
    • The embedded order payload reflects the refreshed deposit balance for the associated client.

PATCH /orders/:id

  • Body (at least one field required):
    • status (draft | confirmed | cancelled)
    • issueDate (ISO date, nullable)
    • dueDate (ISO date, nullable — must be ≥ issueDate)
    • notes (string ≤ 5000 chars, trimmed; empty values cleared to null)
    • dutySuspense (boolean)
    • awrsNumber (string ≤ 32 characters, uppercase letters/digits/spaces/hyphens, or null to clear)
    • groupId (uuid or null — clearing the link resets claim state and delivery window metadata)
    • groupClaimTeamId (uuid or null — requires groupId and a non-unclaimed claim status)
    • groupClaimStatus (unclaimed | claimed | collating | completed — requires groupId)
    • groupEarliestDeliveryAt, groupLatestDeliveryAt (ISO datetime or null — require groupId and must form a valid range when both provided)
  • Response 200:
    {
      "order": {
        "id": "709f31fc-9371-4e7b-a41c-0eb4e7d5d7fa",
        "teamId": "d43c046a-10a1-4f52-bd0a-9bf16f828ab7",
        "clientId": "5e7f...",
        "clientName": "Lantern Taproom",
        "number": "INV-2025-001",
        "status": "confirmed",
        "dispatchStatus": "packed",
        "issueDate": "2025-01-25",
        "dueDate": "2025-02-08",
        "subtotalMinor": 185000,
        "taxTotalMinor": 37000,
        "totalMinor": 222000,
        "totalPaidMinor": 148000,
        "remainingMinor": 74000,
        "currencySymbol": "£",
        "dutySuspense": false,
        "awrsNumber": "XAW000000123",
        "clientAwrsNumber": "XAW000000123",
        "notes": "Leave with cellar team",
        "payments": {
          "summary": {
            "totalPaymentsMinor": 148000,
            "totalRefundsMinor": 0,
            "netPaidMinor": 148000,
            "runningBalanceMinor": 148000,
            "remainingMinor": 74000
          },
          "items": [
            {
              "id": "pay_01H...",
              "amountMinor": 120000,
              "kind": "payment",
              "paymentMethod": "bank_transfer",
              "paymentReference": "INV-2025-001",
              "paidAt": "2025-01-28T11:32:00.000Z",
              "createdAt": "2025-01-28T11:32:05.000Z",
              "runningBalanceMinor": 120000
            },
            {
              "id": "pay_01I...",
              "amountMinor": 28000,
              "kind": "payment",
              "paymentMethod": "card",
              "paymentReference": null,
              "paidAt": "2025-02-03T15:09:00.000Z",
              "createdAt": "2025-02-03T15:09:04.000Z",
              "runningBalanceMinor": 148000
            }
          ]
        },
        "flags": {
          "due": false,
          "paid": false,
          "partial": true,
          "refunded": false
        },
        "createdAt": "2025-01-25T10:02:33.000Z",
        "updatedAt": "2025-01-31T09:12:00.000Z"
      }
    }
  • Errors:
    • 400: dueDate before issueDate.
    • 401: Missing/invalid authentication.
    • 403: User not on the order’s team.
    • 404: Order not found.
  • Notes:
    • The returned order.deposits summary reflects the updated ledger after the patch.
    • Group metadata updates enforce the same membership and claim validation rules as creation: only active group members can link orders, claim teams must also be active members, and delivery windows are cleared automatically when the group link is removed.

PATCH /orders/:id/dispatch

  • Body:
    • dispatchStatus (pending | packed | dispatched | in_transit | delivered)
  • Response 200: Same payload shape as PATCH /orders/:id, reflecting the updated dispatchStatus.
  • Notes:
    • Only team members with access to the order may update dispatch state.
    • This endpoint does not modify delivery note timestamps; use delivery note APIs to do so when available.

POST /orders/:id/group/claim

  • Claims group shipping coordination for a team that is an active member of the order’s linked brewery group.
  • Body:
    • teamId (uuid, required) — the member brewery taking responsibility.
  • Requirements:
    • Caller must belong to the claiming teamId.
    • The order must already be linked to a brewery group and remain unclaimed.
    • The claiming team must be an active member of the same group; retired groups cannot be claimed.
  • Response 200: { "order": { ... } } — full order payload (identical structure to GET /orders/:id) with updated groupClaimTeamId, groupClaimStatus (collating), and claim metadata.
  • Errors:
    • 401 unauthenticated.
    • 403 caller not on the claimant team.
    • 404 order or group not found.
    • 409 when the order is already claimed or the group is retired.

POST /orders/:id/group/transfer

  • Transfers the hub/co-ordination role from the current claimant to another active member before dispatch.
  • Body:
    • teamId (uuid, required) — the new member brewery that will coordinate shipping.
  • Requirements:
    • Caller must belong to the current claim team.
    • The order must still be in a mutable dispatch state (pending, packed).
    • Target team must be an active member of the same group and different from the current claimant.
  • Response 200: { "order": { ... } } — updated order payload with the new groupClaimTeamId and groupClaimStatus (collating).
  • Errors:
    • 401 unauthenticated.
    • 403 caller not on the current claim team.
    • 404 order not found.
    • 409 when the order is unclaimed, already dispatched, or claim data changed during transfer.

POST /orders/:id/group/release

  • Clears the current claim and returns an order to the shared queue.
  • Body: none (the caller’s team is inferred from authentication).
  • Requirements:
    • Caller must belong to the current claim team.
    • The order must remain in a mutable dispatch state (pending, packed); once dispatch starts the claim cannot be released this way.
  • Response 200: { "order": { ... } } — the order payload with groupClaimTeamId: null and groupClaimStatus: "unclaimed".
  • Errors:
    • 401 unauthenticated.
    • 403 caller not on the claim team.
    • 404 order not found.
    • 409 when the order is already unclaimed, the group is retired, dispatch has started, or claim data changed mid-release.
⚠️ **GitHub.com Fallback** ⚠️