core.location

Model.Wms.Location

class anyblok_wms_base.core.location.Location[source]

A stock location.

Locations are perhaps ill-named (see “Location” terminology is misleading), but ultimately they represent the idea of “where” the Goods are. There’s no reason a priori to consider that Locations themselves are not moving.

Locations form a tree-like structure (a forest), which is encoded through the parent field, and have tags which can be used to express functional meaning, especially for quantity computations.

Downstream libraries and applications which don’t want to use this hierarchy and the defaulting of tags that comes along can do so by overriding flatten_hierarchy_with_tags() and resolve_tag()

TODO add location types to encode behavioral properties (internal, EDI, stuff like size ?)

Fields and their semantics

id = <anyblok.column.Integer object>
code = <anyblok.column.Text object>
parent = <anyblok.relationship.Many2One object>
tag = <anyblok.column.Text object>

Tag for Quantity grouping.

This field is a kind of tag that can be used to filter in quantity queries. It allows for location-based assessment of stock levels by recursing in the hierarchy while still allowing exceptions: to discard or include sublocations.

For instance, one may represent a big warehouse as having several rooms (R1, R2) each one with an examination area (R1/QA, R2/QA), which can be further subdivided.

Goods stored in the workshop are not to be sold, except maybe those that are in the waiting area before been put back in stock (R1/QA/Good etc.).

It’s then useful to tag Rooms as ‘sellable’, but in them, QA locations as ‘qa’, and finally the good waiting areas as ‘sellable’ again.

See in unit tests for a demonstration of that.

label = <anyblok.column.Text object>

Methods

quantity(goods_type, recursive=True, **kwargs)[source]

Goods quantity computation within the current location.

This is kept for backwards compatibility and simply calls Wms.quantity()

(see source code for details on arguments passing)

resolve_tag()[source]

Return self.tag, or by default ancestor’s.

classmethod flatten_subquery_with_tags(top=None, resolve_top_tag=True)[source]

Return an SQL subquery flattening the hierarchy, resolving tags.

The resolving tag policy is that a Location whose tag is None inherits its parent’s.

This subquery cannot be used directly: it is meant to be used as part of a wider query; see unit tests (test_location) for nice examples with or without joins. It has two columns: id and tag.

Parameters:top – if specified, the query starts at this Location (inclusive)

This default implementation issues a recursive CTE (WITH RECURSIVE) that climbs down along the parent field.

For some applications with a large and complicated Location hierarchy, joining on this CTE can become a performance problem. Quoting PostgreSQL documentation on CTEs:

However, the other side of this coin is that the optimizer is less able to push restrictions from the parent query down into a WITH query than an ordinary subquery. The WITH query will generally be evaluated as written, without suppression of rows that the parent query might discard afterwards.

If that becomes a problem, it is still possible to override the present method: any subquery whose results have the same columbs can be used by callers instead of the recursive CTE.

Examples:

  1. one might design a flat Location hierarchy using prefixing on code to express inclusion instead of the provided parent. See anyblok_wms_base.core.tests .test_location.test_override_tag_recursion() for a proof of concept of this.
  2. one might make a materialized view out of the present recursive CTE, refreshing as soon as needed.