Would it be possible to view reports from a website remotely?
Yes it is possible but it is a complex setup. There is n inbuilt way to do this, I have acheived this and ill pm you a screenshot of my online reports.
Everything is hand coded
Yes with 5.1.60 we have access to GraphQL and a light weight webserver that can host your web app. GraphQL can be used to query reports and display the return data in your own html setup.
PS We are even experimenting with using GraphQL to make a web based SambaPOS and a Web Based Customer Screen and time clock. The SambaPOS is just for testing but the customer display and time clock will have real use. The point is yes with GraphQL the sky is the limit.
On the left in the following two screenshots is a web browser running the kitchen display. This was built by @QMcKay
The bottom screenshot is my phone using chrome to access my local web server.
Those are examples of some advanced use of GraphQL html and css. Like I said it can query custom reports and you can design your own HTML setup to display it or any app really that supports jscript.
Where is the code available for POORMAN POS ?
Its currently with @QMcKay and MySelf. I personally wont release mine because its a ugly implementation hacked together with limited knowledge. I am using it to learn and I really do not feel like answering peoples questions when they dont understand it so I wont be releasing mine.
QMcKay may feel the same right now he may just not feel up to answering a zillion questions about it.
I suggest you study HTML, JSCRIPT, and practice with the GraphQL examples from release notes.
I understand that
But SAMBAPOS has any default code for that ?
No it does not. The code was all developed by us (Mostly QMcKay lol). It is simple HTML with DIV tags and CSS with JSCRIPT to run the GQL.
PoorMAN is just an example of what you can do it was never meant to be a feature of SambaPOS. Obviously we have proven it is possible though.
I will provide you some example code to get you started if you want. I wont have time to provide full support for this so you need to study it and change appropriate things to fit your system. This is not a production working app. It is very limited test and certainly not finished. Do not ask me on how to do things or how to expand it I will not provide support for that. You need to understand how to edit it yourself.
HTML:
<!DOCTYPE html>
<html>
<head>
<title>PoorMan POS</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="pmpos.css">
<script>
'use strict';
var gqlhost = '192.168.1.64';
var gqlport = '9000';
var gqlpath = '/api/graphql/';
var gqlhub = '/signalr/hubs';
var gqlurl = 'http://' + gqlhost + ':' + gqlport + gqlpath;
console.log("gqlurl:" + gqlurl);
</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
<script>
$.postJSON = function (url, data, callback) {
return jQuery.ajax({
'type': 'POST',
'url': url,
'contentType': 'application/json',
'data': JSON.stringify(data),
'dataType': 'json',
'success': callback
});
};
var orders = [];
$(document).ready(function () {
$('#header').html('<a href="http://192.168.1.64:84/"><b>PoorMan POS</b></a>');
//initialize menu
updateCategories();
$('#categories').on('click', '.cBtn', function () {
updateMenuItems(this.innerHTML);
var highlightID = this.id;
for (var c = 0; c < 30; c++) {
highlightID = 'cm_' + c;
if (document.getElementById(highlightID)) {
document.getElementById(highlightID).style.color = "#363636";
}
}
highlightID = this.id.replace(/c_/, "");
highlightID = 'cm_' + highlightID;
console.log("CATID:" + highlightID);
if (document.getElementById(highlightID)) {
document.getElementById(highlightID).style.color = "#FFBB55";
}
});
$('#menuItems').on('click', '.mBtn', function () {
orders.push({ name: this.innerHTML, quantity: 1, price: this.getAttribute('price') });
updateOrders();
});
$('#addTicketButton').click(function () {
if (!orders || orders.length == 0) return;
var tableName = $('#tableNameEditor').val();
createTicket(orders, tableName);
updateTableColor(tableName);
orders = [];
updateOrders();
$('#tableNameEditor').val('');
});
});
function getCategoriesScript() {
return '{categories: getMenuCategories(menu:"Dine In"){id,name,header,color}}';
}
function getMenuItemsScript(category) {
return '{items:getMenuItems(menu:"Dine In",category:"' + category + '"){id,name,header,color,product{price}}}';
}
function getAddTicketScript(orders, tableName) {
var orderLines = orders.map(function (order) {
return '{name:"' + order.name + '",states:[{stateName:"Status",state:"New"}]}';
});
var entityPart = tableName ? 'entities:[{entityType:"Tables",entity:"' + tableName + '"}],' : '';
return '\n mutation m{addTicket(\n ticket:{ticketType:"Ticket",\n department:"Restaurant",\n user:"Administrator",\n terminal:"Server",\n ' + entityPart + '\n states:[{stateName:"Status",state:"Unpaid"}],\n orders:[' + orderLines.join() + ']\n }){id}}\n ';
}
function getUpdateTableColorScript(tableName) {
return '\n mutation m{updateEntityState(\n entityTypeName:"Tables",\n entityName:"' + tableName + '",\n stateName:"Status",\n state:"New Orders"\n ){name}}\n ';
}
function createTicket(orders, tableName) {
var query = getAddTicketScript(orders, tableName);
$.postJSON(gqlurl, { query: query }, function (response) {
if (response.errors) {
// handle errors
console.log("ERROR: createTicket");
} else {
sendRefreshMessage();
}
});
}
function updateTableColor(tableName) {
if (!tableName) return;
var query = getUpdateTableColorScript(tableName);
$.postJSON(gqlurl, { query: query });
}
function sendRefreshMessage() {
var query = "mutation m{postTicketRefreshMessage(id:0){id}}";
$.postJSON(gqlurl, { query: query });
}
function updateCategories() {
//console.log("gqlurl:"+gqlurl);
var query = getCategoriesScript();
$.postJSON(gqlurl, { query: query }, function (response) {
if (response.errors) {
// handle errors
console.log("ERROR: updateCategories");
} else {
$('#categories').empty();
response.data.categories.forEach(function (category) {
category.name = category.name.toString().replace(/\\r/g, " ");
if (category.color != null) {
bgColor = category.color;
} else {
bgColor = '#FF333333';
}
if (bgColor.indexOf("#") == 0) {
bgColor = bgColor.substr(3, 6);
DL = darkOrLight(bgColor);
bgColor = "#" + bgColor;
tColor = DL == "light-text" ? "#FFFFFF" : "#000000";
}
//console.log(category.name+" c:"+bgColor);
//$('#categories').append(`<div style="clear:both"></div>`);
//$('#categories').append(`<div class='catRow'>`);
$('#categories').append('<div class=\'catRow\'><div id=\'c_' + category.id + '\';\' class=\'cBtn\'>' + category.name + '</div><div id=\'cm_' + category.id + '\' class=\'cBtnMarker\'>|</div></div>');
});
updateMenuItems(response.data.categories[0].name);
}
});
}
function updateMenuItems(category) {
category = category.toString().replace(/\\r/g, " ");
category = category.toString().replace(/&/g, "&");
//console.log("CAT:"+category);
var query = getMenuItemsScript(category);
$.postJSON(gqlurl, { query: query }, function (response) {
if (response.errors) {
// handle errors
console.log("ERROR: updateMenuItems");
} else {
$('#menuItems').empty();
response.data.items.forEach(function (item) {
item.name = item.name.toString().replace(/\\r/g, " ");
if (item.color != null) {
bgColor = item.color;
} else {
bgColor = '#FF333333';
}
if (bgColor.indexOf("#") == 0) {
bgColor = bgColor.substr(3, 6);
DL = darkOrLight(bgColor);
bgColor = "#" + bgColor;
tColor = DL == "light-text" ? "#FFFFFF" : "#000000";
}
//console.log(item.name+" c:"+bgColor);
$('#menuItems').append('<div id=\'m_' + item.id + '\' class=\'mBtn\' ;\' price=\'' + item.product.price + '\'>' + item.name + '</div>');
});
}
});
}
function updateOrders() {
var ttl = 0.00;
$('#orders').empty();
orders.forEach(function (order) {
$('#orders').append('\n <div id="orderLine">\n <div class="orderQuantity">' + order.quantity + '</div>\n <div class="orderName">' + order.name + '</div>\n <div class="orderPrice">' + order.price + '</div>\n </div>\n ');
ttl += Number(order.price);
});
ttl = ttl.toFixed(2);
$('#ticketTotalValue').html('' + ttl);
}
var darkOrLight = function darkOrLight(inColor) {
// convert Hex to Int - parseInt(hexString, 16);
var red = parseInt(inColor.substr(0, 2), 16);
var green = parseInt(inColor.substr(2, 2), 16);
var blue = parseInt(inColor.substr(4, 2), 16);
var brightness;
brightness = red * 299 + green * 587 + blue * 114;
brightness = brightness / 255000;
//console.log("IN:"+inColor+" BR:"+brightness);
//
// values range from 0 to 1
// anything greater than 0.5 should be bright enough for dark text
if (brightness >= 0.5) {
return "dark-text";
} else {
return "light-text";
}
};
</script>
</head>
<body>
<div id="header">REPLACE ME!</div>
<div id="tables">
<span>Table Name:</span>
<input id='tableNameEditor'>
<button id='addTicketButton'>Add Ticket</button>
</div>
<div id="containerMiddle">
<div id="ticketDisplay">
<div id='orders'></div>
</div>
<div id="menuDisplay">
<div id="menuDisplayInner">
<div id="menuCategories">
<div id='categories'></div>
</div>
<div id="mItems">
<div id='menuItems'></div>
</div>
</div>
</div>
</div>
<div id="containerLower">
<div id="ticketTotal">
<div id="ticketTotalLine">
<div id="ticketTotalLabel">
Balance:
</div>
<div id="ticketTotalValue">
0.00
</div>
</div>
</div>
<div id="rightBottom" style="display:inline-block;min-width:59%">
<div id="rightBottomInner" style="display:flex;justify-content:space-between;">
<div id="rightBottomLeft" style="display:inline-block;min-width:29%;background-color:#363636;padding:5px;">
</div>
<div id="rightBottomRight" style="display:inline-block;min-width:69%;background-color:#363636;padding:5px;">
</div>
</div>
</div>
</div>
</body>
</html>
CSS:
body {
font-family: Tahoma, Verdana, Consolas;
font-size: 24px;
background-color: #363636;
}
#header {
width: 100%;
margin-bottom: 20px;
}
#tables {
width: 100%;
margin-bottom: 20px;
}
#tableNameEditor {
font-size: 24px;
}
#addTicketButton {
font-size: 24px;
}
#containerMiddle {
display: flex;
justify-content: space-between;
height: 95%;
width: 98%;
}
#ticketDisplay {
display: inline-block;
min-width: 38%;
color: #FFFFFF;
background-color: #333333;
padding: 15px;
margin-right: 1px;
font-family: Verdana, Tahoma, Consolas;
font-size: 22px;
border: 0.1px solid #404040;
}
#orders {
}
#orderLine {
display: flex;
width: 98%;
margin: 5px;
padding: 5px;
background-color: #333333;
}
.orderQuantity {
display: inline-block;
width: 4%;
min-width: 4%;
color: white;
vertical-align: top;
margin-right: 10px;
}
.orderName {
display: inline-block;
width: 69%;
min-width: 63%;
color: #DDDDDD;
vertical-align: top;
}
.orderPrice {
display: inline-block;
width: 25%;
min-width: 25%;
color: white;
vertical-align: top;
text-align: right;
float: right;
white-space: nowrap;
margin-left: 5px;
}
#menuDisplay {
display: inline-block;
min-width: 59%;
}
#menuDisplayInner {
display: flex;
justify-content: space-around;
}
#menuCategories {
display: inline-block;
min-width: 30%;
background-color: #363636;
padding: 5px;
height: 100%;
}
#categories {
height: 100%;
background-color: #363636;
}
.catRow {
height: 100%;
}
.cBtn {
display: inline-block;
width: 79%;
height: 100%;
cursor: pointer;
padding: 15px;
border-style: solid;
border-width: 0.1px;
border-color: #445C78;
margin-top: 4px;
margin-bottom: 4px;
text-align: center;
font-size: 24px;
background-color: #244061;
color: White;
}
.cBtnMarker {
display: inline-block;
height: 100%;
width: 3%;
font-size: 35px;
font-weight: bold;
vertical-align: top;
color: #363636;
opacity: 1;
}
.cBtn:focus {
border-style: solid;
border-width: 3px;
border-color: #AADDFF;
}
.cBtn:active {
border: 3px solid #363636;
max-width: 78%;
}
#mItems {
display: inline-block;
min-width: 72%;
background-color: #363636;
padding: 5px 0.2px;
font-size: 26px;
}
#menuItems {
max-width: 50%;
text-justify: distribute;
font-size: 24px;
}
.mBtn {
min-width: 35%;
max-width: 90%;
min-height: 20%;
vertical-align: middle;
cursor: pointer;
margin: 6px;
padding: 5px;
border-style: solid;
border-width: 0.1px;
border-color: #FFB226;
background-color: #FFA500;
color: Black;
}
.mBtn:active {
border: 3px solid #363636;
max-width: 89%;
}
.mBtn:hover {
}
#containerLower {
display: flex;
justify-content: space-between;
height: 95%;
width: 98%;
}
#ticketTotal {
display: inline-block;
min-width: 38%;
color: #FFFFFF;
background-color: #363636;
padding: 15px;
margin-right: 1px;
font-family: Verdana, Tahoma, Consolas;
font-size: 22px;
}
#ticketTotalLine {
display: flex;
width: 98%;
}
#ticketTotalLabel {
display: inline-block;
width: 60%;
font-weight: bold;
color: red;
}
#ticketTotalValue {
display: inline-block;
width: 38%;
text-align: right;
float: right;
color: red;
font-weight: bold;
}
#rightBottom {
display: inline-block;
min-width: 59%;
}
#rightBottomInner {
display: flex;
justify-content: space-between;
}
#rightBottomLeft {
display: inline-block;
min-width: 29%;
background-color: #363636;
padding: 5px;
}
#rightBottomRight {
display: inline-block;
min-width: 69%;
background-color: #363636;
padding: 5px;
}
help please share with me your setup if possible
You can use SambaPOS Metrik, if you need a report tool