Core concepts

All of these are implemented as Anyblok Models and are provided by wms-core.

There are two classes of concepts in wms-core: Physical Objects and Operation.


New in version 0.8.0.

In Anyblok / WMS, there is no separate concept of location, i.e, a Model that would represent where the goods are, were or will be. Rather, racks, shelves, carts and even warehouses are just special cases of physical objects. See Containers and locations for more details.

Containers and locations

New in version 0.8.0.

Of course, in any stocks and logistics application, the question where the goods are is a central and crucial one. In Wms Base, that is fulfilled by saying that Physical Objects can themselves contain other ones.

In other words, what one would think of as a location is nothing but a special case of Physical Object. We call them informally containers, because “location” without more context may be understood as something necessarily fixed, or even as coordinates.

In many cases, containers will indeed be fixed (warehouses, alleys, shelves), yet moving containers (boats, trucks, trolleys or even carrying boxes) are also interesting cases.

Technically, containers are characterized by the fact that their Types has the container behaviour. This behaviour can be itself refined by applications, for instance to specify what exactly a given container can hold.

Like any other Physical Object, containers can have Avatars, meaning that they can themselves be inside a bigger container (at some point in time). Anyblok / Wms Base provides quantity queries that are able to recurse through this, optionally at a given point in time.


topmost containers must be created by the dedicated helper method.

Other containers can be created at their own locations by Operations such as Arrival or Apparition, like any Physical Objects.

The fact that there is no strong distinction between goods and their containers may seem surprising for some developers, but it has lots of interesting benefits:

  • containers can be moved in a way that the system is able to track and take into account, e.g, in the quantity queries, whereas with a separate model, we’d probably have a parent field, of which any change of value would impact all times, present, future and even past.
  • containers are automatically typed and have properties, which can be used to encode various functional aspects.
  • containers can be received (after all, warehouse hardware is also purchased and delivered), shipped as a whole, broken, disappear, etc.



This is an overview, see the code documentation for more details.

In Anyblok / WMS Base, what happens to the Physical Objects is represented by the core concept of Operation. This start with creating Operations, such as Arrival and ends with removing Operations, such as Departure.

In principle, end applications should act upon Physical Objects through Operations only.

Operations are polymorphic Models, which means that as Python classes, they inherit from the base Operation class, while they are persisted as two tables in the database: wms_operation for the common data and a specific one, such as wms_operation_arrival.

In general, Operations take Avatars as inputs, but that can be an empty set for some (creation Operations, such as Arrival), and many Operations work just on one Avatar. Conversely, most Operations have resulting Avatars, which we call their outcomes.


That Operations see Physical Objects through their Avatars doesn’t imply they have no effect on the underlying PhysObj records, quite the contrary. In fact, most of PhysObj modifications should occur through Operations.

Operations are linked together in logical order, forming a Directed Acyclic Graph (DAG) that, together with the links between Operations and Avatars, records all operational history, even for planned operations (we may therefore jokingly speak of “history of the future”).

Thanks to this data structure, Operations can be cancelled, reverted and more (see History leveraging).

Lifecycle of operations

Operations start their lifecycle with the create() classmethod, which calls insert() internally. The initial value of state must be passed to create()


downstream libraries and applications should never call insert() nor update the state field directly, except for bug reproduction and automated testing scenarios.

Here are the detailed semantics of Operation states, and their interactions with create() and execute()

  • planned:

    this means that the operation is considered for the future. Upon creation in this state, the system will already create the necessary objects (in particular, Avatars and other Operation records), with appropriate states so that the whole system view is consistent for the present time as well as future times.

    Planned Operations can be either executed or cancelled.

  • started:


    this value is already defined but it is for now totally ignored in the implementation. This part can be therefore considered to be design notes.

    In reality, operations are never atomic, and often cannot be cancelled any more once started.

    In this state, outcomes of the operation are not already there, but the operation cannot be cancelled. The Physical Objects and their Avatars being acted upon should be locked to represent that they are actually not available any more.

    It would be probably too expensive to systematically use this state, therefore, it should be used only if the real life operation takes a really long time to conclude.


    • longer distance moves. If this is really frequent, you can also consider splitting them in two steps, e.g, moving to a location representing some kind of vehicle (even if it is a cart), then moving from the vehicle to the final location. This can be more consistent and explicit than having thousands of Physical Objects, whose present Avatars are still attached to their original locations, but hard locked to represent that they aren’t there any more.
    • unpacking or manufacturing operations. Here also, you can reduce the usage by representing unpacking or manufacturing areas as locations and moving the relevant Physical Objects to them. A reserver for deliveries could then simply ignore what’s inside these locations if their presence there are due to Moves instead of Unpacks or Assemblies.
  • done:

    The execute() method brings a planned Operation in this state, provided the needed conditions are met.

    Also, Operations can be created already in their done state, usually after the real-life fact happened or simultaneously (for a good enough definition of simultaneity), provided the needed conditions are met.

    In this case, the consequences are enforced by the create() method directly.


    Typically, creating directly in the done state is less expensive that creating in the planned state, followed by a call to execute()

History leveraging

The base Operation model provides a few recursive facilities based on the operational history and working on it.

Planned operations can be cancelled, this is provided by the cancel() method. Canceling an Operation removes it, its outcomes and all the dependent operations from the future history.

Operations that have already been done may be reverted: the plan_revert() will issue a bunch of new planned Operations to bring back the Physical Objects and their Avatars as they were before execution (and planning). These new Operations will take place in real life, and as such, will take time, can go wrong etc. Some Operations are always reversible, some never are, and for some, it depends on conditions.

