Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migration of unmigrated content due to installation of a new plugin

This article will show you how to extend already existing modules. This is actually a description how we implemented the Stoppages module.

Use case

The use case is little different than on page Stoppage and makes plugin more extended. First - there should be a possibility to view list of stoppages for single production order by clicking button on details page:

...

Getting started

Before You start - You should check this pages:

Model definition

First we must define model for our plugin. As in Use case we need following elements:

  • order - each stoppage is in relation with some order
  • duration - integer field for stoppage duration
  • reason - text field for reason

Each field should be required. We created following model in file src/main/resources/stoppage/model/stoppage.xml:

Code Block
languagexml
titleFile src/main/resources/stoppage/model/stoppage.xml
<?xml version="1.0" encoding="UTF-8"?>
<model name="stoppage" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://schema.qcadoo.org/model"
	xsi:schemaLocation="http://schema.qcadoo.org/model http://schema.qcadoo.org/model.xsd">
	<fields>
		<belongsTo name="order" model="order" plugin="orders" required="true" />
		<integer name="duration" required="true" />
		<text name="reason" required="true" />
	</fields>
	<hooks>
	</hooks>
</model>
Info
titleModel Fields

For more information check Model Fields wiki page.

Now we must add information about model in src/main/resources/qcadoo-plugin.xml into <modules> tag:

Code Block
languagexml
<model:model model="stoppage" resource="model/stoppage.xml" />

As we use in our plugin other module(orders) we must also add dependency definition to qcadoo-plugin.xml <plugin> tag:

