Skip to Content
Technical Articles

SAP Customer Checkout Plugin Development – Part II

Back to Part I

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 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:

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.

You must be Logged on to comment or reply to a post.
  • 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.




    • 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:


      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.




      • 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 🙂



        • 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:


          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.




          • /
          • 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.

            Implement a method with the ListenToExit as mentioned above. Something like this:


            public void mergePrintTemplateData(Object caller, Object[] args) {
            Map<String, Object> root = (Map<String,Object)args[0];
                if(args[2] instanceof ReceiptDTO) {
                    // further checks if tax receipt should be printed
                    root.put("taxReceiptPrint", "true");
                    root.put("var1", "Value1");
                    root.put("var2", "Value2");


            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


            Julian Wehmann FYI





      • 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.

          $(document).ready(function () {
              $('.customerInfoContainer').after($('<div style="position: relative; right: 0; top: 0; background-color: #FFF; padding: 5px; border: 1px solid #000; width: 25%; height: 68%; font-size: small; margin-left: 5px; margin-right: 5px; margin-top: 5px; float:left" id="customerAgeDiv">Age of Customer: <input type="number" id="customerAge"></div>'));
          // more input fields...

          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.




          • 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?

  • 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.



    • Hi Timothy Mbogo ,

      you can hook into the following method to be sure to get the posted receipt.


      @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...


  • 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.



  • 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);"Call onReceiptPosted : "+args);
    return receipt;

    • Hi Timothy Mbogo ,

      please use the following annotation...

      @PluginAt(pluginClass=IReceiptManager.class, method="finishReceipt", where=POSITION.AFTER)

      Method signature is the same as well as the position of the receipt entity in the array of objects.




        • 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.



          • 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?




            @PluginAt(pluginClass = IReceiptManager.class, method = "finishReceipt", where = PluginAt.POSITION.AFTER)
            public Object afterReceiptPost(Object proxy, Object[] args, Object returnValue, StackTraceElement callStack) throws BreakExecutionException {
          • 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.



  • 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.




  • 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



  • /
  • 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.



  • 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!