Developer Guide Adempiere

91
qwertyuiopasdfghjklzxcvbnmqwertyui opasdfghjklzxcvbnmqwertyuiopasdfgh jklzxcvbnmqwertyuiopasdfghjklzxcvb nmqwertyuiopasdfghjklzxcvbnmqwer tyuiopasdfghjklzxcvbnmqwertyuiopas dfghjklzxcvbnmqwertyuiopasdfghjklzx cvbnmqwertyuiopasdfghjklzxcvbnmq wertyuiopasdfghjklzxcvbnmqwertyuio pasdfghjklzxcvbnmqwertyuiopasdfghj klzxcvbnmqwertyuiopasdfghjklzxcvbn mqwertyuiopasdfghjklzxcvbnmqwerty uiopasdfghjklzxcvbnmqwertyuiopasdf ghjklzxcvbnmqwertyuiopasdfghjklzxc vbnmqwertyuiopasdfghjklzxcvbnmrty uiopasdfghjklzxcvbnmqwertyuiopasdf ghjklzxcvbnmqwertyuiopasdfghjklzxc vbnmqwertyuiopasdfghjklzxcvbnmqw ertyuiopasdfghjklzxcvbnmqwertyuiop asdfghjklzxcvbnmqwertyuiopasdfghjkl Developer’s Guide The ADempiere Bazaar’s Sourcecode Expose 1/9/2008 Mario Calderon, Norbert Wessel and others (from the Spanish account of Victor Perez, then into German)

description

Developer Guide for ERP

Transcript of Developer Guide Adempiere

Page 1: Developer Guide Adempiere

qwertyuiopasdfghjklzxcvbnmqwertyui

opasdfghjklzxcvbnmqwertyuiopasdfgh

jklzxcvbnmqwertyuiopasdfghjklzxcvb

nmqwertyuiopasdfghjklzxcvbnmqwer

tyuiopasdfghjklzxcvbnmqwertyuiopas

dfghjklzxcvbnmqwertyuiopasdfghjklzx

cvbnmqwertyuiopasdfghjklzxcvbnmq

wertyuiopasdfghjklzxcvbnmqwertyuio

pasdfghjklzxcvbnmqwertyuiopasdfghj

klzxcvbnmqwertyuiopasdfghjklzxcvbn

mqwertyuiopasdfghjklzxcvbnmqwerty

uiopasdfghjklzxcvbnmqwertyuiopasdf

ghjklzxcvbnmqwertyuiopasdfghjklzxc

vbnmqwertyuiopasdfghjklzxcvbnmrty

uiopasdfghjklzxcvbnmqwertyuiopasdf

ghjklzxcvbnmqwertyuiopasdfghjklzxc

vbnmqwertyuiopasdfghjklzxcvbnmqw

ertyuiopasdfghjklzxcvbnmqwertyuiop

asdfghjklzxcvbnmqwertyuiopasdfghjkl

Developer’s Guide

The ADempiere Bazaar’s Sourcecode Expose

1/9/2008

Mario Calderon, Norbert Wessel and others

(from the Spanish account of Victor Perez, then into German)

Page 2: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 2

Contents

ADEMPIERE DEVELOPER'S DOCUMENTATION ............................................................................ 4

Class org.compiere.util.GenerateModel ................................................................................................ 5

Java-Beans ............................................................................................................................................ 7

Persistency-Engine ................................................................................................................................ 9

Additional methods of PO............................................................................................................... 10

class POInfo .................................................................................................................................... 13

class POInfoColumn ....................................................................................................................... 15

Business Object Classes ...................................................................................................................... 16

Interface DocAction ............................................................................................................................ 19

class MDocType ................................................................................................................................. 21

class X_C_DocType ....................................................................................................................... 22

Documents Workflow ......................................................................................................................... 23

class DocumentEngine .................................................................................................................... 23

Class MWFActivity (Workflow Activity) .......................................................................................... 26

class MWorkflow ............................................................................................................................ 30

Class ModelValidationEngine ............................................................................................................ 31

Class VDocAction............................................................................................................................... 33

Show a window from menu ................................................................................................................ 35

Class APanel ....................................................................................................................................... 37

Class processModalDialog ................................................................................................................. 38

Class MWFprocess ............................................................................................................................. 40

Class Workflowprocessor ................................................................................................................... 41

Class MWFNode ................................................................................................................................. 42

Class processCtl .................................................................................................................................. 43

Invoice-Preview .............................................................................................................................. 45

Values in Table of a invoice previews ............................................................................................ 45

Calling sequence to show the Invoice-Preview .............................................................................. 45

PrintFormat ......................................................................................................................................... 57

class PrintData..................................................................................................................................... 58

Workflow (WF) im AD....................................................................................................................... 60

Workflow-Window ............................................................................................................................. 61

Window Report & Process .................................................................................................................. 64

Parameter analysis at report call ..................................................................................................... 68

Callouts ............................................................................................................................................... 69

Validation of context values ............................................................................................................... 71

Accounting processor .......................................................................................................................... 73

Class Fact ............................................................................................................................................ 76

Class FactLine ................................................................................................................................. 77

DocTypes and DocBaseTypes ............................................................................................................ 78

Application Dictionary: Financial Report ........................................................................................... 79

Interaction of Application Dictionary-Application-Code using Landed Costs as an example ........... 80

Price lists ............................................................................................................................................. 82

Price calculation .............................................................................................................................. 84

Payment Term ................................................................................................................................. 85

Page 3: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 3

Views RV_C_INVOICE, RV_OPENITEM ....................................................................................... 86

Explanation of the warehouse structure in the DB ............................................................................ 89

Attribute Set Instances .................................................................................................................... 91

If you tell the truth you don't have to remember anything. - Mark Twain

It is not enough just to learn and know, but you ought to possess and own it

- Aristotle

I hope to free my followers from clinging to styles, patterns, or molds

- Bruce Lee, commenting on his Kung Fu

A question may be in the future tense, and so it cannot be answered just yet

- Red1, when asked why things aren’t done

Page 4: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 4

ADEMPIERE DEVELOPER'S DOCUMENTATION

This is a documentation of mainly how ADempiere classes are organized and how they interact with

each other to yield the functionality for which they were conceived. The order is quite random, but it is

intended that each section is as atomic as possible, i.e. each section should not rely too heavily on other

sections to be understood.

It is by no means a complete documentation, but it could serve as a basis to it. It is our hope that the

community uses this document heavily: corrections and enhancements are therefore welcome.

The practical purpose is that a person with a basic knowledge of Java and the basic functions of

ADempiere should be capable after reading this document of doing developments and customizations

on his own, contributing to the improvement of ADempiere.

This paper was born after my visit to Ecuador in August 2007, where Victor Perez told me in a 7-day

jam session the innards of ADempiere.

I added comments which I had written for other aspects of ADempiere. As a result, I wrote a 80+ pages

in German. Another contributors were Norbert Wessel and Moritz Weber of Metas Consult, who were

so kind to translate the original documentation into English and some remarks from the forum by

Karsten Thiemann. Other helpers were XXXXXXX, who set up a Doc Wiki Book, but it was not

continued.

I sincerely hope that this documentation will become a seed of a proper developers' guide.

Mario Calderon

San Salvador, December 26th, 2007

Page 5: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 5

Class org.compiere.util.GenerateModel

package org.adempiere.util public class GenerateModel

The persistence layer of ADempiere consists (mainly) of the PO.java, which is an abstract class and

defines methods like load(), save(), delete(), get_ID(), etc.

Every persistent x object (a table in the database) has to extend PO.java in order to get all the

persistence functionality (in some cases you don't need it but it never hurts.) To ease the expansibility

and because of the fact that most needed methods are just setter and getter methods (the X_* classes are

simply POJOs) ADempiere offers a class to generate X_* classes that extend the PO.java for all tables

(x objects) defined in the application dictionary (AD). This is done with

� org.compiere.util.GenerateModel () before ADempiere 3.3.0

� org.adempiere.util.GenerateModel Now we generate not only an X_* class but also an interface I_* which is implemented by the

X_* class.

Since ADempiere 3.3.0

That is all what you need to have a full functional (in terms of persistency) persistent x object.

The new window tutorial in the wiki gives an example. It creates a table to store material

information - it is just an example (from my company). You don't need to create a M_* class for

it if you just want to store/load the data into an ADempiere window. For that the generated

X_XX_Material.java is sufficient. In the given example the MMaterial.java adds some

functionality in order to get a MMaterial xobject with the given materialno and colorno. This is

a quite common functionality in M_* classes. Give me the x object / all x objects with a given

set of attribute values from the database - but it is only an example and you don't need to

implement this kind of functionality for your x object. In GenerateModel.java:

If someone inputs no or a wrong path, errors will be caused.

It is best practice to change GenerateModel.java

1) Change the default folder to your own

2) optional – change the package name

3) optional – Add entity types like 'D' (Dictionary) to line 97

Run GenerateModel without parameters

• method main() generates java beans (X_xxxxxx.java-files like e.g.e.g. X_C_Invoice.java).

The method main() reads the tabell AD_Table and generates amongst others get- and set-

methods for every column of AD-tables.

• It is possible to generate single beans

See in the text down below.

Page 6: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 6

• In the sources of Adempiere these files can be

• found at org.compiere.model . • The compiled GenerateMdel.class-file is saved at

.../adempiere_trunk/base/build/org/compiere/util • With parameters it is possible to configure the output:

• select the method main() in Eclipse, click the right mouse button and click run in the

context menu

• To input parameters, they must be defined in run • Parameter 0 : Folder

Where to copy the file.

Default –when no parameter set: "C:\\Adempiere\\adempiere-all\\extend\\src\\adempiere\\model\\" In svn trunk this is in /adempiere_trunk/extend/src/compiere/model//

• Parameter 1: Package,that the java bean contains to

Default – when no parameter set: "compiere.model" • Parameter2: entity type

(User-Defined, Adempiere, Dictionary, etc).

In the file there is just User and Application intended. For others like Adempiere,

it should be entered there. Remember that just User-defined will not be deleted in

every new version

Default – when no parameter set: "'U','A'" • Parameter 3: tabelle

Relating to which table this java bean will be generated

Default – when no parameter set: % (all tables)

• Also the code can be changed that way, that the parameters have the wished configuration.

• Every time the application dictionary tables has been changed, the X_files must be regenerated

to acces the field from source code.

Since V. 3.3.0 /Adempiere/adempiere_trunk/base/src/org/adempiere/util/GenerateModelJPA.java is designed to do this.

This class generates files without „X_“-prefix , e.g. „C_InvoiceLine.java“. They must be renamed

before deployment.

Page 7: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 7

Java-Beans

Characteristics of beans

� They are POJOs

� Every business object has a java bean

� They are the connection between source code and the data base data. They help the developer

avoid to access data directly or keeping infomation like Table IDs or column names hard-coded.

Example bp.getM_PriceList_ID(). The object BusinessPartner gives back the id of the price

list.

� They are placed in the package org.compiere.model, where the java bean classes and the

business logic classes are placed

� Declaration of the java bean class.

For example:

public class X_C_Invoice extends PO

Some methods are implemented in the class PO (Persistence Object in org.compiere.model.PO)

For example all inherited constructors call the constructor of the java bean class PO ( super (ctx, rs, trxName) or super (ctx, C_Invoice_ID, trxName) ).

� static final Property Table_ID table contains the ID of the table

example: public static final int Table_ID=MTable.getTable_ID(Table_Name); � protected static Property Model

example: protected static KeyNamePair Model = new KeyNamePair(Table_ID, Table_Name);

� static final Property accessLevel Are values which have been defined at table creation (Client, Organisation,

Client+Organisation, etc).

example: protected BigDecimal accessLevel = BigDecimal.ValueOf(1); � AD_ORGTRX_ID_AD_Reference_ID

ID in dictionary.

� methods

� initPO

will be called by the last PO-constructor of the call chain ( PO (Properties ctx, int ID, String trxName, ResultSet rs) ) .

� toString (e.g.e.g.gives back for example the class C_Invoice: X_C_Invoice)

