Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Updated the tutorial as per the lastest trunk. Here is the link to discussion http://ofbiz.135035.n4.nabble.com/Updates-OFBiz-tutorial-restructuring-amp-release-specific-tutorial-td4705222.html

...

This tutorial will help you in building your first "Demo Application" in OFBiz.

Info
titleImportant!

This tutorial is intended to be used with the branch 14.12 and 15.12 of Apache OFBiz. Very soon you will get a list of tutorials based on branches, release, and trunk. It will help you deciding which one to be referred. It will make further updates easy for this tutorial.

For any questions or concerns, please use OFBiz User Mailing List. Details about the mailing lists are available here.

...

The architecture alone makes it easier for you to customize the applications to your needs, but many of the best flexibility points in the system would be meaningless and even impossible if the system was not distributed as open source software. OFBiz is licensed under the Apache License Version 2.0 (ASL2) which grants you the right to customize, extend, modify, repackage, resell, and many other potential uses of the system.

No restrictions are placed on these activities because we feel that they are necessary for effective use of this type of software. Unlike other open source licenses, such as the GPL, your changes do not have to be released as open source. There are obvious benefits to contributing certain improvements, fixes, and additions back to the core project, but some changes will involve proprietary or confidential information that must not be released to the public. For this reason, OFBiz uses the Apache License Version 2.0 the ASL2 which does not require this. The only required thing is to not remove the "copyright, patent, trademark, and attribution notices" you find in files. For more information on open source licenses see the Open Source Initiative (OSI) website at www.opensource.org.

...

To checkout the source code, simply use the following command (if you are using a GUI client, configure it appropriately):

Since the trunk was split into ofbiz-framework and ofbiz-plugins, the specialpurpose and hot-deploy directories have disappeared.
New components must be put in a plugins directory which works as was the hot-deploy directory.

For OFBiz existing components, check them out using the Gradle tasks below.
To get all components use pullAllPluginsSource. Beware this deletes a previously existing plugins directory.

Code Block
For Linux/Mac: $ ./gradlew pullAllPluginsSource
For Windows: > gradlew pullAllPluginsSource

 

For more details refer Apache OFBiz For more details refer Apache OFBiz Source Repository page.

Running Apache OFBiz

...

 

Code Block
themeConfluence
For Linux/Mac: $ ./antgradlew load-democleanAll startloadAll (ofbiz
For linux/mac it will work, for Windows $OFBIZ_HOME/ant.bat come bundled with OFBiz for Windows)

 

Windows: > gradlew cleanAll loadAll ofbiz

Above command will load demo data, (Sample Data to run apps) which comes with OFBiz, in Derby Database

...

. Derby comes configured with OFBiz ready to use.

For more options see README.MD.

  • Direct your browser to httphttps://localhost:80808443/webtools and login with username "admin" and password "ofbiz" and look around a bit. 
    That's it, Apache OFBiz is now running on your system!!

...

  • framework components:These are lower level components that provides the technical layer and tools to the application components; the features provided by these components are typically the ones provided by any other development framework (data layer, business logic layer, transaction handling, data source pools, etc…)
  • application components:These are generic business components required for ERP applications that can be extended/customized (product, order, party, manufacturing, accounting etc…); application components have access to the services and tools provided by the framework components and to the services published by other application component.
  • special purpose componentsplugins components:These components are similar as applications components but meant for special purpose applications like ecommerce , google base integration , eBay integration etc.
  • hot-deploy components:This is an empty component available for custom development. Custom components can extend/override, the resources published by all the other components.

...

Create the plugin/component

It's very easy to setup a new custom component in OFBiz in hot-deploy in plugins directory. Using command line you just need run the following command and answer questions which are being asked for naming your custom component, component resource, webapp and base permission as shown below:.

 

Code Block
$ ./antgradlew createPlugin create-component
Buildfile: /home/ofbiz_dev/Apache_OFBiz/ofbiz-15.12/build.xml
create-component:-PpluginId=ofbizDemo

 

Image Added
 

Running your first application

Before running our first component, let's say 'Hello to the World'

  1. Simply open $OFBIZ_HOME/plugins/ofbizDemo/widget/OfbizDemoScreens.xml file from ofbizDemo plugin (you just created)

     

    Code Block
    languagexml
    <?xmlversion="1.0"encoding="UTF-8"?>
    <screens xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        

...

  1.  

...

  1.  

...

  1.  

...

  1.  xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/widget-screen.xsd">
        

...

  1. <screen 

...

  1. name="main">
       

...

  1.  

...

  1.  

...

  1.  

...

  1.  

...

  1.  <section>
         

...

  1.  

...

  1.  

...

  1.  

...

  1.  

...

  1.  

...

  1.  

...

  1.  

...

  1. <actions>
    

...

  1.     

...

  1.             <set field="headerItem" 

...

  1. value="main"/><!-- this highlights the selected menu-item with name "main" -->
                

...

  1. </actions>
         

...

  1.        <widgets>
            

...

  1.  

...

  1.  

...

  1.      

...

  1.  <decorator-screen name="OfbizDemoCommonDecorator" location="${parameters.mainDecoratorLocation}">
                

...

  1.  

...

  1.  

...

  1.      

...

  1.  

...

  1. <decorator-section name="body">
                

...

  1.  

...

  1.      

...

  1.       <label text="Hello World!! :)"/>
              

...

  1.        

...

  1.  

...

  1.  

...

 

Component Directory Structure In IDEImage Removed 

Running your first application

Before running our first component, let's say 'Hello to the World'

Simply open $OFBIZ_HOME/hot-deploy/ofbizdemo/widget/OfbizDemoScreens.xml file from ofbizdemo component (you just created)

 

...

languagexml

...

  1.  </decorator-section>
                    </decorator-screen>
                </widgets>
            </section>
        </screen>
    </screens>

    We have only added the <label text="Hello World!! :)" />

  2. Now you will need to restart OFBiz by reloading data($ ./gradlew loadDefault ofbiz). It's required as you have created a new component with some security data for you component (Setup by default in your component data directory as OfbizDemoSecurityGroupDemoData.xml) and as you will restart it, ofbizdemo component will also be loaded.
  3. As OFBiz restarted direct your browser to your application here https://localhost:8443/ofbizDemo
  4. You will be asked to login. Login with user: admin password: ofbiz.
  5. As you login you will see ofbizdemo application up with the hello world message you have put in screen as shown in below given image.
    That's it, congratulations your first component is setup and running.

