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:
...
Getting started
Before You start - You should check this pages:
- How to create an independent module ? - to get some knowledge about qcadoo plugin structure
- Setup a new plugin - page shows how to generate empty project
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:
...
language | xml |
---|---|
title | File src/main/resources/stoppage/model/stoppage.xml |
...
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 | ||||||||
---|---|---|---|---|---|---|---|---|
|
Mockup | ||||||||
---|---|---|---|---|---|---|---|---|
|
Getting started
Before You start - You should check this pages:
- How to create an independent module ? - to get some knowledge about qcadoo plugin structure
- Setup a new plugin - page shows how to generate empty project
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 | ||||
---|---|---|---|---|
| ||||
<?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> |
...
Now we must add information about model in src/main/resources/qcadoo-plugin.xml into the <modules> tag:
Code Block | ||
---|---|---|
| ||
<model:model model="stoppage" resource="model/stoppage.xml" /> |
As we use in our plugin other an entity from another module (ordersorder entity) we must also add a dependency definition to qcadoo-plugin.xml <plugin> tag:
Code Block | ||
---|---|---|
| ||
<dependencies> <dependency> <plugin>orders</plugin> <version>[0.4.1</version> </dependency> </dependencies> |
and to into pom.xml in the <dependencies> tag:
Code Block | ||
---|---|---|
| ||
<dependency> <groupId>com.qcadoo.mes</groupId> <artifactId>mes-plugins-orders</artifactId> <version>0.4.1</version> </dependency> |
We can here now define that an order could have many stoppages (we'll use it in views). Back to qcadoo-plugin.xml and , go to the <modules> section, then element and add:
Code Block | ||
---|---|---|
| ||
<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 the full data model needed for this plugin. The full qcadoo-plugin.xml file plugin descriptor should look like this:
Code Block | ||
---|---|---|
| ||
<?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/model" xmlns:view="http://schema.qcadoo.org/modules/view" xmlns:menu="http://schema.qcadoo.org/modules/menu" xmlns:localization="http://schema.qcadoo.org/modules/localization" xsi:schemaLocation=" http://schema.qcadoo.org/plugin http://schema.qcadoo.org/plugin.xsd 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 little easier views for the All stoppages window tab. First we must add the following things to the plugin descriptors qcadoo-plugin.xml into <modules> element:
- Stoppages menu item in Production orders menu.
- Views definition (for grid and edit form).
Check following lines:
Code Block | ||
---|---|---|
| ||
<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 allStoppages:
Code Block | ||||
---|---|---|---|---|
| ||||
<?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).
The gridNewCopyAndRemoveAction template will add to a button toolbar buttons for 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 | ||
---|---|---|
| ||
For more information check View Definition Overview wiki page. |
Then we Now lets create the add/edit form viewit self:
Code Block | ||||
---|---|---|---|---|
| ||||
<?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 | ||||
---|---|---|---|---|
| ||||
<?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 | ||
---|---|---|
| ||
<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 add also have to add a ribbon extension group view in the same file:
Code Block | ||
---|---|---|
| ||
<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 | ||
---|---|---|
| ||
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 | ||
---|---|---|
| ||
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 | ||
---|---|---|
| ||
<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 | ||
---|---|---|
| ||
<?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.
...