Upload
puby101
View
626
Download
19
Embed Size (px)
DESCRIPTION
Developer Guide for ERP
Citation preview
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)
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
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
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
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.
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.
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.
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
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.
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.
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].
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.
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
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)
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
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()
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
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
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()
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
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().
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
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
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().
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.
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.
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
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.
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()
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
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
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.
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,
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])
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.
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.
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)
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,
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()
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.
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)
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())
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()
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)
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()
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
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
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),
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? :
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
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,
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
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:
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
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:
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
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
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)
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
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?)
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.
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.
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.
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.
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 –
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.
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.
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
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:
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)
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);
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
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
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
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()
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.
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.
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)
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).
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.
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..
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:
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
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.
Developer’s Guide The ADempiere Bazaar
Copyright © 2008 Mario Calderon, Victor Perez, Norbert Wessel et al Page 85
Payment Term
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
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).
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.
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.
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.
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)