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

# Planning Models

> Configure the reusable planning model: horizon, resources, BOMs, variants, tags, and objective weights.

## Overview

A **planning model** is the reusable setup the optimizer plans against. It defines the horizon (how far out, and in what interval size), the resources production consumes, the [BOMs](/product/bom-overview) that describe how things are made, the demand-order and inventory-goal categories the plan will honor, and the weights that trade off the two competing objectives: hitting demand on time versus hitting inventory targets.

One planning model is reused across many [planning runs](/product/planning-runs). The model is the template; each run supplies the run-specific inputs — demand, supply, on-hand inventory, resource capacities — described in [Demand and Supply](/product/demand-and-supply). For how the pieces fit together, start with the [Planning Overview](/product/planning-overview).

<Note>
  You author a planning model in the planning UI, or you [ask Dexter](/product/dexter/chat-and-tasks) to build and revise it for you.
</Note>

## The Two Objectives

The optimizer balances two competing goals: **hitting demand on time** and **holding inventory at its targets**. You decide how much each one matters with a single **balance setting**.

* **All the way toward demand** — the plan ignores inventory targets to hit due dates.
* **All the way toward inventory** — the plan lets demand slip to hold inventory at goal.
* **Balanced** — the default, weighing the two evenly.

There is no third goal for resource utilization. **Resource capacity is a hard limit**, never traded off. If the plan cannot fit inside the capacity you give it, the run is infeasible — you either add capacity or slacken deadlines.

<Tip>
  Set the balance from results, not from theory: if plans chronically miss due dates to protect stock levels, shift it toward demand; if they burn safety stock to hit marginal orders, shift it toward inventory. Small adjustments shift behavior meaningfully.
</Tip>

### Time Decay

Both goals are discounted the further into the future they fall, so near-term commitments count for more than distant ones. Two separate decay settings control this — one for demand lateness, one for inventory:

| Setting                  | Controls                                                               |
| ------------------------ | ---------------------------------------------------------------------- |
| **Demand time decay**    | How fast the penalty for a late order fades for orders due further out |
| **Inventory time decay** | How fast an inventory miss matters less the further out it falls       |

By default both apply a gentle decay. Flatten a decay to weight every interval equally, or steepen it so that only the near term carries weight.

## Horizon

The horizon is a count of intervals plus an interval unit:

* **Number of intervals** — how many intervals the plan covers.
* **Interval unit** — the size of one interval. Typical values: `day`, `week`, `month`.

The interval count is exactly that — a count — so the horizon end is exclusive. A 4-interval weekly horizon starting `2026-06-01` covers `2026-06-01` through `2026-06-28`, with interval indexes `0` through `3`.

## Resources

Resources are the constrained capacities production consumes — production lines, machines, labor pools, tanks, ovens. Each resource has a name and a unit (for example `hours` or `pieces`).

