How create a sheet materials offering on Smidyo

Frank Sandqvist
Smidyo Codex
Published in
12 min readJun 20, 2019

--

Smidyo is a platform that allows small businesses to automate their quotation process — check it out! This article is also available here.

Let’s create a quotation pipeline for a company selling sheet material. This is the sample pipeline that is automatically set up when you sign up on Smidyo, but let’s create it from scratch.​

While we are still working on the visual editor for Smidyo, we make pipelines using code. To keep things simple, we just use JSON — the industry standard for data on the web!

First we need to have some data to work with.

Create a constant

Let’s create a constant for the cost of actually cutting the material, in order to for us to easily be able to change it later. You can set it to whatever you wish, perhaps 2? Let’s give it the slug material-cutting-cost .

The data shape should be number, non-nullable, singular (number ! 1). You can change it by clicking on the three sections next to data shape.​

Create tables

Let’s also set up some tables for our tabular data. Go Tables > New

Materials table

Let’s make a table with the sub-slug materials . You can download this TSV file (download by alt/option-clicking "raw") to use ready-made data. Upload it to your table by clicking Upload TSV.

As you can see, this table is made out of two columns. One with a descriptive name of the material (text , non-nullable, singular), and another for the square meter price (number, non-nullable, singular).

Quantity discount break table

Also create another table with the sub-slug quantity-discount-breaks. Same deal as last time, here is the TSV file for this. This table defines the minimum quantity required for a specific discount percentage.

​Now that we have all the data in place — we can start making pipelines!

Creating the form pipeline

So we are selling sheet material. So in our case getting the required info is quite easy. We want to ask the customer:

  • What material is it you want?
  • Cut to what dimension?
  • How many sheets do you want?

Let’s create a form pipeline to gather this data. It’s quite simple in this case, since we are mainly dealing with number data.

In the dashboard, go to Form pipelines > New and give it the slug sheet-material. Paste in the code from here. Now try to save it!

Seeing “This pipeline has invalid dependencies”?

This is due to Smidyo using absolute references to all data. So you need to replace all references to test-salesdomain to the name of your actual sales domain. This can be done with a simple find-and-replace (ctrl/cmd + f).

Now you should be able to save it. Let’s take a look at what makes it tick.

Defining the output of the form pipeline

First, let’s define the output of the form pipeline — everything we want to have gathered from the customer when they are done.

The width, height, and quantity are all of the same data shape; numbers (type number ), that are always provided (non-nullable), and always a single value (not a list). And then we also have the ID of the material which the customer picked (a text type).

The payloadlet for these outputs comes from a pipeline value of the same name (so in order for output width-mm to get its data, we need to set pipeline value width-mm).

You can read more about data shapes and payloadlets in our article about them!

"outputs": [
{
"name": "width-mm",
"dataShape": {
"type": "number",
"nullable": false,
"list": false
}
},
{
"name": "height-mm",
"dataShape": {
"type": "number",
"nullable": false,
"list": false
}
},
{
"name": "material-id",
"dataShape": {
"type": "text",
"nullable": false,
"list": false
}
},
{
"name": "quantity",
"dataShape": {
"type": "number",
"nullable": false,
"list": false
}
}
]​

Building the form pipeline

Now let’s define the actual steps to get the information from the customer!

This is done in steps.

Step 1

We need to show the materials available in the form. So let’s get the two columns row-names and name from the materials table. Let's assign column row-names to the pipeline value material-keys, and name to pipeline value material-names .

The newly created pipeline values material-keys and material-names are now text lists containing the identifier for the material in question, and it’s name.

We are doing the through using an internal source block TABLE_COLUMNS. We should have documentation up about the different types of blocks soon!

{
"type": "INTERNAL_SOURCE_BLOCK",
"sourceBlock": "TABLE_COLUMNS",
"tableSlug": "test-salesdomain.t.sample-materials",
"out": [
{
"outFrom": "row-names",
"outTo": "material-keys"
},
{
"outFrom": "name",
"outTo": "material-names"
}
]
}​

Step 2

Using the built-in element block select.r.1 we can let the user pick the material through a drop-down menu. We feed its inputs options and option-titles with the pipeline values we just assigned.

The material ID the user picked (an item out of material-keys ) will be assigned to pipeline value material-id - which is also an output of the form pipeline.

{
"type": "ELEMENT_BLOCK",
"elementBlockSlug": "select.r.1",
"label": "Select material",
"in": [
{
"type": "PIPELINE_VALUE",
"inFrom": "material-keys",
"inTo": "options"
},
{
"type": "PIPELINE_VALUE",
"inFrom": "material-names",
"inTo": "option-titles"
}
],
"out": [
{
"outFrom": "option",
"outTo": "material-id"
}
]
}​

Step 3, 4 and 5

We gather the width, height and quantity using three element blocks of type number-input.r.1 .

Let’s just take a look at the code for one of them, since they are are all very similar.

