GQL Modules - GraphQL Modules for Remote Client Browsers

#Customer Display

Use the HUB Actions and Rules. This Module also requires the Workperiod Status Report which is shown in the same section as the HUB Actions and Rules.

Additional components are shown next…

##Scripts

##GraphQL [GQL] (Script)##

Script Name: GraphQL
Script Handler: GQL

Script:

function getCurrentTicket(){
   var data = gql.Exec("{getCurrentTicket{id,number,date,type,uid,totalAmount,remainingAmount,entities{name,type},orders{quantity,name,portion,price,tags{tagName,tag,price,quantity,rate},states{stateName,state}}}}");
   return data;
}

#Actions

##BUS Store Setting [Update Program Setting] (Action)##

Action Name: BUS Store Setting
Action Type: Update Program Setting
###Parameters:###
Setting Name: [:settingName]
Setting Value: [:settingValue]
Update Type: Update
Is Local: [:isLocal]

##Rules

##BUS Store Business Settings [User Logged In] (Rule)##

Rule Name: BUS Store Business Settings
Event Name: User Logged In
Rule Tags:
Custom Constraint List (0):
Execute Rule if: Matches

##Actions (5):##

BUS Store Setting

Constraint: (none)

settingName: BUS_BusinessName
settingValue: My Awesome Restaurant
isLocal: False
BUS Store Setting

Constraint: (none)

settingName: BUS_VenueName
settingValue: My Awesome Restaurant
isLocal: False
BUS Store Setting

Constraint: (none)

settingName: BUS_MSG_Open
settingValue: Please place your Order when Ready!
isLocal: False
BUS Store Setting

Constraint: (none)

settingName: BUS_MSG_Closed
settingValue: Sorry, we are currently Closed.
isLocal: False
BUS Store Setting

Constraint: (none)

settingName: BUS_MSG_Welcome
settingValue: Welcome to
isLocal: False

##DEMO

Firefox on the left, SambaPOS on the right … there is a configurable timeout to go to “idle” screen after payment is made. This demo has it set to 15 seconds, so you will see a portion of the video where nothing is happening.

#Kitchen Display

Use the HUB Actions and Rules.

:exclamation: NOTE: The following documents the Actions/Rules that are required for the Javascript Kitchen Display Module. Actions required by a native Entity Screen are not shown, since the JS Module method supersedes the “old” method.

In addition, the Kitchen Display only supports a single display of a single productType … in this case, the setup for “Food” is shown, while “Drink” is not.

The Printer Template uses HTML and CSS to produce “colorful” Task Cards. You can edit zcss\sambapos.css to change the styles used in the Task Cards …

The Kitchen Display styles in sambapos.css are prefixed with KD_


##Task Type

Also, in the zconfigs\config.js file, find the following line and ensure it matches the Task Type Name that you used above …

var KD_HTMLtaskType = 'KD Task - Food';     // the KD Module works with this Task Type


##Printer

This is a Custom Printer for printing Tasks.

  • use a DOT (.) for the Printer Share Name / Port Name … if you don’t put a DOT (.) here, the Printer will not function!
  • click on the Settings link and enter the Task Type Name: KD Task - Food

##KD Printer - Food [Custom Printer] [Task Printer] (Printer)##

Printer Name: KD Printer - Food
Printer Share Name / Port Name: . (you must use a DOT here!)
Printer Type: Custom Printer [Task Printer]
Character Set: 437
RTL Support: None
Line Count: 0
Line Character Count: 42
Char Replacement:
Custom Task Printer Settings
Task Type: KD Task - Food

##Printer Template

##KD Template - Food (Printer Template)##

Template Name: KD Template - Food
Sort Orders:
Merge Lines: Don't Merge

Template:

[LAYOUT]
{ORDERS}

