core.goods

Model.Wms.Goods

class anyblok_wms_base.core.goods.goods.Goods[source]

Main data type to represent physical objects managed by the system.

The instances of this model are also the ultimate representation of the Goods “staying the same” or “becoming different” under the Operations, which is, ultimately, a subjective decision that has to be left to downstream libraires and applications, or even end users.

For instance, everybody agrees that moving something around does not make it different. Therefore, the Move Operation uses the same Goods record in its outcome as in its input. On the other hand, changing a property could be considered enough an alteration of the physical object to consider it different, or not (think of recording some measurement that had not be done earlier.)

Fields and their semantics

id = <anyblok.column.Integer object>

Primary key.

type = <anyblok.relationship.Many2One object>

The Goods Type

properties = <anyblok.relationship.Many2One object>

Link to Properties.

See also

Goods Properties for functional aspects.

Warning

don’t ever mutate the contents of properties directly, unless what you want is precisely to affect all the Goods records that use them directly.

Besides their type and the fields meant for the Wms Base bloks logic, the Goods Model bears flexible data, called properties, that are to be manipulated as key/value pairs through the get_property() and set_property() methods.

As far as wms_core is concerned, values of properties can be of any type, yet downstream applications and libraries can choose to make them direct fields of the Properties model.

Properties can be shared among several Goods records, for efficiency. The set_property() implements the necessary Copy-on-Write mechanism to avoid unintentionnally modify the properties of many Goods records.

Technically, this data is deported into the Properties Model (see there on how to add additional properties). The properties column value can be None, so that we don’t pollute the database with empty lines of Property records, although this is subject to change in the future.

Methods

get_property(k, default=None)[source]

Property getter, works like dict.get().

Actually I’d prefer to simply implement the dict API, but we can’t direcly inherit from UserDict yet. This is good enough to provide the abstraction needed for current internal wms_core calls.

set_property(k, v)[source]

Property setter.

See remarks on get_property().

This method implements a simple Copy-on-Write mechanism. Namely, if the properties are referenced by other Goods records, it will duplicate them before actually setting the wished value.

Model.Wms.Goods.Type

class anyblok_wms_base.core.goods.type.Type[source]

Types of Goods.

For a full functional discussion, see Goods Type.

Fields and their semantics

id = <anyblok.column.Integer object>

Primary key

code = <anyblok.column.Text object>

Uniquely identifying code.

As a convenience, and for sharing with other applications.

behaviours = <anyblok_postgres.column.Jsonb object>

Goods Types specify with this flexible field how various Operations will treat the represented Goods.

See also

Unpack for a complex example.

The value is a key/value mapping (behaviour name/value).

Warning

direct read access to a behaviour is to be avoided in favour of get_behaviour() (see Goods Type hierarchy and behaviour inheritance).

This field is also open for downstream libraries and applications to make use of it to define some of their specific logic, but care must be taken not to conflict with the keys used by wms-core and other bloks (TODO introduce namespacing, then ? at least make a list available by using constants from an autodocumented module)

Methods

get_behaviour(name, default=None)[source]

Get the value of the behaviour with given name.

This method is the preferred way to access a given behaviour. It resolves the wished behaviour by looking it up within the behaviours dict, and recursively on its parent.

It also takes care of corner cases, such as when behaviours is None as a whole.

Model.Wms.Goods.Properties

class anyblok_wms_base.core.goods.goods.Properties[source]

Properties of Goods.

This is kept in a separate Model (and SQL table) to provide sharing among several Goods instances, as they can turn out to be identical for a large number of them.

Use-case: receive a truckload of milk bottles that all have the same expiration date, and unpack everything down to the bottles. The expiration date would be stored in a single Properties instance, assuming there aren’t also non-uniform properties to store, of course.

Applications are welcome to overload this model to add new fields rather than storing their meaningful information in the flexible field, if it has added value for performance or programmming tightness reasons. This has the obvious drawback of defining some properties for all Goods, regardless of their Types, so it should not be abused.

On Goods, the get_property / set_property API will treat direct fields and top-level keys of flexible uniformely, that, as long as all pieces of code use only this API to handle properties, flexible keys can be replaced with proper fields transparently at any time in the development of downstream applications and libraries (assuming of course that any existing data is properly migrated to the new schema).

Fields and their semantics

id = <anyblok.column.Integer object>

Primary key.

flexible = <anyblok_postgres.column.Jsonb object>

Flexible properties.

The value is expected to be a mapping, and all property handling operations defined in the wms-core will handle the properties by key, while being indifferent of the values.

Note

the core also makes use of a few special properties, such as contents. TODO make a list, in the form of constants in a module

Methods

classmethod create(**props)[source]

Direct creation.

The caller doesn’t have to care about which properties get stored as direct fields or in the flexible field.

This method is a better alternative than insertion followed by calls to set(), because it guarantees that only one SQL INSERT will be issued.

If no props are given, then nothing is created and None gets returned, thus avoiding a needless row in the database. This may seem trivial, but it spares a test for callers that would pass a dict, using the ** syntax, which could turn out to be empty.

duplicate()[source]

Insert a copy of self and return its id.

get(k, *default)[source]
set(k, v)

Model.Wms.Goods.Avatar

class anyblok_wms_base.core.goods.goods.Avatar[source]

Goods Avatar.

See in Core Concepts for a functional description.

Fields and their semantics

id = <anyblok.column.Integer object>

Primary key.

location = <anyblok.relationship.Many2One object>

Where the Goods are/will be/were.

See Location for a discussion of what this should actually mean.

state = <anyblok.column.Selection object>

State of existence in the premises.

see anyblok_wms_base.constants.

This may become an ENUM once Anyblok supports them.

reason = <anyblok.relationship.Many2One object>

Entry point to operational history.

This records the Operation that is responsible for the current Avatar, including its state. In practice, it is simply the latest Operation that affected these goods.

It should renamed as outcome_of or latest_operation in some future.

Note

As a special case, planned Operations do change dt_until on the Avatars they work on without setting themselves as reason.

No setting themselves as reason helps to distinguish their inputs from their outcomes and is in line with dt_until being theoretical in that case anyway.

dt_from = <anyblok.column.DateTime object>

Date and time from which the Avatar is meaningful, inclusively.

Functionally, even though the default in creating Operations will be to use the current date and time, this is not to be confused with the time of creation in the database, which we don’t care much about.

The actual meaning really depends on the value of the state field:

  • In the past and present states, this is supposed to be a faithful representation of reality.
  • In the future state, this is completely theoretical, and wms-core doesn’t do much about it, besides using it to avoid counting several Goods Avatar of the same physical goods while peeking at quantities in the future. If the end application does serious time prediction, it can use it freely.

In all cases, this doesn’t mean that the very same Goods aren’t present at an earlier time with the same state, location, etc. That earlier time range would simply be another Avatar (use case: moving back and forth).

dt_until = <anyblok.column.DateTime object>

Date and time until which the Avatar record is meaningful, exclusively.

Like dt_from, the meaning varies according to the value of state:

  • In the past state, this is supposed to be a faithful representation of reality: apart from the special case of formal Splits and Aggregates, the goods really left this location at these date and time.
  • In the present and future states, this is purely theoretical, and the same remarks as for the dt_from field apply readily.

In all cases, this doesn’t mean that the very same goods aren’t present at an later time with the same state, location, etc. That later time range would simply be another Avatar (use case: moving back and forth).