We are setting limits for the maximum and minimum values of the input using INLINE_VALUE 's. An inline value lets you define values you do not easily be able to change at a later point, so it's useful for configuration values such as this.

If the data is not a list, it’s still provided as [1]. this is because Smidyo payloadlets are always lists. Singular payloadlets just always have a length of exactly one. You can read more about this in our article about data shape and payloadlets.

{
"type": "ELEMENT_BLOCK",
"elementBlockSlug": "number-input.r.1",
"label": "Width",
"in": [
{
"type": "INLINE_VALUE",
"inFrom": {
"dataShape": {
"type": "number",
"list": false,
"nullable": false
},
"data": [1]
},
"inTo": "limit-decimal-places"
},
{
"type": "INLINE_VALUE",
"inFrom": {
"dataShape": {
"type": "number",
"list": false,
"nullable": false
},
"data": [10000]
},
"inTo": "max"
},
{
"type": "INLINE_VALUE",
"inFrom": {
"dataShape": {
"type": "number",
"list": false,
"nullable": false
},
"data": [1]
},
"inTo": "min"
},
{
"type": "INLINE_VALUE",
"inFrom": {
"dataShape": {
"type": "text",
"list": false,
"nullable": false
},
"data": ["mm"]
},
"inTo": "suffix"
}
],
"out": [
{
"outFrom": "number",
"outTo": "width-mm"
}
]
}​

Our form pipeline is done!

Now, if you didn’t already do it earlier, all you need to do is click Save.

Crunching the numbers with process pipelines

In order to transform data, we need to create process pipelines. Process pipelines simply have inputs and outputs. They take in one set of data, output another.

We’ll need to make two of them, one to calculate the square area, and another to find out which quantity discount to use.

Pipeline 1: Calculating the square area

Go to Process pipelines > New and create a new process pipeline with the sub-slug calculate-square-area . Here is the code for it.

As you can see, the syntax is the same for process pipelines as for form pipelines.

The first and second steps takes the width and height and converts them into meters using the built-in length-conversion.r.1 operation block, and stores that in pipeline values height-m and width-m .

Then, all we do is use math-basic.r.1 to multiply them together to get the square area - which we then output on m2!

Pipeline 2: Finding the next discount percentage break

Now let’s create another process pipeline with the slug get-discount-percentage . Here is the code.

Step 1 — searching for the right discount percentage

This process pipeline will take in two lists, min-quantities representing the minimum quantity you need to purchase, and the next one, discount-percentages is the corresponding discount percentage.

