How to Build Inventory Automations with 黑料正能量

Inventory automation is potentially the most transformative tool that logistics teams have at their disposal.
See, managing inventories can be incredibly tricky - owing largely to the complexity of the data and processes involved.
Not only does this make it difficult to understand the current state of our inventory - it can also create mountains of administrative work and introduce unnecessary scope for error.
Inventory automation seeks to address all three of these problems simultaneously.
Today, we鈥檙e checking out how we can use 黑料正能量 to build custom automation solutions around existing data sets - with minimal custom code.
Specifically, we鈥檙e going to build an application to track and collate stock levels based on sales, returns, and procurement.
We鈥檒l also add an admin panel UI so that users can check stock, track stock changes, and restock items by emailing the vendor.
But first, let鈥檚 check out a little bit of background information.
What is inventory automation?
Inventory automation means using digital technology to replace or support manual interactions within your inventory management workflows.
This can range from individual tasks to full-scale processes.
We might also be dealing with relatively generic tasks, like data centralization or order processing. Or, we could be dealing with processes that are highly specific to your particular internal operations.
In any case, the goal is to maximize efficiency, accuracy, visibility, and reliability within inventory management workflows by reducing the need for human interventions.
What are we building?
Our inventory automation app is built around two related clusters of functionalities:
- Back-end automation to update our inventory records based on the outcomes of sales, returns, and procurement processes.
- A front-end admin panel where users can view stock level or recent stock changes - as well as contact vendors to order new items.
We鈥檙e going to build this around an existing Postgresql database, with separate tables representing our inventory, sales, shipments, consignments, vendors, and returns. Alongside this, we鈥檒l create a new 黑料正能量DB table for storing information on inventory changes.
Of course, a lot of the functionality will be handled in the back-end, invisible to end-users. Here鈥檚 what the finished UI will look like:
Our inventory table also features clickable rows, where users can drill into information about each item and get in contact with the relevant vendor to order new stock - directly from within the application:
How to build an inventory automation solution with 黑料正能量 in 7 steps
Let鈥檚 dive right in.
If you haven鈥檛 already, sign up for a free 黑料正能量 application.
Join 200,000 teams building workflow apps with 黑料正能量
1. Create a new 黑料正能量 application
We鈥檙e going to start by creating a new 黑料正能量 application. As ever, we have the option of importing an existing app or using a template - but we鈥檙e starting from scratch:
The first thing we need to do is choose a name for our app, which will also be used as its URL path:
2. Configuring our data
Next, we鈥檒l be prompted to choose which type of data we want to start with. 黑料正能量 offers dedicated connectors for a range of SQL and NoSQL databases, alongside REST, Google Sheets, and our internal database.
For our first data source, we鈥檒l click on PostgreSQL. We鈥檙e then asked to input our configuration details.
We can then select which tables we鈥檇 like to fetch so that we can manipulate them within 黑料正能量. Our database has several tables that we won鈥檛 need for this application. We鈥檙e only selecting the ones we listed earlier:
Straight away, we can use 黑料正能量鈥檚 back-end to perform CRUD actions or edit the schema of any of our tables:
Each of our tables contains different data points that we鈥檒l need to build our desired inventory automations - as well as foreign keys to denote relationships between them. We鈥檒l outline these as and when we鈥檙e using them.
To finish off our data model, we need to add a second data source.
This time, when we鈥檙e prompted, we鈥檒l choose an internal table.
We鈥檒l call this inventory_changes.
We鈥檒l then need to populate the columns we want to store:
These are:
- item_name (text).
- inventory_id (number).
- change_type (options).
- quantity_change (number).
- new_quantity (number).
The options for the change_type attribute will be Return, Sales, and Restock.
That鈥檚 our data model ready.
Next, we鈥檒l start building automation rules. The basic logic of our app is that the inventory table stores the present stock levels of various items. We need automations to update this data, when a new sale, return, or incoming consignment is processed.
We鈥檒l also record the details of these changes on our inventory_changes table.
3. Handling sales
Let鈥檚 start with handling new sales. We鈥檒l create a new automation:
We first need to give this a name and choose a trigger. We鈥檙e calling our SaleAdded and picking a row created trigger.
Then, we can choose which table to point our inventory automation trigger at. We鈥檙e picking sales. So, anytime a row is added to the sales table, any subsequent actions we configure will be carried out.
Now remember, for our inventory_changes table we鈥檒l need the item_name, inventory_id, change_type, quantity_change, and new_quantity. We can get most of this information from the sales table.
However, we鈥檙e going to need to know how many of the items in question we had in stock before the sale in order to figure out the new quantity.
Therefore, our first action will be a query rows step:
And we鈥檒l point this at the inventory table:
As-is, this will make the entire inventory table usable within our subsequent automation steps. We only want the row with an id attribute that corresponds to the inventory_id that we have stored in our new sales row.
We鈥檒l hit define filters to open the filters drawer:
And we can add a filtering expression to only return inventory rows where id equals {{ trigger.row.inventory_id }}.
We can test this by using some dummy data, setting the inventory_id for our trigger row to 1 - which we know is the id of one of our inventory items:
Our query rows step will output a data object that looks like this.
1{
2
3 "rows": [
4
5 {
6
7 "vendor_id": 4,
8
9 "id": 1,
10
11 "quantity": 225,
12
13 "item_name": "Bulldozer",
14
15 "unit_price": 210,
16
17 "_id": "%5B1%5D",
18
19 "tableId": "datasource_plus_2fb21a1bd6ed4e20b3ff449096fad3ed__inventory",
20
21 "_rev": "rev"
22
23 }
24
25 ],
26
27 "success": true
28
29}
Note that there are two attributes here called id and _id. id is the one that already existed in our Postgres table. _id was autogenerated by 黑料正能量, so we can ignore it.
We鈥檒l see how to use the returned data in subsequent automations in a second.
We want to do two more things with this automation rule - update the quantity attribute in the relevant inventory row and add the details of our change to the inventory_changes table.
We鈥檒l start by adding an update row action and pointing it at our inventory table:
We can now input the details we want. item_name, vendor_id, unit_price, and row_id should remain unchanged.
So, we鈥檒l bind them to their respective outputs from the previous step in the format:
1{{ steps.1.rows.0.column_name }}
Steps.1 contains an object called rows. This could potentially contain many rows, so we need to provide the index of the row we want. In our case, this will always be 0 since there is only one row returned by the previous step.
For quantity, we want to use the difference between the original quantity attribute from the row and the quantity attribute from our new sales row.
We鈥檒l do this by binding the following JavaScript expression.
1return $("steps.1.rows.0.quantity") - $("trigger.row.quantity")
Test this and make sure it works as expected. When you鈥檙e happy, add another automation action, this time selecting create row.
We鈥檒l point this one at inventory_changes:
We鈥檒l take our inventory_id, item_name, and quantity_change from our trigger row, in the format:
1{{ trigger.row.column_name }}
There鈥檚 no need for an index this time, since the row object from our trigger can only include a single row.
For change_type, we鈥檒l select Sales.
We can also use the exact same JavaScript function as before to calculate the new quantity.
When we test our automation again, we can see that a new row has been added to the inventory_changes table with the test data that we provided:
4. Handling returns
Next, we鈥檒l duplicate our inventory automation rule and change the name of the new version to ReturnAdded:
This will save us a little bit of time, since we can largely just swap out the data and retain a similar flow - albeit with one extra step.
The first thing we鈥檒l change is the table our trigger is pointed at. We want this to be returns rather than sales.
Now, in the previous automation flow, we could just query our inventory table - since our data model is set up so that sales rows are linked to inventory rows. We can鈥檛 do this with returns, because they aren鈥檛 linked directly to the inventory table.
However, they do have an attribute called sales_id which corresponds to the row of the original order that鈥檚 being returned.
We can query the relevant sales row - and then use the output from this to query the appropriate inventory row.
So, we鈥檒l add another query rows action immediately after our trigger and point it at the sales table.
This time, the filter will be set to retrieve the row where id matches {{ trigger.row.sale_id }}.
Then, we鈥檒l update the filter for the inventory query action to get the row where id equals {{ steps.1.rows.0.inventory_id }}.
Now, we need to update all of the bindings in our subsequent update row and create row actions.
For updating the inventory table, this is easy. All of our existing bindings are just wrong by one step. So, anywhere it says steps.1, we want to replace this with steps.2 - including in our JavaScript function.
We鈥檒l also change our JavaScript function to return the sum of the relevant quantities - since a return adds to our current stock levels:
1return $("steps.2.rows.0.quantity") + $("steps.1.row.0.quantity")
For the create row action we need to populate the values for our new row using our sales and inventory tables. So, item_name, inventory_id, and quantity_change will come in the format:
1{{ steps.1.rows.0.column_name }}
We鈥檒l set the change_type to Return and swap our JavaScript function for:
1return $("steps.2.rows.0.quantity") + $("steps.1.row.0.quantity")
Just like before, we鈥檒l test our automation to confirm that it works:
5. Handling procurement
Now, duplicate the SaleAdded flow again, this time calling the new version ConsignmentAdded.
The consignments table links to inventory - so we only need one query rows action this time.
We鈥檒l swap the table for our trigger to consignments:
And then the only other change we need to make is to swap the JavaScript for updating the quantity to:
1return Number($("steps.1.rows.0.quantity")) + Number($("trigger.row.quantity"))
Remember to do this for both the update row and create row actions.
Then, we鈥檒l verify that this works as we expected:
6. Ordering new stock
The last automation rule we鈥檒l build is a little bit different. Basically, we want to enable users to send an email template to the vendor of any given item in order to request more stock.
In the next step, we鈥檒l create a UI where users can trigger this automation on any item, specifying the quantity of stock they鈥檇 like to order.
We鈥檒l start by creating a new automation which we鈥檒l call OrderStock. This time we鈥檙e using the App Action trigger. This allows us to specify variables that we can pass to our automation whenever it鈥檚 triggered:
We鈥檙e going to add two number fields, called order_quantity and inventory_id.
We鈥檙e going to use the inventory_id to retrieve the appropriate row from our inventory table, using a filtered query rows action - just like before.
Each row in the inventory table is linked to a row in the vendors table - which stores details of the companies that supply our stock.
We need to retrieve the relevant vendors row too.
Add another query rows action, pointed at the vendors table. This has an attribute called v_id. We鈥檒l use a filter expression to return the row where this matches the vendor_id output of our previous step:
The data object we get back looks like this:
1{
2
3 "rows": [
4
5 {
6
7 "ein_number": "10-0774902",
8
9 "street_address_line_2": "Unit 200",
10
11 "services_description": "Sed ante. Vivamus tortor. Duis mattis egestas metus.",
12
13 "contact_email": "adurhamj@slideshare.net",
14
15 "company_name": "O'Reilly-Greenholt",
16
17 "contact_phone": "785-373-6000",
18
19 "v_id": 4,
20
21 "contact_last_name": "Durham",
22
23 "state": "KS",
24
25 "category": "Medical/Nursing Services",
26
27 "purchase_order_billing": "TRUE",
28
29 "vendor_number": 2,
30
31 "street_address": "5 Erie Court",
32
33 "zip": 66611,
34
35 "contact_first_name": "Aaren",
36
37 "_id": "%5B4%5D",
38
39 "tableId": "datasource_plus_2fb21a1bd6ed4e20b3ff449096fad3ed__vendors",
40
41 "_rev": "rev"
42
43 }
44
45 ],
46
47 "success": true
48
49}
This is quite a lot of information, but we only need contact_email and contact_first_name - alongside the item_name value from our inventory query and our original trigger鈥檚 order_quanitity.
Next, add a send email action. You鈥檒l need to have your SMTP credentials set up in 黑料正能量 for this to work - so check out our [email docs]( comes with three different,right side of the page.).
We鈥檒l bind the send to field to the output of our vendors query, using {{ steps.2.rows.0.contact_email }}. Send from and subject can be whatever you want:
For the subject, we鈥檙e using:
1Order Request: {{ steps.1.rows.0.item_name }}
Your email body can be set using standard HTML. For example:
1<p>Dear {{ steps.2.rows.0.contact_first_name }}</p>
2
3<p>We require {{ trigger.fields.quantity }} {{ steps.1.rows.0.item_name }}s.</p>
4
5<p>Can you please process this request</p>
6
7<p>Thanks.</p>
Once you鈥檙e satisfied that this works, we can move on to creating our admin panel.
7. Building an admin panel
Head to the design section and create a new table screen:
We鈥檒l base this on our inventory table:
黑料正能量 will autogenerate a working CRUD screen for our inventory data:
We鈥檙e going to back a few changes to the side panel to replace the existing update form with one that allows users to make new stock requests, based on the email automation we just set up.
We鈥檒l start by capitalizing our title:
We鈥檒l also disable the show button option:
Then we鈥檒l turn off paginate and increase the scroll limit to match the number of rows in our inventory table:
When we鈥檙e happy with the design we鈥檒l hit eject block:
This exposes the constituent components that make up our table block.
We have a component called side panel inside of which there鈥檚 a form block. This is a prebuilt form. We鈥檒l make a couple of design tweaks to this too before we eject it. First, remove the delete button鈥檚 text.
Then, set align labels to top:
Now eject your form block.
We鈥檒l change the headline component to read Restock:
Then, we鈥檒l select the disabled check box under each of our form fields:
Now - recall that our email automation has two trigger fields - inventory_id and order_quantity. We already know the inventory_id, based on the row that the user clicked on.
We need to add a number field to allow them to specify a quantity by setting the field and label to order_quantity:
We鈥檒l also add a validation rule so that this is required:
We then need to update our button to carry out our new action. We can start by updating its text from save to order:
We also need to update the button鈥檚 actions:
Delete everything except validate form:
Add a trigger automation row and select OrderStock as its automation:
Then, we can set the bindings for our two trigger fields. We鈥檝e set order_quantity to {{ form.fields.order_quantity }} and inventory_id to {{ repeater.inventory.id }}.
Now, when a user clicks on the order button under any given item, the relevant vendor will be contacted to make an order. Or - they will once we鈥檝e pushed our app live.
Before that though, we鈥檙e going to make a couple more design tweaks.
First, we鈥檒l choose the lightest option under screen and theme:
Then, we鈥檒l open configure links and remove our menu entry - since our inventory automation app only has one screen anyway.
The last thing we want to do is add some notification cards to display the most recent inventory_changes at the bottom of our screen.
Add a cards block beneath your form, give it a title, and set its data to inventory_changes:
We鈥檒l set its title to the following JavaScript function:
1if ($("Inventory_changes Cards block.inventory_changes.quantity_change") > 0){
2
3 return "+" + $("Inventory_changes Cards block.inventory_changes.quantity_change")
4
5}
6
7else return $("Inventory_changes Cards block.inventory_changes.quantity_change")
This ensures that the quantity_change attribute is always prefixed with a plus or minus symbol.
For the subtitle, we鈥檒l use {{ Inventory_changes Cards block.inventory_changes.item_name }} and for description we鈥檒l use {{ Inventory_changes Cards block.inventory_changes.change_type }}.
Then, publish your app to push it live - including your inventory automation rules:
Here鈥檚 what the finished product should look like once it鈥檚 published:
If you found this tutorial helpful, you might also like our guide to building a warehouse dashboard .