Creating First Database Entity (Table) 

Defining entity

To create custom Entities/Tables in database, you simply need to provide entity definition in  $OFBIZ_HOME/plugins/ofbizDemo/entitydef/entitymodel.xml file of your ofbizdemo application. This file structure is already setup when you used the Gradle task to setup your component. You simply need to go in and provide entity definition as shown below. Here we are going to add two new entities for ofbizdemo application.

 

Code Block
languagexml
<?xml version="1.0" encoding="UTF-8"?>
 
<entitymodel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/entitymodel.xsd">
 
    <title>Entity of an Open For Business Project Component</title>
    <description>None</description>
    <version>1.0</version>
 
    <entity entity-name="OfbizDemoType" package-name="org.apache.ofbiz.ofbizdemo" title="OfbizDemo Type Entity">
        <field name="ofbizDemoTypeId" type="id"><description>primary sequenced 

...

ID</description></

...

field>
        <field name="description" type="description"></field>
   

...

     <prim-key field="ofbizDemoTypeId"/>
    </entity>
 
    <entity 

...

entity-name="OfbizDemo" package-name="

...

org.apache.ofbiz.ofbizdemo" 

...

title="

...

OfbizDemo Entity">
        <field name="ofbizDemoId" type="id"><description>primary sequenced ID</description></field>
        

...

<field name="ofbizDemoTypeId" type="id"></field>
        <field name="firstName" type="name"></field>
        <field 

...

name="lastName" type="name"></field>
        <field name="comments" type="comment"></field>
        

...

<prim-key field="ofbizDemoId"/>
        <relation type="one" 

...

fk-name="ODEM_OD_TYPE_ID" rel-entity-name="OfbizDemoType">
            

...

<key-map field-name="ofbizDemoTypeId"/>
        </

...

relation>
    </

...

entity>
 
</entitymodel>

Now have a look at $OFBIZ_HOME/plugins/ofbizDemo/ofbiz-component.xml file. You already have resource entry made in it for loading these entities from their definitions to database when component loads. As shown below:

We have only added the <label text="Hello World!! :)" />

...

Creating First Database Entity (Table) 

Defining entity

...

 

Code Block
languagexml

...

<entity-resource type="

...

model" 

...

reader-name="

...

main" loader="main" location="entitydef/entitymodel.xml"/>

To check simply re-start OFBiz (Ctrl+C followed by "./gradlew ofbiz") and direct your browser to Entity Data Maintenance Tool here: https://localhost:8443/webtools/control/entitymaint and search for entities OfbizDemoType and OfbizDemo. You will see it as shown in below given image.

Image Added


Preparing data for entity

As you have setup your custom entities, now is the time to prepare some sample data for it. You can do it in data XML files already setup under data directory of your component as  $OFBIZ_HOME/plugins/ofbizDemo/data/OfbizDemoTypeData.xml and  $OFBIZ_HOME/plugins/ofbizDemo/data/OfbizDemoDemoData.xml. Set it up as shown below:

 

Code Block
languagexml
titleOfbizDemoTypeData.xml
 <?xml version="1.0" encoding="UTF-8"?>
<entity-engine-xml>
    <OfbizDemoType ofbizDemoTypeId="INTERNAL" description="Internal Demo - Office"/>
    <OfbizDemoType ofbizDemoTypeId="EXTERNAL" description="External Demo - On Site"/>
</entity-engine-xml>
Code Block
languagexml
titleOfbizDemoDemoData.xml
<?xml version="1.0" encoding="UTF-8"?>
<entity-engine-xml>
    <OfbizDemo ofbizDemoId="SAMPLE_DEMO_1" ofbizDemoTypeId="INTERNAL" firstName="Sample First 1" lastName="Sample Last 1" comments="This is test comment for first record."/>
    <OfbizDemo ofbizDemoId="SAMPLE_DEMO_2" ofbizDemoTypeId="INTERNAL" firstName="Sample First 2" lastName="Sample last 2" comments="This is test comment for second record."/>
    

...

<OfbizDemo ofbizDemoId="SAMPLE_DEMO_3" ofbizDemoTypeId="EXTERNAL" firstName="Sample First 3" lastName="Sample last 3" comments="This is test comment for third record."/>
    <OfbizDemo ofbizDemoId="SAMPLE_DEMO_4" ofbizDemoTypeId="EXTERNAL" firstName="Sample First 4" lastName="Sample last 4" comments="This is test comment for fourth record."/>
</entity-engine-xml>

Now again have a look at $OFBIZ_HOME/plugins/ofbizDemo

...

/ofbiz-component.xml

...

 file. You already have resource entry made in it for

...

loading data prepared in these files as:

 

Code Block
languagexml
titleEntry to be done in ofbiz-component.xml
<entity-resource type="

...

data" reader-name="seed" loader="main" location="data/OfbizDemoTypeData.xml"/>
<entity-resource type="data" reader-name="demo" loader="main" location=

...

"data/OfbizDemoDemoData.xml"/>

 

...

Loading data in entity

At this moment to load this sample data into entities/tables defined you can either run "./gradlew loadDefault" on console or can directly go here in webtools to load entity xml https://localhost:8443/webtools/control/EntityImport.

Simply put your xml data in " Complete XML document (root tag: entity-engine-xml):" text area and hit "Import Text", as shown in below given image

XML Data Import from WebtoolsImage Added

As you will hit Import Text, it will load data and will show the result as shown below

Image Added

After completing the data load process again visit Entity Data Maintenance(

...

Image Removed

) and check your entities, you will find this data here that you just loaded.


That's it, you have successfully imported the data in the database tables, super easy, right!

 

Form and Services

In our previous section, we have seen how to create the entities (tables), now it's time to create a form which will allow you to make entries in that entity.

Create a Service

Before preparing form, let's write a service to create records in database for OfbizDemo entity in service definition xml file ($OFBIZ_HOME/plugins/ofbizDemo/servicedef/services.xml)

...


 

Code Block
languagexml
title

...