Code Block
languagexml
<dependencies>
	<dependency>
		<plugin>orders</plugin>
		<version>[0.4.1</version>
	</dependency>
</dependencies>

and to pom.xml <dependencies> tag:

Code Block
languagexml
<dependency>
	<groupId>com.qcadoo.mes</groupId>
	<artifactId>mes-plugins-orders</artifactId>
	<version>0.4.1</version>
</dependency>

We can here now define that order could have many stoppages (we'll use it in views). Back to qcadoo-plugin.xml and go to <modules> section, then add:

Code Block
languagexml

<model:model-field plugin="orders" model="order">
 <model:hasMany name="stoppages" plugin="stoppage"
 model="stoppage" joinField="order" cascade="delete" />
</model:model-field>

Now we have model properly defined. One look for our qcadoo-plugin.xml file:

...

languagexml

...

This tutorial will show you how to extend an already existing module. This is actually a description how we implemented the Stoppages module.

Use case

First - there should be a possibility to view list of stoppages for single production order by clicking button on details page:

Mockup
Stoppage1
Stoppage1
Version1
NameStoppage1
Second - there should be menu which open tab with all stoppages:

Mockup
Stoppage2
Stoppage2
Version1
NameStoppage2

Getting started

Before You start - You should check this pages:

Model definition

First we must define the stoppage entity for our plugin. As in the Use case we will need the following elements:

  • order - each stoppage is in relation with one order
  • duration - integer field that shows the stoppage duration in minutes
  • reason - text field in which we give a text description why the stoppage occurred

Each field is required. We will define the entity in the file src/main/resources/stoppage/model/stoppage.xml:

Code Block
languagexml
titleFile src/main/resources/stoppage/model/stoppage.xml
<?xml version="1.0" encoding="UTF-8"?>
<model name="stoppage" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://schema.qcadoo.org/model"
	xsi:schemaLocation="http://schema.qcadoo.org/model http://schema.qcadoo.org/model.xsd">
	<fields>
		<belongsTo name="order" model="order" plugin="orders" required="true" />
		<integer name="duration" required="true" />
		<text name="reason" required="true" />
	</fields>
	<hooks>
	</hooks>
</model>
Info
titleModel Fields

For more information check Model Fields wiki page.

Now we must add information about model in src/main/resources/qcadoo-plugin.xml into the <modules> tag:

Code Block
languagexml
<model:model model="stoppage" resource="model/stoppage.xml" />

As we use in our plugin an entity from another module (order entity) we must also add a dependency definition to qcadoo-plugin.xml <plugin> tag:

Code Block
languagexml
<dependencies>
	<dependency>
		<plugin>orders</plugin>
		<version>[0.4.1</version>
	</dependency>
</dependencies>

and into pom.xml in the <dependencies> tag:

Code Block
languagexml
<dependency>
	<groupId>com.qcadoo.mes</groupId>
	<artifactId>mes-plugins-orders</artifactId>
	<version>0.4.1</version>
</dependency>

We can now define that an order could have many stoppages (we'll use it in views). Back to qcadoo-plugin.xml, go to the <modules> element and add:

Code Block
languagexml

<model:model-field plugin="orders" model="order">
 <model:hasMany name="stoppages" plugin="stoppage"
 model="stoppage" joinField="order" cascade="delete" />
</model:model-field>

Now we have the full data model needed for this plugin. The full qcadoo-plugin.xml plugin descriptor should look like this:

Code Block
languagexml
<?xml version="1.0" encoding="UTF-8"?>
<plugin plugin="stoppage" version="0.4.1"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schema.qcadoo.org/plugin"
    xmlns:model="http://schema.qcadoo.org/modules/view
       httpmodel" xmlns:view="http://schema.qcadoo.org/modules/view.xsd"
       httpxmlns:menu="http://schema.qcadoo.org/modules/menu"
       httpxmlns:localization="http://schema.qcadoo.org/modules/menu.xsdlocalization"
      xsi:schemaLocation="
       http://schema.qcadoo.org/modules/localizationplugin
       http://schema.qcadoo.org/modules/localizationplugin.xsd">
    <information>
 <name>MES - Stoppage</name>
 <vendor>
 <name>Qcadoo Limited</name>
 <url>http://qcadoo.com/</url>
 </vendor>
 </information>

 <dependencies>
 <dependency>
 <plugin>orders</plugin>
 <version>[0.4.1</version>
 </dependency>
 </dependencies>

 <modules>
 <localization:translation path="locales" />

 <model:model model="stoppage" resource="model/stoppage.xml" />

 <model:model-field plugin="orders" model="order">
 <model:hasMany name="stoppages" plugin="stoppage"
 model="stoppage" joinField="order" cascade="delete" />
 </model:model-field>

<!-- We will add views definitions here soon -->

 <view:resource uri="public/**/*" />

 </modules>
</plugin>

Views for All Stoppages

Now we will create little easier views for All stoppages window tab. First we must add to qcadoo-plugin.xml into <modules>:

  • Stoppages menu item in Production orders menu.
  • Views definition (for grid and edit form).

Check following lines:

Code Block
languagexml
<menu:menu-category name="orders" />
<menu:menu-item name="stoppages" category="orders" view="allStoppages" />

<view:view resource="view/allStoppages.xml" />
<view:view resource="view/allStoppagesForm.xml" />

We placed stoppage menu item in orders category and bound it with allStoppages view.

Now we must create view allStoppages:

Code Block
languagexml
titleview/allStoppages.xml

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

<view xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xmlns="http://schema.qcadoo.org/view"
    	xsi:schemaLocation="http://schema.qcadoo.org/view http://schema.qcadoo.org/view.xsd"
		name="allStoppages" modelName="stoppage" modelPlugin="stoppage">

		<component type="window" name="window">
			<ribbon>
				<group template="gridNewCopyAndRemoveAction" />
			</ribbon>

			<component type="grid" name="stoppage" reference="grid">
				<option type="column" name="order" fields="order" expression="   http://schema.qcadoo.org/modules/model
       http://schema.qcadoo.org/modules/model.xsd
       http://schema.qcadoo.org/modules/view
       http://schema.qcadoo.org/modules/view.xsd
       http://schema.qcadoo.org/modules/menu
       http://schema.qcadoo.org/modules/menu.xsd
       http://schema.qcadoo.org/modules/localization
       http://schema.qcadoo.org/modules/localization.xsd">

 <information>
 <name>MES - Stoppage</name>
 <vendor>
 <name>Qcadoo Limited</name>
 <url>http://qcadoo.com/</url>
 </vendor>
 </information>

 <dependencies>
 <dependency>
 <plugin>orders</plugin>
 <version>[0.4.1</version>
 </dependency>
 </dependencies>

 <modules>
 <localization:translation path="locales" />

 <model:model model="stoppage" resource="model/stoppage.xml" />

 <model:model-field plugin="orders" model="order">
 <model:hasMany name="stoppages" plugin="stoppage"
 model="stoppage" joinField="order" cascade="delete" />
 </model:model-field>

<!-- We will add views definitions here soon -->

 <view:resource uri="public/**/*" />

 </modules>
</plugin>

Views for All Stoppages

Now we will create views for the All stoppages window tab. First we must add the following things to the plugin descriptors qcadoo-plugin.xml <modules> element:

  • Stoppages menu item in Production orders menu.
  • Views definition (for grid and edit form).
Code Block
languagexml
<menu:menu-category name="orders" />
<menu:menu-item name="stoppages" category="orders" view="allStoppages" />

<view:view resource="view/allStoppages.xml" />
<view:view resource="view/allStoppagesForm.xml" />

We placed stoppage menu item in orders category and bound it with allStoppages view.

Now we must create allStoppages view:

Code Block
languagexml
titleview/allStoppages.xml

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

<view xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xmlns="http://schema.qcadoo.org/view"
    	xsi:schemaLocation="http://schema.qcadoo.org/view http://schema.qcadoo.org/view.xsd"
		name="allStoppages" modelName="stoppage" modelPlugin="stoppage">

		<component type="window" name="window">
			<ribbon>
				<group template="gridNewCopyAndRemoveAction" />
			</ribbon>

			<component type="grid" name="stoppage" reference="grid">
				<option type="column" name="order" fields="order" expression="#order['name']" link="true" width="40" />
				<option type="column" name="duration" fields="duration" link="true" width="20" />
				<option type="column" name="reason" fields="reason" link="true" />

				<option type="correspondingView" value="stoppage/allStoppagesForm" />
				<option type="correspondingComponent" value="form" />
				<option type="correspondingViewInModal" value="false" />

				<option type="searchable" value="reason, duration" />
				<option type="orderable" value="reason, duration" />
				<option type="fullscreen" value="true" />
				<option type="multiselect" value="true" />
				<option type="order" column="duration" direction="desc" />
			</component>
			<option type="fixedHeight" value="true" />
			<option type="header" value="false" />
		</component>
</view>

In view tag we have defined name of the model(allStoppages), model which we use(stoppage) and model plugin(stoppage). 

gridNewCopyAndRemoveAction template will add to button toolbar buttons for The gridNewCopyAndRemoveAction template will add a button toolbar buttons with create, copy or remove entry from actions for the grid. Next In the grid it self we have grid defined. Three columns (three columns: order, duration, reason). Check

Please notice how the production order orders name is displayed (using expression). 

We have also define the corresponding view (stoppage/allStoppagesForm). It will be add/edit form for grid. We can set here correspondingViewInModal to true if we want the add/edit form to be in a modal window.

Info
titleView Definition

For more information check View Definition Overview wiki page.

Then we Now lets  create the add/edit form viewit self:

Code Block
languagexml
titleview/allStoppagesForm.xml
<?xml version="1.0" encoding="UTF-8"?>

<view xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schema.qcadoo.org/view"
    xsi:schemaLocation="http://schema.qcadoo.org/view http://schema.qcadoo.org/view.xsd"
        name="allStoppagesForm" modelName="stoppage">

        <component type="window" name="window">
            <ribbon>
                <group template="navigation" />
                <group template="formSaveCopyAndRemoveActions" />
            </ribbon>

            <component type="form" name="form" reference="form">
            	<component type="lookup" name="order" field="order">
            		<option type="column" name="name" fields="name" link="true"/>
            		<option type="searchable" value="name,number" />
            		<option type="orderable" value="name,number" />
            		<option type="fullScreen" value="true" />
            		<option type="expression" value="#name" />
            		<option type="fieldCode" value="number" />
            	</component>
                <component type="input" name="duration" field="duration" />
                <component type="textarea" name="reason" field="reason" />
                <option type="expression" value="#reason + ' (' + #duration +' min)'" />
                <option type="header" value="true" />
            </component>
        </component>
</view>

We use here additionally one other Here we additionally used another template for buttons - navigation. It will add a Back button Back for to our window(modal or not). Then we have definition of form component with three elements - defined components in which we can edit the: order, duration and reason. Second and third input are normal text inputs (for reason it is textarea). First is

Notice that the first component is a lookup - this is a input field with in-line search and grid search module (after clicking on magnifier icon new window will be opened with production orders grid).
Important things here are:

  • lookup definition
  • expression tag for component form

which allows you to choose an entity by using a grid or a pop-up that shows as you type.

Views for Stoppages for order

Stoppages for an order should be shown by clicking on the Stoppage button in order details tab. We must add this button using a ribbon extension.
Create We'll do this in the file src/main/resources/stoppage/view/ribbonExtension/ribbonExtensionDetails.xml:

Code Block
languagexml
titleFile ribbonExtensionDetails.xml
<?xml version="1.0" encoding="UTF-8"?>

<ribbonExtension xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schema.qcadoo.org/modules/ribbonExtension"
    xsi:schemaLocation="http://schema.qcadoo.org/modules/ribbonExtension http://schema.qcadoo.org/modules/ribbonExtension.xsd"
    plugin="orders"
    view="orderDetails">

     <group name="stoppage">
          <bigButton name="stoppage" action="#{form}.fireEvent(showStoppage);" icon="../../../../../stoppage/public/css/icons/iconStop24.png" disabled="true" />
     </group>
</ribbonExtension>

We create created an additional ribbon group stoppage and add added a button stoppage with an icon (which is stored in src/main/resources/stoppage/public/css/icons/iconStop24.png).  Attribute action performs event showStoppage which When it will get clicked then it will performe the  showStoppage event which is defined in the element <modules> from qcadoo-plugin.xml <modules>:

Code Block
languagexml
<view:view-listener plugin="orders" view="orderDetails" component="form" event="showStoppage"
	class="com.qcadoo.mes.stoppage.StoppageService" method="showStoppage" />

As we can see it refers to the showStoppage method from the com.qcadoo.mes.stoppage.StoppageService class.

Then Now we also have to add also a ribbon extension group view in the same file:

Code Block
languagexml
<view:view-ribbon-group resource="view/ribbonExtension/ribbonExtensionDetails.xml" />

Lets implement the StoppageService class Create new class in the file src/main/java/com/qcadoo/mes/stoppage/StoppageService.java:

Code Block
languagejava
package com.qcadoo.mes.stoppage;
import java.math.BigDecimal;
import java.util.Date;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.qcadoo.localization.api.TranslationService;
import com.qcadoo.view.api.ComponentState.MessageType;
import com.qcadoo.view.api.components.FormComponent;
import com.qcadoo.model.api.DataDefinition;
import com.qcadoo.model.api.DataDefinitionService;
import com.qcadoo.model.api.Entity;
import com.qcadoo.model.api.Entity;
import com.qcadoo.mes.orders.constants.OrdersConstants;
import com.qcadoo.security.api.SecurityService;
import com.qcadoo.view.api.ComponentState;
import com.qcadoo.view.api.ViewDefinitionState;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

@Service
public class StoppageService {

    public void showStoppage(final ViewDefinitionState viewDefinitionState, final ComponentState triggerState, final String[] args){
    	Long orderId = (Long) triggerState.getFieldValue();

    	if(orderId != null){
    		String url = "../page/stoppage/stoppage.html?context={\"order.id\":\""+orderId+"\"}";

    		viewDefinitionState.openModal(url);
    	}
    }
}

showStoppage will open a modal window with the view stoppage, and . It will also set the current order id (for empty form component which I'll descrie later) . If we want to open this view in the main application window we can use herethis code spitte:

Code Block
languagejava
viewDefinitionState.redirectTo(url, false, true);

Now we can create a view with the grid which will contains contain stoppage entries entities for a selected stoppage order in the file src/main/resources/stoppage/view/stoppage.xml:

...

Notice that modelName and modelPlugin refers to the order model and enity in the orders plugin (not stoppage - this is very important). Then we create empty form component order with reference to order. As we definedAfterwards we add a field to the order entity which will make the relationship to stoppages bidirectional.

Code Block
languagexml
<model:model-field plugin="orders" model="order">
 <model:hasMany name="stoppages" plugin="stoppage"
 model="stoppage" joinField="order" cascade="delete" />
</model:model-field>

before - we can now fill grid using as it source: #{order}.stoppages. Rest of grid the grids definition is standard as in allStoppages.xml. There is reference to add/edit form view but the corresponding view will be different: stoppage/stoppageForm.

Now we create stoppage add/edit form view - Wil define it in the file src/main/resources/stoppage/view/stoppageForm.xml contains following view definition:

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

<view xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schema.qcadoo.org/view"
    xsi:schemaLocation="http://schema.qcadoo.org/view http://schema.qcadoo.org/view.xsd"
        name="stoppageForm" modelName="stoppage">

        <component type="window" name="window">
            <ribbon>
                <group template="navigation" />
                <group template="formSaveCopyAndRemoveActions" />
            </ribbon>

            <component type="form" name="form" reference="form">
                <component type="input" name="duration" field="duration" />
                <component type="textarea" name="reason" field="reason" />
                <option type="expression" value="#reason + ' (' + #duration +' min)'" />
                <option type="header" value="true" />
            </component>
        </component>
</view>

Model used here is stoppage, because we refer here to single record from model stoppage, not from order stoppages.

Translation for labels and other elements

You can of course edit src/main/resources/stoppage/locales/stoppage_XX.properties  during component developing process (XX is two-letter language code). But if You don't know how labels are named a good idea is to use your plugin (check all windows etc.) and then check qcadoo-path/logs/translation.log for Missing translation entries. And then of course add all of them to stoppage_XX.properties and fill with translations.

...