Hi all.
I think I know what @GreatShakesBar was talking about… He has a bunch of entities (maybe customers, maybe tables, maybe staff) and he wants to set some types of custom data about various ones of these in an efficient way. This is what I have implemented to set details about our scuba diving clients - see example below
He has SambaPOS open so when he says “load the POS” he means he doesn’t want to have to use a standard customer search screen, find an entity, load a ticket (with the entity selected) then use this ticket not to actually build a ticket, but only for the purpose of giving him access to some Action Buttons to set these pieces of custom data.
What am I doing?
As a dive shop, we have a lot of customers (entities) who each make various reservations (pre-order tickets) and we need to keep track of when size equipment they need. By storing this against the entity and not the ticket or order any time that customer returns their correct equipment can be recalled seamlessly.
Above you can see an entity screen that we use to set the equipment per customer. The “Table Mode” report on the left hand side shows all the divers we have with us today. Then, once you click a customer on the left this will set a program {SETTING:EQUIP-EntityName} and refresh all the widgets on the right hand side. The label widgets on the right hand side use the EntityName stored in {SETTING:EQUIP-EntityName} to display the current value and action buttons on the right hand side pass call a rule with the field to be set and the value of the field passed through the value to be set as the CommandValue. Within the rule itself the {SETTING:EQUIP-EntityName} can be access to update the data for the specific entity.
But, having today seen this “Load Entity” action, potentially some of this could be streamlined. However, to be honest there is a lot of other clever stuff* I am doing in all the rules anyway, so even if the Load Entity action might improve things a little, I will still want to hand off a lot of logic to the rules, rather than setting the value immediately from the button click.
*The clever stuff =
- If no value is set for this particular custom field, then set it the moment the button is pressed
- If a value was already stored, but you press another button to override the old value then ask for confirmation that you really want to change the old data
- Exception the the above logic, if their liability paperwork was set to “issued” or “received, but needs to be printed” then no need to ask for confirmation when changing the status to “confirmed”
- Rather than just storing the value “confirmed” for the paperwork, let’s store today’s date
If we realise that actually they signed the paperwork three months ago, but we forgot to set it, then let’s give the option to set a custom date if you press “confirmed” despite the paperwork already being confirmed - this time around the “are you should you want to update this” question box will include an option for you to select a custom date.
- FYI - for inputting dates I use some JScript to generate a series of buttons to go in to an AskQuestion action. First I ask the year, then then month, then the day (doing this means I can run logic in JScript to ensure I am presenting the correct number of days because I already know the year and month - preventing someone selecting 29th Feb 2021, but allowing then to select 29th Feb 2024).
OK, I kind of follow the story, but can I see the actual config?
Sure…
1 The report
![image](https://forum.sambapos.com/uploads/default/original/3X/8/1/817b13b7af3dc8e385059fb2df5e6c71748075f4.png)
You can see the report is in #Table mode, allowing values to be selected by the user.
The data for the report itself comes from some custom SQL script, but could easily be from standard {REPROT} tags.
2 The report widget
![image](https://forum.sambapos.com/uploads/default/original/3X/1/a/1a74dff36b9c1af7d6dcda77ab423b6722061d95.png)
Need that “Setting mappings” section configured correctly, so the value from column 2 gets set as a ProgramSetting each time a new line of the report is selected by the user.
All the label widgets on the right hand side have Property > Name set to “CurrentValue” therefore all of these labels refresh when a new line in the report is selected.
3 Update display Labels
![image](https://forum.sambapos.com/uploads/default/original/3X/2/2/2207f8143bbaa5bdbd71ca7edfb958eef185c2a7.png)
This is the config for setting the name label, but the other labels for each type of equipment work in a similar way.
{CALL:res.getCustomDataFromEntityName('Full Name','{SETTING:EQUIP-EntityName}')}
This tag runs some script which is part of my “Reservations” set of script, which uses the handler “res”
function getCustomDataFromEntityName(customData,entityName) {
entityName = entityName.replace("@","'+'@'+'");
var qry = "SELECT jsonValue FROM ENTITIES CROSS APPLY OPENJSON(CustomData) WITH (jsonName varchar(50) '$.Name', jsonValue varchar(50) '$.Value') jsonData WHERE jsonName = '"+customData+"' and Name = '"+entityName+"'";
var name = (sql.Query(qry).First);
return name;
}
I do this because the custom data is JSON based and I don’t think there are any tags that give us any easier access to this? BUt I might be wrong. All of my EntityName’s are their email addresses, since this will be unique.
4 The buttons for each value to set
![image](https://forum.sambapos.com/uploads/default/original/3X/f/d/fd5662d26979067939b8522f6cabde55957c48b8.png)
This is the settings for the XL Male wetsuit button. Note that the “Value” which will become the “CommandValue” in the rule has a very specific format. The first -
denotes the separation between the field we want to set and the value we want to set it too. This is because I couldn’t find another reliable way of passing through two values to a rule. So I pass through one string which we will take care of splitting in a later step…
5.1 The rules (Part 1)
I split my rules in to a part1 and part2 because I wanted to set two new ProgramSettings in Part1, which then needed to be access by some actions in Part2. If I just put those actions lower in the list of actions then the ProgramSetting would not have been set it time before the action fired.
The field name and the field value are split by calling this scripts:
function equipType(input) {
// This is only going to work if equipment items DO NOT contain commas or dashes
var output = "";
output = input.substring(0,input.indexOf("-"));
return output;
}
function equipValue(input) {
var output = "";
output = input.substring(input.indexOf("-") +1, input.length);
return output;
}
You’ll notice some really long constraints on the execute command and Ask Question… The idea behind these is the following:
IF
the current value for the field that we are setting == “Issued”, Medical", “Print” or it is BLANK (Empty)
THEN
Run Part2 and set the field to the value of the button that was clicked
ELSE
Ask a question to confirm if we want to do this
Note: Part2 will always be run even after you click “Cancel” to the question, BUT, in Part2 we will check the value of what was pressed, if we pressed “Cancel” then we just won’t do anything
5.2 The rules (Part 2)
First off, in the rule constraints, we won’t fire this rule if the command value was “Cancel” because that would indicate we opted NOT to overwrite the existing value when the AskQuestion was presented in Part1
Next, the AskQuestion will sometimes offer us an option of “Custom” this is related to handling of dates any how some of the values we store are dates and we might need to present a series of prompts that ultimately end up with a Date String YYYYMMDD being generated and stored as “{SETTING:BuildDate}”
Assuming this is an update for a standard piece of entity data, the last action here fires and the update is made.
Note: I use two different entity types for customers… “Customers” and “Buddies”. Customers are the main entities that we will build reservations for. Buddies are their friends/family who will dive with them. For these buddies I don’t need to know there details like address and email address, but I DO need to store their equipment sizes. In the list of today’s divers, some are buddies and some are customers. So at this point we won’t know which one we are dealing with. and therefore I call a script to work that out so the action knows exactly what EntityType it should be updating.
function getEntityTypeFromName(EntityName) {
EntityName = EntityName.replace("@","'+'@'+'");
return sql.Query("SELECT EntityTypes.Name FROM Entities INNER JOIN EntityTypes ON Entities.EntityTypeId = EntityTypes.Id where entities.Name = '"+EntityName+"'").First;
}
Fin
And that’s about it.
I am sure there is a lot that I could do to streamline this, but I put it all together one day when I was in a rush and never really had the need to re-visit it.
In writing it up here I can think of a few other tweaks I could try out, but honestly, If it works, don’t fix it.