services.xml

...

<?xml version="1.0" encoding="UTF-8"?>

...

Code Block
languagexml
titleOfbizDemoDemoData.xml
<?xml version="1.0" encoding="UTF-8"?>
<entity-engine-xml>
    <OfbizDemo ofbizDemoId="SAMPLE_DEMO_1" ofbizDemoTypeId="INTERNAL" firstName="Sample First 1" lastName="Sample Last 1" comments="This is test comment for first record."/>
    <OfbizDemo ofbizDemoId="SAMPLE_DEMO_2" ofbizDemoTypeId="INTERNAL" firstName="Sample First 2" lastName="Sample last 2" comments="This is test comment for second record."/>
    <OfbizDemo ofbizDemoId="SAMPLE_DEMO_3" ofbizDemoTypeId="EXTERNAL" firstName="Sample First 3" lastName="Sample last 3" comments="This is test comment for third record."/>
    <OfbizDemo ofbizDemoId="SAMPLE_DEMO_4" ofbizDemoTypeId="EXTERNAL" firstName="Sample First 4" lastName="Sample last 4" comments="This is test comment for fourth record."/>
</entity-engine-xml>
<services xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/services.xsd">
 
    <description>OfbizDemo Services</description>
    <vendor></vendor>
    <version>1.0</version>
 
    <service name="createOfbizDemo" default-entity-name="OfbizDemo" engine="entity-auto" invoke="create" auth="true">
        <description>Create an Ofbiz Demo record</description>
        <auto-attributes include="pk" mode="OUT" optional="false"/>
        <auto-attributes include="nonpk" mode="IN" optional="false"/>
        <override name="comments" optional="true"/>
    </service>
 
</services>

Now again have a look at $OFBIZ_HOME/plugins/ofbizDemo/ofbiz-component.xml file

...

. You already have resource entry made in it for loading

...

services defined in this file as:

 

Code Block
languagexml

...

<!-- service resources: model(s), eca(s) and group definitions -->
<service-resource type="

...

model" loader="main" location="

...

servicedef/

...

services.xml"/>

 

Loading data in entity

At this moment to load this sample data into entities/tables defined you can either run "ant load-demo" on console or can directly go here in webtools to load entity xml https://localhost:8443/webtools/control/EntityImport.

Simply put your xml data in " Complete XML document (root tag: entity-engine-xml):" text area and hit "Import Text", as shown in below given image

XML Data Import from WebtoolsImage Removed

As you will hit Import Text, it will load data and will show the result as shown below

Image Removed

For this service definition to load you will need to restart OFBiz. To test this service you directly go to webtools --> Run Service option here: https://localhost:8443/webtools/control/

...

 

Image Added

Info

Running service via Web Tools: This a smart utility provided by framework to run your service.

On submission of the form above, you will presented a form to enter IN parameters of the service.


Use of UI Labels (Introduction)

Internationalization of Apache OFBiz is really easy, we define the UI Labels in various languages and on the basis of user's locale, respective label is shown.

Here is the example of UI Labels (while creating component <component-name>UiLabels.xml is created by default, in our case it is OfbizDemoUiLabels.xml)

 

Code Block
languagexml
titleOfbizDemoUiLabels.xml
<?xml version="1.0" encoding="UTF-8"?>
<resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/ofbiz-properties.xsd">
    <property key="OfbizDemoApplication">
        <value xml:lang="en">OfbizDemo Application</value>
        <value xml:lang="zh">OfbizDemo应用程�?</value>
        <value xml:lang="zh-TW">OfbizDemo應用程�?</value>
    </property>
    <property key="OfbizDemoCompanyName">
        <value xml:lang="en">OFBiz: OfbizDemo</value>
 

Form and Services

In our previous section, we have seen how to create the entities (tables), now it's time to create a form which will allow you to make entries in that entity.

Create a Service

Before preparing form, let's write a service to create records in database for OfbizDemo entity in service definition xml file ($OFBIZ_HOME/hot-deploy/ofbizdemo/servicedef/services.xml)

 

Code Block
languagexml
titleservices.xml
<?xml version="1.0" encoding="UTF-8"?> <services xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       <value 
xsi
xml:
noNamespaceSchemaLocation
lang="
http://ofbiz.apache.org/dtds/services.xsd"> <description>OfbizDemo Services</description> <vendor></vendor>
zh-TW">OFBiz: OfbizDemo</value>
    </property>
    <property key="OfbizDemoCompanySubtitle">
        <value xml:lang="en">Part of the Apache OFBiz Family of Open Source Software</value>
    
<version>1.0</version>
 
   
<service name
<value xml:lang="
createOfbizDemo
it"
default-entity-name="OfbizDemo" engine="entity-auto" invoke="create" auth="true"> <description>Create an Ofbiz Demo record</description>
>Un modulo della famiglia di software open source Apache OFBiz</value>
        <value xml:lang="zh">开�?软件OFBiz的组�?部分</value>
        
<auto-attributes include
<value xml:lang="
pk" mode="OUT" optional="false"/
zh-TW">開�?軟體OFBiz的組�?部分</value>
    </property>
    <property key="OfbizDemoViewPermissionError">
        
<auto-attributes include
<value xml:lang="
nonpk
en">You 
mode="IN" optional="false"/> <override name="comments" optional="true"/> </service> </services>

Now again have a look at $OFBIZ_HOME/hot-deploy/ofbizdemo/ofbiz-component.xml file. You already have resource entry made in it for loading services defined in this file as:

 

Code Block
languagexml
<!-- service resources: model(s), eca(s) and group definitions -->
<service-resource type="model" loader="main" location="servicedef/services.xml"/>

For this service definition to load you will need to restart OFBiz. To test this service you directly go to webtools --> Run Service option here: https://localhost:8443/webtools/control/runService

Image Removed

Info
are not allowed to view this page.</value>
        <value xml:lang="zh">�?�?许你�?览这个页�?�。</value>
        <value xml:lang="zh-TW">�?�?許您檢視這個�?�?�.</value>
    </property>
</resource>

 

Running service via Web Tools: This a smart utility provided by framework to run your service.

On submission of the form above, you will presented a form to enter IN parameters of the service.

Create the add Form