It is used in compare() or log.info(toString() � get- and set-metods of all columns of the table.

Exception: columns processing, processed, Created, CreatedBy, etc. that are created in

the method load() of the class PO with the call of loadDefaults()/SetStandardDefaults(). � getDocStatus() � setDocStatus()

� Misc

� All base classes and beans are part of the same package.

This makes it possible to create an object by calling the constructor.

� Date is handled as Timestamp .

� Boolean values are strings with length 1 (Y=True, N=False).

When querying booleans, no get method is called.

Use methods like isApproved(). � There are more beans that can be found in different packages.

Page 8: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 8

They will be partly explained in this document, for example MWFActivity, MWFNode, MWFprocess, MDocType, MWorkflow.

There are java beans which are not related to a business object:

public class X_AD_WF_Activity extends PO

This class is a helper class for MWFActivity

public class MWFActivity extends X_AD_WF_Activity implements Runnable

� Beans and PO together realize persistency in ADempiere:

� Beans keep data

� PO supplies mechanism for persistency

Page 9: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 9

Persistency-Engine

org.compiere.model.PO

Realizes class persistency of ADempiere. PO has methods for DB transfer and also calls triggers and

model validators.

public abstract class PO implements Serializable, Comparator, Evaluatee; � Serializable

For streaming out objects.

� Comparator

methods compare() and equals() are implemented by PO.

� Evaluate

Implements the method get_ValueAsString(), which is also called by

org.compiere.util.Evaluator. evaluateLogicTuple() . The tupels @xxxx@ defined in AD, are

evaluated.

Class hierarchy: an Adempiere-class like MInvoice inherits from X_C_Invoice, who itself inherits from

PO :

� public class MInvoice extends X_C_Invoice implements DocAction � public class X_C_Invoice extends PO

Important constructors:

� the root-constructor is called at last:

public PO (Properties ctx, int ID, String trxName, ResultSet rs) � To create a new instance: if the parameter ID ist set with value 0.

� If the transaction is set as null, a new one will be created.

org.compiere.util.Trx controlls transactions with static methods

� When new transactions are created, Trx sets a random name.

Trx.createTrxName() � Create a new transaction, e.g.

Trx.createTrxName("Cost") � Name of the current transaction will be got

Trx.get(trxName, True) � A new transaction will be created if transaction trxName does not exist and the

second parameter is True.

Trx.get(trxName, True) In Trx.get a new will be created:

{

:

retValue = new Trx (trxName) : }

� Setting the transaction name assures that all DB savings with equal names are

handled as one transaction in commit and rollback contextes.

Page 10: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 10

The root-constructor calls:

{

:

load (int ID, String trxName) : } If ID and transaction are known

� ID > 0

The columns are being retrieved via SQL and instance is filled with values.

� ID <= 0

Creation of new object.

Private class m_createNew gets the value true, so that object knows that it is a

new creation.

Columns processing, processed, Created, CreatedBy, etc. are created with

calling loadDefaults().

loadComplete (boolean success) Can be defined by the subclasses if needed, but it is

currently not used anywhere.

� load(ResultSet rs); load(ResultSet rs) also calls in the end load (int ID, String trxName). For this a result set is used to create an object. The current position of the result set is

taken; no navigation occurs through the result set.

� Constructor PO (Properties ctx, int ID, String trxName) MBPartner bp = new MBPartner (line.getCtx(), line.getC_BPartner_ID(), line.get_TrxName());

� Constructor PO (Properties ctx, ResultSet rs, String trxName)

Additional methods of PO

� public final Object get_Value (String columnName) � It checks whether column is active

� If active, get_Value (int index) is called, which returns m_newValues[index].

See Properties.

� set_ValueNoCheck (String ColumnName, Object Value) The column ColumnName gets a value without checking.

Value can be cut off. That is the reason why sometimes values are typed in into the program and

the programm saves them shortened. They are shorted to the length defined in Adempiere's AD.

The following methods are explained only here and not in the subclasses which inherit from

PO.

Page 11: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 11

� delete() An object is deleted.

Under certain conditions, events are validated with ModelValidationEngine. � delete_Tree()

ID-Trees are deleted

� save() Thr saving of subclasses' instances done here.

Some checks are done: new object, organisation.

In save() other methods are called:

� beforeSave() Is often implemented by business classes like MInvoice, MProduct and so on.

� saveNew() Saving of a new object

� saveUpdate() Saving of an existing object.

Change tracking

� A session variable is created.

MSession session = MSession.get (p_ctx, false) � If a value change occurs, the table MChangeLog saves the state before and after

saving: MChangeLog cLog = session.changeLog

In order to achieve this, configuration must be done in Adempiere A table can

be defined for logging there. If this is done, Adempiere shows the change log in

the window, where the table is displayed, by double-clicking on the bottom rigth

of the window (click right mouse button on the bottom right corner of the table

window).

The following is shown:

� Tablename

� When and by whom the record was created

� When and by whom the record was modified

� change log

� saveNew() and saveUpdate() call saveFinish() at the end, which calls afterSave().

Methods beforeSave() and afterSave() are often implemented by business classes like

MInvoice, MProduct etc. .

Properties of PO (among others)

� POInfo p_info (Column info: Table-ID, Table Name, Access Level, etc).

Information about columns is provided by p_info.

This property is being taken from the root-constructor with

p_info = initPO(ctx); Subclasses implement initPO(). See class POInfo.

Column Information: A call of get_Value("Columnname") causes a call of get_Value (int index); which returns m_newValues[index].

Page 12: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 12

(or m_oldValues[index], when m_newValues==null) � protected transient CLogger log = CLogger.getCLogger (getClass())

To show the subclass-output of the system console private static Clogger s_log = CLogger.getCLogger (PO.class); To show the PO-output in the system console

� private Doc m_doc � private Object[] m_IDs = new Object[] {I_ZERO} // Ids of the data sets � private Object[] m_oldValues = null; // old values � private Object[] m_newValues = null; // new values � private Mattachment m_attachment = null; � etc.

Page 13: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 13

class POInfo

package org.compiere.model public class POInfo implements Serializable

The class provides the access to POInfoColumn-instances

� Contains information about columns of the business object

� is serializeable (for exporting functionality)

� Properties (incomplete list)

• m_AD_Table_ID

• m_TableName

• m_AccessLevel

• POInfoColumn[] m_columns • private Properties m_ctx = null

� Methods (incomplete list)

Refer mostly to properties of m_column using the index.

• The Constructor

POInfo (Properties ctx, int AD_Table_ID, boolean baseLanguageOnly) calls loadInfo(), who in turn

• uses a SQL-Statement which comprises the tables

• AD_Table t • AD_Column c • AD_Val_Rule vr and

• AD_Element e

(Parameter for SQL:m_AD_Table_ID), • Creates for each column an instance of POInfoColumn and fills the

instance with the SQL data

• t.TableName • c.ColumnName • c.AD_Reference_ID

• c.IsMandatory

• c.IsUpdateable

• c.DefaultValue

• e.Name,e.Description • c.AD_Column_ID

• c.IsKey

• c.IsParent

• c.AD_Reference_Value_ID

• vr.Code • c.FieldLength

• c.ValueMin

• c.ValueMax

• c.IsTranslated

• t.AccessLevel • c.ColumnSQL

Page 14: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 14

• c.IsEncrypted

• Information of all columns are copied to m_columns • for Translations table AD_Element_TRL is used and the language is

selected

• public static POInfo getPOInfo (Properties ctx, int AD_Table_ID) Calls the constructor.

getPOInfo() itself is called in initPO() , within the Java Beans for the related

table ID. initPO() is called inside the PO-Constructor.

• getColumnName (int index)

• isColumnMandatory (int index) • String getColumnDescription (int index)

• getColumnCount()

• Class getColumnClass (int index)

Page 15: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 15

class POInfoColumn

package org.compiere.model public class POInfoColumn implements Serializable

� Contains Information about a column of the business Object

� Properties (all public)

• AD_Column_ID • ColumnName • ColumnSQL • DisplayType • ColumnClass • IsMandatory • DefaultLogic • IsUpdateable • ColumnLabel • ColumnDescription • AD_Reference_Value_ID • ValidationCode • FieldLength • ValueMin • ValueMax • ValueMin_BD • ValueMax_BD

• methods

• IsKey • IsParent • IsTranslated • isUpdateable • toString • IsEncrypted

• Relationship between POInfoColumn, PO and beans (X_-classs)

• POInfoColumn instances contain information about properties the business

object (=columns)

• Beans contain data

• PO manages data fetching and data saving

Page 16: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 16

Business Object Classes

package org.compiere.model There are two kind of business objects

• Workflow-Business-Objects

They are called inside a workflow. They must fulfill the requirements of the workflow

additionaly to their BO responsibilities.

• e.g: MInvoice, MOrder • class definition

public class MInvoice extends X_C_Invoice implements DocAction With DocAction the class implements the methods that are needed to realize a workflow.

These methods are triggered by buttons, which can be defined at the window

Table&Columns in the Application Dictionary of Adempiere. Depending of the state

and the next DocAction, the corresponding method is called.

For more information see the Workflow Chapter.

The following methods query states and set actions.

• prepareIt() Events are validated with ModelValidationEngine. Class ModelValidationEngine can be used, to realize custom business logic.

Here listened events are caught and executed, referenced document states and

document types are defined and other methods are called.

An custom class can be defined in the client window, Field validation class.

An example for an own validation class can be found in Libero.

• completeIt() Events are validated amonst others with ModelValidationEngine

• approveIt() • etc.

• Document workflow classses define the property private String m_processMsg, which

shows messages as "PeriodClosed" in the bottom left of the coresponding window.

• Methods that implment the interaction of actual business logic, for example in Minvoice • validatePaySchedule() • testAllocation() • getOpenAmt() • etc.

• Master data of business objects, for example in MProduct

• public class MProduct extends X_M_Product • no implement-part

• Methods that realize the interaction of actual business logic , for example in MProduct • isProductStocked()

• isOneAssetPerUOM()

• getAttributeSet()

Page 17: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 17

• etc.

• properties depend on the purpose of the class .

• All BOs implement trigger methods

• beforeSave() Actions before Data is saved.

• afterSave() Actions after data is saved.

• beforeDelete() Actions before data is deleted

• afterDelete() Actions after data is deleted

• Business-classes implement different constructors depending on their use. Examples:

• Standard Constructor MInvoiceLine (Properties ctx, int C_InvoiceLine_ID, String trxName) MInvoice to = new MInvoice (from.getCtx(), 0, null);

• public MProduct (X_I_Product impP) Constructor of the MProduct class for the product import. Properties of an instantiated

product are set with the value of the object impP . The behavior of the import can be influenced with the class ImportProduct and

X_I_Product or with ValidateModel.

• Business classes often implement static-method get(), that returns an array of objects of its own

class.

Example: public static MProduct[] get(Properties ctx, String whereClause, String trxName)

• Business classes implement a property to log events in the system console

private static Clogger s_log = CLogger.getCLogger (Mproduct.class);

• Business classes implement an object cache

• private static CCache<Integer,MInvoice> s_cache= new CCache<Integer,MInvoice>("C_Invoice", 20, 2); // 2 minutes

• Thr number of objects in cache (here 20) and residence duration (here 2 minutes) are

different for each object.

• org.compiere.util.CacheMgt manages the Cache via the Application Server.

• Business logik

Functionality is explained here based on Minvoice.prepareIt(). Steps:

• Model Validation (if there is one)

See chapter on ModelValidation.

• Gets an instance of DocumentType on basis of "C_DocTypeTarget_ID" • Checks whether a period is open based on the DocBaseTyp

Page 18: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 18

• line validation

• Checking of cashbooks

• Doctype checking or setting

• BOM expanding

• Tax calculation

• Landed Costs per line

• First all costs are added and then distributed to the rows

• In a row all costs are allocated to the product

• MinvoiceLine allocateLandedCosts() calls

MinvoiceLine getBase() Here is calculation of LANDEDCOSTDISTRIBUTION_Costs not implemented

• Validation

• Set Docaction

Page 19: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 19

Interface DocAction

package org.compiere.process public interface DocAction

• WF business objects implement methods defined inDocAction, like in

public class MInvoice extends X_C_Invoice implements DocAction.

A BO that implements DocAction is called a Document. The final shape depends on the object

• approveIt() Sets property isApproved to value true

• closeIt() There is no action possible after this

• invalidateIt() • prepareIt()

Logic before execution

• processIt() Exceution of the business logic

• reActivateIt() after calling closeIt() document can be reactivated.

I depends on the object: an order can be reactivated, but not an invoice

• rejectIt() • reverseAccrualIt() • reverseCorrectIt()

Generates the entries in accounting. In order to accomplish this, a copy of the

original document is taken.

• unlockIt() A method lockIt() does not exist

• voidIt() � Defines static final Properties (String constants) the actions and states

• ACTION_Complete • ACTION_Close • STATUS_Drafted • STATUS_Completed • etc.

� Miscellaneous methods

• getApprovalAmt() • getCtx()

ID of record

• getDocAction() • getDocStatus()

Return sthe state defined as static final Property

• getDocumentInfo() Name of document type and document number.

• getDocumentNo()

• getDoc_User_ID()

Page 20: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 20

Controls sequence

e.g. Selling- Purchase- Payment-No.

• getprocessMsg () Returns the value of the property m_processMsg

• get_TrxName()

• save() • setDocStatus ()

Set a state that is defined as static final Property

• and many more

Page 21: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 21

class MDocType

package org.compiere.model public class MDocType extends X_C_DocType

• Representss a document typ

Is instantiated by BOs with dcument types (in methods like prepare() or

getDocumentInfo() ) like in

• MCash • MInOut • MInventory • MInvoice • MJournal • MMovement • MOrder • MPayment • MPeriod • MRequisition • etc.

• Has few methods and no public properties

• methods

• getOfDocBaseType() returns the base document

• static public MDocType get (Properties ctx, int C_DocType_ID) returns an instance of MDocType from the cache and instanciates an object

• isOffer() Using properties of X_C_DocType it checks whether the BO is a subtype of

Offer. Abstract: DOCSUBTYPESO_Proposal.equals(getDocSubTypeSO())

• isProposal() and

isQuotation() behave similar to isOffer().

Page 22: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 22

class X_C_DocType

Seems also to be generated by GenerateModel .

package org.compiere.model public class X_C_DocType extends PO

� Properties

• public static final int C_DOCTYPEINVOICE_ID_AD_Reference_ID=170; ID_AD_Reference_ID

• public static final String COLUMNNAME_AD_PrintFormat_ID = "AD_PrintFormat_ID"

• public static final String COLUMNNAME_C_DocType_ID = "C_DocType_ID"; • public static final String COLUMNNAME_C_DocTypeInvoice_ID =

"C_DocTypeInvoice_ID" • public static final String COLUMNNAME_C_DocTypeShipment_ID =

"C_DocTypeShipment_ID" • public static final String COLUMNNAME_IsDefault = "IsDefault"

• public static final String DOCSUBTYPESO_Proposal = "ON" • public static final int DOCBASETYPE_AD_Reference_ID=183;

/** AP Credit Memo = APC */ • public static final String DOCBASETYPE_APCreditMemo = "APC";

** AP Invoice = API */ • public static final String DOCBASETYPE_APInvoice = "API";

/** AP Payment = APP */ • usw. für Bank Statement (CMB), GL Document(GLD), Material Movement (MMM), GL

Journal (GLJ) etc. • methods

• many getter methods call PO-methods like e.g.

• getDocSubTypeSO(), which calls get_Value("DocSubTypeSO") in PO

• getName(), which calls get_Value("Name") in PO • also setter methods

Page 23: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 23

Documents Workflow

class DocumentEngine

Package: org.compiere.process public class DocumentEngine implements DocAction Caveat: the business object m_document is called „document“ here, because it implements methods of

DocAction.

DocumentEngine identifies the next action, checks (using the BO state) whether the action is valid and

excecutes this action of the business object if validation succeeds.

Object m_document changes from one state to another during the excecution of the action, which is

done by the DocumentEngine. A state machine with actions and states is modeled by this behaviour.

DocumentEngine:

� on one hand it implements methods of DocAction, which means it executes prepareIt(), processIt(), completeIt() etc.

and

� on the other hand it is instantiated so that a BO changes its state by calling a method of the same

name that is implemented in the BO: processIt() of BO executes processIt() of

DocumentEngine, that is followed by a call of a method like complete() of the BO.

• Properties

• DocAction m_document // A business object casted to DocAction

DocumentEngine implements also the interface DocAction and has a Property

DocAction • String m_status (Default: STATUS_Drafted, defined in DocAction) • String m_message • String m_action

• get and set methods

• setDocStatus(String ignored) does nothing

• isDrafted() return STATUS_Drafted.equals(m_status);

// STATUS_Drafted, defined in DocAction • isInvalid() return STATUS_Invalid.equals(m_status);

• isInProgress() return STATUS_InProgress.equals(m_status);

• isApproved()return STATUS_Approved.equals(m_status) • etc...

• Methods The methods process business object (document)

• Constructor

Page 24: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 24

DocumentEngine (DocAction po, String docStatus) property m_document is set.

• processIt()

• completeIt()

• isValidAction (String action) Check with public String[] getActionOptions() whether the action is valid with

of the current state of the BO.

Example: When the current state=STATUS_Drafted, then the following actions

are possible: {ACTION_Prepare, ACTION_Invalidate, ACTION_Complete, ACTION_Unlock, ACTION_Void} (These actions are defined in DocAction).

After that the the current state of the BO defines the potential aktions.

• The call processIt() triggers the next call of a method within a BO.

Typical use of DocumentEngine e.g. in MInvoice processIt(String processAction):

• DocumentEngine engine = new DocumentEngine (this, getDocStatus()); An instance of DocumentEngine is created for the current BO with its current

state.

return engine.processIt(processAction, getDocAction()); processAction is expected action of the WF and getDocAction() action desired

by the user.

� DocumentEngine: processIt(processAction, docAction) // own logic

Validations are performed, depending on whether WF-Action ist valid,

If WF-Action ist not valid, whether the user action is valid, m_action is set.

See isValidAction(). • After that processIt( m_action) is called. • DocumentEngine: processIt(String action) // Implementation of DocAction

Depending on m_Action the related (custom) method is called, in which

• the state is changed.

• th method of the business objects with the same name is called:

• if (ACTION_Unlock.equals(m_action)) return unlockIt(); calls m_document.approveIt()

• if (ACTION_Invalidate.equals(m_action)) return invalidateIt(); Sets m_document.setDocStatus(STATUS_Invalid)

• if (ACTION_Complete.equals(m_action) ....) completeIt(), which calls m_document.completeIt() of BO.

• etc.

• The new state of the BO is set after the execution of this methods.

• Some DocAction methods like completeIt() of the

DocumentEngine check the BO method for validity of the action

isValidAction (String action).

• A special action is ACTION_Complete, because depending on the

state, it not only does call completeIt(), but also

m_document.save() and postIt().

Page 25: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 25

So it might happen that an action will be checked twice for correctness, if the

DocumentEngine is used.

Page 26: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 26

Class MWFActivity (Workflow Activity)

Using the word „document“ in this context means a business object that implements a DocAction

interface.

Package: org.compiere.wf public class MWFActivity extends X_AD_WF_Activity implements Runnable public class X_AD_WF_Activity extends PO

It does not implement the DocAction interface

• Definition

public class MWFActivity extends X_AD_WF_Activity implements Runnable • Inherits from X_AD_WF_Activity • X_AD_WF_Activity is generated by GenerateModel and inherits from PO

• Properties of MWFActivity • private m_po // Reference to BO, which executes this activity

Extracted with getPO (Trx trx) • m_docStatus

gets it value during run() or performWork() at DocActions:

• doc = (DocAction) m_po; // business object instance is taken success = doc.processIt (m_node.getDocAction());// action is processed

setTextMsg(doc.getSummary()); processMsg = doc.getprocessMsg(); m_docStatus = doc.getDocStatus();

• private Trx m_trx • private MWFNode m_node // Node to which action is referenced to

• private StateEngine m_state = null; • private MWFprocess m_process = null; • private DocAction m_postImmediate = null;

• Methods

• Constructors

• MWFActivity (MWFprocess process, int AD_WF_Node_ID) Here take place the calls of the implemented methods of the interface

X_AD_WF_Activity.

• There are more constructors

• constructor is called in

• Workflowprocessor for servers

• WFActivity seem to be for clients

• VDocAction for grids

• WebInfo

• other classes

• getPO (Trx trx) Gets a reference to a business object

Here interact MWFActivity with MTable and PO.

Page 27: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 27

• getAD_Table_ID() from X_AD_WF_Activity returns the ID of the object in the

table AD_Table.

For this X_AD_WF_Activity calls the method get_Value ("AD_Table_ID") of

PO, which calculates the value.

• With the ID, the table of the business object can be obtained

• getRecord_ID() returns the ID of the business object

To do this X_AD_WF_Activity calls the method get_Value("Record_ID") of PO , which calculates the value.

• After this the reference to PO is queried.

• run() Is called in FormFrame class, startBatch(), which is in turn called by class

VSetup, method actionPerformed() after any kind of events in grids, editors and

so on.

Calls performWork() It is called in MWFprocess.startNext(), when a button was triggered. See

beneath.

• performWork() Calls

success = doc.processIt (m_node.getDocAction()) • getActiveInfo (Properties ctx, int AD_Table_ID, int Record_ID)

Prepares content and and actions for combo boxes.

• public void setWFState (String WFState) See the following example.

• example MinOut.processIt()

Abstract: The workflow processor, which was activated by the server, looks for

all activities that needs to be processed. Each activitythat is checked for validity

and handed over to the MWF process, which considers all activities of a process

and triggers the next valid for execution. The next valid activity calls finally

processIt().

In DocumentEngine it was already explained what happens afterwards

( processIt() of the BO calls the constructor of DocumentEngine and then

documentEgine.processIt() etc.)

The following call chain is executed:

A)

