Shopkeeper Micro-capability¶
Jumpmind Commerce's Shopkeeper Micro-capability supports the following functions:
- Remote Manager Overrides
- Real-time Sales Data Display
- Real-time Device Transaction View
Architecture¶
Shopkeeper consists of a Java server and Angular typescript client like most JMC services. However, unlike most JMC services, Shopkeeper's state management is done via an NGXS integration on the client side, NOT via a Java state manager. These states are populated with data via REST calls to remote (or local) JMC service endpoints.
Additionally, to support real-time data, Shopkeeper's server supports a two-way gRPC connection to JMC's State Relay. The server makes the gRPC connection, while the client is notified of / allowed to pass updates to this stream via a websocket connection to the server. Both the websocket and gRPC connection are established when the Shopkeeper client application is initialized.
The State Relay¶
The JMC State Relay application is necessary to facilitate bi-directional realtime communication between the POS and Shopkeeper.
The State Relay is REQUIRED to access the full breadth of Shopkeeper functionality.
The State Relay is typically run centrally with Shopkeeper running either centrally, or in the store. Shopkeeper and the POS are designed to auto-connect to the State Relay, provided the configurations in the "Technical Configuration" section are setup correctly.
Enablement¶
The Shopkeeper micro-cap's functionality may be accessed via one of two modes:
- Integrated
- Standalone
The Integrated mode will enable bidirectional access between the Commerce application and the Shopkeeper module. Tapping the button on Commerce's Home screen will take you to Shopkeeper's dashboard. In Standalone mode, Shopkeeper will route directly to its dashboard upon opening the application.
Enabling Integrated Mode¶
Integrated mode can be enabled by exposing a "Shopkeeper" button on the Commerce home screen. Optionally, you can add two buttons. This won't do anything functionally, but think about it - you'd have two buttons. That's twice the buttons.
Integration Checklist¶
- [ ] Add a row to
ctx_buttonconfigured as follows:
| button_group_id | button_id | button_name | button_type | button_value | enabled |
|---|---|---|---|---|---|
| home | LaunchShopkeeper | key:home:home.shopkeeper | action | LaunchShopkeeper | 1 |
- [ ] Defeat all who would challenge your claim to Keeper of Shop (this is very important)
Enabling Standalone mode¶
Standalone mode can be run in a development environment and using development-only data using a pre-configured launch
configuration compatible with
IntelliJ IDEA. This launch configuration, which will be created by the project's build.gradle file, will be named "
\<customer> Shopkeeper"
(e.g. "ABC Shopkeeper") if running from a customer's extension layer or "Commerce Shopkeeper**" if running from the
reference implementation of
Commerce.
- Note: Shopkeeper is a mostly client-side application. To rebuild the client in Intellij IDEA and see the changes reflected in your launch, select Build > Rebuild Project.
Data scripts exclusively for use during local testing in standalone mode can be stored in
the src/main/resources/data/base/intellij directory
of the project housing the standalone launcher class RunShopkeeperBase.
- Note: See the "Data" section in this guide for relevant tables you may want to supply scripts for.
Checklist¶
- [ ] Create data scripts as needed in the Shopkeeper module's
src/main/resources/data/base/intellijdirectory. - [ ] Run the auto-generated launch configuration for standalone mode from IntelliJ IDEA and validate the application starts successfully.
Configuration¶
The Shopkeeper micro-cap's functionality is driven by a combination of database data and Spring context configuration, the latter of which supplies both technical and business rule parameters.
Technical Configuration¶
The following application-*.yml parameters govern technical behavior for the Shopkeeper module:
| Key | Purpose | App | Customization Required? |
|---|---|---|---|
| openpos.micro-caps.configs.shopkeeper.server-url | names the URL of the server hosting Shopkeeper services when running in Integrated mode | POS | Y |
| openpos.micro-caps.configs.shopkeeper.remote-entry | names and locates the script invoked to serve the Shopkeeper micro-cap when accessed from another application | POS | Y |
| openpos.state-relay.enabled | enable the state relay to transmit data | POS, Shopkeeper | Y (Default is false for POS) |
| openpos.state-relay.target | the remote host and port of the state relay for the POS and Shopkeeper to connect to | POS, Shopkeeper | Y |
Checklist¶
- [ ] Configure the URL of the server hosting Shopkeeper services
- [ ] Configure the host and port at which the
remoteEntry.jsscript serving the Shopkeeper micro-cap is invokable - [ ] Ensure the state relay is enabled on all valid registers and Shopkeeper
- [ ] Ensure the POS and Shopkeeper are targeting the correct address of the state relay
Data¶
The following database tables must, at a minimum, be populated as described in order to enable all front- and back-end features exposed by the Shopkeeper micro-cap. These may be populated directly (via data scripts) or through integrations with external providers (via e.g. SymmetricDS).
| Table | Qualifier | Features Requiring | Purpose |
|---|---|---|---|
dev_device |
Shopkeeper real-time device state display | Provides Shopkeeper a list of devices to monitor. An devices in this table matching Shopkeeper's business unit ID will be flagged to be monitored. | |
sk_sales_ticker |
Sales Tickers | Provides definitions for Shopkeeper's Sales tickers on its Sales widget. | |
sk_sales_timeline |
Sales Timelines | Provides definitions for lines on Shopkeeper's timeline graph on its Sales widget. |
Checklist¶
- [ ] Populate requisite tables with customer-specific data
Data Model¶
Additional info regarding Data models can be seen in our Swagger output or the DataModel.md file. Swagger provides detailed descriptions of tables and columns that may be of assistance to gain a good conceptual understanding of the models used here.
erDiagram
sk_sales_ticker {
string ticker_id
string title
string subtitle
int polling_interval
}
sk_sales_timeline {
string timeline_id
string title
boolean bind_to_right_axis
}
sk_notification {
string id
string notification_type
boolean resolved
string sent_by_username
string resolved_by_username
date sent_time
date resolved_time
string device_id
string message
}
*Note: Shopkeeper models have no relation to each other.
APIs¶
Shopkeeper has three main API service implementations:
- Shopkeeper Device Management
- Shopkeeper Statistics
APIs are invoked via the rest/shopkeeper path.
If you'd like to see detailed request and response information, please take a look at our Swagger API page. The information below is designed to be a quick reference.
Overriding Endpoint Service Calls¶
Several endpoint service calls in Shopkeeper are implemented with base functionality, but are designed to be overridden based on your own specific data and display requirements.
The "Intended to be Overridden" column in the subsequent tables should clarify which service calls these correspond to:
- "YES" implies the endpoint has some level of out-of-the-box functionality, but is ultimately intended to be customized.
- "NO" implies the endpoint is NOT designed to be overridden.
- "OPTIONAL" implies the endpoint can be overridden, but the base functionality is intended to be serviceable to most clients.
Device Management /device-management¶
The Device Management API is designed to save, recall, and handle information regarding devices, notifications, or other device-specific things relevant to Shopkeeper.
The service has the following service calls:
| Service Call | HTTP | Path | Purpose | Intended to be overridden |
|---|---|---|---|---|
getDeviceIds() |
GET |
rest/shopkeeper/device-management/deviceids/{businessUnitId}/{appId} |
Obtain a list of devices for Shopkeeper to monitor. | OPTIONAL |
getResolvedNotifications() |
POST |
rest/shopkeeper/device-management/notifications/getResolved |
Obtain a list of resolved notifications. | NO |
saveNotifications() |
PUT |
rest/shopkeeper/device-management/notifications/save |
Save a notification | NO |
respondToManagerOverrideRequest() |
POST |
rest/shopkeeper/device-management/{deviceId}/manager-override/{requestId} |
Respond to a manager override request | NO* |
*respondToManagerOverrideRequest() is not designed to be overridden except in the case that a client is not using JMC
POS.
Statistics /statistics¶
The Statistics API is designed to provide models and data for the various Sales displays on Shopkeeper.
The service has the following service calls:
| Service Call | HTTP | Path | Purpose | Intended to be overridden |
|---|---|---|---|---|
getTickers() |
GET |
rest/shopkeeper/statistics/tickers/{businessUnitId} |
Obtain a list of sales tickers for Shopkeeper to display on its Sales widgets. Tickers are defined by SK_SALES_TICKER |
NO |
getTickerValue() |
POST |
rest/shopkeeper/statistics/tickerData/{businessUnitId}/{tickerId} |
Obtain data for a particular ticker. Is called periodically based on pollingInterval supplied in ticker model |
YES |
saveTimelines() |
PUT |
rest/shopkeeper/statistics/timelines/{businessUnitId} |
Obtain a list of timelines for the Shopkeeper timeline graph. There is no limit to timelines, but only two y-axes are available. Timelines are defined by SK_SALES_TICKER |
NO |
getTimelineData() |
POST |
rest/shopkeeper/statistics/timelineValues/{businessUnitId}/{timelineId} |
Obtain a set of data points for the given timeline. This is called once at screen creation, and not periodically (unlike a polling Ticker). | YES |
getDailyGoalValue() |
POST |
rest/shopkeeper/statistics/dailyGoal/{businessUnitId}/current |
Obtain the current daily goal value.This is displayed on the Sales gauge. There is no requirement for this to be a "daily" goal. Any value will suffice. This is polled periodically by Shopkeeper. | OPTIONAL (Default requires sls_trans_summary integration) |
getDailyGoalTarget() |
POST |
rest/shopkeeper/statistics/dailyGoal/{businessUnitId}/target |
Obtain the current target goal value. This is displayed on the Sales gauge. This value is NOT polled, and will only be requested by Shopkeeper on screen creation. | YES |
Flows¶
Shopkeeper is a "stateless" application. It lacks the traditional flows of a typical JMC state-managed application. Instead, Shopkeeper utilizes an NGXS integration to manage state data and a client-side router to route between components. This is notable as unlike a traditional JMC application, Shopkeeper can route to any component from any component.
The NGXS State information for Shopkeeper can be found in client/projects/shopkeeper-ui/src/app/state
The Router for Shopkeeper can be found
in client\projects\shopkeeper-ui\src\app\state\navigation.state.ts.
The router is a custom routing manager, but is largely similar to the Angular router. Routes are predefined and loaded into the state via the
InitializeNavigation NGXS action. Destination objects are created upon the routes being "activated".
FAQ¶
This FAQ contains common questions that may come up during the implementation process of Shopkeeper. If you find you have a question that is not listed here, please add it in if you receive an answer - even if the question is answered somewhere in this document already. Others may have been confused or glossed over the same information.
Q: How do I set my own value for a Ticker, Timeline, or Gauge Data?¶
Overriding a ticker is relatively straightforward. There are 3 steps:
- Create a new endpoint for your service method with a custom
implementationvalue. See our "How to add new implementations of existing service methods" in our microservices.md file. - Use the
@Autowiredtag to pull in any relevant JMC Microservices to your new endpoint - Utilize the microservices to implement your own custom logic
As an alternative, your endpoint can also make a remote call to another service. The ticker data lookup happens asynchronously, so the app will not be held up by lengthy response times.
Q: Are there any restrictions on the data I provide via the "data" and "value" Statistics endpoints?¶
There are no enforced limits on this data, but all data from "value" endpoints is displayed as currency on the application UI. Additionally, timeline data is expected to adhere to one x-axis scale and at most two y-axis scales.
Aside from hard restrictions, keep in mind the polling rate set on tickers. Expensive operations done frequently can become problematic quickly. The tickers will always request new data upon rendering as well, regardless of their polling rate.
Q: Shopkeeper does not show any devices?¶
Shopkeeper will look in the dev_device table to find all entries that match a pos app ID and the business unit ID
Shopkeeper is running on (You should be able to see this on Shopkeeper's dashboard). Ensure there are valid devices in this table.
Q: Shopkeeper shows several devices it's monitoring, but the devices do not appear to be updating in Shopkeeper?¶
Shopkeeper handles device state updates and manager overrides via the JMC State Relay application. Ensure the openpos.state-relay.enabled and openpos.state-relay.target are set correctly
on all registers and Shopkeeper instances. Additionally, ensure the device IDs shown on Shopkeeper match the device IDs you're
trying to monitor, and that the monitored devices are set to the pos app ID.
Q: Shopkeeper was monitoring devices, but no longer appears to be?¶
Shopkeeper's client makes a websocket connection to its server to receive and pass updates to the gRPC protocol that the state relay listens / sends on. Restarting the Shopkeeper client application will attempt to reconnect to this websocket.
If this issue is persistent, consider filing a bug report on our Issue Tracker.
Q: The State Relay doesn't appear to be doing anything?¶
The state relay, by default, is turned off on JMC POS configurations. Ensure openpos.state-relay.enabled is set to true on all relevant devices
Q: How can I add another section to Shopkeeper?¶
You will need to provide three things:
1. A new angular component that Shopkeeper will render for your section.
2. A new NavigationRoute to that component that can be defined in shopkeeper.component.ts
3. A new entry in shopkeeper-action-footer.component.ts that defines a label, an icon, and the path of your section (this should match your NavigationRoute in step 2)
Q: Can I show a relative date on my tickers like you do in the default configuration?¶
Shopkeeper knows how to compute a relative date for several token values. You can set the subtitle data field
in sk_sales_ticker to one of the following values to get the corresponding computed date:
%DATE%(Today)%LASTWEEK%(Last Week)%LASTYEAR%(Last Year)
Q: How do I decline a manager override on Shopkeeper?¶
You cannot currently decline a manager override on Shopkeeper. You can only dismiss it from view.
If you'd like to see this feature in a future release, consider making a Feature Request on our issue tracker.
Q: I don't see a *-flow.yml file for Shopkeeper, where are its flows defined?¶
Shopkeeper is a stateless application which means it does not use the traditional JMC Java-based state manager and flow manager.
Instead, Shopkeeper tracks its internal state with the NGXS state infrastructure, and performs routing navigation via a custom NGXS-action-based router. You can see more information in the "Flows" section above.
Q: What are the relationships between Shopkeeper's data tables?¶
Shopkeeper's data tables are isolated from each other and have no X:X relationships with each other.
Q: I'd like to override one of the endpoint invocations you said not to override above. Can I do that?¶
It's strongly recommended that you refrain from overriding any service calls not recommended to be overridden. These calls are generally simple database lookups of visual models the UI will use to display information. Overriding them can cause unneeded complexity and obscure bugs.