Problem/Motivation

Refer to the general discussion/brainstorm about inventory management here: #2401531: [META] Farm Inventory Brainstorm/Discussion

This issue is specifically for the implementation of a Farm Inventory module that provides the ability to record asset inventory adjustments alongside log entries, similar to the way that Movements work (http://farmos.org/guide/location/).

Proposed resolution

Add an "Inventory" field collection to various log types (similar to the Movement field collection for tracking location), with the following sub-fields:

  • Asset (entity reference) - to indicate which asset the inventory adjustment is being made to
  • Value (fraction) - to define the amount of inventory adjustment

Logs should allow multiple inventory adjustments to be made to different assets. This can be achieved by making the inventory field collection instance allow multiple ("unlimited") values.

To determine the current inventory level of an asset, the system would look back at all logs that have an inventory adjustment assigned to the asset. Only logs that are "done" with a "timestamp" less than or equal to the present moment will be included, similar to the way that location is determined (it should also be possible to pass a timestamp and get inventory levels at a specific time in the past/future). An asset's current inventory should be displayed on the asset view page (/farm/asset/%(/view)).

Custom Views handlers will be necessary for displaying current inventory levels in Views. The custom handler for determining an asset's current location can be used as a guide, but we will also need to perform a SUM aggregation in the database query.

Not all asset types will need to have inventory tracking available. A new database table will be created that stores inventory settings for each asset type, including whether or not inventory is enabled.

There will be a need to differentiate the "default" inventory of certain asset types. For example, if you create a new Animal asset, and do not specify any inventory on it, then we should assume that it represents an "individual" animal. For the purposes of tallying, this will mean that the asset has an inventory of 1, even if it doesn't have any inventory adjustment logs. On the contrary, we will want to have other asset types default their inventory to 0. For example, if we add assets in the future for tracking seed stock, feed stock, or other bulk materials, they should have a default inventory of 0, and require that you add inventory adjustment logs in order to increase/decrease them. Thus, one of the settings associated with asset types should be a boolean indicating whether or not we should treat assets as "individuals" by default.

Only "Animal" asset types will have inventory management enabled at first, to enable head counts. It won't be hard to add additional asset types as a follow-up for tracking things like seed stock, inputs, etc. We can create follow-up feature requests for those separately. This issue is just for the underlying inventory module that will make it all possible.

Remaining tasks

  • Create new Farm Inventory module (farm_inventory).
  • Add an Inventory field collection with sub-fields: Asset and Value.
  • Add Inventory field collection to log types: Input, Activity, Harvest, Seeding, Transplanting, Medical, Observation, Sale, Maintenance.
  • Add function for calculating an asset's inventory: farm_inventory() and farm_inventory_query().
  • Display asset inventory on asset view pages (only if inventory is enabled for the asset type).
  • Add a {farm_inventory_asset_type} database table for storing asset type inventory settings.
  • Implement hooks for entity load, insert, update, and delete to maintain asset type inventory settings.
  • Alter asset type form to show inventory settings.
  • Add functions that check if inventory management is enabled on an asset, and whether or not to treat an asset as an individual by default.
  • Only display inventory if it is enabled for assets.
  • Default the inventory of "individual" assets to 1.
  • Add a View of "logs with inventory adjustments".
  • Show "logs with inventory adjustments" on asset pages.
  • Add Views integration for {farm_inventory_asset_type} table.
  • Add a function that returns a list of asset types with inventory enabled.
  • Add custom Views asset type filter handler that only includes types with inventory enabled.
  • Create an Entity Reference View for selecting assets that have inventory enabled.
  • Use farm_inventory_entityreference_view for selecting assets in inventory adjustment.
  • Add a function for programatically creating a log that sets the current inventory level.
  • Add an Inventory field to asset edit form that allows setting the current inventory (via programatic log creation).
  • Create function for formatting inventory values.
  • Add Views field handler for asset inventory value.

User interface changes

Logs will have a new "Inventory" tab that provides user's with the ability to add inventory adjustments to the log. It will be a multi-value field so more than one adjustment can be added. Each adjustment will reference an asset, and will have a value that can be positive (to record an increase in the asset's inventory) or negative (to record a decrease in the asset's inventory).

Asset type edit forms will expose inventory settings for "enabled" (to enable inventory management for the asset type), and "individual" (to specify that assets of this type should be treated as individuals if they do not have any inventory adjustments).

When editing assets (that have inventory tracking enabled), an "Inventory" field will be available to specify the asset's current inventory level. This will automatically generate an observation log - just like the "Location" field that currently acts as a shortcut for creating movements.

A new Views field handler will be available for displaying inventory in asset lists.

API changes

New functions:

  • farm_inventory(FarmAsset $asset, $time = REQUEST_TIME, $done = TRUE) - Calculate the current inventory level for a specific asset.
  • farm_inventory_query($asset_id, $time = REQUEST_TIME, $done = TRUE) - Helper function for generating a database query for calculating an asset's current inventory.
  • farm_inventory_enabled(FarmAsset $asset) - Check whether or not inventory management is enabled on an asset.
  • farm_inventory_individual(FarmAsset $asset) - Check whether or not an asset is treated as an individual by default.
  • farm_inventory_asset_types() - Returns a list of asset types that have inventory enabled.
  • farm_inventory_set($asset, $inventory, $timestamp = REQUEST_TIME, $log_type = 'farm_observation', $done = TRUE) - Create a log for adjusting asset inventory.

Data model changes

  • New "Inventory" field collection (with "Asset" and "Value" sub-fields) on log types: Input, Activity, Harvest, Seeding, Transplanting, Medical, Observation, Sale, Maintenance.
  • New database table {farm_inventory_asset_type} for storing inventory settings associated with each asset type. Columns: type, enabled, individual.

Comments

m.stenta created an issue. See original summary.

m.stenta’s picture

Issue summary: View changes
m.stenta’s picture

Issue summary: View changes

Filled in the "Proposed resolution" section above with my current thinking.

m.stenta’s picture

Issue summary: View changes

I decided to nix the idea for a "reset" field in the inventory field collections. It was a "nice to have" idea, but it adds a level of complexity that isn't justified at this point. We can reassess if we need to in the future.

I added it originally to accomplish two things:

  1. Make it easy for users to set the current inventory without having to know what the old level is.
  2. Reduce the number of logs that need to be queried to calculate the current inventory level, because instead of starting at the beginning of time, you can start at the most recent "Reset" log.

1 is just a nice-to-have - and you can still reset the inventory manually without it, you just need to know what the current level is and add/subtract to balance it out.

2 is theoretically for performance, but it adds complexity to the queries and code which may not justify itself

premature optimization is the root of all evil

-Donald Knuth

Ultimately, if performance is an issue, there are better ways to solve this (I've had to think about this a lot in the Ledger module - http://drupal.org/project/ledger). Caching is most likely the best approach.

m.stenta’s picture

Issue summary: View changes

Outlined the "Remaining tasks" section. Left it open-ended because there may be more steps necessary, but I think this summarizes the first steps towards a bare-minimum data architecture, calculation, and simple UI.

m.stenta’s picture

Issue summary: View changes

Added content to "User interface changes", "API changes", and "Data model changes" above.

m.stenta’s picture

Issue summary: View changes

We will want to be able to view "Logs with inventory adjustments" on individual asset pages, similar to "Logs with movements", because they will be using a different asset reference field so won't show up automatically.

m.stenta’s picture

Issue summary: View changes

I've pushed a new farm_inventory branch to my farmOS repo fork on Github: https://github.com/mstenta/farmOS/tree/farm_inventory

It does the following:

  • Create new Farm Inventory module (farm_inventory).
  • Add an Inventory field collection with sub-fields: Asset and Value.
  • Add Inventory field collection to log types: Input, Activity, Harvest, Seeding, Transplanting, Medical, Observation, Sale, Maintenance.
  • Add function for calculating an asset's inventory: farm_inventory() and farm_inventory_query().
  • Display asset inventory on asset view pages.

I crossed those items off the list above, and will continue to update as progress is made...

m.stenta’s picture

Issue summary: View changes

Added:

When editing assets (that have inventory tracking enabled), an "Inventory" field will be available to specify the asset's current inventory level. This will automatically generate an observation log - just like the "Location" field that currently acts as a shortcut for creating movements.

m.stenta’s picture

Issue summary: View changes

Another consideration came up in recent discussions... updating the issue to include the following (and associated tasks/changes):

There will be a need to differentiate the "default" inventory of certain asset types. For example, if you create a new Animal asset, and do not specify any inventory on it, then we should assume that it represents an "individual" animal. For the purposes of tallying, this will mean that the asset has an inventory of 1, even if it doesn't have any inventory adjustment logs. On the contrary, we will want to have other asset types default their inventory to 0. For example, if we add assets in the future for tracking seed stock, feed stock, or other bulk materials, they should have a default inventory of 0, and require that you add inventory adjustment logs in order to increase/decrease them. Thus, one of the settings associated with asset types should be a boolean indicating whether or not we should treat assets as "individuals" by default.

I will add a new database table for tracking inventory settings for each asset type. One of them will be whether or not inventory tracking is enabled for that asset type. I'll use that instead of the Units field, which is a cleaner approach overall.

m.stenta’s picture

Issue summary: View changes

Made a bunch of progress on this. Updating issue description accordingly...

m.stenta’s picture

Issue summary: View changes
Related issues: +#2916551: Farm Inventory Units

I decided to split the "Inventory Units" piece out to a separate issue, since it is not an immediate requirement.

See #2916551: Farm Inventory Units

m.stenta’s picture

Issue summary: View changes

Recent discussions in #2469109: [META] Discussion: Asset Groups/Hierarchy have changed the requirements a bit. Groups will NOT have inventory enabled. Animals will. See that issue for more details.

m.stenta’s picture

I think this feature is pretty much done! I'm going to do a little more thinking/testing before merging it in a closing this issue... but we're looking good!

m.stenta’s picture

Status: Active » Fixed

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.