• AdempiereServer: public void run() • in a loop doWork() is called after a certain time.

• Workflowprocessor: protected void doWork() • calls Workflowprocessor.wakeup()

• Workflowprocessor: private void wakeup() • All interrupted, not processed activities of workflow nodes with

inactive actions are identified via a SQL statement.

Tables: AD_WORFLOW->AD_WF_NODE-> AD_WF_ACTIVITY

As a workflow is triggered by a process, the activity can be traced

Page 28: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 28

to a node, the node can be traced to a workflow and the workflow

to a process.

• For each activity

activity.setWFState (String WFState) • MWFActivity: public void setWFState (String WFState)

• it checks whether the new state WFState is valid.

• if yes, the process for Ctx is taken and

• checkActivities(String trxName) is called

• MWFprocess: public void.checkActivities(String trxName) • All activities of the process, which the current activity belongs to,

are scanned:

MWFprocess.getActivities(), which contains

SELECT * FROM AD_WF_Activity WHERE AD_WF_process_ID=? .

• The first activity after one that is marked as “completed” , is given

as parameter to MWFprocess.startNext(). • With all other activities the state is managed

• MWFprocess: private boolean.startNext() • Set last Activity to processed and call save() • next Activity is taken

• processing the logical operators (AND / XOR)

• Check whether Activity t is next in chain.

• Start the Activity using a thread

new Thread(new WFActivity(....) ).start(); • public synchronized void Thread( ).start();

Comment in code: “it calls the run() Method of this thread”

• MWFActivity: public void run() • Calls performWork() • Parameter: m_trx

• Comment in code:

Feedback to process via setWFState -> checkActivities” • MWFActivity: private boolean performWork()

• Fetched from property m_node, which gets the action: String action = m_node.getAction()

• Possible actions: Document Action, Report, process, Email, Set variable, User Choice. Actions like Task, Sub Workflow, User Workbench, User Form and User Windows are offered, but not implemented.

• Is the action about to be excecuted equal Action, then calls

doc.processIt(DocAction of node) the DocAction for the

implementing BO.

Parameter is m_node.getDocAction().

Reminder: Actions are Report, process, Document Action etc.;

whereas DocActions are prepare, complete and so on.

• Sometimes a “post immediate” is prepared.

Page 29: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 29

Other calls of setWFState (String WFState):

• MWFActivity: public void run() • calls MWFActivity.setWFState (String WFState) • call after that performWork()

• MWFprocess: setWFState (String WFState)

• Sets process state and refreshes all actions

• calls MWFActivity.setWFState (String WFState) • seems to be made for Status=closed • i do not track this(Comment of the author)

� InOutGenerate.completeShipment()

Page 30: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 30

class MWorkflow

Package: org.compiere.wf public class MWorkflow extends X_AD_Workflow public class X_AD_Workflow extends PO See explaination in class processModalDialog, where MWorkflow is instantiated.

Properties

� m_nodes: Array of MWFNode-elements

� private static Ccache<String,MWorkflow[]> s_cacheDocValue

methods

� loadNodes() Depending on Workflow_ID all nodes of table AD_WF_Node are read into m_nodes .

� public MWFNode[] getNodes() nodes saved in m_nodes are returned as MWFNode[]

� public MWFNode[] getNextNodes (int AD_WF_Node_ID, int AD_Client_ID) Identify the nodes about to be executed

� public MWFprocess start (processInfo pi) Starts a workflow

� An instance of MWFprocess is created:

retValue = new MWFprocess (this, pi) � is saved

� startWork() of this instance is executed.

Here a validation takes place whether the workflow can be started with its first node.

The first node's activity is identified and executed.

See here also the context of the description of a call of actionButton() in class

processModalDialog. � public int getPrevious (int AD_WF_Node_ID, int AD_Client_ID)

Take the previous node from m_nodes. � aftterSave()

All nodes are saved under some circunstances.

� some get-methods

Page 31: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 31

Class ModelValidationEngine

Package org.compiere.model

public class ModelValidationEngine

• It is mostly used in calls inside of business classes (in the methods prepareIt(), completIt(), closeIt() and so on) as following:

ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_CLOSE);

• method get() yields (and sometimes creates) an instance of ModelValidationEngine • The result of get(). fireDocValidate (a String) is assigned to the property m_processMsg

of the business-class .

• has a property s_engine, which contains an instance of its own class (!)

It therefore seems that there is just one instance of ModelValidationEngine • In the constructor of ModelValidationEngine one ModelValidator is instantiated:

ModelValidator validator = (ModelValidator)clazz.newInstance(); It seems to be a problem here, because an interface is instantiated here, but interfaces are

implemented and not instantiated.

Solution: In client window a Client Validator Class can be set, which includes a client

validation. If is not there, the validation is ignored

Example of a validator-class: compiere.model. ModelValidator, at /extend.

public class MyValidator implements ModelValidator.

Afterwards, initialize() in Validator is called.

• fireDocValidate() The method docValidate() is called for every validator.

• interface ModelValidator

Package org.compiere.model public interface ModelValidator ModelValidator is an interface, which can be implemented by the developer.

If the class is registered in Adempiere, on every change of a record or document a specific

method can be called. Inside this method developers can programm their own actions as e.g.

booking to another account or change booking logic

If accounting rules are changed in Adempiere, RUN_setup must be rerun, so that the application

server can see the changes.

ModelValidator is used to program external logic to adempiere core.

Example of a ModelValidators (by Carlos Ruiz): http://adempiere.svn.sourceforge.net/viewvc/adempiere/trunk/extend/src/compiere/model/MyV

alidator.java?view=markup

• is used inside the constructor ModelValidationEngine

Page 32: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 32

{...

Class clazz = Class.forName(className); ModelValidator validator = (ModelValidator)clazz.newInstance(); initialize(validator, clients[i]); ...}

• defines a constant like

public static final int TYPE_BEFORE_NEW = 1

They are used as parameters of fireDocValidate calls. • declares methods like

• public String docValidate (PO po, int timing) • public void initialize (ModelValidationEngine engine, MClient client) • public String modelChange (PO po, int type) throws Exception

• used to implement custom validations.

Example of a validation-class: compiere.model. ModelValidator, under /extend.

Page 33: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 33

Class VDocAction

package org.compiere.grid.ed class VDocAction extends Cdialog implements ActionListener Shows valid operations of document actions depending on the context.

If e.g. the complete button in the order window is triggered, a form called VDocAction appears, where

the DocAction can be selected.

After pressing OK the selected DocAction of the current BO is called, which causes a state change of

the BO.

• Controlls process window

• Properties

• GridTab m_mTab • m_AD_Table_ID

• Is initialized in the constructor with Env.getContextAsInt() • Is used in dynInit() as parameter at

DocumentEngine.gertValidActions(). • private boolean m_OKpressed = false; • private boolean m_batch = false;

• Graphical elements as panels, combo boxes, scroll panes, text areas, etc.

• Constructor creates a dialog with

• Panel • BorderLayout • ComboBox • TextArea • JButton • etc.

• Methods

• Constructor

VDocAction (int WindowNo, GridTab mTab, VButton button, int Record_ID) Ruft jbInit() and dynInit(Record_ID)auf..

• jbInit() Is called by the constructor.

Initializes window.

The property ActionLabel gets the correct value.

• dynInit()Is called by the constructor. Identifies the valid actions based on the state of the business object. (documents)

The workflow state is detected:

wfStatus=MWFActivity.getActiveInfo()

Calls DocumentEngine.getValidActions(), which realizes the following:

• First the actions are identified depending on the DocState e.g.

in status_NotApproved --> Action_Prepare and Action_Void • Then depending on table and state, additional Actions are added.The

tables are: Order, MinOut (Shipment), Invoice, Payment, GL-Journal,

Page 34: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 34

Allocation, Bank Statement, Inventory Movement, Physical Inventory.

• At least the combo box with shortforms is created: CO, CL, DR, etc.

• actionPerformed() does some query

• save() Causes via a complicated mechanism that this command triggers a saving on the

database:

m_mTab.setValue("DocAction", s_Value[index])

Page 35: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 35

Show a window from menu

� org.compiere.apps

AMenuStartItem.run()

The column Action is extracted from table AD_Menu (or AD_WF_Node, if it is not a menu).

The associated column is read depending on Action: AD_WINDOW_ID, AD_process_ID, AD_WORKFLOW_ID, AD_FORM_ID.

If it is a window, the call is startWindow(0, Window-No.). Window-No. is passed on through the chain of calls.

� org.compiere.apps

AMenuStartItem.startWindow(int AD_Workbench_ID, int AD_Window_ID)

Is used both for workbench and normal windows. If AD_Workbench_ID==0, then

AD_Window_ID is evaluated.

A method is called by the instance called frame of the class AWindow:

frame.initWindow(AD_Window_ID, null) The third parameter is an optional query, which is needed for the final window. Here the third

parameter is null.

� org.compiere.apps

AWindow.initWindow (int AD_Window_ID, MQuery query)

In the Awindow constructor (last method) the class variable m_APanel was initialized.

In initWindow() the method m_APanel.initPanel (0, AD_Window_ID, query) is called.

Side-explanation begin

How can a window be called from an arbitrary place in code.

import org.compiere.model.*; // because of MQuery import org.compiere.apps.*; // because of AWindow

:

: MQuery my_query = new MQuery ("C_BPartner"); // e.g.. C_BPartner my_query.setRecordCount(1); // if only one BP is to be shown my_query.addRestriction("C_BPartner_ID", "=", 1000598); // BP ID AWindow frame = new AWindow(); boolean OK = false; OK = frame.initWindow(123, my_query); // 123=BP window frame.validate();

AEnv.showCenterScreen(frame); When table, restriction and window nr. do not match, a window with a new record will be shown.

Problem: MQuery is a class.

Page 36: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 36

Side-explanation end

� org.compiere.apps

APanel.initPanel(int AD_Workbench_ID, int AD_Window_ID, MQuery query) Here it can be discovered that the workbench alternative was not implemented.

So this is just one window. Tabs will be generated, if there are any.

If there is query in the tab, it will also be defined there:

query = initialQuery (query, gTab)

� MQuery APanel.initialQuery (MQuery query, GridTab mTab) If MQuery exists, is active and has <10 objects, it follows no other query:

if (query != null && query.isActive() && query.getRecordCount() < 10) return query;

Otherwise it is a so called volume table and a filter dialog appears.

Find find = new Find (Env.getFrame(this), m_curWindowNo, mTab.getName(), mTab.getAD_Table_ID(), mTab.getTableName(),

where.toString(), findFields, 10);

It takes care of the dialog of volume tables.

Page 37: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 37

Class APanel

package org.compiere.apps public final class APanel extends CPanel implements DataStatusListener, ChangeListener, ActionListener, ASyncprocess

• actionPerformed()

• Command dispatcher depending on the triggered icon in the window (Save,

Print, Attachment, Forward, Backward, etc.)

• Actions are processed in private methods.

Some use processCtl: process() or m_curTab.dataSave(manualCmd) with

m_curTab as a grid.

• actionButton()

• Creates an instance of VDocAction: VDocAction vda

Shows the dialog with vda.setVisible(true). In the dialog the action can be selected (Complete, Void, etc.).

• calls processModalDialog dialog = new processModalDialog() • Shows the last called window via

aenv.showCenterWindow(Env.getWindow(m_curWindowNo), dialog)

Page 38: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 38

Class processModalDialog

package org.compiere.apps public class processModalDialog extends Cdialog implements ActionListener

Abstract: processCtl manages different kinds of processes and causes if needed the instantiation of a

workflow. The WF instanciates a MWFprocess, which identifies WF, the node and its activitiy.

Finally, the activity is executed with processIt().

It is discussed here mainly because of the call of actionButton():

• actionPerformed(actionEvent e) actionPerformed(actionEvent e) is triggered by events from graphical components.

Calls processCtl: process() • processCtl: process(m_ASyncprocess, m_WindowNo, parameterPanel, m_pi, null)

• processCtl: public static method process() for synchronized or unsynchronized

processes

• m_pi is of the class processInfo • Depending on m_pi.AD_process_ID and m_p.Record_ID, an instance of