Let's create our first form for this service and for that let's edit the existing file at location $OFBIZ_HOME/hot-deployplugins/ofbizdemoofbizDemo/widget/OfbizDemoForms.xml and add Create Form for OfbizDemo as shown below:


 

Code Block
languagexml
titleOfbizDemoForms.xml
<?xml version="1.0" encoding="UTF-8"?>
<forms xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/widget-form.xsd">
 
    <form name="AddOfbizDemo" type="single" target="createOfbizDemo">
        <!-- We have this utility in OFBiz to render form based on service definition. 
             Service attributes will automatically lookedup and will be shown on form
        -->
        <auto-fields-service service-name="createOfbizDemo"/>
        <field name="ofbizDemoTypeId" title="${uiLabelMap.CommonType}">
            <drop-down allow-empty="false" current-description="">
                <!---We have made this drop down options dynamic(Values from db) using this -->
                <entity-options description="${description}" key-field-name="ofbizDemoTypeId" entity-name="OfbizDemoType">
                    <entity-order-by field-name="description"/>
                </entity-options>
            </drop-down>
        </field>
        <field name="submitButton" title="${uiLabelMap.CommonAdd}"><submit button-type="button"/></field>
    </form>
</forms>

Here you can notice we have used auto-fields-service to auto generate the form based on service definition IN/OUT attributes. 

Go to Screens xml file(OfbizDemoScreens.xml) add this form location in decorator body to your screen that you used to show the Hello World... text. As shown below

 

Code Block
languagexml
titleAdding Form Location to the Main Screen
 <?xml version="1.0" encoding="UTF-8"?>
<screens xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/widget-screen.xsd">
 
    <screen name="main">
        <section>
            <actions>
                <set field="headerItem" value="main"/> <!-- this highlights the selected menu-item with name "main" -->
            </actions>
            <widgets>
                <decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}">
                    <decorator-section name="body">
                        <screenlet title="Add Ofbiz Demo">
                            <include-form name="AddOfbizDemo" location="component://ofbizdemoofbizDemo/widget/OfbizDemoForms.xml"/>
                        </screenlet>
                    </decorator-section>
                </decorator-screen>
            </widgets>
        </section>
    </screen>
</screens>

 

Controller Entry for Form

Before you go to the form and start creating OfbizDemo records from add form, you will need to make an entry in $OFBIZ_HOME/hot-deployplugins/ofbizdemoofbizDemo/webapp/ofbizdemoofbizDemo/WEB-INF/controller.xml file for the target service which will called when form is submitted. You can do it as shown below under Request Mappings in your ofbizdemo apps controller file:

 

Code Block
languagexml
<request-map uri="createOfbizDemo">
    <security https="true" auth="true"/>
    <event type="service" invoke="createOfbizDemo"/>
    <response name="success" type="view" value="main"/>
</request-map>


Everything set, let's have a look into to our recently create form httphttps://localhost:80808443/ofbizdemoofbizDemo

Image RemovedImage Added

Primary key(ofbizDemoId) is not needed to be send in with the form, it will be auto sequenced by OFBiz in db records.


Create a Find Form

Let's create a find form for the entity OfbizDemo, so that you search OfbizDemos being created.

1.) Add the forms (FindOfbizDemo and ListOfbizDemo) in OfbizDemoForms.xml

 

Code Block
languagexml
titleOfbizDemoForms.xml
 <form name="FindOfbizDemo" type="single" target="FindOfbizDemo" default-entity-name="OfbizDemo">
    <field name="noConditionFind"><hidden value="Y"/> <!-- if this isn't there then with all fields empty no query will be done --></field>
    <field name="ofbizDemoId" title="${uiLabelMap.OfbizDemoId}"><text-find/></field>
    <field name="firstName" title="${uiLabelMap.OfbizDemoFirstName}"><text-find/></field>
    <field name="lastName" title="${uiLabelMap.OfbizDemoLastName}"><text-find/></field>
    <field name="ofbizDemoTypeId" title="${uiLabelMap.OfbizDemoType}">
        <drop-down allow-empty="true" current-description="">
            <entity-options description="${description}" key-field-name="ofbizDemoTypeId" entity-name="OfbizDemoType">
                <entity-order-by field-name="description"/>
            </entity-options>
        </drop-down>
    </field>
    <field name="searchButton" title="${uiLabelMap.CommonFind}" widget-style="smallSubmit"><submit button-type="button" image-location="/images/icons/magnifier.png"/></field>
</form>
 
<form name="ListOfbizDemo" type="list" list-name="listIt" paginate-target="FindOfbizDemo" default-entity-name="OfbizDemo" separate-columns="true"
    odd-row-style="alternate-row" header-row-style="header-row-2" default-table-style="basic-table hover-bar">
    <actions>
       <!-- Preparing search results for user query by using OFBiz stock service to perform find operations on a single entity or view entity -->
       <service service-name="performFind" result-map="result" result-map-list="listIt">
           <field-map field-name="inputFields" from-field="ofbizDemoCtx"/>
           <field-map field-name="entityName" value="OfbizDemo"/>
           <field-map field-name="orderBy" from-field="parameters.sortField"/>
           <field-map field-name="viewIndex" from-field="viewIndex"/>
           <field-map field-name="viewSize" from-field="viewSize"/>
        </service>
    </actions>
    <field name="ofbizDemoId" title="${uiLabelMap.OfbizDemoId}"><display/></field>
    <field name="ofbizDemoTypeId" title="${uiLabelMap.OfbizDemoType}"><display-entity entity-name="OfbizDemoType"/></field>
    <field name="firstName" title="${uiLabelMap.OfbizDemoFirstName}" sort-field="true"><display/></field>
    <field name="lastName" title="${uiLabelMap.OfbizDemoLastName}" sort-field="true"><display/></field>
    <field name="comments" title="${uiLabelMap.OfbizDemoComment}"><display/></field>
</form>

 

Form or Screen's action tag is used for data preparation logics for your view.

 

Info

We have used OOTB OFBiz generic service performFind to do the search operations which is easy and efficient to use when you have to perform search on one entity or one view entity.

 

2.) In next step, we will include these form in the screen, let's add these form in OfbizDemoScreens.xml file. For this include the FindOfbizDemo screen defined below in the OfbizDemoScreens.xml

 

