Creating stock usually requires the developer to do some heavy lifting across multiple APIs because the workflows for inbounding stock are complicated. There are purchase orders, inbound stock movements, and inbound returns that act as the pending record of what’s been ordered and/or in-transit (these each have separate APIs). Upon delivery, the system converts those objects into receipts (using the Receipt API). Receipts create low-level transactions that become inventory items. These inventory items are usually transferred into their final storage location using putaways (using the Putaway API).
Note: Let me know if you’re interested in learning more about any of those aforementioned APIs, and I will point you in the right direction. These APIs are the safer and more complete approach, while the following approach is a bit more experimental. But once we learn how you plan to use the API we’ll be able to build more coarse-grained REST and state machine endpoints for those use cases.
Since you’re likely just looking to build a quick stock count or stock adjustment feature, we do expose a way to create low-level transactions through the Generic API.
The following request would create a “stock count” (what we call a “product inventory”) transaction, which acts as a baseline count for a set of inventory items, usually for a single product, but you can include multiple products if desired. These transactions reset the inventory, so only use them in the case where you know the count for all inventory items of a product.
curl --location 'https://<REDACTED>/openboxes/api/generic/transaction' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Cookie: JSESSIONID=<REDACTED>' \
--data '{
"inventory": "1",
"transactionType":"11",
"transactionDate": "07/07/2024 00:27:00",
"transactionEntries": [
{
"product": "ff808181907703450190797770480000",
"inventoryItem": "ff8081819077034501908b6f3b1f0020",
"binLocation": "09f5e1168103c8ba0181496a1a416df2",
"quantity": "1"
}
]
}'
This creates a “stock count” transaction for a product that was created using the API.
There are other transaction types besides the Stock Count (Product Inventory) transaction:
- Adjustment (Credit)
- Adjustment (Debit)
- Consumption (Debit)
- Damaged (Debit)
- Expired (Debit)
And you can add others as well. For example, perhaps you want to track stock that was Scrapped or Destroyed. Just remember that all transaction types are either debit or credit, so quantities should always be positive integers.
Lastly, if you ever use Transfer In or Transfer Out, note that for these transactions, you should provide a source and destination, respectively. They might not be required, but it’s a good idea to provide them just so you know the intended from/to.
Limitations
The biggest pains with this approach are:
-
We don’t use the Generic API much, so it’s not very well tested at this point.
-
Creating transactions using the Generic API only seems to work with 0.9.x releases.
-
You’ll need to find or create an inventory item (product, lot, expiry date combination) for each of your transaction entries. If you’re only updating the stock count for a single inventory item then this isn’t a big deal.
But updating multiple inventory items may require you to send multiple requests for each, which adds some chattiness to the conversation.
Note: the API should allow you to provide a product code (or product ID), a lot number (or inventory item ID), and an optional expiration date in the Create Transaction request body and the API should take care of the “find or create” logic for you.
With that said, the find or create inventory item requests are painless.
a. Creating new inventory items is fairly easy to do at the moment.
curl --location 'https://<REDACTED>/api/generic/inventoryItem' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Cookie: JSESSIONID=<REDACTED>' \
--data '{
"product": "ff808181907703450190797770480000",
"lotNumber": "ABC123",
"expirationDate": "03/31/2028"
}'
b. Retrieving can probably be done a few different ways but this is the most straightforward.
curl --location 'https://<REDACTED>/openboxes/api/products/ff808181907703450190797770480000/inventoryItems/ABC123' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Cookie: JSESSIONID=<REDACTED>'
-
All transactions require a reference to an inventory record. Thankfully, every location that manages inventory has a foreign key reference to an inventory record. Unfortunately, we don’t seem to expose this required “inventory_id” anywhere in the UI or API, so you’ll need to hardcode the value (or maintain a location/inventory map in your client application until we have a chance to resolve some of the known limitations of the API.
Here’s the query to find the inventory ID for a given location.
mysql> select id, name, inventory_id from location where name = 'Distribution Center' \G
*************************** 1. row ***************************
id: 8a8a9e9665c4f59d0165c54ec6b10027
name: Distribution Center
inventory_id: 8a8a9e9665c4f59d0165c54ec6b10028
1 row in set (0.01 sec)
-
Just as I was finishing this post I realized that the system seems to be parsing the transactionDate field in the Create Transaction request as a date instead of a datetime. Therefore it doesn’t include a timestamp so it is saved to the database as midnight on the given date. This may not be an issue because we differentiate transactions based on the transaction date, as well as the date the transaction was created. Therefore, it might not lead to a bug, but it may look confusing to the end user with all of the transactions piling up on the same date.