MPInstance is acquired.

• In the static method a processCtl is instantiated: processCtl worker = new processCtl(parent, WindowNo, pi, trx);

• With unsynchronized processes: worker.start() -> starts a new thread

• start() calls new Thread(this).start(). • finally processCtl.run()is called.

• With synchronized processes: worker.run() -> starts the WF, a bit more

complicated.

• processCtl:public boolean run() • The process info is fetched with a complicated SQL-query: 11 columns of the

table AD_process-AD_PInstance:

• column1: processname • column2: Procedurename • column3: Classname • column4: AD_process_ID • column5: isReport • column6: isDirectPrint • column7: AD_ReportView_ID

• column8: AD_Workflow_ID • column9: static case-value • column10: isServerprocess • column11: jasperReport

• processes, Workflows, Jasper Reports, Reports and processes are managed here.

processes call e.g. pi.setPrintPreview(!IsDirectPrint), which ends up in

ReportCtl.start(), where processes for Order, Invoice, Shipment, Project,

Page 39: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 39

Payment and Dunnig are queried and maybe started; If the process does not

fullfil any of these possibilities, the (normal) Report is executed.

• If the process activity is a workflow, it will be called:

processCtl: startWorkflow (AD_Workflow_ID); For other alternatives, see processCtl.

• processCtl: private boolean startWorkflow(int AD_Workflow_ID) • Remote-processes: server-connection is retrieved

• else

wfprocess = processUtil.startWorkFlow(Env.getCtx(), m_pi, AD_Workflow_ID); • processUtil: public static MWFprocess startWorkFlow(Properties ctx, processInfo pi,

int AD_Workflow_ID) • A workflow is instantiated

wf=MWorkflow.get(ctx, AD_Workflow_ID) • and started (in batch mode)

wf.start(pi) or else delayed

wf.startWait(pi) • MWorkflow: public MWFprocess start(processInfo pi)

• A MWFprocess-Instance is created from processInfo • The instance calls save() and startWork() .

• MWFprocess: public boolean startWork() • workflow is fetched; AD_WF_Node_ID detected using the workflow

getWorkflow().getAD_WF_Node_ID() • An instance of MWFActivity is created:

new MWFActivity (this, AD_WF_Node_ID) In this constructor a node will be instantiated using AD_WF_Node_ID.

• activity is started as thread:

new Thread(activity).start() Have a look at MWFActivity, to see how it will proceed: It ends in processIt() of

the referring BO.

• MWFprocess: private MWorkflow getWorkflow() • Call of

MWorkflow.get (getCtx(), getAD_Workflow_ID()) • MWorkflow: public static MWorkflow get (Properties ctx, int AD_Workflow_ID)

• a workflow is instantiated

new MWorkflow (ctx, AD_Workflow_ID, null) • MWorkflow: Constructor

public MWorkflow (Properties ctx, int AD_Workflow_ID, String trxName) • see description of the class

• Properties are set

Nodes are loaded:

loadNodes()

Page 40: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 40

Class MWFprocess

package org.compiere.wf public class MWFprocess extends X_AD_WF_process public class X_AD_WF_process extends PO // This class has persistancy functionality too.

Properties

• private StateEngine m_state = null; • private MWFActivity[]m_activities = null; • private Mworkflow m_wf = null; • private processInfo m_pi = null; • private PO m_po = null; • private String m_processMsg

Methods

� checkActivities() The next Activity after the first completed Activity is started: startNext (activity, activities)

� public boolean startWork() Here is validated, whether the workflow can be started with its first node, whose activity is

detected and executed.

� public void setAD_WF_Responsible_ID () The ID of the WF-Responsible is set.

Page 41: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 41

Class Workflowprocessor

package org.compiere.server public class Workflowprocessor extends AdempiereServer Properties

� private Mworkflowprocessor m_model = null; // model � private StringBuffer m_summary = new StringBuffer(); // Last Summary � private Mclient m_client = null; // Client-Info

methods

� Constructor public Workflowprocessor (MWorkflowprocessor model) m_model gesetzt; m_Client is set using the model

� doWork()

called by AdempiereServer

Calls wakeUp(). � wakeUp()

• All interrupted, not processed activities of workflow nodes with inactive action are

instantiated using an SQL-query

• For each of these activities:

activity.setWFState (String WFState) • private int sendAlertToResponsible (MWFResponsible responsible,

ArrayList<Integer> list, MWFprocess process, String subject, String message, File pdf)

• private int sendEmail (MWFActivity activity, String AD_Message, boolean toprocess, boolean toSupervisor)

Page 42: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 42

Class MWFNode

org.compiere.wf public class MWFNode extends X_AD_WF_Node public class X_AD_WF_Node extends PO // This class has also persistancy functionality

Properties

• private ArrayList<MWFNodeNext> m_next // next node

• private MColumn m_column = null; // column description

• private MWFNodePara[] m_paras = null; // process parameter

methods

• public String getActionInfo() called by toString()

• Action is fetched (goes until PO)

• In cases that Action is an Application process, called for information only

return "process:AD_process_ID=" + getAD_process_ID() • (

Possible actions (public static final Strings, defined in X_AD_WF_Node)

• ACTION_UserWorkbench = "B"

• ACTION_UserChoice = "C"; • ACTION_DocumentAction = "D"; • ACTION_SubWorkflow = "F"; • ACTION_EMail = "M"; • ACTION_Appsprocess = "P"; • ACTION_AppsReport = "R"; • ACTION_AppsTask = "T"; • ACTION_SetVariable = "V"; • ACTION_UserWindow = "W"; • ACTION_UserForm = "X"; • ACTION_WaitSleep = "Z";

) These values must match the values of the Combobox for actions in the node of a

workflow.

• public MWFNodePara[] getParameters() node parameter was fetched: m_paras = MWFNodePara.getParameters(getCtx(), getAD_WF_Node_ID()) getAD_Workflow_ID()get the WF-ID using X_AD_WF_Node until PO

• public MWorkflow getWorkflow() Mit MWorkflow.get(getCtx(), getAD_Workflow_ID())

• public boolean isUserApproval() • Is Action=ACTION_UserChoice?:

ACTION_UserChoice.equals(getAction()) • It is query, whether it was accepted:

"IsApproved".equals(getColumn().getColumnName())

Page 43: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 43

Class processCtl

package org.compiere.apps (Client-Projekt)

public class processCtl implements Runnable Manages Report&process. See its description

Properties

• ASyncprocess m_parent; • processInfo m_pi; // Properties and methods for process (transaction-ame, table-ID,

AD_process_ID, AD_Workflow_ID, parameter, Record-ID, etc)

• private Trx m_trx; • private Waiting m_waiting; • private boolean m_IsServerprocess = false;

Methods

• run() 11 columns of AD_process are read using a SQL-query and m_pi is filled:

• column1: processname • column2: Procedurename • column3: Classname • column4: AD_process_ID • column5: isReport • column6: isDirectPrint • column7: AD_ReportView_ID

• column8: AD_Workflow_ID • column9: static case-value • column10: isServerprocess • column11: jasperReport

Depends on the resulting m_pi-values different methods are executed: if e.g.

AD_Workflow_ID>0, a workflow will be started:

startWorkflow (AD_Workflow_ID) , a private method, which starts WF:

processUtil.startWorkFlow(...).

Alternatively it also could be started:

• Java-classes

Starting with the method run() in processCtl, which calls startprocess(), the methods

prepare() and doIt() of the class ImportInventory are called, which implement the

behavior.

In the report description of AD, this relation is explained more accurately

• Jasper Reports

startprocess() • normal Reports

ReportCtl.start() • Oracle-DB-Procedures

startDBprocess()

Page 44: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 44

• Constructor

public static processCtl process(ASyncprocess parent, int WindowNo, IprocessParameter parameter, processInfo pi, Trx trx)

• An instance of MPInstance is created:

MPInstance instance (mithilfe von pi) • Parameters are read ( save() ) • With synchronized processes the process is executed immediately run()

Class processUtil

org.adempiere.util public final class processUtil No important properties (just the Logger)

Just 3 methods

• public static boolean startDatabaseProcedure(processInfo processInfo, String ProcedureName, Trx trx) Procedure is executed

• public static boolean startJavaprocess(processInfo pi, Trx trx) the class name is read from pi, then the class is obtained. Finally the class will be instantiated as

process.

• public static MWFprocess startWorkFlow(Properties ctx, processInfo pi, int AD_Workflow_ID) • a workflow is instantiated:

wf = MWorkflow.get (ctx, AD_Workflow_ID) • WF is started: wf.start(pi)

Page 45: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 45

Invoice-Preview It is explained here, because it has relation to processCtl.

Explanation: Print formats can be allocated to Business Partners, Document Types and ad_printform.

Values in Table of a invoice previews

AD_TABLE_ID=318 ( C_INVOICE)

AD_process_ID: 116 (Rpt C_Invoice)

AD_PRINTFORMAT_ID: 1000071 (Standard Invoice Header). Its column ad_table_id references to a

table n C_Invoice_Header_v, which represents a invoice print view

Calling sequence to show the Invoice-Preview

APanel.actionPerformed() APanel.cmd_print() processCtl.process() : AD_TABLE_ID=318 ( C_INVOICE), AD_process_ID: 116 processCtl.start() Thread.start() : : The process is executed:

processCtl.run: ReportCtl.start() ReportCtl.startDocumentPrint() ReportEngine.get() (Class method) ReportEngine-Constructor ReportEngine.getQuery() ReportEngine.setPrintData() DataEngine.getPrintData() DataEngine.getPrintDataInfo()

back in getPrintData() loadPrintData() is called, where the SQL-query for data acquisition is executed

backin ReportEngine.get() ReportEngine of the calling method startDocumentPrint() is returned

backin startDocumentPrint(): ReportCtl.CreateOutput() The report is shown, considering whether direct printing or preview is selected.

In the latter case the Code der Viewer takes over:

ReportCtl.preview() ReportViewerProvider provider = getReportViewerProvider(); provider.openViewer(re); SwingViewerProvider. openViewer() -- SwingViewerProvider inherited from ReportViewerProvider Viewer-Constructor() ReportEngine.getView()

Page 46: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 46

ReportEngine.layout() LayoutEngine-Constructor() LayoutEngine.layout() Different handling, depending on whether PrintForm is form or table

LayoutEngine.layoutForm() read configuration of Print Format. Position, max. Width, max. Hight, etc. of each Element is identified. The local variable element contains Print Format. In viewer, the button Print triggers:

Viewer.actionPerformed () Viewer.cmd_print() ReportEngine.print() -- the print is started

Page 47: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 47

Now the same in more detail ( partly from an existing example):

THE BUTTON PRINT PREVIEW IN INVOICE WAS PRESSED HERE

Apanel.actionPerformed(): PrintPreview - 16

Apanel.cmd_print()

ID=116 [12]

AD_TABLE_ID=318 ( C_INVOICE)

AD_process_ID= 116 (Rpt C_Invoice)

processInfo pi:

pi.m_Title=Invoice (Customer) SuperUser@Company Name

pi.m_AD_process_ID=116

pi.m_Record_ID=1004868

processCtl.process()

WindowNo=2 - processInfo[Invoice (Customer) SuperUser@company_name [linux-

jupiter{linux-jupiter-orcl-adempiere}], process_ID=116, Record_ID=1004772,

Error=false,Summary=,Log=0] [12]

(Record_id is id of the invoice) (process_ID=116 ist Rpt C_Invoice)

ReportCtl.start: start()

processInfo[Invoice Print ,process_ID=116,AD_PInstance_ID=1004057,Record_ID=1004772,

Error=false, Summary=, Log=0] [42]

unsynchronized process

AD_PInstance_ID changes always

in AD_PInstance the process ID (here 116) and Record ID (here 1004868) are saved, so the

value is not lost on reload.

Thread is executed

processCtl.run()

with a SQL query on AD_PInstance the configuration of AD_process is read in:

Name, ProcedureName,ClassName, AD_process_ID, isReport (=Y), isDirectPint (=Y),

ReportView-ID (=keine), Workflow-ID (=keine), isServerprocess(=N) and JasperReport-Name

(none).

These values are assigned to the classvariable m_pi. Client- and User-ID do already have the correct values

After that, it checks what is about to be executed (Workflow, Report, process).

As print invoice is a report, the next methode is called.

ReportCtl.start()

Hard-coded dependancy: depending on the process_id, a method will be called.

For standard reports the the call is: startStandardReport(pi). A Report-Engine instance is c

reated and CreateOutput(re, pi.isPrintPreview()) is called. ReportEngine-Instance is

Page 48: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 48

forwarded. startStandardReport(pi) calls immediately ReportEngine.get().

For invoice (process_ID()= 116) is called:

ReportCtl.startDocumentPrint()

calls immediately ReportEngine.get() .

In ReportEngine.get() a SQL query is executed depending on the type (Check, Dunning,

Remmitance, Project, RfQ, Order/Invoice/Shipment). Here the SQL query for an invoice is executed, because the type invoice (=2) was passed

through since ReportCtl.start(). Side explanation Print Format begin

There are 3 places in Adempiere where a Print Format can be defined:

1.- At Business Partner 2.- In register "Client" a Print Format can be set

3.-Window Document Type:

Table c_doctype has besides the Doc Base Type a Print Format.

Window Print Form (default):

AD_PRINTFORM is a table, which has predefined Print Formats (for Invoice, Order, Shipment) as well as

mailtexts (for Invoice, Order, Shipment, Projects...) for each Client and Organization.

As an example, in AD_PRINTFORM the invoice print format id=1000071 is declared for a customer

“MyCustomer”.

This ID is in table AD_PRINT_FORMAT "Invoice_Header", its column ad_table_id refers to table called

C_Invoice_Header_v, which represents an Invoice Print View .

If neither business partner nor document type is declared, it will default to ad_printform.

Side explanation Print Format end

Is the printing of an order required and there is already an invoice, then this invoice is printed

(else the order is printed). ReportEngine.getDocumentWhat() is called for this. It changes the

type and Record-ID to Invoice. The rest is done in ReportEngine.get().

Neededdocument information for printing is fetched with a SQL query.

TheSQL query contains a Join of tables c_invoice, ad_printform, ad_client, c_doctype and

c_bpartner, where Print Format of Order, Shipment, Projekt, Remmitance and Invoice is

identified.

In the case of an Invoice: Priority # 1: BPartner; Priority # 2: DocType: Priority #3:

PrintFormat (ad_printform).

Additionally the number of copies (either from Business Partner or Document Type),

Document-No. of Invoice and BP-ID are fetched:

SELECT pf.Order_PrintFormat_ID,pf.Shipment_PrintFormat_ID, COALESCE (bp.Invoice_PrintFormat_ID, dt.AD_PrintFormat_ID, pf.Invoice_PrintFormat_ID), pf.Project_PrintFormat_ID, pf.Remittance_PrintFormat_ID, c.IsMultiLingualDocument, bp.AD_Language, COALESCE(dt.DocumentCopies,0)+COALESCE(bp.DocumentCopies,1),

Page 49: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 49