Code Block
languagexml
 <!-- Find and list all ofbizdemos in a tabular format -->
<screen name="FindOfbizDemo">
    <section>
        <actions>
            <set field="headerItem" value="findOfbizDemo"/>
            <set field="titleProperty" value="PageTitleFindOfbizDemo"/>
            <set field="ofbizDemoCtx" from-field="parameters"/>
        </actions>
        <widgets>
            <decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}">
                <decorator-section name="body">
                    <section>
                        <condition>
                            <if-has-permission permission="OFBIZDEMO" action="_VIEW"/>
                        </condition>
                        <widgets>
                            <decorator-screen name="FindScreenDecorator" location="component://common/widget/CommonScreens.xml">
                                <decorator-section name="search-options">
                                    <include-form name="FindOfbizDemo" location="component://ofbizdemoofbizDemo/widget/OfbizDemoForms.xml"/>
                                </decorator-section>
                                <decorator-section name="search-results">
                                    <include-form name="ListOfbizDemo" location="component://ofbizdemoofbizDemo/widget/OfbizDemoForms.xml"/>
                                </decorator-section>
                            </decorator-screen>
                        </widgets>
                        <fail-widgets>
                            <label style="h3">${uiLabelMap.OfbizDemoViewPermissionError}</label>
                       </fail-widgets>
                    </section>
                </decorator-section>
            </decorator-screen>
        </widgets>
    </section>
</screen>

3.) Add request mapping for accessing this new Find Ofbiz Demo page in controller.xml

 

Code Block
languagexml
<!-- Request Mapping -->
<request-map uri="FindOfbizDemo"><security https="true" auth="true"/><response name="success" type="view" value="FindOfbizDemo"/></request-map>
  
<!-- View Mapping -->
<view-map name="FindOfbizDemo" type="screen" page="component://ofbizdemoofbizDemo/widget/OfbizDemoScreens.xml#FindOfbizDemo"/>

 

4.) Now, let's add a new menu for showing find option.
Creating a menu is really simple in OFBiz, all the menus are defined is *menus.xml.
When we create a component from ant targeta Gra, we get a file named OfbizDemoMenus.xml

Make the following entry in the OfbizDemoMenus.xml file.

 

Code Block
languagexml
titleOfbizDemoMenus.xml
<?xml version="1.0" encoding="UTF-8"?>
<menus xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/widget-menu.xsd">
    <menu name="MainAppBar" title="${uiLabelMap.OfbizDemoApplication}" extends="CommonAppBarMenu" extends-resource="component://common/widget/CommonMenus.xml">
        <menu-item name="main" title="${uiLabelMap.CommonMain}"><link target="main"/></menu-item>
        <menu-item name="findOfbizDemo" title="${uiLabelMap.OfbizDemoFind}"><link target="FindOfbizDemo"/></menu-item>
    </menu>
</menus>

Use of UI Labels (Completion)

As we have seen above Internationalization of Apache OFBiz is really easy, we define the UI Labels in various languages and on the basis of user's locale, respective label is shown.

Here is we complete the example of UI Labels (while creating component <component-name>UiLabels.xml is created by default, in our case it is OfbizDemoUiLabels.xml)

 

Code Block
languagexml
titleOfbizDemoUiLabels.xml
 <property key="OfbizDemoFind">
    <value xml:lang="en">Find</value>
</property>
<property key="OfbizDemoFirstName">
    <value xml:lang="en">First Name</value>
</property>
<property key="OfbizDemoId">
    <value xml:lang="en">OFBiz Demo Id</value>
</property>
<property key="OfbizDemoLastName">
   <value xml:lang="en">Last Name</value>
</property>

Now simply restart the server, under ofbizdemo application (https://localhost:8443/ofbizdemoofbizDemo/control/main) you will see the Find menu option.

Image RemovedImage Added

Services using other engines

Whenever you have to build a business logic you should prefer to write services to leverage features from its built in Service Engine.

The service "createOfbizDemo" that you created earlier was using engine="entity-auto" and hence you didn't need to provide its implementation and OFBiz took care of create operation.  When you need to work on complex operations in service involving multiple entities from database and custom logics to be built, you need to provide custom implementation to your service. In this section we will focus on this.

Service in Java

You can implement a service in Java as directed here in below given steps:

1.) Define your service, here again we will be operating on the same entity(OfbizDemo) of our custom Ofbiz Demo application. Open your service definition file $OFBIZ_HOME/hot-deployplugins/ofbizdemoofbizDemo/servicedef/services.xml and add a new definition as:

 

Code Block
languagexml
titleservices.xml
 <service name="createOfbizDemoByJavaService" default-entity-name="OfbizDemo" engine="java"
        location="com.companyname.ofbizdemo.services.OfbizDemoServices" invoke="createOfbizDemo" auth="true">
    <description>Create an Ofbiz Demo record using a service in Java</description>
    <auto-attributes include="pk" mode="OUT" optional="false"/>
    <auto-attributes include="nonpk" mode="IN" optional="false"/>
    <override name="comments" optional="true"/>
</service>

Info

Notice we have this time used engine="java".

 

2.) Create package "com.companyname.ofbizdemo.services" in your ofbizDemo components src/main/java directory (create those if they don't exist in your src directory). 

 Example: src/main/java/com/companyname/ofbizdemo/services. Services for your application which have ofbizdemo components src directory e.g. src --> com --> companyname --> ofbizdemo --> services. Services which has to be implemented in Java can be placed in this java directory for your application.

3.) Define new Java Class in file OfbizDemoServices.java here in services directory and implement method, which is going to be invoked by your service definition, as shown below:

OfbizDemoServices.java

 

package com.companyname.ofbizdemo.services;
import java.util.Map;
 
import org.apache.ofbiz.base.util.Debug;
import org.apache.ofbiz.entity.Delegator;
import org.apache.ofbiz.entity.GenericEntityException;
import org.apache.ofbiz.entity.GenericValue;
import org.apache.ofbiz.service.DispatchContext;
import org.apache.ofbiz.service.ServiceUtil;
 
public class OfbizDemoServices {
 
    public static final String module = OfbizDemoServices.class.getName();
 
