Technical Articles
SAP Customer Checkout Plugin Development – Part II
Part II: Create a backend Plugin
Welcome to the second part of this blog series. In this part we will add two plugin properties, one for a list of material Ids and the second one for storing a message, read them from the database and we will show the message to the user if a sales item is added which was configured in the list of material Ids.
Maybe you want to remind the cashier to ask for the customers id for certain sales items?
We will also learn how to debug our code and log messages to the cco log. So let us start.
I copied the plugin we created in the first part, renamed it as blogpluginpart2 in the pom.xml and set the package name accordingly.
Let’s dive in. The first thing we need to do is to override the persistPropertiesToDB() method and return true. This ensures, that CCO handles the persistence of our plugin properties. Open the App.java class, click in the source at the bottom and press Strg + Spacebar. A list of methods will appear that we can override. Select the persistPropertiesToDB() method.
This method should always return true.
The next method we need to override is the getPluginPropertyConfig() method. This method should return a Map<String,String> of our properties.
The key of the HashMap is the name of the plugin property and the value is the type of data which will be stored in this property. So use int for integers or e.g. a boolean will create a checkbox. For now we will use two string properties. The first property will save a comma separated list of material Ids and the second one is a message string, we will show to the cashier, if a new sales item is in the LIST_OF_MATERIALS.
Next thing to do is to let our plugin react when a new sales item is added to the receipt. The Customer Checkout API is aspect-oriented; means whenever SAP is adding so called exit-points to their methods in their classes their API is calling any plugin which has this particular exit-point as parameter in annotation.
So we will add a method checkSalesItem like this:
@PluginAt is the annotation. And we will set this annotation to be called when the addSalesItems Method (method=“addSalesItems“) in the IReceiptManager class (pluginClass=IReceiptManager.class) was executed (where=POSITION.AFTER).
So our method checkSalesItem is called, whenever a sales item was successfully added to the receipt.
In the array of objects (Object[] args) cco sends all necessary entities we need for our process. E.g. the receipt and the added salesItem.
If you want to know, which entities are stored in the array of objects, we will need to setup our debug environment. Double click on the line with the method head to set a break point.
Save your files and build your plugin. Copy the plugin in the plugin folder of your Customer Checkout installation and start cco. Go back to your eclipse and right click on your project folder. Choose Debug As and Debug Configurations….
On the left list look for Remote Java Application and select this entry. Now create a new configuration via click on the new button.
Set a name for your configuration and change the port to 4000 (this is the port we set in part 1 in the run.bat, remember?). Click Apply and Debug.
Login into your cco and add a sales item. Your eclipse should change it’s perspective to debug.
Confirm with yes. In the variables window you will now see the content of the args array.
Disconnect your debug session. We will now implement the checkSalesItem Method. The exitpoint for addSalesItems method of the IReceiptManager class at the POSITION.AFTER is called multiple times. To ensure, that we will only react on the last one, we will check, how many entries the args array has.
Our plugin only reacts if the args array has two entries. If so, we will retrieve the 0 and the 1 indexed entry and cast it to the right type.
Will get our plugin property LIST_OF_MATERIALS and will split the string into a string array. To comfortably check if the new sales item has a material Id we configured we will create a List<String> of this String array so that we are able to use the contains method.
After that we are calling a showMessageToUi method we will implement later. This method will given two strings. The message itself and a “type” of the message.
Whenever the plugin shows the message to the cashier it should also create a log entry. SAP Customer Checkout has everything you need to log your own error messages, exception stacktraces and so on. Simply add this to your global class members:
Now we can log our messages right into the cco logs (and of course the cco console) when we have to.
The last thing we are going to implement in this part is the showMessageToUi method. This method allows you to create simple notifications to the cashier right from your cco plugin.
The UIEventDispatcher takes the „SHOW_MESSAGE_DIALOG“ action as parameter and also a Map where we set the message, in id, the lifetime and the type.
Ok, build your plugin, copy the jar into your plugin folder and start cco! Go to your plugin configuration in your cco backend and set your properties.
Save these changes. SAP Customer Checkout caches the properties from every plugin at startup so if you change this, you should restart.
Now if we add sales items with the configures material Id, we should see the MESSAGE_FOR_CASHIER and also an entry in the console.
And of course, also in the customer checkout log.
That’s all for this part of the course. If you have any questions or feedback, please do not hesitate to get in touch with me.
The code of this parts plugin is hosted in gitlab: https://gitlab.com/ccoplugins/blogpluginpart2
Stay tuned for the next part, where we are implementing a plugin sync job to retrieve data from the b1i and we are implementing a way to send custom monitoring messages to CCOm.
very understandable and clearly explained
Hi Robert,
It looks like “UIEventDispatcher.INSTANCE.dispatchAction("SHOW_MESSAGE_DIALOG", null, dialogOptions);” allows to pass button and input fields to front end.
Can you please give me an example or suggest where can I find info about this. I need to ask question on frontend and get manually inputted reply from user on back end.
Thanks,
Sergei
Hi Sergei,
afaik there is no real API Documentation unfortunately. But take this example to create a small input dialog for the cashier.
JSONObject dialogOptions = new JSONObject();
dialogOptions.put(“message”, “Please insert some value here:”);
dialogOptions.put(“id”, “DIALOG_CONFIG”);
dialogOptions.put(“type”, “info”);
dialogOptions.put(“input”, “true”);
JSONObject btnOkConf = new JSONObject();
btnOkConf.put(“type”, “good”);
btnOkConf.put(“id”, “DIALOG_CONFIG_BTN_OK”);
btnOkConf.put(“text”, “OK”);
btnOkConf.put(“default”, “true”);
JSONObject btnCancelConf = new JSONObject();
btnCancelConf.put(“type”, “bad”);
btnCancelConf.put(“id”, “DIALOG_CONFIG_BTN_CAN”);
btnCancelConf.put(“text”, “Abort”);
dialogOptions.put(“buttons”, new JSONObject[] {btnOkConf, btnCancelConf});
Then implement a method to get the input value:
@ListenToExit(exitName=”genericButtonCallback”)
public void handleDialogInput(Object caller, Object[] args) {
String inputVal= (String) args[2];
}
You may also need to check, if the args are for the right event.
Let me know if this worked for you.
Regards
Robert
Hi Robert,
Thank you very much. It works like charm. It looks like your code for JavaScript, I just tweaked a bit for Java syntax and it works 🙂
Thanks,
Sergei
Hi Sergei,
glad I could help.
Regards
Robert
Hi Robert,
How to print out additional receipt? I need to customize printing template.
Looking forward to hearing from you.
tomo
Hi Tomorjin,
do you need to print another receipt or just extend the existing templates?
When you need to add additional infos to existing printjobs:
annotate a method in your plugin:
@ListenToExit(exitName=”BasePrintJobBuilder.mergeTemplateWithData”)
You will get an array of objects iirc with the printtemplate, the entity which will be printed e.g. the receipt or the voucher. The first entry in the array is iirc an HashMap<String, Object> where you can put additional information like this:
map.put(“MY_CUSTOM_FIELD”, “Hello my name is dr. greenthumb”);
In the printtemplate you can access the text via ${MY_CUSTOM_FIELD}.
When you need your own Printjob:
You can implement your own printjob builder by extending the BasePrintJobBuilder class. When extending you need to implement certain methods which are mostly self explainatory.
But I did not have the chance to implement a custom printjob in production.
Regards
Robert
I want to print a tax reciept via thermal printer in afterRecieptPost function.
Tax reciept looks like below image.
Hi Tomorjin,
I would suggest not to handle this in the afterReceiptPost.
Extend the salesReceipt Printtemplate with the layout of your tax receipt. Add a check for a boolean variable (printTaxReceipt yes / no). Printtemplates are implemented via Apache Freemarker Syntax.
https://freemarker.apache.org/
Implement a method with the ListenToExit as mentioned above. Something like this:
In your print template you can now use the variable taxReceiptPrint, var1, var2 to enrich your template with the values you need to print on your tax receipt.
For multilanguage purposes you may need textblocks in the print template like this:
<@translate key="YOUR_TEXT_CODE">
Then goto your translationsfolder (normally cco/translations) where you find the translation property files.
Open the file for the desired language. Insert:
YOUR_TEXT_CODE=your desired text in the desired language
Save it. And now your text should automatically be inserted in your printtemplate.
If you need further assistance or if you are interested that I shall implement this feature please contact info@hokona.de.
Julian Wehmann FYI
Regards
Robert
Hello Robert,
I saw your input dialog example. It's good example.
But I need to create dialog that has has two or multiple input field. How can I create it?
Hi Tomorjin Arildii ,
depends on what you want to achieve, when and because of what do you need to show more input fields? If you just need more fields rather than the one age field, you can just add more divs to input your data like this.
But this only applies to the retailUI. So please give me a hint, what you want to achieve, and I can try to push you into the right direction.
Regards
Robert
Thank you Robert,
I am using below source to pop up dialog.
public void showInputdialog(String question, String btnTxt) {
JSONObject dialogOptions = new JSONObject();
dialogOptions.put(“message”, question);
dialogOptions.put(“id”, “VAT_REGISTER_DIALOG”);
dialogOptions.put(“type”, “info”);
dialogOptions.put(“input”, “true”);
JSONObject btnDlgCorp = new JSONObject();
btnDlgCorp.put(“type”, “good”);
btnDlgCorp.put(“id”, “VAT_DIALOG_BTN_OK”);
btnDlgCorp.put(“text”, btnTxt);
btnDlgCorp.put(“default”, “true”);
dialogOptions.put(“buttons”, new JSONObject[]{btnDlgCorp});
//A SHOW_MESSAGE_DIALOG event is fired, which will show the message box popup
UIEventDispatcher.INSTANCE.dispatchAction(“SHOW_MESSAGE_DIALOG”, null, dialogOptions);
}
My question is I need to add more input fields to this dialog? And how?
Already saw the answer, but would work for template xml?, look like is focus for jpos template.
pleases, if you can give me some solution for xml.
Hi Robert,
How do I capture the full receipt details ?
Regards,
Timothy.
Hi Timothy Mbogo ,
please explain in more detail, what you want to achieve, because it is not clear for me.
Regards
Robert
Hi Robert,
I have a datecs fiscal printer which Is not integrated to customer checkout. Currently what I have done I have created a service that reads data from a file and send it to the printer. The printer prints the receipt
So the easiest option I have is to try and get the receipt details that is the itemcode,Itemdescription and the amount plus the total amount tendered so that I create the txt file that will be consumed by the printer service I have created.
Regards,
Timothy.
Hi Timothy Mbogo ,
you can hook into the following method to be sure to get the posted receipt.
Regards
Robert
Hi Robert,
Thanks let me try it out.
Regards,
Timothy.
Hi Robert,
If you maybe having some documentation or some learning materials for the CCO api I will greatly appreciate. I think am fumbling too much with less information hehe.
Regards,
Timothy.
Hi Timothy Mbogo ,
unfortunately there is no real api documentation. It's basically learning by doing.
But if you are fumbling too much we could discuss your requirements in detail and can offer you some help with the development in various ways. See:
https://blogs.sap.com/2019/05/29/hokona-sap-customer-checkout-extensions-and-plugins/
Please drop us a line at info@hokona.de
Regards
Robert
Hi Robert,
This is what have done but after I Post the receipt the method is not invoked.
@PluginAt(pluginClass=ReceiptPosService.class, method="postReceipt", where=POSITION.AFTER)
public Object onReceiptPosted(Object proxy, Object[] args, Object ret, StackTraceElement caller) {
// the receipt is stored in the array of objects "args"
ReceiptEntity receipt = (ReceiptEntity) args[0];
// create file for printing...
System.out.println("Call onReceiptPosted : "+args);
logger.info("Call onReceiptPosted : "+args);
return receipt;
}
Hi Timothy Mbogo ,
maybe I have overseen it, but which UI Mode did you use? There was (unfortunately) a change between the receipt handling between Retail and Quickservice.
Regards
Robert
Hi Robert,
I used the Sales Mode, scanned the item so I expected the method to be invoked after clicking "Yes post Receipt"
Regards,
Timothy.
Hello Timothy Mbogo ,
please go to the cco configuration -> POS System -> Sales Screen. There you will find the config property "UI Mode".
Regards
Robert
Hi Robert,
Yeah have seen it. Am using Retail Mode.
Regards,
Timothy
Hi Timothy Mbogo ,
please use the following annotation...
Method signature is the same as well as the position of the receipt entity in the array of objects.
Regards
Robert
Hi Robert,
What does POSITION means? There is BEFORE, AFTER positions?
Dear Tomorjin Arildii,
with this property you can define if your plugin code will be executed BEFORE or AFTER the standard CCO code is executed.
So basically with BEFORE you can manipulate which parameter values the original CCO method (like finishReceipt) gets.
With AFTER you make sure the standard CCO logic was run and you also get all parameters the original method got.
If you want to know more about this programming style try reading what aspect oriented programming is all about.
hth
Robert
Thank you Robert Zieschang ,
One more question. In IReceiptManager class finishReceipt method is "finishReceipt(ReceiptEntity re, boolean bln)" two parameters. But in Plugin code "afterReceiptPost(Object proxy, Object[] args, Object returnValue, StackTraceElement callStack)" afterReceiptPost function has 4 arguments. How can I understand it?
IReceiptManager
Dear Tomorjin Arildii,
the parameters of the original method are stored in the object array "args". The object "proxy" is the instance of the ReceiptManager.
The returnValue object (only with POSITION.AFTER) is the returnValue which the original cco method would return to it's caller.
The callstack is good to follow which method called the finishReceipt method.
Regards
Robert
Hi Robert,
Thank you very much, have been able to create the file with the receipt details. Am now looking for Amount tendered so that I can show the change amount on the receipt.
Regards,
Timothy.
Hi Timothy Mbogo ,
glad you achieved your goal. Happy coding.
Regards
Robert
Hi All,
Kindly guide me to how to put the validation while customer return in Customer Checkout ,i need to throughout the error if the receipt date exists more than one day...or Please guide to retrieve the receipt date while picking from receipt details window
Regards,
Sathish
Hi Robert Zieschang
How can I cancel salesItem in plugin source code(Java)?
I tried salesItem.setStatus(“3”);. But it doesn’t work.
I found below method and don't know how to call it.
Hi Robert,
Thank you for the great blog. It was extremely useful as I am starting to write my first plugin in CCO.
I just have a comment. As you know SAP has released the new UI for retail and quick service and is looking to make this the default interface. The old UI will be depreciated soon as per SAP team.
The plugin is working fine on the old UI, but not working at all on the new retail/quick service UI. Can you tell me how it can be enabled on the new interface.
Thanks in advance
Dear Amr Gamgoum,
I cant answer this in a short way. Please check the other parts (especially part 5) with the new UI.
So basically you need to handle the SALESITEM_ADD event in the eventbus. And then you can handle this totally on the UI level.
When you want to implement this in the backend (java) then the NGUI uses different service classes. E.g. the ReceiptManager is the ReceiptPosService class. In this class there should also be a addSalesItem method available.
Regards
Robert
Hi Robert!
Thanks in advance for sharing all of this with us,
I've been following the steps since part 1, however, I'm facing the next issues
- Every time I tried to start the debugger I get the next message
Failed to connect to remote VM. Connection refused. Connection refused: connect
I look for a solution to this problem on internet and most blogs suggested that this was caused by a wrong port number, however, I set the same configuration as you did on my run.bat
Do you have any idea what could be the reason for the debugger issue?
- I already built and created my blogplugingpart2.jar (I've got no errors during building and code is the same as the example found in the git repository) file and put it on CCO installation path, I infer material ID is the same as Article ID (right?) so that I try to test but I'm not getting the pop-up window message.
Here I have my plugin configuration

Here I have my list of items to test with.
Did I miss something? or Do you have any idea why it may not work?
Any help will be really appreciated.
Best Regards!
Hi Pablo Aaron Mendieta,
sorry for the late reply.
I assume your CCO is running on localhost? Did you try to set your debug config to 127.0.0.1?
Your run.bat also looks fine to me.
Regards
Robert
Hi Robert Zieschang,
why the debug mode does not work when i change the screen mode from retail mode to any other mode??
Hi Osama Badarneh,
what do you mean with "does not work"? Some methods where Plugins can hook in via @PluginAt are different between the old retail UI and the newer QuickService and table mode.
Maybe you debugging does not work, because the screen mode you are using does not even call the method your plugin hooked in.
But I would need more information about your code, to verifiy.
hth
Robert
Hi Robert,
Thank you for the detailed guide. I've managed to get the pop up message going. However, when I try to debug in eclipse, with localhost and port 4000, I got this error "failed to connect to remote vm. connection refused". I put the two lines in run.bat file from part 1 also. I've tried to put firewall on and off and it is not working. Do you have any suggestion?
Hi Hein Aung,
please set the property "suspend" in the line where you configured the remote connection to y. Suspend means, that CCO will stop loading until a remote debugging connection was established. If CCO stops, we can be sure, that this side works.
Is CCO and your IDE running both on localhost?
Regards
Robert
Hi Robert Ziesche,
I put y in the property suspend and ran the CCO, the console doesn't continue but says "listening for transport dt_socket at address: 4000". After I tried to debug from eclipse, it started loading and open the login screen on web page for CCO. But the connection still failed for debug. Yes, both IDE and CCO are on my localhost.
Regards,
Hein
Hi Robert Zieschang,
Please, let me ask one thing, the following plugin method is working in UI Mode of (Retail (Old UI- Deprecated)) at CCO version 2.14.1 and the release ( 2.0 FP14 PL00) but it does not work in UI mode of (Retail service). Please can you help me with how can I do to work this function?
@PluginAt(pluginClass=IReceiptManager.class, method="addSalesItems", where=POSITION.BEFORE)
public void checkSalesItem(Object proxy, Object[] args, Object ret, StackTraceElement caller)
{
}
Hi Robert Zieschang,
please can you help me to explain the following plugin method is not working UI mode of Retail service but it works in the UI mode of Retail (Old UI -Deprecated). we are using CCO release 2.0 FP14 PL00 and the version is 2.14.1? how can I do to work this method in UI mode of Retail service, Please?
@PluginAt(pluginClass=IReceiptManager.class, method="addSalesItems", where=POSITION.BEFORE)
public void checkSalesItem(Object proxy, Object[] args, Object ret, StackTraceElement caller)
{
}