dt.AD_PrintFormat_ID,bp.C_BPartner_ID,d.DocumentNo FROM C_Invoice d INNER JOIN AD_Client c ON (d.AD_Client_ID=c.AD_Client_ID) INNER JOIN AD_PrintForm pf ON (c.AD_Client_ID=pf.AD_Client_ID) INNER JOIN C_BPartner bp ON (d.C_BPartner_ID=bp.C_BPartner_ID) LEFT OUTER JOIN C_DocType dt ON (d.C_DocType_ID=dt.C_DocType_ID) WHERE d.C_Invoice_ID=? AND pf.AD_Org_ID IN (0,d.AD_Org_ID) ORDER BY pf.AD_Org_ID DESC

Result (for example) is a line with following values:

� ORDER_PRINTFORMAT_ID 1000069

� SHIPMENT_PRINTFORMAT_ID 1000073

� PRINTFORMAT_ID 1000100

� PROJECT_PRINTFORMAT_ID

� REMITTANCE_PRINTFORMAT_ID 1000077

� ISMULTILINGUALDOCUMENT Y

� AD_LANGUAGE

� DOCUMENTCOPIES 2

� AD_PRINTFORMAT_ID

� C_BPARTNER_ID 1000317

� DOCUMENTNO VCF_633487_2007

Ad_printformat_id, c_bpartner_id, beleg-Nr and number of copies (if empty, it is set to 1) are

extracted using the SQL and saved in variables. This is info about the print, not printable data

(yet).

An instance of each MPrintFormat, MQuery and PrintInfo is created.

The local variable query assigned to the constructor. I can e.g. be

“C_Invoice_Header_v.C_Invoice_ID=1004868" and is directly created using the invoice type

(Result: C_Invoice_Header_v at Invoice) and the document type + -ID (Result:

“C_Invoice_ID=1004868”).

Side explanation for DOC_TABLES begin

The local variable query uses an indexing of DOC_TABLES for finding the table :

DOC_TABLES[type].

By defining the variables TABLES the table name can be found:

DOC_TABLES = new String[] {"C_Order_Header_v", "M_InOut_Header_v", "C_Invoice_Header_v", "C_Project_Header_v","C_RfQResponse_v","C_PaySelection_Check_v", "C_PaySelection_Check_v", "C_DunningRunEntry_v" }; So, if type==2 (Invoice), the resulting table becomes C_Invoice_Header_v.

Side explanation for DOC_TABLES end

At the end, a ReportEngine is instantiated in ReportEngine.get(), where class variables are

assigned to the extracted instances of the classes MPrintFormat, MQuery and PrintInfo .

The instantiated ReportEngine is returned to the calling method.

What will happens when the constructor of ReportEngine is called? :

Page 50: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 50

Example:

ReportEngine.<init>(Constructor):

MPrintFormat[ID=1000071,Name=Invoice_Header,Language=Language=[Español (El

Salvador), Locale=es_SV, AD_Language=es_SV, DatePattern=DD/MM/YYYY,

DecimalPoint=true],Ite ms=56] -- C_Invoice_Header_v.C_Invoice_ID=1004772 [42]

ReportEngine.getQuery()

ReportEngine.setPrintData().

Instantiates a DataEngine and calls getPrintData().

DataEngine.getPrintData()

Firstly, the name of the table is identified, because we are not in a report view

(SELECT TableName FROM AD_Table WHERE AD_Table_ID=516;): in AD it is the table C_Invoice_Header_v.

The table name is changed to C_Invoice_Header_vt here. Beware: there is no table

C_Invoice_Header_vt in AD; just C_Invoice_Header_v !! Query is changed to: C_Invoice_Header_vt.C_Invoice_ID=1004868 getPrintDataInfo() is called (a huge method).

Next the data is fetched with loadPrintData() using another huge method.

First, let's concentrate on getPrintDataInfo().

DataEngine.getPrintDataInfo()

In this method a SQL query for data extraction is created in variable finalSQL.

The columns for the needed SQL are taken among others from PrintFormat. getPrintDataInfo() is called with parameters like:

Name of report: Invoice_Header TableName=C_Invoice_Header_vt Query=C_Invoice_Header_vt.C_Invoice_ID=1004772 AND C_Invoice_Header_vt.AD_Language='es_SV' Contains the WHERE-condition of the SQL that shall be built in this method.

Format=MPrintFormat[ID=1000071, Name=Invoice_Header, Language=Language=[Español (El Salvador), Locale=es_SV, AD_Language=es_SV, DatePattern=DD/MM/YYYY, DecimalPoint=true], Items=56]

Column order AD_Column_ID=7483

A SQL is executed with ad_printformat_id as parameter (extracted from variable

Format). Columns of Print Format, PrintFormatItem, AD_Column etc. are extracted so a SQL

like this is built:

String sql = "SELECT c.AD_Column_ID,c.ColumnName," // 1..2 + "c.AD_Reference_ID,c.AD_Reference_Value_ID," // 3..4 + "c.FieldLength,c.IsMandatory,c.IsKey,c.IsParent," // 5..8 + "COALESCE(rvc.IsGroupFunction,'N'),rvc.FunctionColumn," // 9..10 + "pfi.IsGroupBy,pfi.IsSummarized,pfi.IsAveraged,pfi.IsCounted, " // 11..14 + "pfi.IsPrinted,pfi.SortNo,pfi.IsPageBreak, " // 15..17

Page 51: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 51

+ "pfi.IsMinCalc,pfi.IsMaxCalc, " // 18..19 + "pfi.isRunningTotal,pfi.RunningTotalLines, " // 20..21 + "pfi.IsVarianceCalc, pfi.IsDeviationCalc, " // 22..23 + "c.ColumnSQL " // 24 + "FROM AD_PrintFormat pf" + " INNER JOIN AD_PrintFormatItem pfi ON (pf.AD_PrintFormat_ID=pfi.AD_PrintFormat_ID)" + " INNER JOIN AD_Column c ON (pfi.AD_Column_ID=c.AD_Column_ID)" + " LEFT OUTER JOIN AD_ReportView_Col rvc ON (pf.AD_ReportView_ID=rvc.AD_ReportView_ID AND c.AD_Column_ID=rvc.AD_Column_ID) " + "WHERE pf.AD_PrintFormat_ID=?" // #1 + " AND pfi.IsActive='Y' AND (pfi.IsPrinted='Y' OR c.IsKey='Y' OR pfi.SortNo > 0) " + "ORDER BY pfi.IsPrinted DESC, pfi.SeqNo";

The result of the SQL execution are the columns as defined in AD and looks like this:

AD_COLUMN_ID COLUMNNAME AD_REFERENCE_ID (Im Code Display Type)

7652 BPValue 10 (Text)

7466 C_Order_ID 30 (Search)

7463 DateInvoiced 15 (Date)

7475 Name 10 (Text)

7448 C_Location_ID 21 (Location (Address) )

7586 PaymentTerm 10 (Text)

7460 M_PriceList_ID 19 (Table Direct)

7483 DocumentNo 10 (Text)

and other columns: AD_REFERENCE_VALUE_ID, FIELDLENGTH, ISMANDATORY, ISKEY, ISPARENT, COALESCE(RVC.ISGROUPFUNCTION,'N'), FUNCTIONCOLUMN, ISGROUPBY, ISSUMMARIZED, ISAVERAGED, ISCOUNTED, ISPRINTED, SORTNO, ISPAGEBREAK, ISMINCALC, ISMAXCALC, ISRUNNINGTOTAL, RUNNINGTOTALLINES, ISVARIANCECALC, ISDEVIATIONCALC, COLUMNSQL

Sort columns in Print Format and all columns, that are marked as key in the SQL, are

included in addition to those selected in the PrintFormat (but marked as “not printed”).

Important: the configuration of the columns is related to their definition at

C_Invoice_Header_v, not by its original table!!!

This means a column C_Order_id is not a key in C_Invoice_Header_v, but in the table

C_Order.

On the one hand these lines are analyzed in a loop and saved line by line in a PrintDataColumn instance (AD_Column_ID, ColumnName, AD_Reference_ID, FieldLength, orderName, isPageBreak).

On the other hand this information is used to generate SQL for the extraction of the printing

data.

This SQL, which is created in getPrintDataInfo() and saved in variable finalSQL looks e.g. like

this: SELECT

(SELECT C_Order.DocumentNo||' - '||TRIM( TO_CHAR( C_Order.DateOrdered,

Page 52: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 52

'DD/MM/YYYY'))

FROM C_Order

WHERE C_Invoice_Header_vt.C_Order_ID=C_Order.C_Order_ID) AS AC_Order_ID,

// This was the first column, called AC_Order_ID

C_Invoice_Header_vt.C_Order_ID,

// The last columns are just displayed instead of c_order_id; see explanation beneath

C_Invoice_Header_vt.DateInvoiced,

C_Invoice_Header_vt.Name,

B.City||'.' AS Baddress, // see explanation

C_Invoice_Header_vt.C_Location_ID, // see explanation

C_Invoice_Header_vt.PaymentTerm,

// for invoice, see explanation

(SELECT M_PriceList.Name FROM M_PriceList

WHERE C_Invoice_Header_vt.M_PriceList_ID=M_PriceList.M_PriceList_ID)

AS CM_PriceList_ID,

// This is another column, called CM_PriceList_ID

C_Invoice_Header_vt.M_PriceList_ID

FROM C_Invoice_Header_vt

LEFT OUTER JOIN C_Location B ON

(C_Invoice_Header_vt.C_Location_ID=B.C_Location_ID)

WHERE C_Invoice_Header_vt.C_Invoice_ID=1006390

AND C_Invoice_Header_vt.AD_Language='es_SV'

AND C_Invoice_Header_vt.AD_Client_ID IN (0,1000001)

AND B.C_Location_ID NOT IN

( SELECT Record_ID FROM AD_Private_Access WHERE AD_Table_ID = 162 AND

AD_User_ID <> 100 AND IsActive = 'Y' )

ORDER BY C_Invoice_Header_vt.DocumentNo

A PrintData instance with column description, finalSQL and table (C_Invoice_Header_vt) is

created and returned.

Side explanation begin

Which field will be shown if there are IDs in Print Format?

A) As example: c_order_id in invoice print.

Each column is described in the AD among other things with the field isIdentifier. All columns having this

field=”Y” are displayed when the ID field is present.

This way, isIdentifier is used as an information source for the whole object, because just the ID itself is not very

useful.

In getPrintDataInfo() it is queried during the column analysis, whether the column is a search field

(ad_reference_id=30). If true, the following will be called: MlookupFactory.getLookup_TableDirEmbed(), where a SQL query is called:

SELECT c.ColumnName,c.IsTranslated,c.AD_Reference_ID,c.AD_Reference_Value_ID FROM AD_Table t INNER JOIN AD_Column c ON (t.AD_Table_ID=c.AD_Table_ID) AND c.IsIdentifier='Y' WHERE TableName='C_Invoice' // or the corresponding table ORDER BY c.SeqNo;

A column like AC_Order_ID is built with this. In c_order, the columns DocumentNo and DateOrdered are

marked as identifier ( isIdentifier =”Y”), which yields this output:

SELECT C_Order.DocumentNo||' - '||TRIM( TO_CHAR( C_Order.DateOrdered, 'DD/MM/YYYY')) FROM C_Order

Page 53: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 53

WHERE C_Invoice_Header_vt.C_Order_ID=C_Order.C_Order_ID.

Next, the “normal” SQL code is created:

C_Invoice_Header_vt.C_Order_ID.

B)At C_Invoice the columns are: DocumentNo, DateInvoiced and GrandTotal. The SQL is then:

SELECT C_Invoice.DocumentNo||' - '||TRIM(TO_CHAR(C_Invoice.DateInvoiced,'DD/MM/YYYY'))||' - '||TRIM(TO_CHAR(C_Invoice.GrandTotal,'999G999G999G990D00')) FROM C_Invoice WHERE C_Invoice_Header_vt.C_Invoice_ID=C_Invoice.C_Invoice_ID Next the “normal” SQL code is created: C_Invoice_Header_vt.C_Invoice_ID.

C) If the reference type of the column = Location, then the SQL in the programm is changed in a way that

B.City||'.' AS Baddress is returned. Next the “normal” SQL-Code is created:

C_Invoice_Header_vt.C_Location_ID. Something similar will happen if the reference type the column =

Account, Locator oder PAttribute.

Side explanation end.

End of DataEngine.getPrintDataInfo().

back in DataEngine.getPrintData(), loadPrintData() is called immediately, where the SQL is executed.

DataEngine.loadPrintData() The result of the SQL is a single or multiple lines. Example with a single line:

� BPVALUE 0191 (Key)

� AC_ORDER_ID 701517

� C_ORDER_ID 1004302

� DATEINVOICED 22-AUG-07

� NAME NAME OF A COMPANY

� NAME2 FARMACIA DEL PUEBLO

� DUNS 126615-2

� BADDRESS SANTA ROSA DE LIMA.

� C_LOCATION_ID 1000300

� TAXID

� DESCRIPTION COMPRA/VENTA DE MEDICINAS

� SALESREP_NAME TORRES CARBALLO, RIGOBERTO

� PAYMENTTERMNOTE 30 DIAS

� CC_INVOICE_ID VCF_633487_2007 - 22/08/2007 - 302.98

� C_INVOICE_ID 1005618

� TOTALLINES 268.12

In loadPrintData() all lines are read in a big loop with all lines and there the columns are

analyzed singly within a small loop .

The variable rowNo counts the rows, counter the columns (the last references to the next

column).

Small loop:

Page 54: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 54

� The variable pdc (PrintDataColumn) contains infos about the columns that needs to be

edited (using indexing of the input parameter pd).

� m_ad_Column_id is the definition of the column in ad_column, within the table C_Invoice_Header_v.

� m_alias is the name of the column in SQL, e.g. AC_Order_ID

Alias is only used if the column is concatenated, caused by the combination ID-

identifier.

Using aliases the next column is fetched immediately, because it follows suit in

the SQL (see SQL of getPrintDataInfo()) .

� m_column_name: is the column that can be found in Print Format, e.g.

C_Order_ID.

� the actual column depends amongst other things whether it is a ID and also

which fields are called as identifier.

� Boolean values, long texts, DateTime-values, strings and numbers) are managed

� At the end of the small loop an object pde (PrintDataElement) is created, which contains

the name of the column, content, whether it contains a key or page break. The object pde

is added to pd and added to the variable m_group.

� m_column_name: is the column, that is set in Print format, e.g.: C_Order_ID

� m_display_type: = AD_REFERENCE_ID (see previously in text)

� m_value

Contains the pair in concatenated columns (otherwise just m_value)

� m_key e.g. 1000300

The ID of the related table. For c_invoice_id it is c_invoice, at

c_location_id it is table c_location, etc.