    public static Map<String, Object> createOfbizDemo(DispatchContext dctx, Map<String, ? extends Object> context) {
        Map<String, Object> result = ServiceUtil.returnSuccess();
        Delegator delegator = dctx.getDelegator();
        try {
            GenericValue ofbizDemo = delegator.makeValue("OfbizDemo");
            // Auto generating next sequence of ofbizDemoId primary key
            ofbizDemo.setNextSeqId();
            // Setting up all non primary key field values from context map
            ofbizDemo.setNonPKFields(context);
            // Creating record in database for OfbizDemo entity for prepared value
            ofbizDemo = delegator.create(ofbizDemo);
            result.put("ofbizDemoId", ofbizDemo.getString("ofbizDemoId"));
            Debug.log("==========This is my first Java Service implementation in Apache OFBiz. OfbizDemo record created successfully with ofbizDemoId: "+ofbizDemo.getString("ofbizDemoId"));
        } catch (GenericEntityException e) {
            Debug.logError(e, module);
            return ServiceUtil.returnError("Error in creating record in OfbizDemo entity ........" +module);
        }
        return result;
    }

 

4.) Stop server and re-start using "./ant clean build startgradlew ofbiz", it will compile your class and will make it available when ofbiz restarts which updated jar file.

5.) Test service implemented using webtools --> Run Service option(https://localhost:8443/webtools/control/runService) or simply update the service name being called by your controller request to use this service instead and use add form in your app that you prepared earlier. By doing this your Add OfbizDemo form will call this java service.

 

<request-map uri="createOfbizDemo">
    <security https="true" auth="true"/>
    <event type="service" invoke="createOfbizDemoByJavaService"/>
    <response name="success" type="view" value="main"/>
</request-map>

 

To make sure this new service implementation is being executed, you can check this line in console log that you have put in your code using Debug.log(....). For logging in OFBiz you must always use Debug class methods in Java classes.

Console Log

 

[java] 2014-06-24 12:11:37,282 (http-bio-0.0.0.0-8443-exec-2) [  OfbizDemoServices.java:28 :INFO ] ==========This is my first Java Service implementation in Apache OFBiz. OfbizDemo record created successfully with ofbizDemoId: ......

 

Service in Groovy

To utilize feature of on the fly compilation and less line of code you can implement services for building business logics in OFBiz using Groovy DSL.

To implement a service using Groovy you can follow below given steps:

1.) Add new service definition to services/services.xml file as:

services.xml

 

<service name="createOfbizDemoByGroovyService" default-entity-name="OfbizDemo" engine="groovy"
        location="component://ofbizdemoofbizDemo/script/com/companyname/ofbizdemo/OfbizDemoServices.groovy" invoke="createOfbizDemo" auth="true">
    <description>Create an Ofbiz Demo record using a service in Java</description>
    <auto-attributes include="pk" mode="OUT" optional="false"/>
    <auto-attributes include="nonpk" mode="IN" optional="false"/>
    <override name="comments" optional="true"/>
</service>

 

2.) Add new groovy services file here  component://ofbizdemoofbizDemo/script/com/companyname/ofbizdemo/OfbizDemoServices.groovy

3.) Add service implementation to the file OfbizDemoServices.groovy

OfbizDemoServices.groovy

 

import org.apache.ofbiz.entity.GenericEntityException;

def
createOfbizDemo() {
    result = [:];
    try {
        ofbizDemo = delegator.makeValue("OfbizDemo");
        // Auto generating next sequence of ofbizDemoId primary key
        ofbizDemo.setNextSeqId();
        // Setting up all non primary key field values from context map
        ofbizDemo.setNonPKFields(context);
        // Creating record in database for OfbizDemo entity for prepared value
        ofbizDemo = delegator.create(ofbizDemo);
        result.ofbizDemoId = ofbizDemo.ofbizDemoId;
        logInfo("==========This is my first Groovy Service implementation in Apache OFBiz. OfbizDemo record "
                  +"created successfully with ofbizDemoId: "+ofbizDemo.getString("ofbizDemoId"));
      } catch (GenericEntityException e) {
          logError(e.getMessage());
          return error("Error in creating record in OfbizDemo entity ........");
      }
      return result;

 

4.) Stop server and re-start using"./ant startgradlew ofbiz", this time we just need to load the new service definition, no explicit compilation is required as its a service implementation in Groovy.

