> For the complete documentation index, see [llms.txt](https://docs.akinon.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.akinon.com/technical-guides/oms/fulfillment/package-state-transition-shipment.md).

# Package State Transition Shipment

## <mark style="color:red;">Trigger chain</mark>

A shipment is created when the `PKG_STATE_TRANSITION` command runs on a package and "create shipment" is enabled in the transition's settings. This setting is **disabled** by default.

**1. The request arrives**

* The store app, ERP, or Omnitron interface sends a command request for the package.
* The request specifies which transition to run (transition ID or slug) and the transition's parameters.
* OMS finds the command the transition is bound to: `PKG_STATE_TRANSITION`.

**2. Pre-checks run**

* Are the conditions defined on the transition met? (e.g., "click & collect orders only")
* If barcode or unit weight validation is configured, the submitted values are validated.
* The package is locked so that no other process can modify it at the same time.
* Does the package's current state match the transition's source state?
* Is there an active transfer linked to the package? (`waiting`, `in_transit`, `ready_to_shipment`, `out_of_stock`) If so, the command returns an error.

**3. The package moves forward**

* The package moves to the destination state: `preparing` or `waiting_remote_action`.
* If the destination is `preparing`, the items in the package also move to `preparing`.
* Submitted barcodes and unit weights are saved (if the related setting is enabled and validation passed).

**4. The shipment is created** ◄ *critical step*

* Runs only if "create shipment" is enabled on the transition.
* The shipment and cargo label are created using the order's tracking number.
* If the setting is disabled, the package still moves to the destination state, but no shipment is **created**.

**5. Omnitron is updated**

* If the destination state is `preparing` and "send status to Omnitron" is enabled, the order items in Omnitron are updated to `preparing`.
* For a `waiting_remote_action` transition, nothing is sent to Omnitron even if this setting is enabled.

**6. Final check**

* OMS verifies that the package is actually in the destination state.

All of these steps run as a single transaction: if any step fails, no changes are saved.

### <mark style="color:red;">How the shipment is created</mark>

1. If the package already has a shipment, a new one is **not created**; only the label is completed if it is missing.
2. The cargo company is selected:
   1. the cargo company of the first order item in the package,
   2. otherwise, the order's cargo company,
   3. if that is also missing or **inactive**, the first active cargo company in the system.
3. The cargo company is called:
   * If the order **already has a tracking number** (the shipment was opened outside OMS, e.g., in Omnitron or on the sales channel side), no new shipment is opened with the cargo company. The shipment is created with this number and marked as "registered externally". Since there is no label, only the label is requested from the cargo company.
   * If there is **no tracking number**, a shipment is opened with the cargo company. When selecting the cargo company's integration, a different integration/setting may be used if rules are defined by sales channel, click & collect package, or transfer. The returned tracking number and query number are always saved. The label and return barcode are saved only if the cargo company also returned the label link; if it did not, the label is requested separately in the next step.
4. The shipment is created in the "waiting for pickup by the cargo company" (`waiting_for_pick_up`) state. After the operation completes successfully, a "shipment created" notification (webhook) is sent to external systems.
5. If there is still no label, the label is requested from the cargo company. If the cargo company does not return a label, no error is raised; the shipment remains without a label.

{% hint style="info" %}
The shipment is created **after** the package moves to the destination state. However, since the entire command runs as a single transaction, if the cargo company returns an error, everything is rolled back: the package does not move to `preparing` and no shipment is created. The response the client receives depends on the error type:

* Errors returned by the cargo company and OMS business rule errors → `406` with an error code. This is the usual case.
* Invalid submitted parameters → `400`.
* Unexpected errors (e.g., a cargo company response timeout or unexpected data in the integration) → most likely `500`.
* If the cargo company cannot be reached at all, no error is raised and an empty response is received; the outcome depends on how the relevant cargo integration handles this case.
  {% endhint %}

### <mark style="color:red;">Which transitions create a shipment</mark>

| Command                                                  | Typical transition                                                                       | `create_shipment` default                                                                                                                             |
| -------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PKG_STATE_TRANSITION`                                   | `waiting` / `accepted` / `waiting_remote_action` → `preparing` / `waiting_remote_action` | **`false`** — must be explicitly set to `true`                                                                                                        |
| `COMPLETE_PACKAGING`, `CHANNEL_BASED_COMPLETE_PACKAGING` | → `packed` / `waiting_cargo_label`                                                       | `true`                                                                                                                                                |
| `SHIP_THE_PACKAGE_ADVANCED`                              | `packed` → `shipped` / `waiting_cargo_label`                                             | `true`                                                                                                                                                |
| `SHIP_THE_PACKAGE_BASIC`                                 | `packed` → `shipped`                                                                     | Ignores the config and always creates one; the cargo API is not called, and a shipment without a label is created with an AWB number generated by OMS |
| `RECREATE_SHIPMENT_OF_THE_PACKAGE`                       | `packed` / `shipped` / `delivery_failed` / `attempted_delivery` (state does not change)  | Ignores the config and recreates the shipment                                                                                                         |
| `COMPLETE_PACKAGING_WITHOUT_SHIPMENT`                    | → `packed`                                                                               | Does not create a shipment                                                                                                                            |

Only `PKG_STATE_TRANSITION` and `CHANNEL_BASED_COMPLETE_PACKAGING` pass `order.tracking_number` to shipment creation. `COMPLETE_PACKAGING` does not pass a tracking number, and `SHIP_THE_PACKAGE_ADVANCED` passes the `tracking_number` parameter from the request; for these two commands, the cargo API is called even if the order already has a tracking number.

In other words, without configuration, the shipment is created at the packaging (`COMPLETE_PACKAGING`) step at the latest. If the shipment should be created **when preparation starts** (`preparing`), `create_shipment: true` must be added to the `PKG_STATE_TRANSITION` transition.

### <mark style="color:red;">Configuration keys related to</mark> <mark style="color:red;">`PKG_STATE_TRANSITION`</mark>

Read from `StateTransition.configuration` (JSON):

| Key                        | Default (for this command) | Effect                                                                                                                                                   |
| -------------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `create_shipment`          | `false`                    | If `true`, a shipment and label are created during the transition                                                                                        |
| `sync_remote_order_status` | `false`                    | If `true` and the destination state is `preparing`, Omnitron order items are updated to `preparing`                                                      |
| `barcode_validation`       | none                       | Validates the `barcodes` parameter using `look_ups` (product fields) and `enforce_unique_selected_barcode`; if the key is absent, barcodes are not saved |
| `save_unit_weight`         | `false`                    | If `true`, the `unit_weights` parameter is validated and saved                                                                                           |

### <mark style="color:red;">How to enable / disable</mark> <mark style="color:red;">`create_shipment`</mark>

The setting is stored in the `configuration` field of the state transition (`StateTransition`) record the command runs on. The command reads this field every time it runs, so updating the relevant transition record is enough to change the setting; it is not defined anywhere else. If the key is missing, `PKG_STATE_TRANSITION` treats it as `false`.

It can be changed in two ways:

* **Omnitron interface:** Open the relevant `PKG_STATE_TRANSITION` transition on the OMS state transitions screen and enter `{"create_shipment": true}` in the configuration field.
* **API:** Follow the steps below.

In the examples, replace `<OMS_HOST>` with the environment's address (OMS is served under the Omnitron host at the `/api/v1/oms/` path) and `<TOKEN>` with an Omnitron user token.

**1. Find the transition**

```bash
curl -s "https://<OMS_HOST>/api/v1/oms/state-transitions-manage/?command=PKG_STATE_TRANSITION" \
  -H "Authorization: Token <TOKEN>"
```

Find the `id` of the transition the store uses in the results. If the list is long, you can narrow it down by slug: `&slug=preparing`.

**2. Read the current setting**

```bash
curl -s "https://<OMS_HOST>/api/v1/oms/state-transitions-manage/42/" \
  -H "Authorization: Token <TOKEN>"
```

Sample response (abbreviated):

```json
{
  "id": 42,
  "label": "Start Preparing",
  "command": "PKG_STATE_TRANSITION",
  "slug": "package-pkg_state_transition-waiting-preparing",
  "source_state": 1,
  "destination_state": 4,
  "configuration": {
    "sync_remote_order_status": true
  }
}
```

**3. Enable `create_shipment`**

Add `create_shipment` while keeping the existing keys in `configuration`:

```bash
curl -s -X PATCH "https://<OMS_HOST>/api/v1/oms/state-transitions-manage/42/" \
  -H "Authorization: Token <TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"configuration": {"sync_remote_order_status": true, "create_shipment": true}}'
```

**4. To disable it**

Send the same request with `"create_shipment": false`, or remove the key from the JSON:

```bash
curl -s -X PATCH "https://<OMS_HOST>/api/v1/oms/state-transitions-manage/42/" \
  -H "Authorization: Token <TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"configuration": {"sync_remote_order_status": true, "create_shipment": false}}'
```

{% hint style="warning" %}

* PATCH does **not merge** `configuration` with the existing value; it **replaces it entirely**. If only `{"create_shipment": true}` is sent, the other keys on the transition (e.g., `sync_remote_order_status`, `barcode_validation`) are deleted. Always read the current value first using step 2.
* The value is not validated; send `true` / `false` as booleans (a string such as `"true"` is also treated as "enabled").
* The change is written to the audit log. Because the package's list of available transitions is cached for 60 seconds, it may take up to a minute for the change to appear on screens.
* The `42`, slug, and state IDs in the examples are illustrative; actual values vary by environment.
  {% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.akinon.com/technical-guides/oms/fulfillment/package-state-transition-shipment.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
