17 July 2019

Table of Contents

1. Introduction

Welcome to the Apache OFBiz developer manual. This manual provides information to help with customizing and developing OFBiz. If you are new to OFBiz and interested in learning how to use it, you may want to start with the "Apache OFBiz User Manual".

OFBiz is a large system composed of multiple subsystems. This manual attempts to introduce the overall architecture and high level concepts, followed by a detailed description of each subsystem. In addition, the manual will cover topics necessary for developers including the development environment, APIs, deployment, security, and so on.

1.1. Main systems

OFBiz at its core is a collection of systems:

  • A web server (Apache Tomcat)

  • A web MVC framework for routing and handling requests.

  • An entity engine to define, load and manipulate data.

  • A service engine to define and control business logic.

  • A widget system to draw and interact with a user interface.

On top of the above mentioned core systems, OFBiz provides:

  • A data model shared across most businesses defining things like orders, invoices, general ledgers, customers and so on.

  • A library of services that operate on the above mentioned data model such as "createBillingAccount" or "updateInvoice" and so on.

  • A collection of applications that provide a user interface to allow users to interact with the system. These applications usually operate on the existing data model and service library. Examples include the "Accounting Manager" and "Order Manager".

  • A collection of optional applications called "plugins" that extend basic functionality and is the main way to add custom logic to OFBiz.

1.2. Components

The basic unit in OFBiz is called "component". A component is at a minimum a folder with a file inside of it called "ofbiz-component.xml"

Every application in OFBiz is a component. For example, the order manager is a component, the accounting manager is also a component, and so on.

By convention, OFBiz components have the following main directory structure:

component-name-here/
├── config/              - Properties and translation labels (i18n)
├── data/                - XML data to load into the database
├── entitydef/           - Defined database entities
├── groovyScripts/       - A collection of scripts written in Groovy
├── minilang/            - A collection of scripts written in minilang (deprecated)
├── ofbiz-component.xml  - The OFBiz main component configuration file
├── servicedef           - Defined services.
├── src/
    ├── docs/            - component documentation source
    └── main/java/       - java source code
    └── test/java/       - java unit-tests
├── testdef              - Defined integration-tests
├── webapp               - One or more Java webapps including the control servlet
└── widget               - Screens, forms, menus and other widgets

It is apparent from the above directory structure that each OFBiz component is in fact a full application as it contains entities, data, services, user interface, routing, tests, and business logic.

Both core OFBiz applications as well as plugins are nothing more than components. The only difference is that core applications reside in the "applications" folder whereas plugins reside in the "plugins" folder; also OFBiz does not ship with plugins by default.

1.3. Example workflow

Many basic concepts were explained so far. An example would help in putting all of these concepts together to understand the bigger picture. Let us take an example where a user opens a web browser and enters a certain URL and hits the enter key. What happens? It turns out answering this question is not quite simple because lots of things occur the moment the user hits "enter".

To try to explain what happens, take a look at the below diagram. Do not worry if it is not fully understandable, we will go through most of it in our example.

ofbiz architecture

1.3.1. User enters URL

In the first step in our example, the user enters the following URL:

If we break down this URL, we identify the following parts:

  • localhost: Name of the server in which OFBiz is running

  • 8443: Default https port for OFBiz

  • accounting: web application name. A web application is something which is defined inside a component

  • control: Tells OFBiz to transfer routing to the control servlet

  • findInvoices: request name inside the control servlet

1.3.2. Control servlet takes over

The Java Servlet Container (tomcat) re-routes incoming requests through web.xml to a special OFBiz servlet called the control servlet. The control servlet for each OFBiz component is defined in controller.xml under the webapp folder.

The main configuration for routing happens in controller.xml. The purpose of this file is to map requests to responses.

Request Map

A request in the control servlet might contain the following information:

  • Define communication protocol (http or https) as well as whether authentication is required.

  • Fire up an event which could be either a piece of code (like a script) or a service.

  • Define a response to the request. A response could either be another request or a view map.

So in this example, the findInvoices request is mapped to a findInvoices view.

View Map

A view map maps a view name to a certain view-type and a certain location.

View types can be one of:

  • screen: A screen widget which translates to normal HTML.

  • screenfop: A PDF screen designed with Apache FOP based constructs.

  • screencsv: A comma separated value output report.

  • screenxml: An XML document.

  • simple-content; A special MIME content type (like binary files).

  • ftl: An HTML document generated directly from a FreeMarker template.

  • screenxls: An Excel spreadsheet.

In the findInvoices example, the view-map type is a normal screen which is mapped to the screen: component://accounting/widget/InvoiceScreens.xml#FindInvoices

1.3.3. Widget rendered

Once the screen location is identified and retrieved from the previous step, the OFBiz widget system starts to translate the XML definition of the screen to actual HTML output.

A screen is a collection of many different things and can include:

  • Other screens

  • Decorator screens

  • Conditional logic for hiding / showing parts of the screen

  • data preparation directives in the <action> tag

  • Forms

  • Menus

  • Trees

  • Platform specific code (like FreeMarker for HTML output)

  • Others (portals, images labels etc …​)

Continuing the example, the FindInvoices screen contains many details including two forms. One form is for entering invoice search fields and the other form displays search results.

2. Web Framework

3. Web Applications

The OFBiz webapp is one of the core framework components. It is tightly integrated with other framework components.

3.1. Cross-domains Single Sign On (SSO)

In some cases you need to split the OFBiz applications on different servers, and possibly in production on different domains. This can happen for different reasons, most often for performance reason.

As it’s annoying to give each time a credential when changing from an OFBiz application to another on the same server, the same applies when changing from an OFBiz application to another on another domain.

To prevent that on the same server, the ExternalLoginKey mechanism is used. The cross-domains SSO feature allows to navigate from a domain to another with automated SSO.

It based on 3 technologies:

JWT

JWT Official site - Wikipedia for JWT

CORS

CORS (Mozilla doc) - Wikipedia for CORS

Ajax

Ajax, now well known I guess, in OFBiz we use jQuery for that.

The mechanism is simple.

On the source side:
  1. When an user log in in an application (webApp) a webappName.securedLoginId cookie is created. This cookie will be used by the mechanism to know the current logged in user. Note that all webappName.securedLoginId cookies are deleted when the user session is closed or time out. Hence (apart also using an intrinsically secured cookie) the mechanim is secured, even on shared machines. Of course if people are sharing a machine during their sessions, things could get complicated. This unlikely later case is not taken in account.

  2. The user is given a JavaScript link which passes the URL to reach and the calling webapp name to the sendJWT() Ajax function.

  3. The sendJWT() Ajax function calls the loadJWT() Ajax function which in turn calls the CommonEvents::loadJWT method through the common controller.

  4. The CommonEvents::loadJWT method uses the calling webapp name to retrieve the userLoginId from the secured webappName.securedLoginId cookie, creates a JWT containing the userLoginId, and returns it to the loadJWT() Ajax function.

  5. Then the sendJWT() Ajax function sends an Authorization header containing the JWT to the URL to reach. At this stage, if all things are correct, the flow leaves the source side.

On the server side:
  1. A CORS policy is needed. Without it, the Authorization token containing the JWT will be rejected. It’s a simple policy but you need to strictly define the authorized domains. Never use the lazy "*" for domains (ie all domains), else the preflight request will not work. Here is an example for Apache HTTPD (domain value is "https://localhost:8443" for official OFBiz demo):

Header set Access-Control-Allow-Origin domain
Header set Access-Control-Allow-Headers "Authorization"
Header set Access-Control-Allow-Credentials "true"
  1. The checkJWTLogin preprocessor, similar to the checkExternalLoginKey, intercepts the JWT, checks it and if all is OK signs the user on. That’s it !