5.) Test service implemented using webtools --> Run Service option(https://localhost:8443/webtools/control/runService) or simply update the service name being called by your controller request to use this service instead and use add form in your app that you prepared earlier for testing. By doing this your Add OfbizDemo form will call this groovy service.

controller.xml

 

<request-map uri="createOfbizDemo">
    <security https="true" auth="true"/>
    <event type="service" invoke="createOfbizDemoByGroovyService"/>
    <response name="success" type="view" value="main"/>
</request-map>

 

To make sure this new service implementation is being executed, you can check this line in console log that you have put in your code using Debug.log(....). For logging in OFBiz you must always use Debug class methods in Java classes.

 

[java] 2014-06-24 12:11:37,282 (http-bio-0.0.0.0-8443-exec-2) [  OfbizDemoServices.java:28 :INFO ] ==========This is my first Groovy Service implementation in Apache OFBiz. OfbizDemo record created successfully with ofbizDemoId: .....

 

To get more details around using Groovy DSL for service and events implementation in Apache OFBiz you can refer document created by Jacopo Cappellato in OFBiz Wiki here.

...

 

package com.companyname.ofbizdemo.events;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.ofbiz.base.util.Debug;
import org.apache.ofbiz.base.util.UtilMisc;
import org.apache.ofbiz.base.util.UtilValidate;
import org.apache.ofbiz.entity.Delegator;
import org.apache.ofbiz.entity.GenericValue;
import org.apache.ofbiz.service.GenericServiceException;
import org.apache.ofbiz.service.LocalDispatcher;
 
public class OfbizDemoEvents {
 
 public static final String module = OfbizDemoEvents.class.getName();
 
    public static String createOfbizDemoEvent(HttpServletRequest request, HttpServletResponse response) {
        Delegator delegator = (Delegator) request.getAttribute("delegator");
        LocalDispatcher dispatcher = (LocalDispatcher) request.getAttribute("dispatcher");
        GenericValue userLogin = (GenericValue) request.getSession().getAttribute("userLogin");
 
        String ofbizDemoTypeId = request.getParameter("ofbizDemoTypeId");
        String firstName = request.getParameter("firstName");
        String lastName = request.getParameter("lastName");
 
        if (UtilValidate.isEmpty(firstName) || UtilValidate.isEmpty(lastName)) {
            String errMsg = "First Name and Last Name are required fields on the form and can't be empty.";
            request.setAttribute("_ERROR_MESSAGE_", errMsg);
            return "error";
        }
        String comments = request.getParameter("comments");
 
        try {
            Debug.logInfo("=======Creating OfbizDemo record in event using service createOfbizDemoByGroovyService=========", module);
            dispatcher.runSync("createOfbizDemoByGroovyService", UtilMisc.toMap("ofbizDemoTypeId", ofbizDemoTypeId,
                    "firstName", firstName, "lastName", lastName, "comments", comments, "userLogin", userLogin));
        } catch (GenericServiceException e) {
            String errMsg = "Unable to create new records in OfbizDemo entity: " + e.toString();
            request.setAttribute("_ERROR_MESSAGE_", errMsg);
            return "error";
        }
        request.setAttribute("_EVENT_MESSAGE_", "OFBiz Demo created succesfully.");
        return "success";
    }
}

 

...

Okay so we are here in the last part of OFBiz tutorial. In this part we will focus on customizing UI layer of Apache OFBiz for business management apps i.e. backend apps . The and esp. Most of the time you will find the OFBiz Widgets are enough. But sometimes the important thing here is to develop applications as user users exactly want it. 

So to customize UI part of your application first of all to make it easy we will be using Freemarker Templates instead of inbuilt form widgetsForm Widgets. First of all we will see how to use Freemarker and Groovy scripts with Apache OFBiz and then we'll see how to put on custom styling on it by defining your own decorators. Initially we will be using OFBiz default decorators.

...

1.) Add two Freemarker files at location $ OFBIZ_HOME/hot-deployplugins/ofbizdemoofbizDemo/webapp/ofbizdemoofbizDemo/crud/AddOfbizDemo.ftl and ListOfbizDemo.ftl, as shown below:

...

2.) Add new Groovy file for data fetching logic at location $ OFBIZ_HOME/hot-deployplugins/ofbizdemoofbizDemo/webapp/ofbizdemoofbizDemo/WEB-INF/actions/crud/ListOfbizDemo.groovy and add code as shown to list out OfbizDemo records:

...

 

<screen name="AddOfbizDemoFtl">
    <section>
        <actions>
            <set field="titleProperty" value="PageTitleAddOfbizDemos"/>
            <set field="headerItem" value="addOfbizDemoFtl"/>
        </actions>
        <widgets>
            <decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}">
                <decorator-section name="body">
                    <screenlet title="${uiLabelMap.OfbizDemoListOfbizDemos}">
                        <platform-specific>
                            <html><html-template location="component://ofbizdemoofbizDemo/webapp/ofbizdemoofbizDemo/crud/ListOfbizDemo.ftl"/></html>
                         </platform-specific>
                    </screenlet>
                    <screenlet title="${uiLabelMap.OfbizDemoAddOfbizDemoServiceByFtl}">
                        <platform-specific>
                            <html><html-template location="component://ofbizdemoofbizDemo/webapp/ofbizdemoofbizDemo/crud/AddOfbizDemo.ftl"/></html>
                        </platform-specific>
                    </screenlet>
                </decorator-section>
            </decorator-screen>
        </widgets>
    </section>
</screen>

 

...

 

<!--Request Mapping-->
<request-map uri="AddOfbizDemoFtl">
    <security https="true" auth="true"/>
    <response name="success" type="view" value="AddOfbizDemoFtl"/>
</request-map>
 
<!--View Mapping-->
<view-map name="AddOfbizDemoFtl" type="screen" page="component://ofbizdemoofbizDemo/widget/OfbizDemoScreens.xml#AddOfbizDemoFtl"/>

 

...

6.) Run your ofbiz demo application and go to the new tab you just added. You should have view as:

...

Image Added

Creating Custom Decorator

...

We will be doing it by defining custom decorator for your application view. A decorator in OFBiz is nothing but a screen that you define and reuse it afterwards by including it in your other screens of application. You are already doing it with default decorator (main-decorator –> ApplicationDecorator) which came comes with OFBiz. Just observe your screens you have prepared so far, you will find that, you were using this main decorator, please refer below line in OfbizDemoScreens.xml.

...

 

<context-param>
    <description>The location of the main-decorator screen to use for this webapp; referred to as a context variable in screen def XML files.</description>
    <param-name>mainDecoratorLocation</param-name>
</context-param>

 

...

2.) Create two new directories namely "css" and "js" at location $ OFBIZ_HOME/hot-deployplugins/ofbizdemoofbizDemo/webapp/ofbizdemoofbizDemo/

3.) Copy bootstrap-3.3.7/dist/css/bootstrap.min.css to $ OFBIZ_HOME/hot-deployplugins/ofbizdemoofbizDemo/webapp/ofbizdemoofbizDemo/css

4.) Copy bootstrap-3.3.7/dist/js/bootstrap.min.js to $ OFBIZ_HOME/hot-deployplugins/ofbizdemoofbizDemo/webapp/ofbizdemoofbizDemo/js.

5.) Open $ OFBIZ_HOME/hot-deployplugins/ofbizdemoofbizDemo/webapp/ofbizdemoofbizDemo/WEB-INF/web.xml and make entries for css and js directories in allowedPaths at the end as shown below:

...

6.) Add new directory named "includes" at location $ OFBIZ_HOME/hot-deployplugins/ofbizdemoofbizDemo/webapp/ofbizdemoofbizDemo/ and create two new files in this new directory you just added named PreBody.ftl and PostBody.ftl. We will be using(including) these two files in our decorator screen to build complete HTML page.

...

 