[ORDERS]
++{ORDER TIME} - {ENTITY NAME:Table} {ENTITY NAME:Customer}
(Id={NAME}-{ORDER UID})
(Color=#FF333333)
<div class="KD_Task_Product"><span class="KD_Task_Product_Quantity">[=('{QUANTITY}'>1 ? '{QUANTITY}' : '')]</span> {PRODUCT NAME}</div>
[='{PORTION}'=='' ? '' : '<div class="KD_Task_Portion">{PORTION}</div>']
{SORTED ORDER TAGS}

[ORDERS:Modified]
++{ORDER TIME} - {ENTITY NAME:Table} {ENTITY NAME:Customer}
(Id={NAME}-{ORDER UID})
(Color=#FF555533)
<div style="font-size:12px;">&nbsp;</div>
<div class="KD_Task_Modified">!!!! MODIFIED !!!!</div>
<div class="KD_Task_Product"><span class="KD_Task_Product_Quantity">[=('{QUANTITY}'>1 ? '{QUANTITY}' : '')]</span> {PRODUCT NAME}</div>
[='{PORTION}'=='' ? '' : '<div class="KD_Task_Portion">{PORTION}</div>']
{SORTED ORDER TAGS}

[ORDERS:Gift]
++{ORDER TIME} - {ENTITY NAME:Table} {ENTITY NAME:Customer}
(Id={NAME}-{ORDER UID})
(Color=#FF333333)
<div class="KD_Task_Product"><span class="KD_Task_Product_Quantity">[=('{QUANTITY}'>1 ? '{QUANTITY}' : '')]</span> {PRODUCT NAME}</div>
[='{PORTION}'=='' ? '' : '<div class="KD_Task_Portion">{PORTION}</div>']
{SORTED ORDER TAGS}

[ORDERS:Void]
++{ORDER TIME} - {ENTITY NAME:Table} {ENTITY NAME:Customer}
(Id={NAME}-{ORDER UID})
(Color=#FF550000)
<div style="font-size:12px;">&nbsp;</div>
<div class="KD_Task_Void">XXXX VOID XXXX</div>
<div class="KD_Task_Product">[=('{QUANTITY}'>1 ? ('{QUANTITY}'+'  ').substr(0,2) : '  ')] {PRODUCT NAME}</div>
[='{PORTION}'=='' ? '' : '<div class="KD_Task_Portion">{PORTION}</div>']

[SORTED ORDER TAGS:VIP Discount]
-- do not print

[SORTED ORDER TAGS:Happy Hour Discount]
-- do not print

[SORTED ORDER TAGS:Bread]
-- do not print

[SORTED ORDER TAGS:Meat]
<div class="KD_Task_OrderTags KD_Task_OrderTags_Meat"><span class="KD_Task_OrderTags_Quantity">[=('{ORDER TAG QUANTITY}'>1 ? '{ORDER TAG QUANTITY}'+'x' : '')]</span> {ORDER TAG NAME}</div>

[SORTED ORDER TAGS:Cheese]
<div class="KD_Task_OrderTags KD_Task_OrderTags_Cheese"><span class="KD_Task_OrderTags_Quantity">[=('{ORDER TAG QUANTITY}'>1 ? '{ORDER TAG QUANTITY}'+'x' : '')]</span> {ORDER TAG NAME}</div>

[SORTED ORDER TAGS:EXTRA Addons]
<div class="KD_Task_OrderTags KD_Task_OrderTags_ExtraAddons"><span class="KD_Task_OrderTags_Quantity">[=('{ORDER TAG QUANTITY}'>1 ? '{ORDER TAG QUANTITY}'+'x' : '')]</span> {ORDER TAG NAME}</div>

[SORTED ORDER TAGS:Veggies]
<div class="KD_Task_OrderTags KD_Task_OrderTags_Veggies"><span class="KD_Task_OrderTags_Quantity">[=('{ORDER TAG QUANTITY}'>1 ? '{ORDER TAG QUANTITY}'+'x' : '')]</span> {ORDER TAG NAME}</div>

[SORTED ORDER TAGS:Condiments]
<div class="KD_Task_OrderTags KD_Task_OrderTags_Condiments"><span class="KD_Task_OrderTags_Quantity">[=('{ORDER TAG QUANTITY}'>1 ? '{ORDER TAG QUANTITY}'+'x' : '')]</span> {ORDER TAG NAME}</div>

[SORTED ORDER TAGS:Sides]
<div class="KD_Task_OrderTags KD_Task_OrderTags_SideServings"><span class="KD_Task_OrderTags_Quantity">[=('{ORDER TAG QUANTITY}'>1 ? '{ORDER TAG QUANTITY}'+'x' : '')]</span> [='{ORDER TAG NAME}'.replace('Side ','')]</div>

[SORTED ORDER TAGS:Special Instructions]
<div class="KD_Task_OrderTags KD_Task_OrderTags_SpecialInstructions"><span class="KD_Task_OrderTags_Quantity">[=('{ORDER TAG QUANTITY}'>1 ? '{ORDER TAG QUANTITY}'+'x' : '')]</span> [='{ORDER TAG NAME}'.replace('SPIOTV ','')]</div>

[SORTED ORDER TAGS GROUP|ADD,Sauteed,OTS,Side,SPIOTV]

[SORTED ORDER TAGS GROUP:ADD]
<div class="KD_Task_OrderTagGroupHeader">     ~~~ ADDONS ~~~</div>

[SORTED ORDER TAGS GROUP:Sauteed]
<div class="KD_Task_OrderTagGroupHeader">     ~~~ SAUTEED ~~~</div>

[SORTED ORDER TAGS GROUP:OTS]
<div class="KD_Task_OrderTagGroupHeader">     ~~~ ON THE SIDE ~~~</div>

[SORTED ORDER TAGS GROUP:Side]
<div class="KD_Task_OrderTagGroupHeader">     ~~~ SIDES ~~~</div>

[SORTED ORDER TAGS GROUP:SPIOTV]
<div class="KD_Task_OrderTagGroupHeader">     ~~~ SPECIAL ~~~</div>


##Print Job

##KD Print Tasks - ANY [All Lines] (Print Job)##

Print Job Name: KD Print Tasks - ANY
Printing Content: All Lines
Always Exclude Tax: unchecked
Mappings (2)
DepartmentTicket TypeTerminalProduct GroupProduct TagProductPrinterPrinter Template
****productType=Food*KD Printer - FoodKD Template - Food
****productType=Drink*KD Printer - DrinkKD Template - Drink

##Custom Product Tag Caption

Create a Product Tag Caption in Manage > Settings > Program Settings with the name productType. This will be used to separate Food and Drink.


##Product Tag Editor

Set your Food and Drink Products to have the proper productType in Manage > Products > Product Tag Editor


##Entity Screen

##KD Kitchen Display [] (Entity Screen)##

Name: KD Kitchen Display
Ticket Type: Ticket
View Mode: Layout
Search Value Replace Pattern:
Appearance
Background Image:
Background Color: #00FFFFFF
Use State Display Format: unchecked
Column Count: 0
Row Count: 0
Button Height: 0
Page Count: 1
Font Size: 50
Header Button Font Size: 0
Entity List (0)
Entity Type:
Display State:
State Filter:
Entities: (none)
Details Template (none)(none)
Mappings (1)
Terminal User Role Department Ticket Type Visibility
****All

##Entity Screen Widgets

##KD HTML Viewer [Html Viewer] (Widget)##
Entity Screen: KD Kitchen Display

###Properties (13):###

Widget Type: Html Viewer
Name: KD HTML Viewer
X: 0
Y: 0
Height: 100
Width: 100
Zindex: 0
Angle: 0
Scale: 0
Corner Radius: 0
Auto Refresh: checked
Auto Refresh Interval: 0
Margin:

###Settings (4):###

Allow Scripting: checked
Is Toolbar Visible: unchecked
Url: http://localhost/?module=kitchen_display
Zoom: 100

##Automation Command

##KD Kitchen Display [KD Kitchen Display] (Automation Command)##

Name: KD Kitchen Display
Category: KD Kitchen Display
Button Header: Kitchen\rDisplay
Color: #FFCA6919
Font Size: 26
Confirmation: None
Values (0): (none)
Navigation Settings
Symbol:
Image:
Auto Refresh: 0
Tile Cache: 0
Navigation Module: Entity
Nav Module Parameter: KD Kitchen Display
Template: ```

</details>

<details>
<summary><b><u>Mappings</u></b></summary><table><tr><td><b>Terminal</b> </td><td><b>User Role</b> </td><td><b>Department</b> </td><td><b>Ticket Type</b> </td><td><b>Enabled States</b> </td><td><b>Visible States</b> </td><td><b>Visibility</b> </td></tr><tr><td><code>*</code></td><td><code>*</code></td><td><code>*</code></td><td><code>*</code></td><td><code>*</code></td><td><code>*</code></td><td><code>Display on Navigation</code></td></tr></table></details>

----------

##Actions

<img src="/uploads/default/original/3X/7/4/748ed1ff1a088c0915542335b07bae22cc9909c7.png" width="690" height="427">

>##KD Update Order KDStatus `[Update Order State]` (Action)##
<table><tr><td><b>Action Name:</b> </td><td><code>KD Update Order KDStatus</code></td></tr><tr><td><b>Action Type:</b> </td><td><code>Update Order State</code></td></tr></table>
###Parameters:###
<table><tr><td><b>State Name:</b> </td><td><code>KDStatus</code></td></tr><tr><td><b>Group Order:</b> </td><td><code></code></td></tr><tr><td><b>Current State:</b> </td><td><code>[:CurrentState]</code></td></tr><tr><td><b>State:</b> </td><td><code>[:NewState]</code></td></tr><tr><td><b>State Order:</b> </td><td><code></code></td></tr><tr><td><b>State Value:</b> </td><td><code></code></td></tr></table>

----------

<img src="/uploads/default/original/3X/9/d/9da4633213c3cd7bebb5610ff1c06c2eb1b31e51.png" width="690" height="560">

>##KD Execute Print Job - ANY `[Execute Print Job]` (Action)##
<table><tr><td><b>Action Name:</b> </td><td><code>KD Execute Print Job - ANY</code></td></tr><tr><td><b>Action Type:</b> </td><td><code>Execute Print Job</code></td></tr></table>
###Parameters:###
<table><tr><td><b>Print Job Name:</b> </td><td><code>KD Print Tasks - ANY</code></td></tr><tr><td><b>Print Ticket:</b> </td><td><code>True</code></td></tr><tr><td><b>Ticket Ids:</b> </td><td><code></code></td></tr><tr><td><b>High Priority:</b> </td><td><code>[:hiPriority]</code></td></tr><tr><td><b>Order State Name:</b> </td><td><code>[:orderStateName]</code></td></tr><tr><td><b>Order State:</b> </td><td><code>[:orderState]</code></td></tr><tr><td><b>Order State Value:</b> </td><td><code></code></td></tr><tr><td><b>Order Tag Name:</b> </td><td><code></code></td></tr><tr><td><b>Order Tag Value:</b> </td><td><code></code></td></tr><tr><td><b>Ignore Selected Orders:</b> </td><td><code>[:ignoreSelectedOrders]</code></td></tr><tr><td><b>Copies:</b> </td><td><code>1</code></td></tr></table>

----------

##Rules

<img src="/uploads/default/original/3X/9/d/9d16d940859216040ee6b913b49d8ad74725b6be.png" width="690" height="725">

>##KD Order Added to Ticket - ANY `[Order Added]` (Rule)##
<table><tr><td><b>Rule Name:</b> </td><td><code>KD Order Added to Ticket - ANY</code></td></tr><tr><td><b>Event Name:</b> </td><td><code>Order Added</code></td></tr><tr><td><b>Rule Tags:</b> </td><td><code></code></td></tr></table>
<i>Custom Constraint List (0):</i>
<table><tr><td><b>Execute Rule if:</b> </td><td><code>Matches</code></td></tr></table>
<table></table>

##Actions (2):##

<details>
<summary><b><u>KD Update Order KDStatus</u></b></summary>

<b>Constraint:</b> <code>'{ITEM TAG:productType}' == 'Food'</code>
<table><tr><td><b>CurrentState:</b> </td><td><code> </code></td></tr><tr><td><b>NewState:</b> </td><td><code>FNotPrinted</code></td></tr></table></details>

<details>
<summary><b><u>KD Update Order KDStatus</u></b></summary>

<b>Constraint:</b> <code>'{ITEM TAG:productType}' == 'Drink'</code>
<table><tr><td><b>CurrentState:</b> </td><td><code> </code></td></tr><tr><td><b>NewState:</b> </td><td><code>DNotPrinted</code></td></tr></table></details>

##Mappings##



<details>
<summary><b><u>Mappings</u></b></summary><table><tr><td><b>Terminal</b> </td><td><b>User Role</b> </td><td><b>Department</b> </td><td><b>Ticket Type</b> </td></tr><tr><td><code>*</code></td><td><code>*</code></td><td><code>*</code></td><td><code>*</code></td></tr></table></details>


----------

<img src="/uploads/default/original/3X/c/a/cafe4a82c8970752f88a38728ff487f1566ffbeb.png" width="690" height="716">

>##KD Update Printed Status - Food - Void or Cancel Void `[Automation Command Executed]` (Rule)##
<table><tr><td><b>Rule Name:</b> </td><td><code>KD Update Printed Status - Food - Void or Cancel Void</code></td></tr><tr><td><b>Event Name:</b> </td><td><code>Automation Command Executed</code></td></tr><tr><td><b>Rule Tags:</b> </td><td><code></code></td></tr></table>
<i>Custom Constraint List (3):</i>
<table><tr><td><b>Execute Rule if:</b> </td><td><code>Matches</code></td></tr></table>
<table><tr><td><code>Automation Command Name</code></td><td><b><i>Equals</i></b></td><td><code>Void</code></td></tr><tr><td><code>Automation Command Name</code></td><td><b><i>Equals</i></b></td><td><code>Cancel Void</code></td></tr><tr><td><code>{ITEM TAG:productType}</code></td><td><b><i>Contains</i></b></td><td><code>Food</code></td></tr></table>

##Actions (1):##

<details>
<summary><b><u>KD Update Order KDStatus</u></b></summary>

<b>Constraint:</b> <i>(none)</i>
<table><tr><td><b>CurrentState:</b> </td><td><code> </code></td></tr><tr><td><b>NewState:</b> </td><td><code>FNotPrinted</code></td></tr></table></details>

##Mappings##



<details>
<summary><b><u>Mappings</u></b></summary><table><tr><td><b>Terminal</b> </td><td><b>User Role</b> </td><td><b>Department</b> </td><td><b>Ticket Type</b> </td></tr><tr><td><code>*</code></td><td><code>*</code></td><td><code>*</code></td><td><code>*</code></td></tr></table></details>


----------

<img src="/uploads/default/original/3X/9/9/991428b714e31d1a69b00042c5d4f91383e338cf.png" width="690" height="609">

>##KD Ticket Closing - Update KDStatus - Food `[Ticket Closing]` (Rule)##
<table><tr><td><b>Rule Name:</b> </td><td><code>KD Ticket Closing - Update KDStatus - Food</code></td></tr><tr><td><b>Event Name:</b> </td><td><code>Ticket Closing</code></td></tr><tr><td><b>Rule Tags:</b> </td><td><code></code></td></tr></table>
<i>Custom Constraint List (0):</i>
<table><tr><td><b>Execute Rule if:</b> </td><td><code>Matches</code></td></tr></table>
<table></table>

##Actions (1):##

<details>
<summary><b><u>KD Update Order KDStatus</u></b></summary>

<b>Constraint:</b> <i>(none)</i>
<table><tr><td><b>CurrentState:</b> </td><td><code>FNotPrinted</code></td></tr><tr><td><b>NewState:</b> </td><td><code>FPrinting</code></td></tr></table></details>

##Mappings##



<details>
<summary><b><u>Mappings</u></b></summary><table><tr><td><b>Terminal</b> </td><td><b>User Role</b> </td><td><b>Department</b> </td><td><b>Ticket Type</b> </td></tr><tr><td><code>*</code></td><td><code>*</code></td><td><code>*</code></td><td><code>*</code></td></tr></table></details>


----------

<img src="/uploads/default/original/3X/4/1/4127485ec9b148571366b6e19a7b1144e1fbf96d.png" width="605" height="1000">

>##KD Print Order - Food `[Order State Updated]` (Rule)##
<table><tr><td><b>Rule Name:</b> </td><td><code>KD Print Order - Food</code></td></tr><tr><td><b>Event Name:</b> </td><td><code>Order State Updated</code></td></tr><tr><td><b>Rule Tags:</b> </td><td><code></code></td></tr></table>
<i>Custom Constraint List (4):</i>
<table><tr><td><b>Execute Rule if:</b> </td><td><code>Matches</code></td></tr></table>
<table><tr><td><code>State Name</code></td><td><b><i>Equals</i></b></td><td><code>KDStatus</code></td></tr><tr><td><code>State</code></td><td><b><i>Equals</i></b></td><td><code>FPrinting</code></td></tr><tr><td><code>Previous State</code></td><td><b><i>Equals</i></b></td><td><code>FNotPrinted</code></td></tr><tr><td><code>{ITEM TAG:productType}</code></td><td><b><i>Contains</i></b></td><td><code>Food</code></td></tr></table>

##Actions (3):##

<details>
<summary><b><u>KD Execute Print Job - ANY</u></b></summary>

<b>Constraint:</b> <i>(none)</i>
<table><tr><td><b>hiPriority:</b> </td><td><code> </code></td></tr><tr><td><b>orderStateName:</b> </td><td><code>KDStatus</code></td></tr><tr><td><b>orderState:</b> </td><td><code>FPrinting</code></td></tr><tr><td><b>ignoreSelectedOrders:</b> </td><td><code> </code></td></tr></table></details>

<details>
<summary><b><u>KD Update Order KDStatus</u></b></summary>

<b>Constraint:</b> <i>(none)</i>
<table><tr><td><b>CurrentState:</b> </td><td><code>FPrinting</code></td></tr><tr><td><b>NewState:</b> </td><td><code>FPrinted</code></td></tr></table></details>

<details>
<summary><b><u>HUB ExecAMC</u></b></summary>

<b>Constraint:</b> <i>(none)</i>
<table><tr><td><b>AMCname:</b> </td><td><code>HUB Broadcast - Task Printed</code></td></tr><tr><td><b>AMCvalue:</b> </td><td><code>{ITEM TAG:productType}</code></td></tr><tr><td><b>runInBG:</b> </td><td><code> </code></td></tr><tr><td><b>delay:</b> </td><td><code> </code></td></tr></table></details>

##Mappings##



<details>
<summary><b><u>Mappings</u></b></summary><table><tr><td><b>Terminal</b> </td><td><b>User Role</b> </td><td><b>Department</b> </td><td><b>Ticket Type</b> </td></tr><tr><td><code>*</code></td><td><code>*</code></td><td><code>*</code></td><td><code>*</code></td></tr></table></details>

#CHAT

##Task Type

There used to be some Actions and Rules for CHAT, but I removed them. Instead, I embed the JS CHAT module in an HTML Viewer Widget on a Custom Entity Screen set to Layout Mode.


##Script

Used to parse the JSON string that contains the message, user, terminal, etc. This is used purely for when running the Module embedded in a SambaPOS HTML Viewer Widget, so that it can display a Popup in the bottom-right of the screen when a new message is received.

##CHAT [chat] (Script)##

Script Name: CHAT
Script Handler: chat

Script:

function receive(d,p) {
  var m = JSON.parse(d)
  var usr  = (m["userName"] ? m["userName"] : '');
  var term = (m["terminal"] ? m["terminal"] : '');
  var sid  = (m["sid"] ? m["sid"] : '');
  var msg  = (m["message"] ? m["message"] : '');

  switch (p) {
    case 'user':
      return usr;
      break;
    case 'terminal':
      return term;
      break;
    case 'sid':
      return sid;
      break;
    case 'msg':
      return msg;
      break;
    default:
      break;
  }

  return '['+term+'] '+usr+': '+msg;  
}

##Entity Screen

A Custom Entity Screen with the View Mode set to “Layout”.

:bulb: This technique can be used to embed ANY of the modules into SambaPOS. For example, I embedded Kitchen Display, Timeclock, and Reports in this way.

##CHAT Display [UNKNOWN] (Entity Screen)##

Name: CHAT Display
Ticket Type: Ticket
View Mode: Layout
Search Value Replace Pattern:
Appearance
Background Image:
Background Color: #00FFFFFF
Use State Display Format: unchecked
Column Count: 0
Row Count: 0
Button Height: 0
Page Count: 1
Font Size: 50
Header Button Font Size: 0
Entity List (0)
Entity Type: UNKNOWN
Display State:
State Filter:
Entities: (none)
Details Template (none)(none)
Mappings (1)
Terminal User Role Department Ticket Type Visibility
****All

##Automation Command

A button for your Nav Screen to load/display the Entity Screen.

##CHAT Show Chat Display [Messaging] (Automation Command)##

Name: CHAT Show Chat Display
Category: Messaging
Button Header: CHAT\rMessages
Color: #FFC0504D
Font Size: 26
Confirmation: None
Values (0): (none)
Navigation Settings
Symbol:
Image:
Auto Refresh: 0
Tile Cache: 0
Navigation Module: Entity
Nav Module Parameter: CHAT Display
Template: ``` [CHAT Messages:1] {REPORT TASK DETAILS: T.StartDate,'',T.StartTime,TSC.terminal,TSC.user,T.ContentText: (TST=CHAT Message Task) AND T.Completed=False: [=FD('{0}','yyyy-MMM-dd')] [=FD('{0}','ddd')] {2} {3} {4} >> {5} } ```
Mappings
Terminal User Role Department Ticket Type Enabled States Visible States Visibility
******Display on Navigation

##HTML Viewer Widget Settings

Navigate to the CHAT Screen, go into Design Mode, add an HTML Viewer Widget and modify the Settings as such:

  • URL: http://localhost/?module=chat

  • Enable Scripting: CHECKED

For the URL, supply the correct URL for your Webserver (and optionally the Port). The URL can be a hostname or IP address, and for most users, the Port can be ommitted. Then specify the Module that you wish to use in the HTML Viewer with this syntax:

http://yourServer:optionalPort/?module=chat

For other Modules, such as Kitchen Display, it would look like this:

http://localhost/?module=kitchen_display

:exclamation: The Allow Scripting CheckBox is required so that the JS can know that the Module is running within SambaPOS.

#Timeclock & Timeclock Policies

:warning: WARNING: The Reports (@@SQL handlers) for Timeclock use JSON functions available only in SQL 2016, so you will need to upgrade to SQL Express 2016 to use the Reporting features.


##Task Types

##TC Punch Task (Task Type)##

Name: TC Punch Task
Custom Fields(none)

##TC Punch Control Task (Task Type)##

Name: TC Punch Control Task
Custom Fields(none)

##TC Policy Task (Task Type)##

Name: TC Policy Task
Custom Fields
Field Name Field Type Editing Format Display Format
activeNumber
typeString
nameString
dateStartDate
dateEndDate
valueString

##Scripts (SQL)

##TC_EmployeeHours [@@TC_EmployeeHours] (Script)##

Script Name: TC_EmployeeHours
Script Handler: @@TC_EmployeeHours

Script:

-- Hours 15 and 30

-- PARM for Employee Entity Type
declare @entityType varchar(20)  = '@1'
-- PARM for Employee Name
declare @entityName varchar(20)  = '@2'
-- PARM for Date Filter Start
declare @StartDateIn varchar(25) = '@3'
-- PARM for Date Filter End
declare @EndDateIn varchar(25)   = '@4'

-- if Employee Entity Type is invalid, set default as 'Employees'
IF (@entityType = '') OR (@entityType is null) OR (@entityType = '$1') SET @entityType = 'Employees'

-- if Date Filter START is invalid, set default to beginning of Current Month
IF (@StartDateIn = '') OR (@StartDateIn is null) OR (@StartDateIn = '$3') SET @StartDateIn = left(CONVERT(VARCHAR(25), GETDATE(), 126),7)+'-01'

-- if Date Filter END is invalid, set a default
IF (@EndDateIn = '') OR (@EndDateIn is null) OR (@EndDateIn = '$4') SET @EndDateIn  = dateadd(Month,1,@StartDateIn)

-- set START and END date for Report Period
declare @StartDate datetime = convert(varchar(25),@StartDateIn,126)
declare @EndDate datetime = convert(varchar(25),@EndDateIn,126)
declare @EndDateInc datetime = convert(varchar(25),dateadd(day,-1,@EndDate),126)

-- set names of Employee Custom Data Rate Fields
declare @RateField1 varchar(20) = 'Rate1'
declare @RateField2 varchar(20) = 'Rate2'
declare @RateField3 varchar(20) = 'Rate3'




-- NOTHING TO SET BEYOND HERE

DECLARE @RATE1 money = 0
DECLARE @RATE2 money = 0
DECLARE @RATE3 money = 0

-- get RATES

SELECT
--[Name] as [EntityName]
  @RATE1 = SUM([R1])
, @RATE2 = SUM([R2])
, @RATE3 = SUM([R3])
FROM(
SELECT
 e.[Id]
,e.[Name]
--,e.[CustomData]
,cdName
,CASE cdName WHEN @RateField1 THEN convert(decimal(10,2),cdValue) ELSE 0.00 END as [R1]
,CASE cdName WHEN @RateField2 THEN convert(decimal(10,2),cdValue) ELSE 0.00 END as [R2]
,CASE cdName WHEN @RateField3 THEN convert(decimal(10,2),cdValue) ELSE 0.00 END as [R3]
FROM [Entities] e
JOIN [EntityTypes] et on et.[Id] = e.[EntityTypeId]
-- here we "join" the [Entities] table to itself and use the OPENJSON function
-- on the [CustomData] column
CROSS APPLY OPENJSON(e.[CustomData])
-- this WITH portion allows explicit definition of the schema JSON Keys for output
-- and gives references to the columns/field above in the SELECT portion
WITH (   
 cdName         varchar(50) '$.Name'
,cdValue        varchar(50) '$.Value'
) jsonData
WHERE et.[Name] = @entityType
   AND e.[Name] = @entityName
   AND cdName IN (@RateField1,@RateField2,@RateField3)
) rates
--GROUP BY [Name]



-- get Policies

DECLARE @policies table
(
[ID]  INT IDENTITY(1,1) NOT NULL 
,[taskId] int
,[taskName] varchar(255)
,[taskContent] varchar(255)
)

INSERT INTO @policies
SELECT
 t.[Id]
,t.[Name]
,t.[Content]
FROM [Tasks] t
JOIN [TaskTypes] tt on tt.[Id] = t.[TaskTypeId]
WHERE 1=1
AND tt.[Name] = 'TC Policy Task'
AND [Completed] = 0


-- Calculate

SELECT
-- [EmployeeName]
-- [StartDate]
--,[EndDate]
left(datename(YEAR,[StartDate]),4)+' '+left(datename(MONTH,[StartDate]),3)+' '+substring(convert(varchar,[StartDate],126),9,2) as [DT]
,left(DATENAME(weekday,[StartDate]),3) as [DT]
--,[H]
,[Hours]
,[REG]
,[OT]
,[HOL]
--,FORMAT(SUM([REG]*@RATE1 + [OT]*@RATE2 + [HOL]*@RATE3) ,'0000.00') as [EARNED]
FROM (
SELECT
 t.[Id]
,tt.[Name] as [TaskType]
,t.[Name] as [EmployeeName]
--,[Identifier]
--,[Completed]
--,[State]
--,substring(convert(varchar(10),[StartDate],126),6,5)
,[StartDate]
,[EndDate]
--,datediff(SECOND,t.[StartDate],t.[EndDate]) as [DUR_s]
--,datediff(SECOND,t.[StartDate],t.[EndDate])/60.0 as [DUR_m]
--,datediff(SECOND,t.[StartDate],t.[EndDate])/60.0/60.0 as [DUR_h]
,FORMAT(
datediff(SECOND,t.[StartDate],t.[EndDate])/60.0/60.0
,'0.00')
 as [Hours]

,CASE ISNULL(( SELECT [taskContent] FROM @policies WHERE [taskName] like '%Holiday%' AND [taskContent]=substring(convert(varchar(10),[StartDate],126),6,5) ),0) WHEN '0' THEN '0' ELSE '1' END as [H]

-- HOLIDAY
,FORMAT(
 CASE WHEN ISNULL(( SELECT [taskContent] FROM @policies WHERE [taskName] like '%Holiday%' AND [taskContent]=substring(convert(varchar(10),[StartDate],126),6,5) ),0) != '0'
 THEN datediff(SECOND,t.[StartDate],t.[EndDate])/60.0/60.0
 ELSE 0
 END ,'0.00') as [HOL]

-- OVERTIME
,FORMAT(
 CASE WHEN datediff(SECOND,t.[StartDate],t.[EndDate])/60.0/60.0  > ISNULL((SELECT [taskContent] FROM @policies WHERE [taskName] like '%OT Hour Limit%'),9999) AND (ISNULL(( SELECT [taskContent] FROM @policies WHERE [taskName] like '%Holiday%' AND [taskContent]=substring(convert(varchar(10),[StartDate],126),6,5) ),0) = '0')
 THEN (datediff(SECOND,t.[StartDate],t.[EndDate])/60.0/60.0) - ISNULL((SELECT [taskContent] FROM @policies WHERE [taskName] like '%OT Hour Limit%'),9999)
 ELSE 0
 END ,'0.00') as [OT]

-- REGULAR
,FORMAT(
 CASE WHEN (ISNULL(( SELECT [taskContent] FROM @policies WHERE [taskName] like '%Holiday%' AND [taskContent]=substring(convert(varchar(10),[StartDate],126),6,5) ),0) != '0')
 THEN 0
 ELSE CASE WHEN datediff(SECOND,t.[StartDate],t.[EndDate])/60.0/60.0  > ISNULL((SELECT [taskContent] FROM @policies WHERE [taskName] like '%OT Hour Limit%'),9999)
           THEN 8
	       ELSE (datediff(SECOND,t.[StartDate],t.[EndDate])/60.0/60.0)
	       END
 END ,'0.00') as [REG]

--,[LastUpdateTime]
--,[Content]
--,[CustomData]
--,[StateLog]
--,[UserName]
FROM [Tasks] t
JOIN [TaskTypes] tt on tt.[Id] = t.[TaskTypeId]
WHERE 1=1
AND t.[Completed] = 1
AND t.[Name] = @entityName
AND t.[StartDate] >= @StartDate
AND t.[StartDate] <  @EndDate
AND tt.[Name] = 'TC Punch Control Task'
--AND t.[State] = 'Punch Cycle Complete'
----ORDER BY t.[Name], t.[StartDate],t.[EndDate]
) HRS

GROUP BY
-- [EmployeeName]
 [StartDate]
--,[EndDate]
,left(datename(YEAR,[StartDate]),4)+' '+left(datename(MONTH,[StartDate]),3)+' '+substring(convert(varchar,[StartDate],126),9,2)
,left(DATENAME(weekday,[StartDate]),3)
--,[H]
,[Hours]
,[HOL]
,[OT]
,[REG]
ORDER BY 1,2,3


##TC_EmployeeHoursTTL [@@TC_EmployeeHoursTTL] (Script)##

Script Name: TC_EmployeeHoursTTL
Script Handler: @@TC_EmployeeHoursTTL

Script:

-- Hours 15 and 30

-- PARM for Employee Entity Type
declare @entityType varchar(20)  = '@1'
-- PARM for Employee Name
declare @entityName varchar(20)  = '@2'
-- PARM for Date Filter Start
declare @StartDateIn varchar(25) = '@3'
-- PARM for Date Filter End
declare @EndDateIn varchar(25)   = '@4'

-- if Employee Entity Type is invalid, set default as 'Employees'
IF (@entityType = '') OR (@entityType is null) OR (@entityType = '$1') SET @entityType = 'Employees'

-- if Date Filter START is invalid, set default to beginning of Current Month
IF (@StartDateIn = '') OR (@StartDateIn is null) OR (@StartDateIn = '$3') SET @StartDateIn = left(CONVERT(VARCHAR(25), GETDATE(), 126),7)+'-01'

-- if Date Filter END is invalid, set a default
IF (@EndDateIn = '') OR (@EndDateIn is null) OR (@EndDateIn = '$4') SET @EndDateIn  = dateadd(Month,1,@StartDateIn)

-- set START and END date for Report Period
declare @StartDate datetime = convert(varchar(25),@StartDateIn,126)
declare @EndDate datetime = convert(varchar(25),@EndDateIn,126)
declare @EndDateInc datetime = convert(varchar(25),dateadd(day,-1,@EndDate),126)

-- set names of Employee Custom Data Rate Fields
declare @RateField1 varchar(20) = 'Rate1'
declare @RateField2 varchar(20) = 'Rate2'
declare @RateField3 varchar(20) = 'Rate3'




-- NOTHING TO SET BEYOND HERE

DECLARE @RATE1 money = 0
DECLARE @RATE2 money = 0
DECLARE @RATE3 money = 0

-- get RATES

SELECT
--[Name] as [EntityName]
  @RATE1 = SUM([R1])
, @RATE2 = SUM([R2])
, @RATE3 = SUM([R3])
FROM(
SELECT
 e.[Id]
,e.[Name]
--,e.[CustomData]
,cdName
,CASE cdName WHEN @RateField1 THEN convert(decimal(10,2),cdValue) ELSE 0.00 END as [R1]
,CASE cdName WHEN @RateField2 THEN convert(decimal(10,2),cdValue) ELSE 0.00 END as [R2]
,CASE cdName WHEN @RateField3 THEN convert(decimal(10,2),cdValue) ELSE 0.00 END as [R3]
FROM [Entities] e
JOIN [EntityTypes] et on et.[Id] = e.[EntityTypeId]
-- here we "join" the [Entities] table to itself and use the OPENJSON function
-- on the [CustomData] column
CROSS APPLY OPENJSON(e.[CustomData])
-- this WITH portion allows explicit definition of the schema JSON Keys for output
-- and gives references to the columns/field above in the SELECT portion
WITH (   
 cdName         varchar(50) '$.Name'
,cdValue        varchar(50) '$.Value'
) jsonData
WHERE et.[Name] = @entityType
   AND e.[Name] = @entityName
   AND cdName IN (@RateField1,@RateField2,@RateField3)
) rates
--GROUP BY [Name]



-- get Policies

DECLARE @policies table
(
[ID]  INT IDENTITY(1,1) NOT NULL 
,[taskId] int
,[taskName] varchar(255)
,[taskContent] varchar(255)
)

INSERT INTO @policies
SELECT
 t.[Id]
,t.[Name]
,t.[Content]
FROM [Tasks] t
JOIN [TaskTypes] tt on tt.[Id] = t.[TaskTypeId]
WHERE 1=1
AND tt.[Name] = 'TC Policy Task'
AND [Completed] = 0


-- Calculate

SELECT
-- [EmployeeName]
-- [StartDate]
--,[EndDate]
 left(datename(YEAR,[StartDate]),4)+' '+left(datename(MONTH,[StartDate]),3)+' TTL' as [DT]
,'TOTALS'
--,left(DATENAME(weekday,[StartDate]),3) as [DT]
--,[H]
--,[Hours]
,FORMAT(sum([Hours]) ,'0.00') as [Hours]
,FORMAT(sum([REG]) ,'0.00') as [REG]
,FORMAT(sum([OT]) ,'0.00') as [OT]
,FORMAT(sum([HOL]) ,'0.00') as [HOL]
--,FORMAT(SUM([REG]*@RATE1 + [OT]*@RATE2 + [HOL]*@RATE3) ,'0000.00') as [EARNED]
FROM (
SELECT
 t.[Id]
,tt.[Name] as [TaskType]
,t.[Name] as [EmployeeName]
--,[Identifier]
--,[Completed]
--,[State]
--,substring(convert(varchar(10),[StartDate],126),6,5)
,[StartDate]
,[EndDate]
--,datediff(SECOND,t.[StartDate],t.[EndDate]) as [DUR_s]
--,datediff(SECOND,t.[StartDate],t.[EndDate])/60.0 as [DUR_m]
--,datediff(SECOND,t.[StartDate],t.[EndDate])/60.0/60.0 as [DUR_h]
,--FORMAT(
datediff(SECOND,t.[StartDate],t.[EndDate])/60.0/60.0
--,'000.00')
as [Hours]

,CASE ISNULL(( SELECT [taskContent] FROM @policies WHERE [taskName] like '%Holiday%' AND [taskContent]=substring(convert(varchar(10),[StartDate],126),6,5) ),0) WHEN '0' THEN '0' ELSE '1' END as [H]

-- HOLIDAY
,--FORMAT(
 CASE WHEN ISNULL(( SELECT [taskContent] FROM @policies WHERE [taskName] like '%Holiday%' AND [taskContent]=substring(convert(varchar(10),[StartDate],126),6,5) ),0) != '0'
 THEN datediff(SECOND,t.[StartDate],t.[EndDate])/60.0/60.0
 ELSE 0
 END-- ,'000.00')
  as [HOL]

-- OVERTIME
,--FORMAT(
 CASE WHEN datediff(SECOND,t.[StartDate],t.[EndDate])/60.0/60.0  > ISNULL((SELECT [taskContent] FROM @policies WHERE [taskName] like '%OT Hour Limit%'),9999) AND (ISNULL(( SELECT [taskContent] FROM @policies WHERE [taskName] like '%Holiday%' AND [taskContent]=substring(convert(varchar(10),[StartDate],126),6,5) ),0) = '0')
 THEN (datediff(SECOND,t.[StartDate],t.[EndDate])/60.0/60.0) - ISNULL((SELECT [taskContent] FROM @policies WHERE [taskName] like '%OT Hour Limit%'),9999)
 ELSE 0
 END-- ,'000.00')
  as [OT]

-- REGULAR
,--FORMAT(
 CASE WHEN (ISNULL(( SELECT [taskContent] FROM @policies WHERE [taskName] like '%Holiday%' AND [taskContent]=substring(convert(varchar(10),[StartDate],126),6,5) ),0) != '0')
 THEN 0
 ELSE CASE WHEN datediff(SECOND,t.[StartDate],t.[EndDate])/60.0/60.0  > ISNULL((SELECT [taskContent] FROM @policies WHERE [taskName] like '%OT Hour Limit%'),9999)
           THEN 8
	       ELSE (datediff(SECOND,t.[StartDate],t.[EndDate])/60.0/60.0)
	       END
 END-- ,'000.00')
  as [REG]

--,[LastUpdateTime]
--,[Content]
--,[CustomData]
--,[StateLog]
--,[UserName]
FROM [Tasks] t
JOIN [TaskTypes] tt on tt.[Id] = t.[TaskTypeId]
WHERE 1=1
AND t.[Completed] = 1
AND t.[Name] = @entityName
AND t.[StartDate] >= @StartDate
AND t.[StartDate] <  @EndDate
AND tt.[Name] = 'TC Punch Control Task'
--AND t.[State] = 'Punch Cycle Complete'
----ORDER BY t.[Name], t.[StartDate],t.[EndDate]
) HRS

GROUP BY
-- [EmployeeName]
-- [StartDate]
--,[EndDate]
left(datename(YEAR,[StartDate]),4)+' '+left(datename(MONTH,[StartDate]),3)+' TTL'
--,[H]
--,[Hours]
--,[HOL]
--,[OT]
--,[REG]
ORDER BY 1,2,3


##Reports

##TC Employee Hours [0] (Report)##

Report Name: TC Employee Hours
Page Size: 15cm
Display in Report Explorer: checked
Visual Printing: unchecked

Template:

[Current Hours $2:1,1, 1, 1, 1, 1]
>Date|Day|Hours|REG|OT|Start
{REPORT TASK DETAILS:
T.StartDate,T.StartTime
,=F([T.Duration]/60)
,=if([T.Duration]/60 > 8, 8, [T.Duration]/60)
,=if([T.Duration]/60 > 8, [T.Duration]/60 - 8, 0):
(TST=TC Punch Task) AND T.Completed=False AND T.State="Punched In" AND T.Name="$2":
[=FD('{0}','yyyy-MMM-dd')]|[=FD('{0}','ddd')]|[=F('{2}')]|[=F('{3}')]|[=F('{4}')]|{1}
}

[Previous Hours $2:2,1, 1, 1, 1, 1]
>Date|Day|Hours|REG|OT|HOL
@@TC_EmployeeHours:$1,$2,$3,$4,$5
>>Month| |Hours|REG|OT|HOL
>@@TC_EmployeeHoursTTL:$1,$2,$3,$4,$5

##Reports

Requires a special Report in SambaPOS:

###GQLM Custom Reports###

[Custom Reports:1,1,1,4,4,1,1,1,1]
>>id|reportType|displayInExplorer|name|template|pageSize|layouts|ortOrder|visualPrint
{REPORT SQL DETAILS:
SELECT
[Id]
,[ReportType]
,[DisplayInExplorer]
,[Name]
,convert(varchar(max),convert(varbinary(max),[Template]),2) as [Template]
,[PageSize]
,[Layouts]
,[SortOrder]
,[VisualPrint]
FROM [CustomReports]
WHERE [Name] not like 'GQLM%'
ORDER BY [SortOrder],[Name]
:F.Id,F.ReportType,F.DisplayInExplorer,F.Name,F.Template,F.PageSize,F.Layouts,F.SortOrder,F.VisualPrint}

##Ticket Explorer

Uses 1 of the HUB Rules/Actions (HUB Display Ticket in SambaPOS), but that is only required if the Module is embedded in SambaPOS HTML Viewer Widget, otherwise this Module has no special requirements.

##Task Editor

Requires 2 special Reports in SambaPOS:

###GQLM Task Types##

[Task Types:2,8]
>>id|name
{REPORT SQL DETAILS:
SELECT
 [Id]
,[Name]
FROM [TaskTypes]
ORDER BY [Name]
:F.Id,F.Name}

###GQLM Task Type Custom Fields###

[Task Type Custom Fields:1,1,4,4,2,2,2]
>>id|taskTypeId|taskType|name|fieldType|editingFormat|displayFormat
{REPORT SQL DETAILS:
SELECT
 cf.[Id]
,cf.[TaskTypeId]
,tt.[Name] as [TaskType]
,cf.[Name]
,CASE [FieldType]
 WHEN 0 THEN 'String'
 WHEN 1 THEN 'Number'
 WHEN 2 THEN 'Date'
 END as [FieldType]
,isnull([EditingFormat],'') as [EditingFormat]
,isnull([DisplayFormat],'') as [DisplayFormat]
FROM [TaskCustomFields] cf
JOIN [TaskTypes] tt on tt.[Id] = cf.[TaskTypeId]
ORDER BY tt.[Name], cf.[Id]
:F.Id,F.TaskTypeId,F.TaskType,F.Name,F.FieldType,F.EditingFormat,F.DisplayFormat}

1 Like

##Punch Editor

Not done.

##POS

Use the HUB Actions and Rules, and Special Reports.

:warning: Requires SambaPOS 5.1.61+

:bulb: TIP: Ensure you edit /zonfigs/config.js to set POS-specific parameters such as menuName, departmentName, ticketTypeName, etc.


###zconfigs\config.js###

POS configuration parameters:

// POS
var menuName        = 'Menu';
var departmentName  = 'Restaurant';
var ticketTypeName  = 'Ticket';
var POS_EntityTypes = ['Tables','Customers'];
var POS_EntityTypesAuto = false; // override static Entity Types above with automatic Ticket Type Entity Types
var POS_PrintJobs   = ['Print Bill','Print Orders to Kitchen Printer'];

3 Likes

You sir are a legend. The samba community is lucky to have you as a member!! @QMcKay

1 Like

OMG, gonna take a year to digest it :wink: )

1 Like

This is AWESOME! Can’t wait to play around with it! :smile:

@QMcKay maybe you can consider putting the code on GitHub, then if people want to start using (and modifying) this, it’s easy for them to get updates from your source and merge the changes?

1 Like

LOL, could do, but I don’t know how to use Github :stuck_out_tongue_winking_eye:

2 Likes

I am still reaching for the tissue box at the fact I need to understand such a beautiful thing!
Going to need an API for just hooking into the API…:sob:

2 Likes

I can help. I’m pretty sure you’ll love it.

4 Likes

5 posts were split to a new topic: Customer Display on Second Monitor?

Well… These samples are really exciting but we need to clarify something.

What @QMcKay did here is one of the biggest (really biggest) contribution in the entire SambaPOS history. However the intention of that release is demonstrating us what can be done with new SambaPOS GraphQL API. If you’re not interested in GQL at the moment, it will be a better idea to wait until we officially release related products. At this point it will be really hard to fulfill support requests regarding these samples.

5 Likes

It is a very big proof of concept and not meant to be a supported live project. If you want to use it you will need to study it on your own for the most part. Learn how to code HTML, PHP, and learn the GQL then implement your own solution.

4 Likes

I always create HTML based samples but there is no reason to limit it with HTML. IOS or Android developers can also benefit from GraphQL API.

3 Likes