quantity.operation

Model.Wms.Operation.Split

This Operation Model inherits from Mixin.WmsSingleInput

class anyblok_wms_base.quantity.operation.split.Split[source]

A split of Goods record in two.

Splits replace their input’s Goods record with two of them, one having the wished quantity, along with Avatars at the same location, while keeping the same properties and the same total quantity.

This is therefore destructive for the input’s Goods, which is not conceptually satisfactory, but will be good enough at this stage of development.

While non trivial in the database, they may have no physical counterpart in the real world. We call them formal in that case.

Formal Splits are operations of a special kind, that have to be considered internal details of wms-core, that are not guaranteed to exist in the future.

Formal Splits can always be reverted with Aggregate Operations, but only some physical Splits can be reverted, depending on the Goods Type.

See also

Model.Wms.Goods.Type for a full discussion including use-cases of formal and physical splits and reversal of the latter.

In the formal case, we’ve decided to represent this as an Operation for the sake of consistency, and especially to avoid too much special cases in implementation of various concrete Operations.

The benefit is that Splits appear explicitely in the history, and this helps implementing history manipulating methods a lot.

The drawback is that we get a proliferation of Goods records, some of them even with a zero second lifespan, but even those could be simplified only for executed Splits.

Splits are typically created and executed from Splitter Operations, and that explains the above-mentioned zero lifespans.

TYPE = 'wms_split'

Polymorphic key

Specific members

wished_outcome

Return the Goods record with the wished quantity.

This is only one of outcomes

Return type:Wms.Goods

Optional methods of Operation subclasses

is_reversible()[source]

Reversibility depends on the relevant Goods Type.

See on Model.Goods.Type

Mandatory methods of Operation subclasses

after_insert()[source]
execute_planned()[source]
plan_revert_single(dt_execution, follows=())[source]

Model.Wms.Operation.Aggregate

class anyblok_wms_base.quantity.operation.aggregate.Aggregate[source]

An aggregation of Goods record.

Aggregate is the converse of Split.

Like Splits, Aggregates are operations of a special kind, that have to be considered internal details of wms_core, and are not guaranteed to exist in the future.

Aggregates replace some records of Goods sharing equal properties and type, with Avatars at the same location with a single one bearing the total quantity, and a new Avatar.

While non trivial in the database, they may have no physical counterpart in the real world. We call them formal in that case.

Formal Aggregate Operations can always be reverted with Splits, but only some physical Aggregate Operations can be reverted, depending on the Goods Type. See Model.Wms.Goods.Type for a full discussion of this, with use cases.

In the formal case, we’ve decided to represent this as an Operation for the sake of consistency, and especially to avoid too much special cases implementation of various Operations. The benefit is that they appear explicitely in the history.

TODO implement plan_revert_single()

Specific members

UNIFORM_GOODS_FIELDS = ('type', 'properties', 'code')
UNIFORM_AVATAR_FIELDS = ('location',)
static field_is_equal(field, goods_rec1, goods_rec2)[source]

Return True if given field is equal in the two goods records.

This is singled out because of properties, for which we don’t want to assert equality of the properties lines, but of their content.

TODO: see if implementing __eq__ etc for properties would be a bad idea (would it confuse the Anyblok or SQLAlchemy?)

Optional methods of Operation subclasses

is_reversible()[source]

Reversibility depends on the relevant Goods Type.

See Operation for what reversibility exactly means in that context.

classmethod check_create_conditions(state, dt_execution, inputs=None, **kwargs)[source]

Check that the inputs to aggregate are indeed indistinguishable.

This performs the check from superclasses, and then compares all fields from UNIFROM_AVATAR_FIELDS on the inputs (Avatars) and UNIFORM_GOODS_FIELDS (on the underlying Goods).

Mandatory methods of Operation subclasses

after_insert()[source]

Business logic after the inert insertion

execute_planned()[source]

Splitter Mixins: splitting Goods if needed

class anyblok_wms_base.quantity.operation.splitter.WmsSplitterOperation[source]

Mixin for operations on a single input that can split.

This is to be applied after Mixin.WmsSingleInputOperation. Use WmsSplitterSingleInputOperation to get both at once.

It defines the quantity field to express that the Operation only works on some of the quantity held by the Goods of the single input.

In case the Operation’s quantity is less than in the Goods record, a Split will be inserted properly in history, and the Operation implementation can ignore quantities completely, as it will always, in truth, work on the whole of the input it will see.

Subclasses can use the partial field if they need to know if that happened, but this should be useful only in special cases.

Fields and their semantics

partial = <anyblok.column.Boolean object>

Record if a Split will be or has been inserted in the history.

Such insertions should happen if the operation’s original Goods have greater quantity than the operation needs.

This is useful because once the Split is executed, this information can’t be deduced from the quantities involved any more.

quantity = <anyblok.column.Decimal object>

The quantity this Operation will work on.

Can be less than the quantity of our single input.

Implemented subset of the operation subclass API

(we list only those methods that override the base classes).

classmethod before_insert(state='planned', follows=None, inputs=None, quantity=None, dt_execution=None, dt_start=None, **kwargs)[source]

Override to introduce a Split if needed

In case the value of quantity does not match the one from the goods field, a Split is inserted transparently in the history, as if it’d been there in the first place: subclasses can implement after_insert() as if the quantities were matching from the beginning.

check_execute_conditions()[source]

Check that the quantity (after possible Split) is as on the input.

If a Split has been inserted, then this calls the base class for the input of the Split, instead of self, because the input of self is in that case the outcome of the Split, and it’s normal that it’s in state future: the Split will be executed during self.execute(), which comes once the present method has agreed.

execute_planned()[source]

Execute the Split if any, then self.

class anyblok_wms_base.quantity.operation.splitter.WmsSplitterSingleInputOperation[source]

Use this mixin to get both SingleInput and Splitter at once.

Model.Wms.Operation.Move

class anyblok_wms_base.quantity.operation.move.Move[source]

Override to use quantity where needed.

Implemented subset of the operation subclass API

Methods

revert_extra_fields()[source]

Take quantity into account for reversal.

See also this method’s documentation in the base class.

Model.Wms.Operation.Unpack

class anyblok_wms_base.quantity.operation.unpack.Unpack[source]

Override to use quantity where needed.

Implemented subset of the operation subclass API

Methods

create_unpacked_goods(fields, spec)[source]

Create just a record, bearing the total quantity.

See also this method in the base class

TODO: introduce a behaviour (available in spec) to create as many records as specified. Even if wms-quantity is installed, it might be more efficient for some Goods types. Use-case: some bulk handling alongside packed goods by the unit in the same facility.

Model.Wms.Operation.Departure

class anyblok_wms_base.quantity.operation.splitter.Departure[source]

Override making Departure a Splitter Operation.

As with all Splitter Operations, Departures can be partial, i.e., there’s no need to match the exact quantity held in the underlying Goods record: an automatic Split will occur if needed.

In many scenarios, the Departure would come after a Move that would bring the goods to a shipping location and maybe issue itself a Split, so that actually the quantity for departure would be an exact match, but Departure does not rely on that.