In the example component, the FormWidgetExamples screen contains 2 new fields in the LinksExampleForm which demonstrate the use from a local instance to the trunk demo instance.

If you are interested in more details you may refer to https://issues.apache.org/jira/browse/OFBIZ-10307

3.2. Control Servlet

3.2.1. Requests

3.2.2. Views

4. Entity Engine

4.1. Entities

4.1.1. Standard Entities

4.1.2. View Entities

4.1.3. Extended Entities

4.1.4. Dynamic View Entities

4.2. XML Data

4.3. Entity engine configuration

4.4. Supported databases

5. Service Engine

5.1. Declaration and Implementation

5.2. Supported languages

5.3. Transaction management

5.4. Web services

6. Widget System

6.1. Screen Widget

6.1.1. Decoration Pattern

6.2. Form Widget

6.3. Menu Widget

6.4. Tree Widget

6.5. Portal Widget

6.6. Platform Specific Code

7. FrontJs Portal

frontJsPortal component included in frontJsPortal plugin aim to handle user interface with modern javascript framework (Vue.Js, React, Angular, ect…​).

It use portlet/portal system defined by XML and a javascript application.

This component is actually at POC (Proof Of Concept) state, so it aim to show some concrete ideas in order to discuss about them.
It is produced in an "Agility" spirit which mean that it focus to result in despite of some technical debt.

This documentation aim to,

  • Explain the current state of the component, how it work and what points had been choose or took away.

  • Prepare future documentation by explaining element meant for future users.

The first goal of using a javascript framework for user interface is to increase interactive elements in screen, even if theses screens are based on standard modules :

  • update some part of the screen by action or by data update;

  • modify forms field as function of other field values;

  • simpler screen configuration (portal page) without having to worry about portlets interaction.

7.1. POC Vuejs Portal

Actually the first javascript application enabling to use portlet system - portal Apache OFBiz is written with the Vue.js framework.

Vue.js has been choose for its easy learning curve and its community driven development instead of a corporation.

7.1.1. POC VuejsPortal installation

  1. From a standard trunk ofbiz-framework with 1837232 version or superior (that can be downloaded at documentation standard SourceRepository using svn).

  2. Create the 'plugins' repository at ofbiz-framework root folder.

  3. In the plugins repository, download the 2 next repositories using git :

    1. vuejsportal plugin that can be downloaded at https://gitlab.ofbizextra.org/ofbizextra/ofbizplugins/vuejsPortal

    2. example plugin (by Apache OFBiz) which have been modified for the need of this POC, that can be downloaded at https://gitlab.ofbizextra.org/ofbizextra/ofbizplugins/example on the vuejsPortal branch.

  4. After that you have to modify some files of ofbiz-framework.
    Being located in the 'plugins/VuejsPortal' :
    ./tools/applyOfbizFilesPatchs.sh
    And then copy some files
    rsync -a --exclude-from=./ofbizFiles/rsyncExcludes ./ofbizFiles/ ../../

  5. To be able to build the vue.js application you must have Node.js installed on your system.

    1. To install Node.js on a debian based system, execute the following commands :
      curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
      sudo apt-get install -y nodejs
      In case of another system, you can consult https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions

    2. Meanwhile an integrated gradle command

      1. Being located at 'plugins/vuejsPortal/webapp/vuejsPortal'

        • npm i ⇐ to install the project dependencies

        • npm run build ⇐ to build the project

  6. You can now launch OFBiz as regular :
    ./gradlew cleanAll loadAll ofbiz
    (being located at the root of ofbiz-framework )

  7. You can now connect to the application portal at https://localhost:8443/exampleapi/control/main

7.1.2. POC choices

Apache OFBiz trunk branch usage.

Principe and architecture of Apache OFBiz Portal Page and Porlet usage.

The whole use cases are applied to example component and during the the POC some new files used to be located in common have been moved to example aiming to simplify the installation and update process.

An additional webapp (exampleapi) had been created to centralize allowed URIs for vuejsPortal component, which are listed in constantes.js.
vuejsPortal component is not bound to a component, it just need a base URI for send its requests. This uri is temporary set in constantes.js but it will be more flexible later aiming to use vuejsPortal with multiple components