It is possible to completely forget about an Operation, to express that it never happened in reality, despite what the data says. This is again a recursion over the dependents, and is provided by the obliviate() method

More sophisticated history manipulation primitives are being currently thought of, see Superseding of planned operations.



This is an overview, see the code documentation for more details.

Arrivals represent the physical arrival of something that wasn’t previously tracked in the application, in some Containers and locations.

This does not encompass all “creations” of Physical Objects with Avatars, but only those that come in real life from the outside. They would typically be grouped in a concept of Incoming Shipment, but that is left to applications.

Arrivals initialise the properties of their outcomes. Therefore, they carry detailed information about the expected objects, and this can be used in validation scenarios.

Arrivals are irreversible in the sense of History leveraging.



This is an overview, see the code documentation for more details.

Departure represent Physical Objects leaving the system.

Like Arrivals, don’t mean to encompass all “removals” of Physical Objects, but only those that leave the facilities represented in the system.

They would typically be grouped in a concept of Outgoing Shipment, but that is left to applications.

Departures are irreversible in the sense of History leveraging.


New in version 0.8.0.


see the code documentation for more details.

Apparitions are similar to Arrivals in that they create previously untracked Physical Objects, but they are meant to be used in inventory assessments: they represent the fact that some Physical Objects have been discovered, with no known explanation.

In concrete applications, Apparitions would typically be optionally tied to some higher level Inventory Model that would be backing some user interface while grouping and maybe creating them. Anyblok / Wms Base 0.8 does not provide such Inventories, but there are plans to include them in a new optional Blok.

Apparitions are always in the done state, as other states don’t make sense in their case. In other words, only direct creations in the done state are allowed.

Apparitions are irreversible in the sense of History leveraging.


New in version 0.8.0.


see the code documentation for more details.

Disparitions are inventory Operations that record that the goods are missing, for no known reason. In other words, they are to Departures what Apparitions are to Arrivals:

  • they cannot be planned nor started; only direct creations in the done state are allowed.
  • they are irreversible.
  • they should be tied in applications to higher level Inventory objects.

Same as for Departure, the effect of a Disparition is not to erase the The PhysObj Model: Physical Objects, but only to put the given Avatar in the past state.



This is an overview, see the code documentation for more details.

Moves represent goods being carried over from one place to another, with no other change. They are always reversible in the sense of History leveraging.



This is an overview, see the code documentation for more details.

Teleportations are inventory Operations that record that the goods are not missing, but changed places, for no known reason. In other words, they are to Moves what Apparitions are to Arrivals:

  • they cannot be planned nor started; only direct creations in the done state are allowed.
  • they are irreversible.
  • they should be tied in applications to higher level Inventory objects.

Apart from that, their have the same effect as Moves.



This is an overview, see the code documentation for more details.

Observations are the preferred way to alter the Properties of Physical Objects and give it a timely feeling.

The idea is to represent that at some point in time and space, something has been measured or assessed about the object, such as physical measurements (weight etc.) or conditions (quality control etc.).



This is an overview, see the code documentation for more details.

Unpacks replace some Physical Objects (let’s call them “packs”) with their contents. The Properties of the packs can be partially or fully carried over to the outcomes of the Unpack.

The outcomes of an Unpack and its handling of properties are entirely specified by the unpack behaviour of the Type of the packs, and in the packs properties. They can be entirely fixed by the behaviour, be entirely dependent on the specific packs being considered or a bit of both. See the documentation of this method for a full discussion with concrete use cases.

Unpacks can be reverted by an Assembly of the proper name (by default, 'pack'), provided that no extra input Physical Objects are to be consumed by the Assembly, in other words that either:

  • the wrapping is not been tracked in the system
  • the wrapping is tracked, is among the outcomes of the Unpack and can be reused.


New in version 0.7.0.


This is an overview, see the code documentation for more details, and especially specification

Packing and simple manufacturing needs are covered by the Assembly Operations : several inputs are consumed to produce a single outcome. More general manufacturing cases fall out of the scope of the wms-core Blok.

Assemblies have an outcome Type, and a name, so that a given Type can be assembled in different ways.

As an edge case, Assemblies can have a single input, how weird that may sound, and are, in fact, the preferred way to alter some PhysObj record to produce a new one with new or different Properties, whether the Type has changed or not. Use case: one may wish to consider that cutting the edges of a piece of timber makes it different enough that it must be considered a new PhysObj record.

Assemblies are governed by a flexible specification, which is built from the assembly behaviour of the outcome Type and from their optional parameters field. This specification includes:

  • how to build Properties on the outcome, depending on the state been reached. For example, it is possible to use a Model.System.Sequence to build up a serial number once the Assembly reaches the started state. It’s also possible to forward Properties from one or several inputs to the outcome.

  • expected inputs, with various required Properties depending on the state been reached. Variable inputs are also supported (must be explicitely turned on).

    These inputs rules are useful for checking purposes and to perform selective forwarding of Properties to the outcome. The result been stored in the match field, it can be used as a support for end user display and machine control if needed.

  • special rules for the contents Property which is used by Unpack to describe the variable part of its outcomes.

Assemblies have also programmatic hooks for applications to implement more complex cases (at the time of this writing, only for the build of outcome Properties).

Assemblies can be reverted by Unpacks, if the outcome Type supports them. If appropriate, it’s possible to tune the Assembly so that a later Unpack reuses the input PhysObj records, to underline that they are actually unchanged.