min-quantities:    discount-percentages:
[ [
1, 0,
10, 20,
100 40
] [

So if we purchase 5 items, we get 0% off, 15 items 20% off, and so on.

We can search number lists by using the operation block find-list-index-by-number.r.1 . If we use provide the condition input with the text payloadlet ["equal-or-closest-less"] we get the behaviour we are looking for.

If it finds the input by-number according to the condition in number-list it outputs the index of that item, otherwise it outputs null.

Step 2 — Making sure we found something

If we haven’t set up our data sources correctly, we may end up without a result. So let’s make sure we really got one, for this, we can use the ASSERT block.

{
"type": "ASSERT",
"inPriority": [
{
"type": "PIPELINE_VALUE",
"inFrom": "index-maybe"
}
],
"fallback": {
"type": "REJECT",
"message": {
"type": "INLINE_VALUE",
"inFrom": {
"dataShape": {
"type": "text",
"nullable": false,
"list": false
},
"data": ["No discount percentage was found."]
}
}
},
"out": [
{
"outTo": "index"
}
]
}

The assert block takes in a list of inPriority items. It begins with the first one and moves down the list until it finds an item which is not nullish.

If all of the items are nullish, we resort to the fallback. In this case we do the REJECT fallback type, which exits the execution of the pipeline, and the also all parent pipelines it may be running within. It can be provided with a message. This is can be from any payloadlet of type text.

You can also provide an assert block with FALLBACK_DATA instead of rejecting. You can read more about the assert block in the rest of our documentation.

Now our “for-sure” index is outputted to a new pipeline value index . This one is now known to be non-nullish.

Step 3 — Picking the discount percentage from the other list

Now that we know the corresponding index to the discount percentage, we can use it to pick it out from that list using the operation block pick-list-item-by-index.r.1 .

And with that, we have our output: discount-percentage !

Done!

Now it’s time to create a yield pipeline.

Calculating the price with a yield pipelie

Now that we have gathered all the data we need from the customer, and are able to turn that data into a price using the process pipelines we created in the last step, we can create a yield pipeline.

The duty of the yield pipeline is to transform the collected data into payloadlets of type number, and finally tell Smidyo how to put together a total price from those numbers.

You know the drill, go Yield pipelines > New.

Paste in this code, and replace all references to test-salesdomain to the slug of your own sales domain.

Inputs

If you take a look at the inputs of this pipeline, you can see that they are pretty much the same as the outputs of the form pipeline we created earlier.

This is because we will later pair together the two when we create the offering.

Quote steps & Order steps

As you can see, we have both quoteSteps and orderSteps in our pipeline.

Quote steps get executed when we poll our yield pipeline for a price, for an instant price preview, for example.

And once the customer decides to place an order, the order steps get executed.

For now, let’s focus on the quote steps!

Step 1

In step 1, using internal source block TABLE_COLUMNS (like we also used in the form pipeline) we fetch the quantity discount breaks. We store column min-quantity in pipeline value min-quantities, and column discount-percentage in discount-percentages.

Step 2

Let’s use another internal source block: TABLE_CELLS. It works the same way as TABLE_COLUMNS, but instead of getting the entire column of data, we specify which specific row-name we want in the block's in parameter.

We fetch the row with the name provided from the pipeline input material-id, and store the data in pipeline values with the same name as the column.

Step 3

We can use the simple internal source block CONSTANT to get the cutting cost from the constant material-cutting-cost and store it in material-cost.

Step 4

Now let’s calculate what we need for the price. The square meter area, and the actual discount percentage.

For this, we use the SUB_PROCESS_PIPELINE block. Provide the slug of the process pipeline we created earlier in the property subProcessPipelineSlug (with the sales domain slug + .pp. ).

Feed the sub process pipeline with the width and the height, and from the pipeline we get the square area, and store it in the pipeline value m2.

{
"type": "SUB_PROCESS_PIPELINE",
"subProcessPipelineSlug": "test-salesdomain.pp.calculate-square-area",
"in": [
{
"type": "PIPELINE_VALUE",
"inFrom": "width-mm",
"inTo": "width-mm"
},
{
"type": "PIPELINE_VALUE",
"inFrom": "height-mm",
"inTo": "height-mm"
}
],
"out": [
{
"outFrom": "m2",
"outTo": "m2"
}
]
}

Step 5

Same deal here, but we use the get-discount-percentage process pipeline. Now we have the discount-percentage!

{
"type": "SUB_PROCESS_PIPELINE",
"subProcessPipelineSlug": "test-salesdomain.pp.get-discount-percentage",
"in": [
{
"type": "PIPELINE_VALUE",
"inFrom": "discount-percentages",
"inTo": "discount-percentages"
},
{
"type": "PIPELINE_VALUE",
"inFrom": "min-quantities",
"inTo": "min-quantities"
},
{
"type": "PIPELINE_VALUE",
"inFrom": "quantity",
"inTo": "quantity"
}
],
"out": [
{
"outFrom": "discount-percentage",
"outTo": "discount-percentage"
}
]
}

Defining the price sequence

Now we have all the data in the form of payloadlets of type number to put everything together into a price.

Let’s take a look at the yield pipeline’s quotePriceSequence property.

{
"type": "ADD",
"title": "Material",
"specification": {
"type": "PIPELINE_VALUE",
"inFrom": "material-name"
},
"name": "m2-price"
},
{
"type": "MULTIPLY",
"title": "Usage, m2",
"name": "m2"
},
{
"type": "ADD",
"title": "Cutting cost",
"name": "cutting-cost"
},
{
"type": "PERCENTAGE_OFF",
"title": "Quantity discount",
"name": "discount-percentage"
},
{
"type": "MULTIPLY",
"title": "Quantity",
"name": "quantity"
}

This should be pretty straight-forward;

  • Start from 0.
  • ADD the payloadlet stored in m2-price.
  • MULTIPLY that with the payloadlet stored in m2.
  • ADD cutting-cost.
  • Detract the percentage stored within discount-percentage, using PERCENTAGE_OFF.
  • MULTIPLY everything by quantity.

And we end up with a total price. Nice!

The specification is hi-lighted

As you can see, we can also provide a specification property for all steps in the price sequence. All this is is a reference to a pipeline value of type text, so we can provide a descriptive specification for that particular line. In this case, the title of the material.

Marrying the form pipeline to the yield pipeline

We can now finally create the actual offering! Go Offerings > New

​You can give it any slug you want, since we won’t be referencing it anymore in this tutorial.

Pick the yield pipeline we just created, and click Create.

Pairing the form- to the yield pipeline

An offering is made up from either just a yield pipeline, or a form pipeline + a yield pipeline.

In this case, since we want the offering to be purchasable by a human, we want to pair a form pipeline. Do this by selecting the form pipeline we created in the dropdown menu to the left.

As you can see, we now have the form pipeline’s outputs listed below. Connect the form pipeline’s outputs to the corresponding input of the yield pipeline.​

Now all we have to do is click Save. And we’re done!

You can now go to the sales domain, and your new offering will be listed in the menu! Try it out.

Nicely done!

That’s it for this little tutorial — feel free to reach out, or comment down below if you have any specific questions!

--

--