<html>
  <head>
    <title>${layoutSettings.companyName}</title>
    <meta name="viewport" content="width=device-width, user-scalable=no"/>
    <#if webSiteFaviconContent?has_content>
      <link rel="shortcut icon" href="">
    </#if>
    <#list layoutSettings.styleSheets as styleSheet>
      <link rel="stylesheet" href="${StringUtil.wrapString(styleSheet)}" type="text/css"/>
    </#list>
    <#list layoutSettings.javaScripts as javaScript>
      <script type="text/javascript" src="${StringUtil.wrapString(javaScript)}"/></script>
    </#list>
  </head>
  <body data-offset="125">
    <h4 align="center"> ==================Page PreBody Starts From Decorator Screen========================= </h4>
    <div class="container menus" id="container">
      <div class="row">
        <div class="col-sm-12">
          <ul id="page-title" class="breadcrumb">
            <li>
                <a href="<@ofbizUrl>main</@ofbizUrl>">Main</a>
            </li>
            <li class="active"><span class="flipper-title">${StringUtil.wrapString(uiLabelMap[titleProperty])}</span></li>
            <li class="pull-right">
              <a href="<@ofbizUrl>logout</@ofbizUrl>" title="${uiLabelMap.CommonLogout}">logout</i></a>
            </li>
          </ul>
        </div>
      </div>
      <div class="row">
        <div class="col-lg-12 header-col">
          <div id="main-content">
              <h4 align="center"> ==================Page PreBody Ends From Decorator Screen=========================</h4>
              <h4 align="center"> ==================Page Body starts From Screen=========================</h4>

 

...

7.) Open Common Screens file of your component $ OFBIZ_HOME/hot-deployplugins/ofbizdemoofbizDemo/widget/CommonScreens.xml, this is the file we will define our custom decorator.

...

 

<screen name="OfbizDemoCommonDecorator">
    <section>
        <actions>
            <property-map resource="OfbizDemoUiLabels" map-name="uiLabelMap" global="true"/>
            <property-map resource="CommonUiLabels" map-name="uiLabelMap" global="true"/>
   
<set field="layoutSettings.companyName" from-field="uiLabelMap.OfbizDemoCompanyName" global="true"/>
            
<!-- Including custom CSS Styles that you want to use in your application view. [] in field can be used to
                 set the order of loading CSS files to load if there are multiple -->
            <set field="layoutSettings.styleSheets[]" value="/ofbizdemoofbizDemo/css/bootstrap.min.css"/>
   
            <!-- Including custom JS that you want to use in your application view. [] in field can be used to
                 set the order of loading of JS files to load if there are multiple -->
           <set field="javaScripts[+0]" value="/ofbizdemo/js/bootstrap.min.js" global="true"/
-->
           <set field="layoutSettings.
companyName" from-field="uiLabelMap.OfbizDemoCompanyName
javaScripts[+0]" value="/ofbizDemo/js/bootstrap.min.js" global="true"/>
        </actions>
        <widgets>
            <section>
                <condition>
                    <if-has-permission permission="OFBIZDEMO" action="_VIEW"/>
                </condition>
                <widgets>
                    <platform-specific><html><html-template location="component://ofbizdemoofbizDemo/webapp/ofbizdemoofbizDemo/includes/PreBody.ftl"/></html></platform-specific>
                    <decorator-section-include name="pre-body"/>
                    <decorator-section-include name="body"/>
                    <platform-specific><html><html-template location="component://ofbizdemoofbizDemo/webapp/ofbizdemoofbizDemo/includes/PostBody.ftl"/></html></platform-specific>
                </widgets>
                <fail-widgets>
                    <label style="h3">${uiLabelMap.OfbizDemoViewPermissionError}</label>
                </fail-widgets>
            </section>
        </widgets>
    </section>
</screen>


In the code above you may have noticed the layoutSettings.styleSheets[] and layoutSettings.javaScripts[+0] notations. You can use the layoutSettings. notation for any files.

If you want to order styleSheets or javaScripts with empty square brackets you simply add the file at the end of the layoutSettings.styleSheets or layoutSettings.javaScripts list, with [+0] you add it at front of it.

 

9.) Use this decorator in your Freemarker screen that you created in last part as:

...

 

<screen name="AddOfbizDemoFtl">
    <section>
        <actions>
            <set field="titleProperty" value="OfbizDemoAddOfbizDemoFtl"/>
            <set field="headerItem" value="addOfbizDemoFtl"/>
        </actions>
        <widgets>
            <decorator-screen name="OfbizDemoCommonDecorator" location="${parameters.mainDecoratorLocation}">
                <decorator-section name="body">
                     <label style="h4" text="${uiLabelMap.OfbizDemoListOfbizDemos}"/>
                     <platform-specific>
                         <html><html-template location="component://ofbizdemoofbizDemo/webapp/ofbizdemoofbizDemo/crud/ListOfbizDemo.ftl"/></html>
                     </platform-specific>
                     <label style="h4" text="${uiLabelMap.OfbizDemoAddOfbizDemoFtl}"/>
                     <platform-specific>
                         <html><html-template location="component://ofbizdemoofbizDemo/webapp/ofbizdemoofbizDemo/crud/AddOfbizDemo.ftl"/></html>
                     </platform-specific>
                </decorator-section>
            </decorator-screen>
        </widgets>
    </section>
</screen>

 

...

10. Now restart OFBiz as you have made entries to allowedPaths in web.xml. As it reloads hit https://localhost:8443/ofbizdemoofbizDemo/control/AddOfbizDemoFtl you should see page with custom styles that you have used instead of using default OFBiz theme. It should look like:

Image RemovedImage Added

Here you can now play with it as you want. Try changing header or having new one, adding footer, putting in validations etc. So this way you can customize UI layer of OFBiz with Freemarker templates, CSS and JS.

...

If you have followed all the steps and developed practice application from this tutorial then this will help you in understanding other implementation in OFBiz. These things are basic foundation of working in OFBiz. Now you know, how you can start development in OFBiz. Don't leave behind the extra links provided in this tutorial as they will help you a lot in understanding the things which are given there in detail.
Here is another good reading can be of help is available at FAQ Tips Tricks Cookbook HowTo.
Now the next thing comes in the way is the business processes which are really needed to be understood well for understanding OOTB process flow in OFBiz and OOTB data model, so for this, books are available at : OFBiz Related Books. Understanding well the OFBiz OOTB available data model and business processes will help in building better business solutions top of it.

...