� m_value, e.g. “SANTA ROSA DE LIMA..” In loadPrintData() the lines are counted programmatic (starting with the line, with the comment:

// Add Total Lines

The calculation of functions like Count, Mean, Sum, Minimum, Maximum, Derivation etc. are

started at “// Check last Group Change “ in loadPrintData() with the call m_group.getValue() PrintDataGroup.getValue (): PrintDataFunction.getValue(char function).

Back in ReportEngine.get() the ReportEngine of the calling method startDocumentPrint() is returned.

startDocumentPrint(): CallCreateOutput() : The report is displayed depending on whether it is direct

printing or preview.

In the latter case the viewer is shown with this code:

ReportViewerProvider provider = getReportViewerProvider(); provider.openViewer(re) The layout is created as consequence of provider.openViewer(re) using re.getView(). The variable re is

of the class ReportEngine and contains the SQL for data extraction.

Multiple methods are processed, amongst others LayoutEngine.layout(), LayoutEngine.layoutForm().

Side explanation begin

LayoutEngine

Page 55: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 55

The class LayoutEngine has 3 methods that are important for printing

� layout() In the constructor of LayoutEngine the parameter Data that is used amongst other things for

data extraction is submitted and the m_data is saved. m_data contains m_sql, which is used

for data extraction

The SQL for the aging report is:

SELECT T_Aging.DueAmt, A.Value||'-'||A.Name AS Aname, T_Aging.SalesRep_ID, (SELECT C_SalesRegion.Name FROM C_SalesRegion WHERE T_Aging.C_SalesRegion_ID=C_SalesRegion.C_SalesRegion_ID) AS BC_SalesRegion_ID, T_Aging.C_SalesRegion_ID, T_Aging.BPValue, T_Aging.SalesRep_Name, T_Aging.SalesRegionName, T_Aging.DateInvoiced, (SELECT C_BPartner_Location.Name FROM C_BPartner_Location WHERE T_Aging.C_BPartner_Location_ID=C_BPartner_Location.C_BPartner_Location_ID) AS CC_BPartner_Location_ID, T_Aging.C_BPartner_Location_ID, T_Aging.LocationName, (SELECT C_BPartner.Value||' - '||C_BPartner.Name FROM C_BPartner WHERE T_Aging.C_BPartner_ID=C_BPartner.C_BPartner_ID) AS DC_BPartner_ID, T_Aging.C_BPartner_ID, SELECT C_BP_Group.Name FROM C_BP_Group WHERE T_Aging.C_BP_Group_ID=C_BP_Group.C_BP_Group_ID) AS EC_BP_Group_ID, T_Aging.C_BP_Group_ID FROM T_Aging LEFT OUTER JOIN AD_User A ON (T_Aging.SalesRep_ID=A.AD_User_ID) WHERE T_Aging.AD_PInstance_ID=1017227 AND T_Aging.C_BPartner_ID=1000598.0 AND T_Aging.IsListInvoices='N' AND T_Aging.IsSOTrx='Y' AND T_Aging.AD_Client_ID IN (0,1000001) AND A.AD_User_ID NOT IN ( SELECT Record_ID FROM AD_Private_Access WHERE AD_Table_ID = 114 AND AD_User_ID <> 100 AND IsActive = 'Y' ) Also if the condition is C_BPartner_ID=1000598.0 the correct value is returned.

� layoutForm()

� layoutTable()

Side explanation end

The data of an embedded print format, like e.g. Invoice Line Tax, are extracted like:

Page 56: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 56

LayoutEngine.layout() LayoutEngine.layoutForm(),

LayoutEngine.includeFormat() DataEngine.getPrintData() is called (see above)

Variable PrintData includedData contains data for printing.

Call of LayoutEngine.layoutTable() -- e.g. using tables

The data are analysed here.

If it is a field, the method LayoutEngine.createFieldElement()is called in

LayoutEngine.layoutForm().

LayoutEngine.createFieldElement() Here the output is done

� Conversion into string

� Display of ID elements

� Numbers are called as words at output:

Msg.getAmtInWords (m_format.getLanguage(), stringContent) This depends on the selected language. For Spanish the method

AmtInWords_ES.getAmtInWords() is called.

� Color

Page 57: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 57

PrintFormat

Table AD_PRINT_FORMAT

� contains all entries to fill the equal-named window

Page 58: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 58

class PrintData

Container for actual data printing. Here are explained only class variables, but not methods.

class variables

� PrintDataColumn[] m_column_info

An array of elements, which describe the columns that are printed

PrintDataColumn is also a container with only get methods, a constuctor and the

following class variables:

� private int m_AD_Column_ID;

� private String m_columnName;

� private int m_displayType;

� private int m_columnSize;

� private String m_alias;

� private boolean m_pageBreak;

� Properties m_ctx -- Context

� String m_name: -- Name of the print format

� ArrayList<Object> m_nodes -- Array of PrintDataElement Arrays

Extra lines as sum.

PrintDataElement Klassenvariablen:

� private String m_columnName;

� private int m_displayType;

� private boolean m_isPageBreak;

� private boolean m_isPKey;

� m_value;

� ArrayList<ArrayList<Object>> mrows -- Array of PrintDataElement arrays

Contains the real data as rows and columns.

� m_sql

Data is extracted with this e.g.

SELECT C_Invoice_LineTax_vt.ProductValue, C_Invoice_LineTax_vt.QtyEntered, C_Invoice_LineTax_vt.Name, C_Invoice_LineTax_vt.Discount, C_Invoice_LineTax_vt.PriceEntered, C_Invoice_LineTax_vt.LineNetAmt, C_Invoice_LineTax_vt.C_InvoiceLine_ID – was automaticly fetched FROM C_Invoice_LineTax_vt WHERE C_Invoice_LineTax_vt.C_Invoice_ID=1005618 AND C_Invoice_LineTax_vt.AD_Language='es_SV' AND C_Invoice_LineTax_vt.AD_Client_ID IN (0,1000001)

Page 59: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 59

ORDER BY C_Invoice_LineTax_vt.Line

� m_TableName, e.g. C_Invoice_LineTax_vt

Page 60: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 60

Workflow (WF) im AD