Resources are declared on the model, but their **per-interval capacity ceilings live on each run**, not on the model — the same model can be run against a light week or a peak week without redefinition. See [resource capacity](/product/demand-and-supply#resource-capacity) for how run-level capacity works.

The model also carries a **bill of resources** — the mapping from a BOM to the resources its production consumes and how much of each one unit of output requires. A single BOM can carry more than one bill-of-resources entry: each is an alternative production route (say, the same output made on a fast line or a slow one), and the plan is free to split production across them. These requirements live with the planning model, not on the BOM itself, so the same BOM can carry different resource usage under different models. A BOM with no bill-of-resources entry is unconstrained by capacity.

## BOMs

The model carries an explicit list of the BOMs the optimizer sees. **A BOM not attached to the model is invisible to the optimizer**, even if it exists in the factory. This lets one factory carry several planning models with different scopes without cross-contamination.

### Material Classification

Every attached BOM is classified automatically from its position in the BOM graph:

| Type             | Definition                                          | Where it appears in run inputs                                                                                                                   |
| ---------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| **RAW**          | BOM with no components (a leaf)                     | [Supply orders](/product/demand-and-supply#supply-orders)                                                                                        |
| **INTERMEDIATE** | Has components AND is consumed by another BOM       | Produced internally by the plan — never a demand or supply order, though it can be seeded via on-hand inventory or targeted by an inventory goal |
| **SKU**          | Has components AND is not consumed by any other BOM | [Demand orders](/product/demand-and-supply#demand-orders)                                                                                        |

Classification follows from the graph — you do not set it by hand.

<Note>
  The right-hand column is the *order* slot each type uses — supply for raws, demand for SKUs. On-hand inventory and inventory goals are separate: both accept any material, whatever its classification, so an intermediate can still show up in a run's inputs there.
</Note>

<Warning>
  A BOM with an empty component list is classified **RAW** regardless of intent. This is the most common reason a finished good gets rejected as a demand line: its BOM has no components yet, so the optimizer sees it as a raw material.
</Warning>

## Variants

Variants let one entity carry many recipes. A BOM can attach a **variant condition** — a set of attribute rules, all of which must hold — and the optimizer routes a demand line to a BOM when the BOM's condition is a **subset** of the demand's variant. A condition can test a yes/no flag, a value drawn from a fixed set of text options or numbers, or a numeric range.

A BOM with no condition is the **default recipe** and matches any order for that entity. See [BOMs](/product/bom-overview) for the full variant-condition syntax.

<Warning>
  When several BOMs exist for the same entity, their variant conditions must be either **identical** or **mutually exclusive** — never partially overlapping. Identical conditions make the BOMs alternative recipes for the same configuration, and the plan may split production across them; mutually exclusive conditions each own a distinct configuration. A condition that overlaps another without matching it exactly is ambiguous and fails [validation](/product/planning-runs#validating-inputs).
</Warning>

## Order Tags

An **order tag** classifies demand orders and carries the objective parameters for that class. Every demand order references one tag.

| Field             | What it does                                                                                                                                                                                                                                                                                                                                        |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Deadline type** | `soft` — missing the due date costs a penalty. `hard` — missing the due date makes the run infeasible.                                                                                                                                                                                                                                              |
| **Weight**        | Integer multiplier on the lateness penalty for orders in this class. Higher = more important.                                                                                                                                                                                                                                                       |
| **Buffer**        | Pulls the effective due date **earlier**. Finish by the due date minus the buffer and the order pays no penalty; miss that earlier date and the penalty starts to grow.                                                                                                                                                                             |
| **Penalty shape** | How fast the penalty grows as an order slips further past its date. It can climb at a steady rate (each interval late costs the same), rise steeply at first and then flatten, or start gently and then bite hard. The gentle-then-hard shape is **only allowed on hard-deadline tags**, where the hard cutoff keeps the penalty from running away. |

Note that the buffer is a *pre-deadline* margin, not post-deadline slack: it moves the zero-penalty point **earlier** — to the due date minus the buffer — so it pressures the optimizer to finish early, useful for orders that need packing or QA time between production and shipment. On a hard-deadline tag, the same buffer also tightens the hard constraint: the order must be fulfilled by the due date minus the buffer, not merely by the due date.

For a **soft** deadline, leaving an order entirely unfulfilled is not free — its cost equals the lateness penalty evaluated at the end of the horizon (the maximum lateness). The optimizer therefore weighs "ship it late" against "don't ship it at all" rather than treating non-fulfillment as costless.

## Inventory Tags

An **inventory tag** classifies inventory goals. It is simpler than an order tag — just a type and a weight.

| Field        | What it does                                                                                                                                                                                                                                                                                                                                                          |
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Tag type** | **Rolling** — the goal is a target the plan can sit above or below, with loss growing as the miss grows. **Non-rolling** — at each interval the goal covers, the entity's inventory resets to zero at the start of the interval (nothing carries in from the interval before) and no penalty is charged there. Use it for spoilage-bounded targets or one-off builds. |
| **Weight**   | Multiplier on inventory loss for goals in this class.                                                                                                                                                                                                                                                                                                                 |

An entity with no inventory goal behaves as **rolling** with zero weight — the optimizer holds whatever inventory the demand plan implies.

## Custom Constraints

A planning model can carry optional **custom constraints** on top of the standard structure — for example minimum production quantities per interval, maximum daily output for a specific SKU, or sequencing rules. It can also carry custom **objective terms** — extra goals folded into what the optimizer balances, beyond the two built-in ones. Both are authored per model and apply to every run against it.

<Note>
  So a model extends in two ways: custom **constraints** (hard rules the plan must obey) and custom **objective terms** (added goals such as "minimize cost" or "smooth production" that the optimizer weighs alongside demand and inventory). See [Custom Constraints & Objectives](/product/planning-custom-constraints) for what you can express.
</Note>
