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

# Allotments

> The allotments module is Voyant's shared held-inventory contract: the allotment state machine, per-slot counter math, slot enumeration, and pickup ledger status reused by every block type.

The allotments module is the canonical lifecycle for held inventory. An allotment is a negotiated block of capacity that an operator holds against a supplier or its own inventory, then draws down over time as travelers book against it. The same lifecycle applies whether the held unit is a hotel room night, a square metre of function space, or a coach seat. Only the unit and the type-specific tables differ.

This package owns the shared contract, not a polymorphic table. It ships the state machine, the counter math, the slot enumeration, and the pickup ledger status, and each consumer keeps its own thin transactional service that maintains its counters in the same transaction as each ledger write. It ships as `@voyant-travel/allotments`.

```bash theme={null}
pnpm add @voyant-travel/allotments
```

The package is pure logic. It has no database or Drizzle dependency, no routes, and no schema of its own. It defines no TypeID prefixes. The type-specific tables live in the consuming modules (`room_block_nights` and `room_block_pickups` in `@voyant-travel/accommodations`, space blocks in `@voyant-travel/operations`).

## Key concepts

The contract rests on a clear separation between the block header lifecycle, which tracks negotiation, and the pickup counters, which track draw-down.

* **Allotment.** A held block of capacity with an option, cutoff, and release lifecycle plus live pickup counters.
* **Block header status.** The negotiation lifecycle of the block, not its pickup progress. The stages are `inquiry`, `held`, `confirmed`, `released`, `cancelled`, and `expired`, exposed as `ALLOTMENT_STATUSES`.
* **Closed statuses.** `released`, `cancelled`, and `expired` can no longer accrue pickups, exposed as `CLOSED_ALLOTMENT_STATUSES` and tested with `isClosedAllotmentStatus(status)`.
* **Pickup ledger.** An append-only record of draw-downs. A pickup is `active` or `reversed`, exposed as `ALLOTMENT_PICKUP_STATUSES`. The ledger is compensated by reversal, never deleted.
* **Counters.** Per-slot `held`, `pickedUp`, and `released` numbers. Remaining capacity is `held − pickedUp − released`.
* **Slots.** One date is one allotment slot, such as a hotel night or a space-day. A stay occupies each date from `start` (inclusive) to `end` (exclusive).

<Note>
  Pickup progress is derived from the counters at read time, never stored. `allotmentPickupProgress(counters)` returns `none`, `partial`, or `full` so the projection cannot drift from the ledger.
</Note>

## The state model

A block header moves through the negotiation lifecycle: an `inquiry` becomes `held` (an option deadline against the supplier), then `confirmed` once the operator commits. From there the block can be `released` (capacity handed back), `cancelled`, or `expired` (a cutoff passed). The three closed statuses are terminal for pickup accrual.

```mermaid theme={null}
stateDiagram-v2
    [*] --> inquiry
    inquiry --> held
    held --> confirmed
    inquiry --> cancelled
    held --> released
    held --> cancelled
    held --> expired
    confirmed --> released
    confirmed --> cancelled
    confirmed --> expired
    state "closed (no further pickups)" as closed
    released --> closed
    cancelled --> closed
    expired --> closed
    closed --> [*]
```

Independently of the header status, each slot tracks its own counters. Travelers booking against the block record a pickup, incrementing `pickedUp`; capacity handed back increments `released`. Remaining capacity falls out of the math:

```ts theme={null}
import {
  allotmentRemaining,
  allotmentPickupProgress,
} from "@voyant-travel/allotments"

const counters = { held: 10, pickedUp: 4, released: 1 }

allotmentRemaining(counters) // 5
allotmentPickupProgress(counters) // "partial"
```

Consumers guard the counter integrity with table CHECK constraints and row locks, and treat the maintained counters as a projection of the append-only pickup ledger. The allotments package supplies the math; the transaction discipline lives in each consumer.

## Working with it

Enumerate the slots a stay or hold occupies. Dates are `YYYY-MM-DD` and parse as UTC to avoid timezone drift across the day boundary:

```ts theme={null}
import { eachDateInRange } from "@voyant-travel/allotments"

eachDateInRange("2026-07-01", "2026-07-04")
// ["2026-07-01", "2026-07-02", "2026-07-03"]
```

The end date is exclusive, so a three-night stay yields three slots. An invalid or non-positive range returns `[]`.

Check whether a block can still accept pickups before recording one:

```ts theme={null}
import { isClosedAllotmentStatus } from "@voyant-travel/allotments"

if (isClosedAllotmentStatus(block.status)) {
  // released, cancelled, or expired, so refuse the pickup
}
```

## Links to other modules

* **Accommodations.** Room blocks are an allotment. The room-block service in `@voyant-travel/accommodations` owns the `room_block_nights` and `room_block_pickups` tables and maintains its per-night counters using `allotmentRemaining`, `allotmentPickupProgress`, `eachDateInRange`, and `isClosedAllotmentStatus`. See [Accommodations](/docs/platform/modules/accommodations).
* **Operations.** Space blocks reuse the same contract for function-space allotments. See [Operations](/docs/platform/modules/operations).
* **Bookings.** A pickup is recorded against a booking item, so the draw-down counters move as bookings are placed and reversed. See [Bookings](/docs/platform/modules/bookings).

## Next steps

<CardGroup cols={2}>
  <Card title="Accommodations" icon="bed" href="/docs/platform/modules/accommodations">
    Room blocks, the reference allotment consumer with the night-level ledger.
  </Card>

  <Card title="Operations" icon="map" href="/docs/platform/modules/operations">
    Space blocks and the shared availability spine.
  </Card>

  <Card title="Bookings" icon="ticket" href="/docs/platform/modules/bookings">
    The booking items that draw pickups down against a block.
  </Card>

  <Card title="Glossary" icon="book-open" href="/docs/concepts/glossary">
    The held-inventory vocabulary behind allotments and pickups.
  </Card>
</CardGroup>