WF-Types are found this way (by the way: How are combo boxes designed((Author) )

� Login as System Admin � Menu, Window Workflow � Zoom at field Window � In Window, Tab & Field with name Workflow, Tab Workflow, Field Workflow Type zoom to

column WorkflowType.

� Table&Column with name AD_Workflow to Tab Column, zoom at Field Reference Key (is

AD_Workflow Type)

� Select Reference with namen AD_Workflow Type, Tab List Validation in window.

values General, Document process and Document Value can be found there.

There are three WF-Types in Adempiere

� General (common process) G

WFs, that can be seen and used as normal user

� Accounting Setup

� BP Setup

� Price List Setup

� Product Setup

� Request Setup

� Requisition Setup

� Sales Setup

� Tax Setup

� etc.

� Document process P

� process_Cash

� process_Inventory

� process_Invoice

� process_Journal

� process_Journal Batch

� process_Movement

� process_Order

� process_Payment

� process_Requisition

� etc.

� Document Value V

No entries (maybe in Garden World?)

Page 61: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 61

Workflow-Window

� Tab Workflow � Workflow Type (see above)

� Data Access Level (All, Organisation, Client, etc)

� Start Node

� Workflow processor (it is empty in all entries; there is just only one: System Workflow

processor)

� Tab Node � Combo Box WF Responsible ( Invoker, Organisation)

� Combo Box Start Mode (Automatic, Manual) � Combo Box Join Element (XOR, AND)

� Combo Box Split Element (XOR, AND)

� Combo Box Action (Apps process, Apps Report, Apps Task, Document Action, Email, Set Variable, Sub Workflow, User Choice, User Form, User Window, Wait (Sleep). They seem equal to the possibilities in MWFActivity: performWork(). Task, Sub Workflow, User Workbench, User Form and User Window are offered, but not

implemented.

Fields are offered depending on the selected Action : in User Window it is a combo box

window with all possible windows; whereas in Document Action, it is a combo box

with entries from the reference _Document Action (Approve, Close, Complete,

Invalidate, Post, Prepare, Void, Unlock etc. They are equal to DocAction-methods in

source code of BOs).

� Tab Transition � Next Node: next node to execute

� There are multiple node possible to be the next.

One of them is Standard.

Operation of the node defines when which node is executable.

� Tab Condition rarely used

� And/Or

� column

� Operation (+, -, etc.)

� Value

Conclusion:

In Tab Workflow is defined which node is started.

The Node defines for actions, which action is executed.

The option are close, prepare, etc. for an action of type document action.

This results in a call of the equal named BO method und a change of state.

Transition defines which node is next.

ERM

� Static

Is all what is defined in the AD

� An AD_WORKFLOW can have multiple AD_WF_NODEs.

Page 62: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 62

� Dynamic

processes are created during the execution

� an AD_WF_ACTIVITYs can have a AD_WF_NODE � an AD_WORKFLOW can have multiple AD_WF_processs

� an AD_WF_process can have multiple AD_WF_ACTIVITYs

Actions in Tab Node:

� Reference: WF_Action

� List Validation Search Key

� Apps process P

� Apps Report R

� Apps Task T

� Document Action D

� Email M

� Set Variable V

� Sub Workflow F

� User Choice C

� User Form X

� User Window W

� User Workbench B (inactive)

� Wait (Sleep) Z

Workflow documentation for BOs:

The history of the workflow, in which this BO is involved, can be inspected using the icon Active

Workflows (two squares connected with a arrow) in any of the BO lines, which implement DocAction.

So it can be seen where the workflow is.

Loading:

� APanel: actionPerformed (ActionEvent e) is the button dispatcher.

� For the workflow button AEnv: public static void startWorkflowprocess (int AD_Table_ID, int Record_ID) is called.

� A dataset of AD_WF_process is read in here (for the current Tabelle and its current dataset).

Saving:

Object X_AD_WF_process, father class of MWFprocess is a X_AD_WF_process. But X_AD_WF_process is subclass of PO too, so that intances of it are saved.

Somewhere in the constructor it is saved and refreshed.

Page 63: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 63

Example of a workflow: Order

Here it is the other way around: starting with the window and end with the source code.

� Sales Order of menu

� Zoom Window Sales Order, Tab Order, Feld Table (Content: C_Order). � Table C_Order, Tab Column, column name processing

� Field Reference is a button; Field process references to C_Order process.

Zoom into this:

� feld Workflow has the content Process_Order (one of the defined general workflows)

Window Workflow Editor

� Reads nodes of a workflow, transition to the next node, join constraints and split constrains

and presents it in a graphical visualisation.

� XOR as a join-condition means that the first possible action is selected.

Page 64: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 64

Window Report & Process

The dispatcher ist processCtl.run().

Characteristics of reports/processes:

• Document Action

Already explained in processCtl • Java-class

The class of a report definition in AD can be found in the field Class Name, which is executed

(in this example org.compiere.process.ImportInventory).

Its methods prepare() and doit() control the behavior of the buttons.

How are prepare() and doit() of the class ImportInventory called?

• processCtl manages process calls as explained.

• After detecting that it is a java call, run() in processCtl calls method startprocess() • processCtl: private boolean startprocess ()

Calls (for local processes) startJavaprocess(m_pi, m_trx) • processUtil: public static boolean startJavaprocess(processInfo pi, Trx trx)

calls a processCall-method: startprocess(Env.getCtx(), pi, trx) • interface processCall declares this method, that some class must implement:

public boolean startprocess (Properties ctx, processInfo pi, Trx trx) This is done by Svrprocess.

• Because java-class ImportInventory is defined as following:

public class ImportInventory extends Svrprocess • and Svrprocess this way:

public abstract class Svrprocess implements processCall Especially Svrprocess implements method startprocess() of processCall.

• So Svrprocess implements startprocess(). The following happens inside: private method process() is called, where immediately

and after each other prepare() and doIt() is called.

Both are definined as abstract in Svrprocess, so both must be defined in the subclass.

This happens in ImportInventory, and so these methods are executed by

ImportInventory.

• protected void prepare() Reads the calling parameter from process info variable m_pi and assigns Properties with

it.

• protected String doIt()

The process is implemented.

Returns a String, which is used for among other things fault handling and messaging.

This way AD and programm code interact during java class-calls .

For more explaination, see description of the class processCtl.

Page 65: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 65

• Oracle-Procedure

• Workflow

• Report

• Jasper Report

Call chain for parameter displaying in Report&Process calls

Here is just explained how parameters in AD are displayed according to its definition.

How Report&Process works is explained in the previous section.

� AMenuStartItem.startprocess() org.compiere.apps

� processDialog.init() org.compiere.apps

Fetches the process info

� processParameterPanel.init() org.compiere.apps

Fertches all call parameter of the process SELECT p.Name, p.Description, p.Help, p.AD_Reference_ID, p.AD_process_Para_ID, p.FieldLength,

p.IsMandatory, p.IsRange, p.ColumnName, p.DefaultValue, p.DefaultValue2, p.VFormat, p.ValueMin, p.ValueMax, p.SeqNo, p.AD_Reference_Value_ID, vr.Code AS ValidationCode FROM AD_process_Para p LEFT OUTER JOIN AD_Val_Rule vr ON (p.AD_Val_Rule_ID=vr.AD_Val_Rule_ID) WHERE p.AD_process_ID=? AND p.IsActive='Y' // z.B.238 ORDER BY SeqNo

createField() is called for each parameter.

� processParameterPanel.createField() org.compiere.apps

GridFieldVO.createParameter() is called first. Next, the constructor new GridField (voF) is called, where loadLookup() is called.

Variable vot.AD_Column_ID references to ID of the Parameter in the table AD_process_Para

and not ID in AD_Column!

� GridFieldVO.createParameter() org.compiere.model A constructor for GridFieldVO is called.

Data like ColumnName, Name, Description, AD_Reference_ID, FieldLength, etc. are read from

the parameter. Class variable lookupInfo contains possible lookup information.

� GridFieldVO.initFinish() org.compiere.model When field Reference of report&process-parameters equals List, Table, TableDirect or Search:

Lookup-handling.

Field Reference is extracted from table AD_Reference, where e.g. “List“ and „Search“ have IDs 17 and 30.

Calling getLookupInfo(). The result is saved in MLookupInfo lookupInfo (just a variable, no

data), a class variable of GridFieldVO.

� MLookupFactory.getLookupInfo() org.compiere.model Different handling, depending on List, Table, TableDirect or Search.

Lists: getLookup_List(). Table or Search and field Reference Value with valid value: getLookup_Table(). The field Reference Value is extracted from table AD_Reference, where e.g. “AD_User –

Page 66: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 66

SalesRep“ has ID 190.

Otherwise (including TableDirect), the call is getLookup_TableDir(). The SQL is completed furthermore after the lookup call (e.g. to add Client_ID condition and

the role condition). An object with the information of this parameter is returned to initFinish().

� MLookupFactory.getLookup_Table() org.compiere.model

A SQL query is sent. SELECT t.TableName,ck.ColumnName AS KeyColumn,cd.ColumnName AS

DisplayColumn,rt.IsValueDisplayed,

cd.IsTranslated,rt.WhereClause,rt.OrderByClause,t.AD_Window_ID,t.PO_Window_ID,

t.AD_Table_ID

FROM AD_Ref_Table rt

INNER JOIN AD_Table t ON (rt.AD_Table_ID=t.AD_Table_ID)

INNER JOIN AD_Column ck ON (rt.AD_Key=ck.AD_Column_ID)

INNER JOIN AD_Column cd ON (rt.AD_Display=cd.AD_Column_ID)

WHERE rt.AD_Reference_ID=190 -- "AD_User - SalesRep "

AND rt.IsActive='Y'

AND t.IsActive='Y'

result is a line with e.g. the following content of the AD-column SalesRep,

which is saved in variables:

� TABLENAME AD_User

� KEYCOLUMN AD_User_ID

� DISPLAYCOLUMN Name

� ISVALUEDISPLAYED Y

� ISTRANSLATED N

� WHERECLAUSE EXISTS (SELECT * FROM C_BPartner bp

WHERE

AD_User.C_BPartner_ID=bp.C_BPartner_ID AND

bp.IsSalesRep='Y')

This where-Clause will not work correctly, if it is executed in SQL Developer.

Within the SQL query, which is build later it causes like the following: SELECT * FROM C_BPartner bp inner join AD_User adu on (adu.C_BPartner_ID=bp.C_BPartner_ID) WHERE bp.IsSalesRep='Y'

In this example all users are shown, which are marked as SalesRep in the

C_BPartner-table .

� ORDERBYCLAUSE --

� AD_WINDOW_ID 108 (the same asWindow Namens Task)

� PO_WINDOW_ID --

� AD_TABLE_ID 114 (the same as table Namens User/Contact)

AD_RefTable contains attributes of instances of the table AD_Reference, specially the

table referred to, their key- and display columns, as well as a constrain asSQL, which

must be valid for this reference. These Feld can be found in Adempiere in tab Table Validation of window Reference.

This makes the result of the last query understandable.

Page 67: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 67

If KeyColumn does not end with _ID, NULL will be inserted instead.

If KeyColumn ends with _ID, NULL will be inserted.

A SQL query is created with this values:

SELECT AD_User.AD_User_ID,

NULL,

AD_User.Value || '-' || AD_User.Name,

AD_User.IsActive

FROM AD_User

WHERE EXISTS (SELECT * FROM C_BPartner bp WHERE

AD_User.C_BPartner_ID=bp.C_BPartner_ID AND bp.IsSalesRep='Y')

ORDER BY 3

Parameters with a reference=Table must be defined as reference with the attributes

Table Name, Key Column, Displayname, SQL-Constrain.

An instance of MLookupInfo is created with values (TABLENAME,

KEYCOLUMN, etc) and SQL.

Result of this SQL would be (shortened) AD_USER_ID NULL AD_USER.VALUE||'-'||AD_USER.NAME ISACTIVE

1000000 -VidesAdmin Y

1000001 -VidesUser Y

1000808 05-TORRES Y

1000311 15-TOLENTINO Y

101 gardenad-GardenAdmin Y

102 gardenusr-GardenUser Y

1000002 labvides-LabVIDESAdmin Y

1000003 labvides-LabVIDESUser Y

1000004 lsaravia-LSaravia Y

1001142 wbeltran-W.BELTRAN Y

etc.

� MLookupFactory.getLookup_TableDir() org.compiere.model At TableDirect the field muss be named TabellenName_ID! Otherwise it will result in

an error.

Displaying of the columns, that are marked as Identifier .

� getLookup_List() Depending on the selected field in Reference Key a list is shown here.

An empty list is shown when there is no value in the field Reference Key.

Page 68: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 68

Parameter analysis at report call

Parameters are analyzed, after they were typed in.

� Parameter are saved with (org.compiere.apps) processParameterPanel.saveParameters() in table AD_PInstancePara.

Depending on the data type the parameters are saved in different columns.

� The parameters are read from the table AD_PInstancePara during the execution of the

report . To achieve this, the method (org.compiere.model) MQuery.get() is called in

(org.compiere.print) ReportEngine.get()

� Then it goes on as described in paragraph Printing

Page 69: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 69

Callouts From Adempiere-Wiki + own research:

• What is or what mean "callout"?

• A Callout is a java method that is executed after the focus has changeg away from a

field and the field's Value is changed?.

• Callout is a java method which is executed when a field in Adempiere window is

modified. A callout class (extends CalloutEngine) groups different methods that are

called when the column is changed using the UI. For a column (see AD_Column.Callout

database column and Table and Column tab), you may specify a list of fully qualified

methods (separated by ";").

• Callouts are not for data validation - use dynamic validation (AD) instead

• Callouts can read the field or other fields. It is used for data entry consequences like

calculate totals that need direct feedback at the GUI.

• Callouts are deployed in the package org.compiere.model, as well as CalloutInvoice and CalloutEngine

• If you do calculations in callouts you have to repeat them in the related PO classes (beforeSave()) to allow the acces via a different UI like HTML interface.

• Callouts in AD

• System Admin

• Window Table&Column, Tab Column • Field Callout contains method, e.g. the contents of Table C_Order, Column Target

Document Type, Field Callout is org.compiere.model.CalloutOrder.docType • Callouts in Code

• Callout-Hierarchy in an example

• public class CalloutOrder extends CalloutEngine Package: org.compiere.model

• public class CalloutEngine implements Callout • The implemented method start() parses the text and invokes the Method

with parameters : (String) method.invoke(this, args). start() is called by ????

• Method (e.g. docType)is called and executed.

• How it proceeds to call the specific callout I can not explain.

• public interface Callout

• Callout signature

Every callout has the same signature.

public String my_callout_method (Properties ctx, int WindowNo, GridTab mTab, GridField mField, Object Value) Example in class CalloutOrder: public String docType (Properties ctx, int WindowNo, GridTab mTab, GridField mField, Object Value)

• Properties ctx Used in cases like MPriceList.getStandardPrecision(ctx, M_PriceList_ID) Env.setContext(ctx, WindowNo, "OrderType", DocSubTypeSO) MWorkflow.get (ctx, AD_Workflow_ID)

• int WindowNo Used mostly with org.compiere.util.Env class:

Page 70: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 70

Env.setContext(ctx, WindowNo, "OrderType", DocSubTypeSO) Env.getContextAsInt(ctx, WindowNo, "C_BankAccount_ID") Env.getContext(ctx, WindowNo, "DiscountSchema") Env.getContextAsDate(ctx, WindowNo, "PayDate")

• GridTab mTab It represents all columns within a record. Used in cases like (BigDecimal)mTab.getValue("QtyEntered") mTab.setValue ("M_Product_ID", new Integer (M_Product_ID)) mTab.setValue("DateAcct", Value)

• GridField mField The methods of the Grid Field Model class GridField can be used

(getAD_Tab_ID(), getValue(), isDisplayed(), getAD_Column_ID() etc.). Example: String colName = mField.getColumnName();

• Object Value Value represents the Value of the field and has to be cast to the class required. It

depends on the data type of the underlying field. Examples: ((Integer)Value).intValue() (Timestamp)Value (BigDecimal)Value

• Functionality

• using getValue() and setValue() to interpret and change logic.

• To avoid loops (not always used):

at the beginning of a callout: setCalloutActive(true); at the end of a callout: setCalloutActive(false)

Page 71: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 71

Validation of context values

� Configuration in AD

� Display

Window, Tab&Field of a windows Any tab, field Display Logic contains commands

� @IsEmployee@=N � 1=2 // wird also nie angezeigt

� @$Element_U2@=Y � @processed@=Y & @#ShowAcct@=Y � @IsCustomer@='Y' � etc

� Read Only

Table&Column of a table Any column, field Dread Only Logic contains commands like

� @OrderType@='WP' � @IsDropShip@=Y

� @AD_OrgBP_ID@!0 � @ProductType@=R | @ProductType@=E | @ProductType@=O � @CostingMethod@!x & @CostingMethod@!S // was is this?

(in MCost, column Current Cost Price)

� etc.

� Mandatory

Table&Column of a table Checkbox Mandatory

� Code I

� APanel: initPanel() calls m_curTab.getTableModel().setChanged(false) � m_cur_tab is the current tab at initialization

� (initPanel() is called by initWorkbench() in AWindow ) � Initializes panel.

� public class GridTab implements DataStatusListener, Evaluatee, Serializable � query() and getTableModel() call initTab(). � initTab() in the class GridTab calls loadTab() � GridTab: protected boolean loadTab()

� calls loadFields() � does order by

� GridTab: private boolean loadFields() Gets all fields separated as Standard fields and content fields (

Standard fields like Created, CreatedBy etc.) Calls list of the column names this field depends on: field.getDependentOn()

� public class GridField implements Serializable, Evaluatee GridField: public ArrayList<String> getDependentOn(): { : Evaluator.parseDepends(list, m_vo.DisplayLogic);

Page 72: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 72

Evaluator.parseDepends(list, m_vo.ReadOnlyLogic); Evaluator.parseDepends(list, m_vo.MandatoryLogic); : Evaluator.parseDepends(list, m_lookup.getValidation()) : }

� Evaluator

public static void parseDepends (ArrayList<String> list, String parseString) Parses the corresponding string: everything between @.

� Code II

Evaluate-class

evaluateLogic() is also used in methods of GridTab ( isReadOnly() ) and Grid Field (

isMandatory() ). These calls are often done within the program.

And analyse

� Code III

Evaluate class

There is also isAllVaraiblesDefined() in Evaluate

Page 73: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 73

Accounting processor

Comments by Karsten Thiemann in the forum:

The Accounting processor works with all document tables (all documents that have a post button) and creates the entries of Fact_Acct. See Acctprocessor.java for details. Especially the postSession() method. The posting is done by the Doc_* classes (Line 119 String error = doc.post(false, false);) Class Doc

� package org.compiere.acct � public abstract class Doc

� Classes with accounting logic as Doc_GLJournal, Doc_Cash, Doc_Bank, Doc_Invoice,

Doc_Inventory, Doc_InOut, Doc_Order, Doc_Payment, Doc_Requisition inherit from Doc.

this

Accounting logic is implemented in these classes.

� Each BO, which inherites from PO (as the Bos discussed above), has a reference to a Doc

instance, which is the document of the business object:

� private Doc m_doc, with its access methods

� public Doc getDoc() � public void setDoc(Doc doc)

� example: public class Doc_GLJournal extends Doc � Some methods as loadDocumentDetails(), createFacts(), getBalance() must be overwritten with

their own logic by the subclasses.

� Properties

� All IDs of the Table, which process documents (postings, documents) public static int[] documentsTableID Now: C_Invoice, C_Allocation, C_Cash, C_BankStatement, C_Order, C_Payment, M_InOut, M_Inventory, M_Movement, M_Production, GL_Journal, M_MatchInv, M_MatchPO, C_ProjectIssue, M_Requisition

� All names of tables which process documents

public static String[] documentsTableName

� Constants for document types

� C_Invoice: ARI, ARC, ARF, API, APC e.g. for Accounts Payable Invoices public static final String DOCTYPE_APInvoice = "API";

� C_Payment: ARP, APP � C_Order: SOO, POO � Transaction: MMI, MMM, MMS, MMR � C_BankStatement: CMB � C_Cash: CMC � C_Allocation: CMA � GL_Journal: GLJ � and so on

� Constants for posting state

� Notposted: N

Page 74: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 74

e.g.: public static final String STATUS_NotPosted = "N";

� NotBalanced b � NotConvertible c � PeriodClosed p � InvalidAccount i � PostPrepared y � Posted Y � Error E

� Account Type-Constants

� Invoice – Charge e.g. public static final int ACCTTYPE_Charge = 0;

� Invoice - AR � Invoice - AP � AP Service � AR Service � etc.

� Other properties

� Accounting Schema private MAcctSchema[]m_ass = null;

� Properties

private Properties m_ctx = null; � “document” BO, derived from PO

protected PO p_po = null; � Doc Lines

protected DocLine[] p_lines; Debit and Credit is accounted here

� Facts

private ArrayList<Fact> m_fact = null; � Furthermore

Document type, -state, -No, description, GL-Category, period, accounting date,

document date, business partner, bank account, currency

� Methods

� public final String post(boolean force, boolean repost) I did not find an other callers except postImmediate() (Comment of the author).

� Check validity of document state

� Checks whether accounting schema an BO refers to the same Client.

� Locks the data set.

� loadDocumentDetails() is called (is scaffolded by each Doc class with its own

logic)

Example Doc_Invoice:

� MInvoice is instantiated

� document date is calculated

� Quantities are calculated

� getGrandTotal() � getTotalLines() � getChargetAmt() � Steuern ermittelt

Page 75: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 75

� Invoice lines loaded: Using loadLines() p_lines and quantity is set

(docLine.setAmount() ). � if repost==true, then deleteAcct() is called, while the table and record_id entry is

deleted from Fact_Acct-table using a SQL query

� m_fact is filled with data from accounting schema:

postLogic()is called for each accounting schema, where createFacts() is called by

the subclasses and m_fact is fillled. The State of postings is set too.

� The BO gets its document using setDoc(this)

� BO is validated

� postCommit (String status) , where Facts&Receipts are saved

� A MNote is instantiated + filled with data as DocumentNo, booking date,

quantity, state, periode open?, balanced?.

� public static String postImmediate (MAcctSchema[] ass, int AD_Table_ID, int Record_ID, boolean force, String trxName)

� calls post(force, true) � it is called e.g. by DocumentEngine: postIt().

� public ArrayList<Fact> createFacts(MAcctSchema as) � Responsible for the accounting logic

� overwritten by the accounting classes as Doc_Invoice: Sets the Facts (accounting logic) for ARI, ARC, ARF, API, APC in a huge method

� fact.createLine() defines a line with debit and credit

� m_fact is filled in method post().

� public BigDecimal getBalance()

Page 76: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 76

Class Fact

� package org.compiere.acct � public final class Fact � In the end there is a class Balance

� Properties

� private Doc m_doc = null; � private MacctSchema m_acctSchema = null; � private ArrayList<FactLine> m_lines = new ArrayList<FactLine>()

Lines in Fact_Act � Methods

� Constructor public Fact (Doc document, MAcctSchema acctSchema, String defaultPostingType) properties are set

� public FactLine creatLine(DocLine docLine, MAccount account, int C_Currency_ID, BigDecimal debitAmt, BigDecimal creditAmt) Defines an instance of FactLine.

� More createLine-methods

� public FactLine[] getLines() FactLines are returned as array

� public boolean save (String trxName) All lines are saved.

� etc.

Page 77: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 77

Class FactLine

� package org.compiere.acct � public final class FactLine extends X_Fact_Acct � public class X_Fact_Acct extends PO

FactLine has persistancy too

� Realizes functionality of table Fact_Acct � Is often used by createFacts in the accounting classes

� Properties

� private Maccount m_acct = null � private MacctSchema m_acctSchema = null � private Doc m_doc = null;

Document Header

� private DocLine m_docLine = null;

� Methods

� public void setDocumentInfo(Doc doc, DocLine docLine) Client. Date Acct, Period, Tax, Product, Quantity, BP, Project, Campain, Activity, usw.

� public void setLocationFromBPartner (int C_BPartner_Location_ID, boolean isFrom) � public BigDecimal getSourceBalance()

The line

� public BigDecimal getAcctBalance() Accounted Balance

� beforeSave() because of PO

� createRevenueRecognition() Called by FactLine.save()

� public boolean updateReverseLine (int AD_Table_ID, int Record_ID, int Line_ID, BigDecimal multiplier) Caller: Doc_MatchInv

� etc.

Page 78: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 78

DocTypes and DocBaseTypes

� DocBase Types

are defined in AD : There is an AD reference called C_DocType DocBaseType as List Validation (=Combo Box) with the following parameters

� DocBase Type Search Key

� AP Credit Memo APC � AP Invoice API � AP Payment APP � AR Credit Memo ARC � AR Invoice ARI � GL Journal GLJ � Material Movement MMM � Material Receipt MMR � etc. (at all 23)

� DocTypes

All accounting actions are processed with the Document Type.

All possible document types in accounting are defined at application level.

Additionally to DocBase Type there can be defined in Document Type the following: Print

Format (selectively), document count, number of copies etc.

Example in an application (Spanish):

� DocType DocBase Type

� CXP Nota de Crédito AP Credit Memo

� CxP Retención de IVA AP Credit Memo

� CxP Crédito Fiscal AP Invoice

� CxP Factura AP Invoice

� CxC Crédito Fiscal AR Invoice

� Return Material Sales Order

� CxP Pedido Purchase Order

� etc. (at all 44 in in the application)

Page 79: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 79

Application Dictionary: Financial Report

A window appears clicking to Adempiere-Menue/Performance Analysys/Financial Reporting/Financial Report. What is this button for and where is the executing place in Adempiere?

Answer

� Login as System Administrator

� Application Dictionary/Menue

Select path to Financial Reporting window

� Zoom (right mouse button) into the window of the report (has content Financial Report)

� Select feld Create Report (which is defined as button)

� Zoom at the column processing-process Now

� The column processing of the table PA_Report_Financial Report appears.

� In the column process FinReport is set.

� In Application Dictionary/Report & process is actually FinReport the content of the column

Search Key of the report Create Report (column Description: Create Financial Report).

� Column Classname of report Create Report is set to org.compiere.report.FinReport. Starting Eclipse/Netbeans, file FinReport.java, which contains class FinReport, can be found

at /adempiere_trunk/base/src/org/compiere/report. =>This class is executed. There are database activities.

� column ReportView of reports Create Report has an entry T_Report. Zomming the T_Report leads to Temporary Reporting Table, which has a predefined number of

columns (29) with their format. The report uses them and fills them the corresponding value

into it. I assume that other reports use T_Report(Author).

Page 80: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 80

Interaction of Application Dictionary-Application-Code using Landed Costs as

an example

Functionality

� Application Dictionary: Landed Costs and Java method, that implements it.

� Log in as system adminiastrator

� Window Menu/Requisition-to-invoice/Invoice (Vendor), Tab Landed Costs: the method

can be selected here and the distribution can be started with the button Distributed Costs.

Which class is triggered?

Log in as System Administrator, open Menu-Window.

Goto window Landed Costs (Vendor). Tab Landed Costs

Field name Distribute Costs

Zoom into Column (the column name is processing). This will lead to the table

definition of C_LANDED_COST. There is the column Processing defined as a button,

which calls the process C_Landed_Cost_Distribution.

Zooming into process C_Landed_Cost_Distribution leads to its definition: field

Classname is org.compiere.process.LandedCostDistribute.

� Application: Perparation of calculation of Landed Costs

� In the invoice a business process must be set, as well as Company Agent, etc.

Creating some invoice lines with product/charge, quantity and price in tab invoice lines

� In Tab Landed Costs Cost Distribution (value, quantity, etc.), Cost Element (Transport,

etc.) and material receipt are set, if necessary with its line.

Saving: table C_LandedCost contains the data.

There can be multiple Landed Costs defined for eachinvoice line.

� Tab Landed Cost Allocation must be empty; It is programmatically filled after

calculation.

� Activation of Button Distribute Costs:

Landed Costs of an invoice line are triggered by the application. So if someone likes to calculate a whole invoice with multiple invoice lines, this must be

done manually for each line

� Code

� processCtl:Call of processUtil.startJavaprocess() � processUtil.startJavaprocess(): Call of process.startprocess() � LandedCostDistribute (Svrprocess): startprocess() calls process(). � LandedCostDistribute (Svrprocess): process() calls prepare() and doIt(). � LandedCostDistribute (Svrprocess): doIt() creates an instance of Landed Cost and calls

m_lc.allocateCosts().

As this is a doIt()-method, this must be a AD-process that is called somewhere.

Page 81: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 81

Log entry as:

LandedCostDistribute.doIt: MLandedCost[1000000,CostDistribution=Q,M_CostElement_ID=1000006,M_InOut_ID=1000039]

� MLandedCost: allocateCosts(): calls invoiceLine.allocateLandedCosts(). � MInvoiceLine: allocateLandedCosts() does:

� All defined Landed Costs for the invoice line are read from table C_LandedCost

and instantiated as MLandedCost array.

� All entries of the table C_LandedCostAllocation of corresponding invoice line

are deleted, if there are any.

� it is distinguished between single and multiple lines

� One single line:

The shipmentand a list of the shipment's lines is set using the calculated Landed

Cost.

The complete sum is calculated

� Etc..

Page 82: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 82

Price lists

� Each product is part of a Product Category.

� Each product can have multiple Price List versions. They differ in in List Price, Standard Price

and Limit Price.

� There is a Price List Schema (table: M_DiscountSchema) at Material Management/Material Management Rules. For each Price List Schema fields Valid From, Discount Type and the

button Renumber can be seleted here; additionally multiple conditions for each Price List schema can be defined as schema line (table: M_DiscountSchemaLine). For each condition can

be defined with which BO it is valid: Business Partner, Product Category or Product. Schema lines have a sequence(number) which defines the order to be processed: starting with the small

an ending with the big sequence numbers. Steps of 10 are defined as default. Conditions are

mostly prices and discounts (List Price Discount in %, List Price Min Margin, List Price Max

Margin etc.).

� There is a Price List (table: M_PriceList) in Material Management/Material Management Rules. Price lists and its versions can be defined here (table: M_PriceListVersion). It is more

important that product prices can be set in register version for each price list. Price List Schema (see above) and one or none aktive Price List Version (in the programm mistakengly called

Base Price List!!) are needed for this. In the combo box all active Price List Versions are

displayed for selection.

Finally a price list can be defined based on a Price List Schema and Price List Version. Click

button Create Price List for this.

This can also be followed using the database: The Feld M_PRICELIST_VERSION_BASE_ID,

that refers to a dataset of table M_PRICELIST_VERSION, is in the table

M_PRICELIST_VERSION . Paradoxically this field is defined as foreign key.

� Summarized:

Page 83: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 83

M_DiscountSchemaLine

M_DiscountSchema_ID

M_DiscountSchema

M_PriceList

M_PriceListVersion

M_Pricelist_ID

M_DiscountSchema_ID

Ein Price List Schema (M_DiscounSchema) hat mehrere Schema Lines (M_DiscounSchemaLine).

Eine Price List hat mehere Price List Versions.

Die Price List Version Verweist auf ein Price List Schema.

Wenn man also aus eine Price List Version die Preise kalkulieren will, greift Adempiere auf die im Price List

Schema definierten Schema Lines zurück

Page 84: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 84

Price calculation

M_Product

M_ProductCategory_ID

M_ProductPrice

M_PriceListVersion_ID

M_Product_ID

M_PriceList

M_PriceListVersion

M_PriceList_ID

M_ProductCategory

Ein Product gehoert zu einer Product Category.

Eine Price List hat mehere Price List Versions.

Ein Product und eine Price List Version verweisen auf einen Satz von Product Price.

Dazu kommen in M_ProductPrice die Spalten PriceList, PriceStd, PriceLimit, die für Listenpreis, Standardpreis und

Mindestpreis stehen.

In M_ProductPrice wird das Ergebnis der Preiskalkulation festgehalten.

Im Fenster Price List, Register Product Price werden die Werte für Listenpreis, Standardpreis und Mindestpreis

angezeigt.

Page 85: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 85

Payment Term

Page 86: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 86

Views RV_C_INVOICE, RV_OPENITEM

RV_OPENITEM is a view, that is called when Menue/Open Items/Open Items is selected.

View RV_C_INVOICE is used in RV_OPENITEM ; so it is inspected first.

1.- In RV_C_INVOICE the table C_INVOICE is conected to the tables C_DocType, C_BPartner,

C_BPartner_Location and C_Location using an inner join. The result contains all lines that have

reference to this table . So there can not be more lines as in C_INVOICE.

Semantics: probably just completed invoices are processed.

Almost all columns of the view RV_C_INVOICE (52 columns) derive from table C_INVOICE (60

columns) excepted C_CountryID, C_Region_ID, Postal and City, which derive from table C_Location.

There is a column DocStatus in the table C_INVOICE; its values consist of 2 letters. CO -> completed,

DR -> Drafted. See other sections in this documentation for a complete list.

Columns ChargeAmt, Totallines and GrandTotal of the table C_INVOICE can switch to negative

values while creating the view, if Multiplier gets the value-1, when the third letter of the column is

DocBaseType equals „C“.

DocBaseType is a column of the table , and has values like:

� CMC (Cash Journal), � APC (Vendor Credit Memo), � ARC (Credit Memo), � MMR (Vendor Delivery), � POO (purchase Order), � SOO (Order Confirmation, Proposal, Quotation, etc).

CMC , APC and ARC are the only values, that have a „C“ in third position. It seems that only these

document types are used to create the view RV_C_INVOICE.

The function charAt() read the n-th letter in a string (the code is simple: RETURN SUBSTR(p_string, p_pos, 1) ).

2.- view RV_C_INVOICE is used in View RV_OPENITEM. This is a bit more complicated.

• view RV_C_INVOICE consists of a union

Remember:

� data type of the selected table columns must be equal in both tables during the union.

� A simple union returns non-repeating lines, which means same lines are filtered.

� A UNION ALL returns repeating lines.

Because of this the column names of the second part of the UNION and and their order are

Page 87: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 87

equal to the first of this part

• First set fo union:

� First an inner join between the View RV_C_INVOICE and the table C_PaymentTerm is

created.

Explaination: A Payment Term can have multiple schedules. The Payment Term is

calculated in register Invoice during the creation of an invoice.

The values for DueDate, DaysDue, DiscountDate, DiscountAmt, PaidAmt, OpenAmt are

calculated from selected Payment Term during this first join.

� Calcuations are made using funtions as paymentTermDueDate(), which calls the method org.compiere.sqlj.PaymentTerm.dueDate(i.C_PaymentTerm_ID, i.DateInvoiced). This

method calculates the days to maturity using the invoiced date and the payment conditions.

� Other Oracle functions as addDays(i.DateInvoiced,p.DiscountDays) are used.

DiscountDays from the table C_PaymentTerm is fetched here. This function is solved as

following: RETURN TRUNC(p_date) + p_days.

� Some columns of view RV_OPENITEM are defined new in the view: DueDate, DaysDue,

DiscountDate, DiscountAmt, PaidAmt, OpenAmt. Their values are calculated using

functions. Calculation of these columns are the difference to the 2. join.

� Almost all other columns columns are from the view RV_C_INVOICE. As almost all

columns of the view RV_C_INVOICE derive from table C_INVOICE, is can be said that

view RV_OPENITEM is created using the table C_INVOICE adding some calculated fields.

� where-condition defines that amog other things that lines are selected where payment has

not been done (i.IsPayScheduleValid<>'Y') and document should not have state „Draft“

(i.DocStatus<>'DR'). • 2. set of union

� An inner join is done between the View RV_C_INVOICE and the table

C_InvoicePaySchedule.

Info: table C_InvoicePaySchedule is displayed inwindow Invoice (Customer), displayed as

4. tab (named Payment Schedule). A Payment Schedule and some parameter as maturity date and discount date are selected here. Payment Schedule depends on Payment Term,

which is selected in register Invoice.

Window Invoice (Customer) is executed in Menue/Quote-to-Invoice/Sales Invoices/Invoice (Customer).

� The same columns as during the first join are used; just the selection ofcolumns as DueDate,

DaysDue, DiscountDate, DiscountAmt, PaidAmt, OpenAmt is different.

� where-condition has an additionally component: wheter payschedule is valid.

So what does this view? It selects the mature invoices for the set of invoices and calculates the current

day and the discounts for every invoice. I dont know, why this value are calculated twice (once via

Payment Term and once via Schedules) (Author).

Page 88: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 88

Interestingly this view is saved into temporary table T_Aging (i think it is because of performance

reasons).

The dictionary results as following:

� The Report&Process RV_T_Aging uses Report View T_Aging,

� RV_T_Aging uses org.compiere.process.Aging as classname, where amongst other view

RV_OpenItem is called.

� T_Aging defined as table (and not as view like in other reports).

The relations between the table are shown in the diagram:

Comments: As example the table C_InvoicePaySchedule and the register definition of Payment Schedule in dictionary it can be seen that not all columns, which are defined in the table, are

accessable via the dictionary. There are 17 columns in the table definition of C_InvoicePaySchedule. In

Application Dictonary just 13 columns can be selected for register definition of Payment Schedule.

The columns created, createdBy, updated and updatedBy are not accessable via the dictionary.

Page 89: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 89

Explanation of the warehouse structure in the DB

� warehouse is found in Adempiere in Material Management/Material Management Rules/Warehouse and Locator. Here the warehouse, it(s) locator(s) and the storage per

locator can be seen.

� In the table m_warehouse the warehouses are saved.

Each warehouse can have multiple Locators. The are registered in the table m_locator.

Locators have

■ Search value (for fast lookup). A format-proposal for the value field is for 3-

dimensional warehouses xx-yy-zz, where 08-13-06 defines the 8. Aisle, 13.Din and dort

6. level.

■ Aisle oder "X"

■ Bin oder "Y"

■ Level oder "Z"

A lot of locators for any kind of 3-dimensional warehouse can be modeled this way.

Each Locator and Product be addressed to a storage.

It is best practice to select this format for Search Key: warehouse xx-yy-zz. In reports it

would be displayed as: “Movement from central warehouse 23-44-3 to distribution warehouse 21-44-4“.

� Storages are saved in the table m_storage and have on hand quantity, reserverd quantity,

ordered quantity, last inventory count, etc as parameters.

It is allowed to use different storages for each product and locator, so that a 3-dimensional

storage is not needed.

This is useful because e.g. with charges the same product can have different charge numbers

and warranty dates.

Eentries of m_storage can not be deleted (delete from m_strorage where ad_client_id=1000001 is useless too). If there is a wrong m_storage-entry, ad_client_id,

m_product or m_locator must be changed.

Page 90: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 90

� SQL query to find the relation locator-warehouse

select w.m_warehouse_id, w.name, l.m_locator_id FROM m_warehouse w INNER JOIN m_locator l ON (w.m_warehouse_ID=l.m_warehouse_ID) where w.ad_client_id=xxxxx

Pay attention that ad_client_id is correct.

Page 91: Developer Guide Adempiere

Developer’s Guide The ADempiere Bazaar

Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 91

Attribute Set Instances

This is where description of an attributsetinstance is defined.

� MAttributeSetInstance.setDescription() The descriptor is composed of the attributes

Amongst other MAttributeSet.getLotCharStart() and MAttributeSet.getLotCharEnd() are called.

� values can be defined in window Attribute Set, field Lot Char Start Overwrite and lot Char Start Overwrite. Blanks are not processed.

Additional table relations:

1 n

MAttributesetInstance ------> MStorage

(Lot, (m_attributesetinstance_id Serno, qtyonhand,

Guaranteedate) qtyreserved,

qtyordered)

So there can be different charges(MattributesetInstances) in a locator for the same product.

SQL query to get locator, product, quantity and description:

select loc.value, prd.value, st.QTYONHAND, asi.m_attributesetinstance_id, asi.description

from m_storage st

join m_attributesetinstance asi on

(st.M_ATTRIBUTESETINSTANCE_ID=asi.M_ATTRIBUTESETINSTANCE_ID)

join m_locator loc on (st.M_LOCATOR_ID=loc.M_LOCATOR_ID)

join m_product prd on (st.M_PRODUCT_ID=prd.M_PRODUCT_ID)

order by asi.lot, asi.guaranteedate, prd.value;

Further relationships:

1 n

MAttributesetInstance ------> MOrderline

(Lot, (m_attributesetinstance_id Serno, qtyordered,

Guaranteedate) qtyreserved,

qtydelivered

m_product_id)