Portal component ( https://localhost:8443/exampleapi/control/login ) use ofbiz standard login by cookies mechanism, it will later use ofbiz login by token (cf. https://issues.apache.org/jira/browse/OFBIZ-9833).

screens, forms, menus used for portlets are defined with xml, and dedicated files for better readability.

There is a component (vuejs) for each ofbiz screen element ( SingleItemRow ⇒ vue.single-item-row ), which are defined by renderer for screen, form and menu ( and for html renderer that correspond globally to macro.ftl ). Actually, for the POC, to list them, you can search for output.*Screen( .

XML tags used

During the POC, few new properties or XML tags had been added but some of existing tags and properties had been distorted for an exclusive Vue.Js use.

  • screen

    • in <container tag

      • auto-update-target is used to bind a watcher to a container.

      • id is used to identify the container and enable to inject content into it with setArea later.

  • form

    • in <on-event-update-area tag (there can be many of them and they will be executed in xml apparition order. It will wait current task is completed before starting the next one. If any of these events is set, the submit button will prevent the form default behaviour, so if you need to post in addition to vuejsPortal feature you have to add an event-type submit)

      • event-type
        Define the action to be executed during the form submitting (click on submit button). It can take one of three next values :

        • setArea update area with id areaId with the post result at URI area-target with included parameter elements.

        • setWatcher update watcher areaId with included parameter. In case no parameter was passed to setWatcher it will use all form fields.

        • submit will submit the form to area-target with provided parameter elements. Like setArea if no parameter are defined it will use the whole form. Error and success messages will be displayed.

    • in <on-field-event-update-area tag (there can be many of them and they will be executed in xml apparition order. It will wait current task is completed before starting the next one.)

      • event-type
        Define the action to be executed during the form submitting (click on submit button). It can take one of four next values :

        • post will post at URI area-target with the parameter elements included in the tag. If there are no parameters, it will use the whole form. It will display error and success messages.

        • setArea update area identified by id area-id with the post result to URI area-target with included parameter elements. If no parameters was passed it will use the whole form.

        • setWatcher update watcher with watcherName area-id with included parameter elements. If no parameters was passed it will use the whole form

        • setFieldInForm will set the value of property area-target from form area-id to the actual field value.

    • in …​-event-update-area tag

      • auto-parameters-portlet allow to automatically fill necessary portlet parameters :

        • portalPageId

        • portalPortletId

        • portletSeqId

        • currentAreaId

    • in <hyperlink tag

      • link-type="anchor" is used to make an internal call to FrontJs.

      • target-window is used to design the watcherName that have to be updated.

  • menu

    • in <link tag

      • link-type="anchor" is used to make an internal call to FrontJs.

      • target-window is used to design the watcherName that have to be updated.

FrontJs Renderer

A new viewHandler and a new set of renderer had been created.
A new package had been created in org.apache.ofbiz.widget.renderer named frontJs which contain all new renderer.

In this package, there is a new FrontJsOutput class which allow to construct necessary elements whit the wished format. An object of this class is instantiate at the begin of viewHandler process, then is completed by rendered object call.

Uri showPortletFj of component exampleApi use the new viewHandler to return screen render result, showPortlet.

This URI is used by Vue.Js application for display information gathering.
New viewHandle return a json which contain 2 maps ( viewScreen and viewEntities ).
viewScreen contain all information about display. viewEntities contain all information about data set.

In future revision, it would be only necessary to gather data set.

ShowPortlet, ShowPortal

showPortlet screen is redefine in this PAC ( in example/widget/example ) for simplification purpose, to only do what we want.

there is a showPortal URI in FrontJs dedicated exampleApi.

Added field

A new field had been added to PortalPagePortlet entity : watcherName which is the name of the watcher that fire portlet update.

7.1.3. POC current situation

The Objective here is to make different use cases portal page to work.
At the menu, there are different pages, actually the name of portal pages name are MgmtPageFrontJs and RecapPageFrontJs.
The first page aim to find and edit examples. The second page aim to show all properties of a given example.

For each portlet present in theses pages, a portlet component is created in Vue.js with the portlet name which initialise itself with showPortletFj.

ShowPortletFj give the component all information about components it have to render (viewScreen) and data set it have to work with (viewEntities). Actually ShowPortletFj respond with a JSON.

Imbrication des container
ViewScreen and ViewEntities

Actually FrontJsViewHandler send all information to component who need update. In a client side logic, ViewScreen would be send only the first time the portlet call information then would only care about ViewEntity which update the data set it work on.

Actually "use-when" are handled by ofbiz in model model management whatever level they belong to (screen, form, field, menu), so we can’t get this information in the renderer. Later, we had to transmit to renderer all screen elements and if there was "use-when" for manage them in the frontend. This way next call could sent "use-when" correct value to properly update the final screen.

7.1.4. POC’s remaining todos

Minors

showPortletFj must verify security about field SecurityServiceName and SecurityMainAction.

Majeurs

7.1.5. POC’s use cases

Operational
A mettre en oeuvre

7.1.6. Vue.js

Vue.js general

todo

Components

A Vue.Js component is defined by 3 distinct blocks ( template, script, style ) which are regrouped in a single '.vue' file.

  1. Template :

    • Template must be contained in a single root element (div, span, table, ect…​ )

    • In template we can access computed properties and function with double curly bracket {{}}

    • Classic html attributes can be preceded by ':' to use script properties and js code instead of plain text

    • Vue.Js give us some directives (v-for, v-if v-on:click, ect…​) that are bind to script context and work like a classic attribute preceded by ':' and so can bind to script part and interpret js.

  2. Script

    • Script section is wrapped into an export default {} that contain the whole script elements. TIP: import have to be made before this export

    • This export is a map that can contain predefined keys used by Vue.Js (data, computed, methods, props, created, ect…​)

      • data() is a function that contain variable used is the component context.
        All variable contained in data() are reactive, which mean that Vue.Js will track any change on them and will reverberate theses changes everywhere there are used to reevaluate render.

      • computed is a map that contain evaluation based on reactive data.
        They can be used like a reactive variable in the component and will reevaluate itself when one of its entry had changed.

      • methods is a map containing helper function (handleClick, doPost, ect…​).
        WARNING: these functions don’t aim to be reactive.

      • props is an array of string who give attribute to the component for passing data from its parent to it.

      • This section can also contain hooks of the component life cycle (created(), mount(), beforeUpdate(), ect…​).
        Theses block of code will be executed when the hook is fired.
        See below :
        Component lifecycle

  3. Style
    This part is dedicated to internal css of the component.
    Style of this part is applied before other style of the project.

Vuex

Vuex is the centralised state system of the application. It allow us to create 'store' which contain data that can be accessed/modified by any component of the application.

A store is composed of 4 elements :

  1. State
    State is a map that contain all stored informations.
    State can only be modify by mutation. Default state’s data will be reactive.
    If Data had to be added in the state after its creation, they must be added with the method Vue.set() for them to be reactive.

  2. Mutations
    Mutations is a map that contain function allowed to alter state. By convention, mutation’s key have to be upper-case. Mutations can’t be accessed through code, they must be called by actions. Mutations must be synchronous.

  3. Actions
    Mutation first aim to fire mutation.
    Actions can be asynchronous, in which case there return a promise.

  4. Getters
    Getters allow to access state data.
    Getters are reactive.
    Getters can use parameters, if they are they return a function instead of a reactive variable.

Store can be split into modules who can be described in an index.js file, stores can so be stored into a modules repository.

Reactivity

todo

7.2. modular and generic UI

Aiming to have an open and modular ERP, it’s important that the user interface configuration allow to manage multiple screens from a set of screen "module" for manage same business object type in different business context.

Next chapters describe a user interface management system’s POC aiming to respond to theses modularity needs.

7.2.1. Use cases for POC-UI

Theses use cases are to be used for new UI POC, documentation associated and selenium unit task test.

All these use cases should be done with existing entities and services, if it’s necessary to develop one, simplify the use case, the goal is UI, not service or entity.

These use case description are done in a agile philosophy, only main point is present and during realization details choices will be discuss and done.

Preliminary remarks :
  1. In this document, the term "application" corresponding to "specialpurpose/component" in the ofbiz terminology which is not same as a "applications/component" in ofbiz terminology. An application is dedicated for a business purpose for a user type, it’s build by assembling piece of ofbiz components, sometimes without any specifics entities, services and screen (ex: CRM-B2C, CRM-B2B, SFA are 3 applications uses by sales men)

  2. Each use case is on an "application" and is part of one of the menu of this application.

    Of course, this document describe only menu-option needed by the use-case. As it’s difficult to do a clear definition of "web-page" because it’s depending of theme/template, use case is for me at two level :

    1. screen, which can be well define

    2. page, which depend on default theme

    Of course, some of use case (screen or page) will be done by a previous one (ex : sometime edit is done by the same screen as add). It’s even, one of the re-usable important point (UI, Doc and Selenium)

  3. Each time a line (or point) start by a "?" the question is "is this point is needed ?"

7.2.2. Release and goal

Goal is the POC realization, not doing multiple super applications. POC realization should generate discussion and decision, each time be careful to mainly discuss on UI, Doc or Selenium and not on use case business justification. Use case are to be realistic but mainly as a support for a specifics UI, Doc or Selenium needed.

V1

Main Goal is to proof the technical points. Of course UI re-organization is the driver for this phase, documentation start to work in parallel but will wait first realization to test integration for help. For selenium unit task, it will be a simple usage of the new UI with the default theme, the sole purpose being to check that test with default theme is simple.

So never mind if all cases is not done, the list exist to give different classic case what we will need to know how to process it. Some case which seem to be duplicate from other can be use for beginner who want to help and check if the concept is understanding.

During V1 realization, UseCase list will be updated

V2

Main Goal is to check if the solution is useful for the different type of contributors

  • experiment developer

  • beginner developer

  • functional consultant for parameters

  • functional consultant for personalization

  • ? end user - for parameters ? (if it’s still possible with the new architecture)

Use Case list will be use as a deployment plan. The list contains similar case to these which are realize in V1, so those on V2 can be achieve by all types of contributors.

Documentation Goal
  • Apache OFBiz User documentation (asscidoc)

  • Apache OFBiz web site wiki

  • OFBiz Help

Selenium test
  1. demo data for each use case and scenario

  2. selenium scenario test for each page (or page group)

  3. selenium unit test for each screen

7.2.3. Simple Party Management

Which Sub-Application
  1. in HR application, simple employee management

  2. in CRM B2C application, simple customer (person) management

  3. in eCommerce, simple profile page

  4. in HR application, simple organization/place (group) management

  5. in CRM B2B application, simple customer (company) management

  6. in Facility application, simple worker management

Which Party sub-component
  1. Party - Person - PartyGroup

  2. Contact Mech,

    • with postal address, phone and mail;

    • one or two fixes purpose (ex: phone fix number and mobile phone number)

  3. Role

  4. Party Identification

  5. Party association

  6. Not UserLogin because all Security entities should be use and it will generate a too large domain for this POC

Which Screen
  1. Party

    • find, list

    • A person

      • add / edit, show

    • A group

      • add / edit, show

    • A company

      • add / edit, show

    • show a synthesis view (party/person/company, contact informations, roles, Identification)

      • Person

      • Company

      • PartyGroup

  2. Contact information

    • all contact informations (for one party / facility)

      • with and without purpose

      • with and without history

      • deactivated

    • add / edit postal address

    • add / edit mail

    • add / edit phone

  3. Role

    • list for a party

    • add a role (for a parent RoleType)

    • add a role in two step :

  4. select parent RoleType

  5. select the role

    • remove a role

  6. Party Identifications

    • list, add, remove

7.2.4. HR Employee management

In HR Component, starting person management with the more complete form about person.

  • Menu option to manage employee

    • find, list, show, add, edit and manage his

      • contact information

      • identification (3 idTypes, one mandatory, two optionals)

  • template page with a header (or sidebar or …​) to show on which employee we are

Use Case Screen :
  1. find Person

    • simple form (only on party or person)

    • with an add button (which can be show or not depending on parameter or authorization)

  2. Person list with an add button (which can be show or not depending on parameter or authorization)

  3. add a Person

  4. show a Person

  5. show a Person with sub-menu with two options : contact informations and Identifications

  6. edit a Person

  7. List of all contact informations for a person, with an add button (which can be show or not depending on parameter or authorization)

  8. add a postal address

  9. add a mail

  10. add a phone number (to go step by step, without purpose management, will be done in next Use Case group)

  11. edit a postal address

  12. edit a mail

  13. edit a phone number

  14. List of all identification number for a person, with an add button (which can be show or not depending on parameter or authorization)

  15. add a identification number with choice of identification type

  16. edit a identification number with choice of identification type

  17. add a identification number with a fix identification type

  18. edit a identification number with a fix identification type

Use Case Page :
  1. create a person

  2. search a person

  3. visualize a person

  4. manage informations about a person

  5. template page with a header (or sidebar or …​) to show on which employee we are, (for example to show all his knowledges, or his skills, or his positions, or his …​)

  6. manage informations about a person on one page, and with access at this page directly by a field (auto-completion on id, first, last name)

7.2.5. CRM B2C, customer mgnt

In a CRM B2C application, the customer (so, in this context, a person) management.
The difference from previous use case group is :

  1. person form is more simple than in HR

  2. role will be used to characterize customer position (suspect, prospect, with_Quote, customer)

Menu option to manage employee

  • find (with role field), list, show, add, edit and manage his

    • contact informations

    • identification (3idTypes, one mandatory, two optionals)

  • template page with a header (or sidebar or …​) to show on which customer we are

Use Case Screen :
  1. find Person with an add button (which can be show or not depending on parameter or authorization)

    • search field same as in HR find person

    • role field which can appear or not, when not appear a fix value has been put as parameters.

    • contact information field, phone, mail, town. These fields can be show or not by the user with a "deploy" button

  2. Person list with an add button (which can be show or not depending on parameter or authorization)

    • role field appear or not, when not appear a fix value has been put as parameters, so only person with this role appear

  3. add a Person, all main informations in the form

    • role

    • less field about person than in HR form

    • 1 postal address

    • 2 phone number

    • 1 identification number

  4. show a Person, all main informations in the screen with indicator for contact information and identification when there are more data that what it’s show.

  5. show a Person with sub-menu with options :

    • contact informations

    • Identifications

    • role history

    • change role : a direct action button

  6. edit a Person, only "Person" field

  7. a button bar to change role (ex: for a suspect, there are the 3 options), this use case is for having a action bar, in this business process case it’s maybe not a need, but for more complex object like order or task, it’s a classical need.

  8. List of all contact informations for a person, with one or multiple add buttons (which can be show or not depending on parameter or authorization) and purpose are show, it’s the second step, with purpose management.

  9. add a postal address (or just a purpose)

  10. add a mail

  11. add a phone number

  12. edit a postal address

  13. edit a mail

  14. edit a phone number

  15. List of all identification number for a person, with an add button (which can be show or not depending on parameter or authorization)

  16. add a identification number with choice of identification type

  17. edit a identification number with choice of identification type

Use Case Page
  1. create a new entry in CRM (role is choose during creation)

  2. search a "customer" (or suspect, prospect, …​)

  3. visualize a "customer"

  4. manage informations about a "customer"

  5. template page with a header (or sidebar or …​) to show on which "customer" we are, (for example to show all his quotes, or his orders, or …​)

  6. manage informations about a person on one page, and with access at this page directly by a field (auto-completion on id, first, last name)

7.2.6. eCommerce, profile page

A simple profile page.
The difference from previous use case will be mainly on Use Case Page because eCommerce theme could be more original and public user interface should be, most of the time, more simple.

Use Case Screen :
  1. show the person, all main informations in the screen with indicator for contact information and identification when there are more data that what it’s show.

  2. show the Person with sub-menu with options :

    • contact informations

    • Identifications

  3. edit a Person, only "Person" field

  4. List of all contact informations for a person, with an add button and purpose are show, purpose is need for invoice or shipping.

  5. add a postal address (or just a purpose)

  6. add a mail

  7. add a phone number

  8. edit a postal address

  9. edit a mail

  10. edit a phone number

Use Case Page :
  1. visualize the profile (the person) with edit button

  2. manage his contact informations

  3. manage his identifications

  4. All in one page, which can be look as a long page.

7.2.7. HR organization mgnt

In HR component, a simple organization/place (group) management.
Now PartyGroup management (very simple), but with complex screen to manage hierarchy. In this use case group we will use the word "group" for service or department, or subsiadiry.

  • Menu option to manage the Company organization

    • manage group

    • associated employee in a group

    • manage a hierarchy of group

Use Case Screen :
  1. find group (with a specific partyType)

    • simple form (only on party or partyGroup)

    • with an add button (which can be show or not depending on parameter orauthorization)

  2. PartyGroup list with an add button (which can be show or not dependingon parameter or authorization)

  3. add a group

  4. show a Person, all informations in screen with sub-menu with two options : contact informations and Identifications

  5. edit a Group

  6. List all contact informations for a group, with an add button (which can be show or not depending on parameter or authorization)

  7. add a postal address

  8. add a phone number

  9. edit a postal address

  10. edit a phone number

  11. List all identification number for a group, with an add button (which can be show or not depending on parameter or authorization)

  12. add a identification number with choice of identification type

  13. edit a identification number with choice of identification type

  14. add a identification number with a fix identification type

  15. edit a identification number with a fix identification type

  16. List all person associated to the group with two add buttons (which can be, individually, show or not depending on parameter or authorization)

    • add a manager

    • add a member

  17. List all group associated to the group (the child) with two add buttons (which can be, individually, show or not depending on parameter or authorization)

    • add an existing group as a child

    • create a new group and add it as a child

    • in the list, each group is a link to this screen, to be able to navigate top-down

    • a third button to go to the parent level, to be able to navigate bottom-up

    • the name of the group manager appear above the list

  18. ? List all parent group for a group or for a person ?

  19. show group hierarchy as a tree with action or detail at each level, top-down

  20. show group hierarchy as a tree with action or detail at each level, bottom-up

Use Case Page :
  1. search a group

  2. manage a group

  3. manage its contact informations

  4. manage hierarchy step by step (parent to child or child to parent)

  5. manage hierarchy with a tree view

  6. in HR employee, show the tree, top-down or bottom-up with the template "for an employee"

7.2.8. CRM B2B customer mgnt

In a CRM B2B application, the customer (so, in this context, a company) management.
For clarification, in these Use Cases, B2B is an other application than B2C.
The "CRM B2C & B2B" will be a third, but not in this list because it contains no specificity on screen-page definition

The main difference between B2C is :

  1. company versus person,

  2. contact management with PartyAssociation

  3. ? customer organization management ?

Use Case Screen :
  1. find customer (a company (specific partyType)) with an add button (which can be show or not depending on parameter or authorization)

    • search field are on multiple entities with some part deploy or not

    • role field which can appear or not, when not appear a fix value has been put as parameters.

    • contact information field, phone, mail, town. These fields can be show or not by the user with a "deploy" button

  2. Company list with an add button (which can be show or not depending on parameter or authorization)

    • role field appear or not, when not appear a fix value has been put as parameters, so only company with this role appear

  3. add a Company, all main informations in the form

    • role

    • field from PartyGroup

    • 1 postal address

    • 2 phone number

    • 2 identification number

  4. show a Company, all main informations in the screen with indicator for contact informations and identification when there are more data that what it’s show.

  5. show a Company with sub-menu with options :

    • contact informations

    • Identifications

    • role history

    • change role : a direct action button

  6. edit a Company, only "Company" field

  7. a button bar to change role (ex: for a suspect, there are the 3 options), this use case is for having a action bar.
    In this business process case it’s maybe not a need, but for more complex object like order or task, it’s a classical need.

  8. List of all contact informations for a company, with an add button (which can be show or not depending on parameter or authorization) and purpose are show, (so, with purpose management).

  9. add a postal address (or just a purpose)

  10. add a mail

  11. add a phone number with purpose

  12. edit a postal address

  13. edit a mail

  14. edit a phone number

  15. List of all identification number for a company, with an add button (which can be show or not depending on parameter or authorization)

  16. add a identification number with choice of identification type

  17. edit a identification number with choice of identification type

  18. list of contact (person) associated to this company with an add button (which can be show or not depending on parameter or authorization)

    • a contact is a person with contact information

    • list with only one line per contact

    • list of block with contact details for each

  19. edit a contact or his contact information

Use Case Page :

Exactly the same as the CRMB2C

  1. create a new entry in CRM (role is choose during creation)

  2. search a "customer" (or suspect, prospect, …​)

  3. visualize a "customer"

  4. manage informations about a "customer"

  5. template page with a header (or sidebar or …​) to show on which "customer" we are, (for example to show all his quotes, or his orders, or …​)

  6. manage informations about a company on one page, and with access at this page directly by a field (auto-completion on id, first, last name).

7.2.9. Facility worker mgnt

In Facility application, simple facility’s worker management.
For this last use case group, it’s a simplification of the previous one.
Only a very simple and short process for adding people.

It’s the last one, because the goal is to check if it’s easy and rapid to create (or parametrize) a new small application from existing one.

In the Warehouse Management application (simple version OOTB)

  • in the administration menu

    • the user menu to manage internal user per facility In the standard business process, it will be used mainly for login and authorization, in our case we will only manage person, his phone number and his facility (where he’s authorized)

    • the facility menu to manage contact informations and person authorized

Use Case Screen :
Already existing screen used
  1. find Person

    • simple form (only on party or person)

    • with an add button

  2. Person list with an add button

  3. add a Person, simple form 3-6 fields

  4. show a Person

  5. show a Person with sub-menu with option to manage contact informations

  6. edit a Person

  7. List of all contact informations for a person, with one or multiple add button

  8. add a mail

  9. add a phone number

  10. edit a mail

  11. edit a phone number

New Screen
  1. add a facility, simple form, if service exist, including some contact informations

  2. List of all existing facility

  3. List of all contact informations for a facility, with one or multiple add button

  4. List of all persons associated to the facility, with two add button

    • add an existing person

    • create a new person and add it to the facility

  5. List of all facility associated to a person, with one add button

    • add an existing facility

Use Case Page :
  1. manage facilities

  2. manage persons

  3. visualize a facility details (info, contact informations, persons associated)

7.3. Portlet

Portlet is an autonomous part of the screen, which mean that there can be action that only alter this part of the screen, mainly fired by itself.

Portlet allow a huge modularity gain in user interface, that way an user action (click on a link, type in a field, ect…​) must not precise the portlet itself but a logical name which can be subscribed one or more portlet.

This logical name which is subscribed by portlets is called watcherName, this field is in the association table between PortalPage and PortalPortlet.

7.3.1. Portlet update

It’s the update of the data stored in js client side store who trigger portlet render update.

7.4. Portal Page

7.5. FrontJs Glossary

watchers

They are objects stored in js store that can be altered remotely. watcher’s subscribers update themselves with the new value when it change.

watchers

c’est nom du store VueJs utilisé pour stocker les différentes variable sur lequel les container ou portletsont abonné et donc se mette à jours quand celui-ci change.

watcherName

It is the key of a watcher

8. Core APIs

9. Development environment

9.1. Setup your environment

9.1.1. Java SE

9.1.2. IDE

Eclipse
Intellij Idea

9.1.3. Database

9.2. Web tools

10. Testing

10.1. Unit Tests

10.2. Integration Tests

11. Deployment

12. Security

12.1. Impersonation

12.1.1. What is Impersonation in Apache OFBiz

The Apache OFBiz Project Release 17.12

Introduction to User impersonation

User Impersonation is a feature that offer a way to select a user login and impersonate it, i.e. see what the user could see navigating through the application in his name.

How do this work ?

An authorized user (see security and controls section for configuration), can select a user that will be impersonated.

The impersonation start, if everything is well configured, in current application (partymgr for the demo). Everything appears like if we were logged in with the userLoginId and the valid password (though we know nothing about it)

The only thing showing that we currently are impersonating a user is the little bottom-right image :

Impersonate icon

This icon indicates, when clicking on it, the user impersonated, and offer a way to depersonate.

The impersonate period is stored for audit purpose, and if the impersonator forgot to depersonate, the period is terminated one hour after impersonation start.

Security

This feature can draw some concerns about security aspect. This paragraph will introduce every controls and properties that have been implemented around the impersonation feature.

Caution
These configuration steps are not to be neglected for a production environment since this feature offer a way to act in place of another user.
Properties

The security.properties file introduce two properties that control impersonation feature :

security.disable.impersonation = true

This property, set by default to true, controls the activation of impersonation feature. If no configuration is done any user trying to use impersonation will face an error message, indicating that the feature is disabled.

To enable impersonation this property need to be set to false

security.login.authorised.during.impersonate = false

This property controls the way impersonation occurred to the impersonated user :

In default configuration, the impersonated user see nothing and can use the application without knowing that he is currently impersonated. Several authorized user can impersonate a same login without any issue.

Note
This configuration is intended for testing/QA environment allowing any authorized user to impersonate a login to validate its configuration, test the application etc.

Set to true, this configuration improve the control of the data generated by the impersonated user. Indeed, Only one authorized user can impersonate a login at the same time, and during the impersonation process, the impersonated user is unable to act within the application.

Since the impersonation period is stored in database, the actions done by the authorized user can be identified if there is the need to do so.

Note
This configuration is intended for production environment
Controls
The permission

First, to be able to use impersonation, a user need to possess IMPERSONATE_ADMIN permissions. Demo data offer IMPERSONATION security group for this purpose.
In demo data, FULLADMIN security group also possess the permission.

Permission based user restriction

An authorized user cannot impersonate any user. There are two main controls that will restrict the impersonation feature.

Cannot impersonate Admin user

It is impossible to impersonate a user that is granted any of the admin permission :

"IMPERSONATE_ADMIN"
"ARTIFACT_INFO_VIEW"
"SERVICE_MAINT"
"ENTITY_MAINT"
"UTIL_CACHE_VIEW"
"UTIL_DEBUG_VIEW"
Cannot impersonate more privileged user

It is impossible to impersonate a user that has more permission than your user. Even if the missing persmission is a minor one.

13. Appendices

14. From Mini Language to Groovy

This is a small guide for everybody involved in converting the Mini Language into Groovy.

Note
Why is this important?
This tutorial is directly linked to the efforts of converting all scripts in Mini Language to newer Groovy Scripts. All of this is done, because Groovy is much more readable and easier to review, more up to date and many other reasons, which can be found here: Proposal for deprecating Mini Language

To contribute, or just be up to date with the current process, you can look at the existing JIRA issue OFBIZ-9350 - Deprecate Mini Lang

14.1. Groovy DSL (dynamic scripting library)

14.1.1. How to get Groovy support in your IDE

The following paragraph is for Eclipse users.

It is possible to get Groovy support in Eclipse by converting the loaded project to a Groovy Project. The project itself will work as before.

To do this just follow these few steps:

  1. Right-click on the project that has to be converted

  2. Click on "Configure"

  3. Click on "Convert to Groovy Project"

Eclipse will automatically load the file OfbizDslDescriptorForEclipse.dsld , in which the known fields and methods used in Groovy Scripts are defined.

14.1.2. Known Fields

property name: 'parameters'
type : 'java.util.Map'

These are the parameters given to the Groovy Script, when it is called as a service. It is equivalent to Map<String, Object> context in the Java-Service-Definition.

property name: 'context'
type: 'java.util.Map'

More parameters, which are, for example, given through a screen or another Groovy Script. This is important when the script is called through an action segment of a screen.

property name: 'delegator'
type: 'org.apache.ofbiz.entity.Delegator'

Normal instance of the Delegator, which is used for special database access.

property name: 'dispatcher'
type: 'org.apache.ofbiz.service.LocalDispatcher'

Normal instance of the LocalDispatcher, which is used to call services and other service-like operations.

property name: 'security'
type: 'org.apache.ofbiz.security.Security'

Normal instance of the Security-Interface with which permission checks are done.

14.2. Known Methods

method name: 'runService'
type: 'java.util.Map'
params: [serviceName: 'String', inputMap: 'java.util.Map']

Helping method to call services instead of dispatcher.runSync(serviceName, inputMap). Also possible: run service: serviceName, with: inputMap

method name: 'makeValue'
type: 'java.util.Map'
params: [entityName: 'String']

Helping method to make a GenericValue instead of delegator.makeValue(entityName). Creates an empty GenericValue of the specific entity.

method name: 'findOne'
type: 'java.util.Map'
params: [entityName: 'String', inputMap: 'java.util.Map']

Helping method to find one GenericValue in the database. Used instead of delegator.findOne(entityName, inputMap)

method name: 'findList'
type: 'java.util.List'
params: [entityName: 'String', inputMap: 'java.util.Map']

Helping method to find many GenericValue in the database. Used instead of delegator.findList(entityName, inputMap, null, null, null, false)

method name: 'select'
type: 'org.apache.ofbiz.entity.util.EntityQuery'
params: [entity: 'java.util.Set']

Helping method used instead of EntityQuery.use(delegator).select(…​)

method name: 'select', type: 'org.apache.ofbiz.entity.util.EntityQuery', params: [entity: 'String…​']
As above.

method name: 'from'
type: 'org.apache.ofbiz.entity.util.EntityQuery'
params: [entity: 'java.lang.Object']

Helping method used instead of EntityQuery.use(delegator).from(…​)

method name: 'success'
type: 'def'
params: [message: 'String']

Helping method used instead of ServiceUtil.returnSuccess(message)

method name: 'failure'
type: 'java.util.Map'
params: [message: 'String']

Helping method used instead of ServiceUtil.returnFailure(message)

method name: 'error'
type: 'def'
params: [message: 'String']

Helping method used instead of ServiceUtil.returnError(message)

method name: 'logInfo'
type: 'void'
params: [message: 'String']

Helping method used instead of Debug.logInfo(message, fileName)

method name: 'logWarning'
type: 'void'
params: [message: 'String']

Helping method used instead of Debug.logWarning(message, fileName)

method name: 'logError'
type: 'void'
params: [message: 'String']

Helping method used instead of Debug.logError(message, fileName)

method name: 'logVerbose'
type: 'void'
params: [message: 'String']

Helping method used instead of Debug.logVerbose(message, fileName)

The actual definition of the methods can be found in `/framework/service/src/main/java/org/apache/ofbiz/service/engine/GroovyBaseScript.groovy, the variables dctx, dispatcher and delegator are set in the file GroovyEngine.java which can be found in the same location.

14.3. Services

14.3.1. From MiniLang to Groovy

To see additional examples and finished conversions, which may help with occurring questions, click: OFBIZ-9350 - Deprecate Mini Lang There is a chance that a similar case has already been converted.

Important
When a simple-method ends, it will automatically at least return a success-map.

All the Groovy Services have to return success at least, too.

return success()

14.3.2. Getting started

MiniLang files consist of services, which, in most cases, implement services.

The get converted to Groovy like the following:

<!-- This is MiniLang -->
<simple-method method-name="createProductCategory" short-description="Create an ProductCategory">
   <!-- Code -->
</simple-method>
// This is the converted Groovy equivalent
/**
 * Create an ProductCategory
 */
def createProductCategory() {
    // Code
}

It will be useful for future developers, and everybody who has to check something in the code, to put at least the short-description as the new Groovydoc. This will hopefully more or less explain, what the method should or shouldn’t do. If the short-description isn’t helpful enough, feel free complete it.

The structure of if and else in MiniLang is a little different than the one from Groovy or Java and can be a bit confusing when first seen, so here is an example:

<if-empty field="parameters.productCategoryId">
    <sequenced-id sequence-name="ProductCategory" field="newEntity.productCategoryId"/>
<else>
    <set field="newEntity.productCategoryId" from-field="parameters.productCategoryId"/>
    <check-id field="newEntity.productCategoryId"/>
    <check-errors/>
</else>
</if-empty>
Note
Notice, that the else always starts before the if-tag is closed, but sometimes isn’t indented as one would expect it.

When navigating through bigger if-phrases, the navigation itself will be much easier through just clicking in the opening or closing if-tag; Eclipse will automatically mark the matching opening or closing if-tag for you.

There are two possibilities to initialize a field/variable in Groovy.

  1. To define a field/variable with its correct typing
    String fieldName = "value"`

  2. To just "define" a field/variable. The IDE you are working with may not recognize the typing, but OFBiz can work with it:
    def fieldName = "value"

14.4. Checking Fields

Minilang Groovy
<if-empty field="fieldName"></if-empty>
 //checks if fieldName is existent and/or empty
if (!fieldName) {}
<if-empty field="fieldName.property"></if-empty>
 // fieldName has to be existent, property doesn't need to
 // if known, that property does exist, the ? can be left out
if (!fieldName?.property) {}
 // CAUTION: every query like this in Groovy evaluates to a Boolean type
 // everything that is empty or false will turn into false:
 // null, [], [:], "", false -> false

if (UtilValidate.isEmpty(fieldName)) {}
<if>
    <condition>
        <or>
            <if-empty field="field1"/>
            <if-empty field="field2"/>
        </or>
    </condition>
    <then>
        <!-- code in if -->
    </then>
    <else>
        <!-- code in else -->
    </else>
</if>
if (!field1 || !field2) {
 // code in if
} else {
 // code in else
}
<if-compare-field field="product.primaryProductCategoryId" to-field="parameters.productCategoryId" operator="equals">
    <!-- code -->
</if-compare-field>
 // this will even work, if product is not existent or null
if (UtilValidate.areEqual(product?.primaryProductCategoryId, parameters.productCategoryId)) {
    // code
}
<if-instance-of field="parameters.categories" class="java.util.List"></if-instance-of>
if (parameters.categories instanceof java.util.List) {}

14.5. Setting Fields

Minilang Groovy
<set field="fieldName" value="value"/>
 // if fieldName is not initialized
String fieldName = "value"
 // if fieldName is initialized
fieldName = "value"
<set field="otherFieldName.property" value="value"/>
<set field="otherFieldName.otherProperty" value="true" type="Boolean"/>
<set field="otherFieldName.otherProperty" from-field="parameters.property/>
 // if otherFieldName is not yet initialized, you have to do it first
 // MiniLang does that automatically
Map otherFieldName = [:] // empty Map
 // now put the values in
otherFieldName = [
    property: "value",
    otherProperty: true
]
 // or the less efficient way
otherFieldName.property = "value"
otherFieldName.otherProperty = true

 // it is possible to put different values in later:
otherFieldName.property = parameters.property
<set field="thisFieldName" value="${groovy: []}" type="List"/>
 // this is easier in Groovy
List thisFieldName = []
<property-to-field resource="CommonUiLabels" property="CommonGenericPermissionError" field="failMessage"/>
<!-- there are different cases of this, which are not distinguished in MiniLang -->
<property-to-field resource="general.properties" property="currency.uom.id.default" field="parameters.rateCurrencyUomId"/>
String failMessage = UtilProperties.getMessage("CommonUiLabels", "CommonGenericPermissionError", parameters.locale)
 // in Groovy there can be a difference for the second case
parameters.rateCurrencyUomId = UtilProperties.getPropertyValue('general.properties', 'currency.uom.id.default')
<clear-field field="product.primaryProductCategoryId"/>
product.primaryProductCategoryId = null

14.6. Starting Services

Minilang Groovy
<set field="relatedCategoryContext.parentProductCategoryId"  from-field="defaultTopCategoryId"/>
<call-service service-name="getRelatedCategories" in-map-name="relatedCategoryContext">
    <result-to-field result-name="categories" field="resCategories"/>
</call-service>
def relatedCategoryContext = [parentProductCategoryId: defaultTopCategoryId]
def serviceResult = run service: "getRelatedCategoryies", with: relatedCategoryContext
def resCategories = serviceResult.categories
 // if it is not too confusing to read you can leave out the extra variable
run service: "getRelatedCategoryies", with: [parentProductCategoryId: defaultTopCategoryId]
<set-service-fields service-name="productCategoryGenericPermission" map="parameters" to-map="productCategoryGenericPermissionMap"/>
<call-service service-name="productCategoryGenericPermission" in-map-name="productCategoryGenericPermissionMap">
    <results-to-map map-name="genericResult"/>
</call-service>
 // instead of setting the service fields from parameters, it is possible to run the service with the parameters map
Map genericResult = run service: "productCategoryGenericPermission", with: parameters

14.7. Preparing Service Results

Minilang Groovy
<field-to-result field="fieldBudgetId" result-name="budgetId"/>
 // MiniLang knows this implicitly
def result = success()
result.budgetId = fieldBudgetId
return result

14.8. Database Communication

Minilang Groovy
<make-value entity-name="FinAccountTrans" value-field="newEntity"/>
<set-nonpk-fields map="parameters" value-field="newEntity"/>
<set-pk-fields map="parameters" value-field="newEntity"/>
 // this is the easy way
GenericValue newEntity = makeValue("FinAccountTrans", parameters)
 // this is also possible
GenericValue newEntity = makeValue("FinAccountTrans")
newEntity.setPKFields(parameters)
newEntity.setNonPKFields(parameters)
<entity-and entity-name="BudgetStatus" list="budgetStatuses">
    <field-map field-name="budgetId" from-field="parameters.budgetId"/>
    <order-by field-name="-statusDate"/>
</entity-and>
 // this can also be done in one line, but it can easily become unreadable
def budgetStatuses = from("BudgetStatus")
    .where("budgetId", paramters.budgetId)
    .orderBy("-statusDate")
    .queryList()
<entity-one entity-name="StatusValidChange" value-field="statusValidChange">
    <field-map field-name="statusId" from-field="budgetStatus.statusId"/>
    <field-map field-name="statusIdTo" from-field="parameters.statusId"/>
</entity-one>
<!-- entity-one can be called without child elements, too -->
<entity-one entity-name="Product" value-field="product" auto-field-map="true"/>
 // MiniLang has false set for useCache as the default value
statusValidChange = findOne("StatusValidChange", [statusId: budgetStatus.statusId, statusIdTo: parameters.statusId], false)
 // this is also possible
statusValidChange = from("StatusValidChange")
    .where("statusId", budgetStatus.statusId, "statusIdTo", parameters.statusId)
    .queryOne()
 // if there are no child elements, this can be used
GenericValue product = from("Product").where(parameters).queryOne()
<find-by-primary-key entity-name="ProductCategoryMember" map="lookupPKMap" value-field="lookedUpValue"/>
GenericValue lookedUpValue = findOne("ProductCategoryMember", lookupPKMap, false)
 // this is also possible
lookedUpValue = from("ProductCategoryRole")
    .where(lookupPKMap)
    .queryOne()
<entity-condition entity-name="ProductCategoryContentAndInfo" list="productCategoryContentAndInfoList" filter-by-date="true" use-cache="true">
    <condition-list combine="and">
        <condition-expr field-name="productCategoryId" from-field="productCategoryList.productCategoryId"/>
        <condition-expr field-name="prodCatContentTypeId" value="ALTERNATIVE_URL"/>
    </condition-list>
    <order-by field-name="-fromDate"/>
</entity-condition>
<!-- entity-condition can also be used with the "or" operator -->
<entity-condition entity-name="ProdCatalogCategory" list="prodCatalogCategoryList" filter-by-date="true">
    <condition-list combine="and">
        <condition-expr field-name="productCategoryId" from-field="parameters.productCategoryId"/>
        <condition-list combine="or">
            <condition-expr field-name="prodCatalogCategoryTypeId" value="PCCT_VIEW_ALLW"/>
            <condition-expr field-name="prodCatalogCategoryTypeId" value="PCCT_PURCH_ALLW"/>
        </condition-list>
    </condition-list>
</entity-condition>
 // the Groovy methods use the "and" and "equals" operator as default values
List productCategoryContentAndInfoList = from("ProductCategoryContentAndInfo")
    .where("productCategoryId", productCategoryList.productCategoryId, "prodCatContentTypeId", "ALTERNATIVE_URL")
    .cache().orderBy("-fromDate")
    .filterByDate()
    .queryList()
 // with the use of the "or" operator you have to build your condition like this
EntityCondition condition = EntityCondition.makeCondition([
    EntityCondition.makeCondition([
        EntityCondition.makeCondition("prodCatalogCategoryTypeId", "PCCT_VIEW_ALLW"),
        EntityCondition.makeCondition("prodCatalogCategoryTypeId", "PCCT_PURCH_ALLW")
    ], EntityOperator.OR),
    EntityCondition.makeCondition("productCategoryId", parameters.productCategoryId)
])
List prodCatalogCategoryList = from("ProdCatalogCategory").where(condition).filterByDate().queryList()
<make-value entity-name="FinAccountTrans" value-field="newEntity"/>
<set-nonpk-fields map="parameters" value-field="newEntity"/>
<!-- In this case multiple fields of the GenericValue are set -->
<make-value entity-name="ProductCategoryRollup" value-field="newLimitRollup"/>
<set field="newLimitRollup.productCategoryId" from-field="newEntity.productCategoryId"/>
<set field="newLimitRollup.parentProductCategoryId" from-field="productCategoryRole.productCategoryId"/>
<set field="newLimitRollup.fromDate" from-field="nowTimestamp"/>
def newEntity = makeValue("FinAccountTrans", parameters)
 // you can set multiple fields of a GenericValue like this
def newLimitRollup = makeValue("ProductCategoryRollup", [
    productCategoryId: newEntity.productCategoryId,
    parentProductCategoryId: productCategoryRole.productCategoryId,
    fromDate: nowTimestamp
])
<set field="statusValidChange.prop" value="value"/>
statusValidChange.prop = "value"
<create-value value-field="newEntity"/>
newEntity.create()
<store-value value-field="newEntity"/>
<store-list list="listToStore"/>
newEntity.store()
delegator.storeAll(listToStore)
<clone-value value-field="productCategoryMember" new-value-field="newProductCategoryMember"/>
def newProductCategoryMember = productCategoryMember.clone()
<remove-value value-field="lookedUpValue"/>
lookedUpValue.remove()
<sequenced-id sequence-name="ProductCategory" field="newEntity.productCategoryId"/>
newEntity.productCategoryId = delegator.getNextSeqId("ProductCategory")
<check-id field="newEntity.productCategoryId"/>
UtilValidate.checkValidDatabaseId(newEntity.productCategoryId)
<make-next-seq-id value-field="newEntity" seq-field-name="linkSeqId"/>
delegator.setNextSubSeqId(newEntity, "linkSeqId", 5, 1)
 // the numbers 5 and 1 are used in the Java implementation of the MiniLang method
 // and can also be found as the default values in the MiniLang documentation

14.9. Permissions

Caution
To also check for admin-permissions, this method has to be used:
hasEntityPermission(permission, action, userLogin)

If the method is used with wildcards, it is important to not forget the underscore, which comes before the parameter action!

Minilang Groovy
<check-permission permission="CATALOG" action="_CREATE">
    <alt-permission permission="CATALOG_ROLE" action="_CREATE"/>
    <fail-property resource="ProductUiLabels" property="ProductCatalogCreatePermissionError"/>
</check-permission>
<check-errors/>
if (!(security.hasEntityPermission("CATALOG", "_CREATE", parameters.userLogin)
    || security.hasEntityPermission("CATALOG_ROLE", "_CREATE", parameters.userLogin))) {
    return error(UtilProperties.getMessage("ProductUiLabels", "ProductCatalogCreatePermissionError", parameters.locale))
}
<set field="hasCreatePermission" value="false" type="Boolean"/>
<if-has-permission permission="${primaryPermission}" action="${mainAction}">
    <set field="hasCreatePermission" value="true" type="Boolean"/>
</if-has-permission>
 // this will automatically be set to false if the user doesn't have the permission
def hasCreatePermission = security.hasEntityPermission(primaryPermission, "_${mainAction}", parameters.userLogin)

14.10. Timestamp And System Time

The first two simple-method are deprecated; the third method should have been used instead.

Minilang Groovy
<now-timestamp field="nowTimestamp"/>
Timestamp nowTimestamp = UtilDateTime.nowTimestamp()
<now-date-to-env field="nowDate"/>
Timestamp nowDate = UtilDateTime.nowTimestamp()
<!-- this method also has the parameter "type", which is set to 'java.sql.timestamp' as default -->
<now field="fooNow"/>
Timestamp fooNow = UtilDateTime.nowTimestamp()
<if-compare-field field="productCategoryMember.thruDate" to-field="expireTimestamp" operator="less" type="Timestamp">
    <!-- code -->
</if-compare-field>
Timestamp thruDate = productCategoryMember.thruDate
if (thruDate && thruDate.before(expireTimestamp)) {
    // code
}

14.11. Logging

Since all of the log methods are know to the Groovy Language, it is possible to just nearly use them as they are in MiniLang.
For further explanation, here are some examples:

Minilang Groovy
<log level="verbose" message="Permission check failed, user does not have permission"/>
logVerbose("Permission check failed, user does not have the correct permission.")
<log level="info" message="Applying feature [${productFeatureId}] of type [${productFeatureTypeId}] to product [${productId}]"/>
logInfo("Applying feature [${productFeatureId}] of type [${productFeatureTypeId}] to product [${productId}]")

14.12. General

Minilang Groovy
<call-simple-method method-name="checkCategoryRelatedPermission"/>
<check-errors/>
 // simple-methods inside of classes, as long as they are not services, will be called like normal methods
Map res = checkCategoryRelatedPermission("updateProductCategory", "UPDATE", null, null)
if (!ServiceUtil.isSuccess(res)) {
    return res
}
<iterate list="subCategories" entry="subCategory">
    <!-- code -->
</iterate>
for (def subCategory : subCategories) {
    // code
}
subCategories.each { subCategory ->
    // code
}
<iterate-map map="parameters.productFeatureIdByType" key="productFeatureTypeId" value="productFeatureId">
    <!-- in here something should happen with value and key -->
</iterate-map>
for (Map entry : parameters.productFeatureIdByType.entrySet()) {
    def productFeatureTypeId = entry.getKey()
    def productFeatureId = entry.getValue()
    // in here something should happen with value and key
}
<if>
    <condition>
        <not>
            <or>
                <if-has-permission permission="CATALOG" action="_${checkAction}"/>
                <and>
                    <if-has-permission permission="CATALOG_ROLE" action="_${checkAction}"/>
                    <not><if-empty field="roleCategories"/></not>
                </and>
            </or>
        </not>
    </condition>
    <then>
        <!-- code -->
    </then>
</if>
if (!security.hasEntityPermission("CATALOG", "_${checkAction}", parameters.userLogin)
    && !(security.hasEntityPermission("CATALOG_ROLE", "_${checkAction}", parameters.userLogin)
    && roleCategories)) {
    // code
}
<set field="validDate" from-field="parameters.validDate"/>
<if-not-empty field="validDate">
    <filter-list-by-date list="productCategoryMembers" valid-date="validDate"/>
</if-not-empty>
def query = from("ProductCategoryMember").where("productCategoryId", parameters.productCategoryId)
if (parameters.validDate) {
    query.filterByDate()
}
List productCategoryMembers = query.queryList()
<order-map-list list="productsList">
    <order-by field-name="sequenceNum"/>
</order-map-list>
productsList = EntityUtil.orderBy(productsList, ["sequenceNum"])

14.13. Where to find MiniLang implementation

If you find yourself in a position, where you don’t know how to convert a certain tag from MiniLang to Groovy, you can always check the Java implementation of the MiniLang method.
All of the methods have an existing Java implementation and you can find all of them in this folder: /ofbiz/trunk/framework/minilang/src/main/java/org/apache/ofbiz/minilang/method

The interesting part of this implementation is the method exec(), which actually runs the MiniLang tag.
The tag <remove-by-and> for example is realized using this part of code here:

@Override

public boolean exec(MethodContext methodContext) throws MiniLangException {
    @Deprecated
    String entityName = entityNameFse.expandString(methodContext.getEnvMap());
    if (entityName.isEmpty()) {
        throw new MiniLangRuntimeException("Entity name not found.", this);
    }
    try {
        Delegator delegator = getDelegator(methodContext);
        delegator.removeByAnd(entityName, mapFma.get(methodContext.getEnvMap()));
    } catch (GenericEntityException e) {
        String errMsg = "Exception thrown while removing entities: " + e.getMessage();
        Debug.logWarning(e, errMsg, module);
        simpleMethod.addErrorMessage(methodContext, errMsg);
        return false;
    }
    return true;
}

In this you can find one important part of code, which is:

delegator.removeByAnd(entityName, mapFma.get(methodContext.getEnvMap()));

This tells you, that, if you’re trying to convert the tag <remove-by-and>, you can use delegator.removeByAnd() in Groovy.