Stock Level by Scanning Barcode

So i have a setup where you add an order to the ticket, select it and press my Stock Level auto command button and it will show you the stock level in an ask question box. It uses the following in the ask question to display the stock level:

<color black><bold><size 40>{NAME} Stock Level: {REPORT INVENTORY:{ITEM TAG:Inventory Name}:Local Warehouse}</size></bold></color>

This outputs as:

What im trying to do now is get the ask question to display the stock level without adding an item to the ticket and by just scanning a barcode instead.

I have setup a program setting so that when a barcode is scanned it saves that barcode as a program setting.

I have an item tag setup on each product for barcode as below
image

So what i need to do is link the ITEM TAG:Barcode with the program setting barcode saved in the first step, into the below so it displays the quantity in an ask question

<color black><bold><size 40>{NAME} Stock Level: {REPORT INVENTORY:{ITEM TAG:Inventory Name}:Local Warehouse}</size></bold></color>

I know this isnt correct but something along the lines of the following

<color black><bold><size 40>{NAME} Stock Level: {REPORT INVENTORY:{ITEM TAG:Barcode = {SETTING:Stock Level Barcocde}:Local Warehouse}</size></bold></color>

This is the bit i need to sort if its possible?

{ITEM TAG:Barcode = {SETTING:Stock Level Barcocde}

1 Like

You wouldn’t put that in the display for Ask Question; you would put it in the Action Constraint (or Rule Constraint).d

However, you will not be able to read {ITEM TAG:Barcode} without selecting the item. The Order needs to be added to the Ticket to be able to read any information about it.

So you can watch Order Added event, read the Tag, store it and/or compare it, or whatever, and have a Cancel Order action in the Rule to remove the item.

1 Like

so im trying to get an order added to the ticket by pressing an auto commands button for “Stock Level Enquiry”, this opens a numberpad to set a program setting which is the barcode scanned.

Adding a show message action shows that the barcode scanned is successfully saved.

Now I want to add an order to the ticket based on this stored program setting. I have an custom item tag setup on each product called barcode. I was hoping i could then add the product to the ticket using the saved program setting barcode as the “tag” in the add order action
image

So my next question is how to i add an order to the ticket using that “tag” option in the add order action?

My tags for each product are as below:
image

I didnt know if the “tag” field represented the tag box under the group code box, or whether is represented a “Custom Tag” so i put the barcode 00001 in both places to test.

I cant get the order to add if i just use {SETTING: Stock Level Barcode} in the tag field, in this case that would equal 00001 and match the tag box on the product screen so though it would add but it doesnt.

If the "tag"field reads the custom tags then im not sure what to put in the add order action “tag” field as it would need to be {ITEM TAG:Barcode}={SETTING:Stock Level Balance} and im not sure how to format that in the action to work, any ideas?

There must be a way to use the tag field to add an order

ive also tried this in the tag field but it still doesnt work
image

It represents the Tag field only, not Custom Tags.

There is not a way to do Add Order action to add an Order by Tag, presumably because many Products could have the same Tag value. It can only be done by Menu Item Name (Product Name). The Tag field in the Add Order action is only for setting the Product Tag for the Order (which would presumably override the Product’s default Tag value), and nothing else.

Theoretically, the Add Order action should be able to add an Order by the Barcode (because they should be unique), however as we can clearly see, the action does not have that field. The Action would need to be updated to have that capability in a future version.

1 Like

Now that I think about it, that ^ does not make much sense to me (overriding the default Product Tag). Maybe it actually acts as a filter, even though no 2 Products having the same Name is allowed. However, the Menu Item Name in the Add Order action cannot be left empty AFAIK. So try this:

When you store the Barcode in a Program Setting, store the Product Name, and the default Product Tag as well. Then try the Add Order action while specifying values for both fields of Menu Item Name and Tag.

Maybe I don’t understand your desired flow. Or maybe you need to take it in a different direction.

Ive already got it setup so that i can add order to ticket, select the order, then press stock level button to display current stock level.

What i was trying to do was a setup where we press the stock level button first, this opens a [?prompt] keypad where we can scan the product barcode which then produced an ask question/show message etc with the current stock level

i was trying to find a way to link the scanning of the barcode to the product through either the “tag” or custom item tag options just so the flow was better without adding items to tickets and then removing them

Would be easy if we could access the barcode field lol

1 Like

The only thing I can think of is to:

  • use SQL to scan the [MenuItems] table for the Barcode
  • when found, get the Product Name from the table and store it in a Program Setting
  • run the Report Tag with {REPORT INVENTORY:{SETTING:Product Name}:Local Warehouse}`

Ooh, waitaminutethere … check out this GraphQL query - much easier!

It can actually get the Product information by searching Barcode, Product Tag (default or custom), and/or GroupCode!

query q1 {product:getProducts(barcode:"0001")
  {
    id,name,barcode,groupCode,price
  }
}

1 Like

Ive not used graphql yet so not sure how it works/set it up but this seems like it will be the best way to do what i want.

I also have a price enquiry button that when pressed you scan the barcode and an ask question show the product name and price, but this is still clunky, Graphql could probably solve that issue too.

My thoughts are to now combine those two buttons (price enquiry and stock level) and have one button called Product Information

Could graphql do this:

Product information button pressed
opens popup to scan the product barcode
graphql uses that barcode to identify the product
Display product information such as Name, Price, Stock Level etc?

If so how do i go about setting it up having never used graphql before?

Using GraphQL in JScript inside SambaPOS is fairly easy compared to development of a WebApp, since for most things we don’t need to mess with Authentication and Tokens, etc. We just run the queries and mutations and it gives back a response in JSON format. We then do JSON.parse(data) which gives us an Object which is easy to access the data as properties or attributes.

Query:

When we execute this Query …

query q1 {products:getProducts(barcode:"0001")
  {
    id,name,barcode,groupCode,price
  }
}

Return:

We get something like this in return:

{
  "data": {
    "products": [
      {
        "id": 48,
        "name": "Belmont Boost",
        "barcode": "0001",
        "groupCode": "Tobacco",
        "price": 4
      }
    ]
  },
  "errors": null
}

That ^ is JSON-formatted data. We then turn it into an Object using JSON.parse() …

Script:

Name: GQL
Handler: GQL

function getProductDataByBarcode(barcode,whatToReturn) {

// for testing
var barcode = '0001';
var whatToReturn = 'name';


// define the query/mutation
var qry = 'query q1 {products:getProducts(barcode:"' + barcode + '") {id,name,barcode,groupCode,price}}';

// execute
var respJson = gql.Exec(qry);

// convert JSON result to Object
var respObj = JSON.parse(respJson);

// get the first product in the products array at index 0
var product = respObj.data.products[0];

/* now we have access to these:
product.id         /  product["id"]
product.name       /  product["name"]
product.barcode    /  product["barcode"]
product.groupCode  /  product["groupCode"]
product.price      /  product["price"]
*/

// return data from function
return product[whatToReturn];

}

Test:

Here I comment out the “testing” values and use the Test facility to try out the script using barcode 0002 and return the price …

1 Like

i can copy that and do the test and it works, but how can i now:

  • Get the script to run when pressing an auto command button from pos screen
  • Use the barcode entered after pressing the auto command button do to the check against instead of the “hard coding” the 0002 for example that you did in your example

Ive got a price check button working already with previous help, however now that i have a second price list it lists all portions and all prices from different price lists. What needs to be changed in this script to only show the “main” price list?

heres the flow:

Press the price check button

Scan barcode

Price details displayed

The ÂŁ1 is from the second price list that i dont want to be displayed, if the product has portions heres how it displays:

All the portions with the second price list of ÂŁ1.00 i need to remove

heres the script

function checkPrice(inputBarcode) {																	
 	var qry = "SELECT m.[Name], s.[Name], p.[Price] FROM [MenuItemPrices] p LEFT JOIN [MenuItemPortions] s ON s.[Id] = p.[MenuItemPortionId] LEFT JOIN [MenuItems] m ON m.[Id] = s.[MenuItemId] WHERE m.[Barcode]='" + inputBarcode +"'";
	var priceList = sql.Query(qry).All;
	var priceCount = priceList.Length;
	var response = 'Product Name: ' + priceList[0].substr(0,priceList[0].indexOf(',')) + '\r';
	for (t = 0; t < priceCount; t++) {
		var list = priceList[t].split(','); //list[0] = Product Name, list[1] = Portion Name, list[2] = Price, 
		list[1] = list[1].replace('Normal','');
		list[2] = 'ÂŁ'+list[2];
		response += '\r' + list[1] + (list[1] ? ' ' : '') + list[2];
    }
	return response;
}

I was hoping that we could do something similar with stock level along the lines of the above, press the stock level button, enter barcode, ask question message appears with product name and current level without the need to add a product to the ticket like in the example above with the price check.

This is the ask question settings that displays the price check without the need to add product to the ticket

<bold><size 50>{CALL:Price Check Full.checkPrice('[?Scan Barcode;;;OCN]')}</size></bold>

UPDATE:
Ive managed to get the script you did (GQL) to work so that i press the auto command button, scan the barcode and the product name appears, as below

What can you add to the script so that it displays product name, portions (if any), prices (just off the 1 main price list), any of the custom product tags and if poss now that we have the name can we link to the inventory level?

So the output would be:

Product: Test Product
Price: ÂŁ10.00
Custom Tag: Info saved from product setup
Custom Tag: info saved from product setup

Current Stock Level: 100

This would create a Product information “screen” using the ask question to display

You could use the Product Name only if it was the same as the Inventory Item Name. Looking back to your original post, I see you use a Custom Tag for Inventory Name …

{REPORT INVENTORY:{ITEM TAG:Inventory Name}:Local Warehouse}

We can get the Tags of a Product using this query:

query q1 {
  product:getProduct
  (
    id:12, name:"Belmont Blue"
  )
  {
      id, name, barcode, groupCode, price
    , portions{id,name,productId,price}
    , tags{name,value}
  }
}

We need to use the id or the name parameter in that query (no need to use both) - I suggest using the id rather than the name. We already have the id and name from the Barcode query. You can see the query gives a lot of detail about a Product, including Portions with Prices and Tags.

// return product[whatToReturn];

qry = 'query q1 { product:getProduct(id:' + product.id + ') { id,name,barcode,groupCode,price, portions{id,name,productId,price}, tags{name,value} } }';

// execute
var respJson = gql.Exec(qry);

// convert JSON result to Object
var respObj = JSON.parse(respJson);

//dlg.AskQuestion(respJson,"Ok");
return respJson;


// getProduct() returns a single product, not an array, so no need for array index
product = respObj.data.product;

/* now we have access to these:
product.id         /  product["id"]
product.name       /  product["name"]
product.barcode    /  product["barcode"]
product.groupCode  /  product["groupCode"]
product.price      /  product["price"]
// product.portions is an array
product.portions[p].id
product.portions[p].name
product.portions[p].productId
product.portions[p].price
// product.tags is an array
product.tags[t].name
product.tags[t].value
*/

for (var t=0; t<product.tags.length; t++) {
  if (product.tags[t].name == 'inventoryName') {
    return product.tags[t].name + ' : ' + product.tags[t].value;
  }
}

image

Script

function getProductDataByBarcode(barcode,whatToReturn) {

// for testing
//var barcode = '0001';
//var whatToReturn = 'name';


// define the query/mutation
var qry = 'query q1 {products:getProducts(barcode:"' + barcode + '") {id,name,barcode,groupCode,price}}';

// execute
var respJson = gql.Exec(qry);

// convert JSON result to Object
var respObj = JSON.parse(respJson);

// getProducts() returns an array, potentially containing more than 1 product
// get the first product in the array at index 0
var product = respObj.data.products[0];

/* now we have access to these:
product.id         /  product["id"]
product.name       /  product["name"]
product.barcode    /  product["barcode"]
product.groupCode  /  product["groupCode"]
product.price      /  product["price"]
*/

// return data from function
//return product[whatToReturn];

qry = 'query q1 { product:getProduct(id:' + product.id + ') { id,name,barcode,groupCode,price, portions{id,name,productId,price}, tags{name,value} } }';

// execute
respJson = gql.Exec(qry);

// convert JSON result to Object
respObj = JSON.parse(respJson);

//dlg.AskQuestion(respJson,"Ok");
//return respJson;

// getProduct() returns a single product, not an array, so no need for array index
product = respObj.data.product;

/* now we have access to these:
product.id         /  product["id"]
product.name       /  product["name"]
product.barcode    /  product["barcode"]
product.groupCode  /  product["groupCode"]
product.price      /  product["price"]
// product.portions is an array
product.portions[p].id
product.portions[p].name
product.portions[p].productId
product.portions[p].price
// product.tags is an array
product.tags[t].name
product.tags[t].value
*/

var productData = '';
productData += 'Product: ' + product.name;
productData += '<br/>' + 'Price: ' + Helper.Format(product.price);

productData += '<br/>' + 'Portions:';
for (var p=0; p<product.portions.length; p++) {
  productData += '<br/>   ' + product.portions[p].name + ': ' + Helper.Format(product.portions[p].price);
}

productData += '<br/>' + 'Tags:';
for (var t=0; t<product.tags.length; t++) {
  productData += '<br/>   ' + product.tags[t].name + ': ' + product.tags[t].value;
  if (product.tags[t].name == whatToReturn) {
    return product.tags[t].value;
  }
}

if (whatToReturn == 'productData') {
  //dlg.AskQuestion(productData,"Ok");
  return productData;
}

return product[whatToReturn];
}

We cannot run Report Tags in JScript, so we need to invoke the script twice, and store the return in Program Settings.

First, get all the product data, which is nicely formatted in the script, like the image I showed above:

{CALL:GQL.getProductDataByBarcode('0002','productData')}

Then get the inventoryName Tag Value so we can use it in a Report Tag:

{CALL:GQL.getProductDataByBarcode('0002','inventoryName')}

My implementation …

2018-04-01_11-01-30


PRD Product Data [prd] (Script)

Script Name: PRD Product Data
Script Handler: prd

Script:

function getData(barcode,whatToReturn) {

// for testing
//var barcode = '0001';
//var whatToReturn = 'name';


// define the query/mutation
var qry = 'query q1 {products:getProducts(barcode:"' + barcode + '") {id,name,barcode,groupCode,price}}';

// execute
var respJson = gql.Exec(qry);

// convert JSON result to Object
var respObj = JSON.parse(respJson);

// getProducts() returns an array, potentially containing more than 1 product
// get the first product in the array at index 0
var product = respObj.data.products[0];

/* now we have access to these:
product.id         /  product["id"]
product.name       /  product["name"]
product.barcode    /  product["barcode"]
product.groupCode  /  product["groupCode"]
product.price      /  product["price"]
*/

// return data from function
//return product[whatToReturn];

qry = 'query q1 { product:getProduct(id:' + product.id + ') { id,name,barcode,groupCode,price, portions{id,name,productId,price}, tags{name,value} } }';

// execute
respJson = gql.Exec(qry);

// convert JSON result to Object
respObj = JSON.parse(respJson);

//dlg.AskQuestion(respJson,"Ok");
//return respJson;

// getProduct() returns a single product, not an array, so no need for array index
product = respObj.data.product;

/* now we have access to these:
product.id         /  product["id"]
product.name       /  product["name"]
product.barcode    /  product["barcode"]
product.groupCode  /  product["groupCode"]
product.price      /  product["price"]
// product.portions is an array
product.portions[p].id
product.portions[p].name
product.portions[p].productId
product.portions[p].price
// product.tags is an array
product.tags[t].name
product.tags[t].value
*/

var productData = '';
productData += 'Product: ' + product.name;
productData += '<br/>' + 'Price: ' + Helper.Format(product.price);

productData += '<br/>' + 'Portions:';
for (var p=0; p<product.portions.length; p++) {
  productData += '<br/>   ' + product.portions[p].name + ': ' + Helper.Format(product.portions[p].price);
}

productData += '<br/>' + 'Tags:';
for (var t=0; t<product.tags.length; t++) {
  productData += '<br/>   ' + product.tags[t].name + ': ' + product.tags[t].value;
  if (product.tags[t].name == whatToReturn) {
    return product.tags[t].value;
  }
}

if (whatToReturn == 'productData') {
  //dlg.AskQuestion(productData,"Ok");
  return productData;
}

return product[whatToReturn];
}

PRD Product Data [Products] (Automation Command)

Name: PRD Product Data
Category: Products
Button Header: Product\rData
Color:
Font Size: 26
Confirmation: None
Values (0): (none)

Navigation Settings

Symbol: î‚‘
Image:
Auto Refresh: 0
Tile Cache: 0
Navigation Module:
Nav Module Parameter:

Template:


Mappings

Terminal User Role Department Ticket Type Enabled States Visible States Visibility
Display on Navigation


PRD Store Value [Update Program Setting] (Action)

Action Name: PRD Store Value
Action Type: Update Program Setting

Parameters:

Setting Name: [:settingName]
Setting Value: [:settingValue]
Update Type: Update
Is Local: True

PRD Ask Question [Ask Question] (Action)

Action Name: PRD Ask Question
Action Type: Ask Question

Parameters:

Question: [:question]
Buttons: [:buttons]
Description: [:desc]
Automation Command Name: [:AMCname]
Execute Command In Background: [:execBG]
Background Color: [:bgColor]
Transparent Color: [:tpColor]
Inactivity Command Name: [:inacAMCname]
Inactivity Timeout Seconds: [:inacTimeoutSeconds]
Execute Inactivity Command In Background: [:inacExecBG]

PRD Product Data [Automation Command Executed] (Rule)

Rule Name: PRD Product Data
Event Name: Automation Command Executed
Rule Tags:
Custom Constraint List (1):
Execute Rule if: Matches
Automation Command NameEqualsPRD Product Data

Actions (5):

PRD Store Value

Constraint: (none)
settingName: PRD_productBarcode
settingValue:

[?Barcode;;;OCN]

PRD Store Value

Constraint: (none)
settingName: PRD_productData
settingValue:

{CALL:prd.getData('{SETTING:PRD_productBarcode}','productData')}

PRD Store Value

Constraint: (none)
settingName: PRD_inventoryName
settingValue:

{CALL:prd.getData('{SETTING:PRD_productBarcode}','inventoryName')}

PRD Store Value

Constraint: (none)
settingName: PRD_inventoryLevel
settingValue:

{REPORT INVENTORY:{SETTING:PRD_inventoryName}:SHOP}

PRD Ask Question

Constraint: (none)
question:

[='<size 18>' + '{SETTING:PRD_productData}' + '<br/><br/>StockLevel: ' + '{SETTING:PRD_inventoryLevel}' + '</size>']
buttons: Ok=ok
desc:
AMCname:
execBG: False
bgColor:
tpColor:
inacAMCname:
inacTimeoutSeconds: 5
inacExecBG:

1 Like

Brill thanks!! looks good

Im just having one issue, the stock level doesnt display it just shows blank, can you see where i went wrong in the actions, i can see a mistake anywhere
image

Displays:

Looks like you capitalized InventoryName it should be inventoryName in the CALL:X

Not sure, but if you look at my video, you will see product 0001 has no Stock Level either, but 0002 does have a Stock Level.

I did not look into it, but just assumed either there was no Inventory movement (ie. no data in Inventory Transactions), or maybe the Inventory Item does not exist yet.