User talk:Leetaizhu

From ADempiere
Jump to: navigation, search
This Wiki is read-only for reference purposes to avoid broken links.


Developer’s Guide The ADempiere Bazaar


ADEMPIERE DEVELOPER'S DOCUMENTATION


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

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

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

sections to be understood.

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

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


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

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

on his own, contributing to the improvement of ADempiere.


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

jam session the innards of ADempiere.

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

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

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

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

continued.


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


Mario Calderon

San Salvador, December 26th, 2007


                                  Page 4  



页面 5-----------------------

Developer’s Guide The ADempiere Bazaar


Class org.compiere.util.GenerateModel


package org.adempiere.util

public class GenerateModel


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

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


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

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

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

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

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

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


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


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


       The new window tutorial in the wiki gives an example. It creates a table to store material 
       information - it is just an example (from my company). You don't need to create a M_* class for 
       it if you just want to store/load the data into an ADempiere window. For that the generated 
       X_XX_Material.java is sufficient. In the given example the MMaterial.java adds some 
       functionality in order to get a MMaterial xobject with the given materialno and colorno. This is 
       a quite common functionality in M_* classes. Give me the x object / all x objects with a given 
       set of attribute values from the database - but it is only an example and you don't need to 
       implement this kind of functionality for your x object. 


       In GenerateModel.java: 
       If someone inputs no or a wrong path, errors will be caused. 
       It is best practice to change GenerateModel.java 
       1) Change the default folder to your own 
       2) optional – change the package name 
       3) optional – Add entity types like 'D' (Dictionary) to line 97 


       Run GenerateModel without parameters 


   *   method main() generates java beans (X_xxxxxx.java-files like e.g.e.g. X_C_Invoice.java). 
       The method main() reads the tabell AD_Table and generates amongst others get- and set- 
       methods for every column of AD-tables. 
   *   It is possible to generate single beans 
       See in the text down below. 


                                   Page 5  



页面 6-----------------------

Developer’s Guide The ADempiere Bazaar


   *   In the sources of Adempiere these  files can be 
   *   found at org.compiere.model . 
   *   The compiled GenerateMdel.class-file is saved at 
       .../adempiere_trunk/base/build/org/compiere/util 
   *   With parameters it is possible to configure the output: 
          *   select the method main() in Eclipse, click the right mouse button and click run in the 
              context menu 
          *   To input parameters, they must be defined in run 
                  *   Parameter 0 : Folder 
                      Where to copy the file. 
                      Default –when no parameter set:  "C:\\Adempiere\\adempiere- 
                      all\\extend\\src\\adempiere\\model\\" 
                      In svn trunk this is in /adempiere_trunk/extend/src/compiere/model// 
                  *   Parameter 1: Package,that the java bean contains to 
                      Default – when no parameter set:  "compiere.model" 
                  *   Parameter2: entity type 
                      (User-Defined, Adempiere, Dictionary, etc). 
                      In the file there is just User and Application intended. For others like Adempiere, 
                      it should be entered there. Remember that just User-defined will not be deleted in 
                      every new version 
                      Default – when no parameter set:  "'U','A'" 
                  *   Parameter 3: tabelle 
                      Relating to which table this java bean will be generated 
                      Default – when no parameter set: % (all tables) 
   *   Also the code can be changed that way, that the parameters have the wished configuration. 
   *   Every time the application dictionary tables has been changed, the X_files must be regenerated 
       to acces the field from source code. 


Since V. 3.3.0 /Adempiere/adempiere_trunk/base/src/org/adempiere/util/GenerateModelJPA.java is

designed to do this.


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

before deployment.


                                Page 6  



页面 7-----------------------

Developer’s Guide The ADempiere Bazaar


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.Value Of(1); 
        AD_ORGTRX_ID_AD_Reference_ID 
         ID in dictionary. 
        methods 
                initPO 
                will be called by the last PO-constructor of the call chain ( PO (Properties ctx, int ID, 
                String trxName, ResultSet rs) ) . 
                toString (e.g.e.g.gives back for example the class C_Invoice: X_C_Invoice) 
                It is used in compare() or    log.info(toString() 
                get- and set-metods of all columns of the table. 
                Exception:   columns processing, processed, Created , CreatedBy, etc. that are created in 
                the method load() of the class PO with the call of loadDefaults()/SetStandardDefaults(). 
                        getDocStatus() 
                        setDocStatus() 
        Misc 
                All base classes and beans are part of the same package. 
                This makes it possible to create an object by calling the constructor. 
                Date is handled as Timestamp . 
                Boolean values are strings with length 1 (Y=True, N=False). 
                When querying  booleans,       no get method is called. 
                Use methods like  isApproved(). 
                There are more beans that can be found in different packages. 


                                          Page 7  



页面 8-----------------------

Developer’s Guide The ADempiere Bazaar


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


              There are java beans which are not related to a business object: 
              public class X_AD_WF_Activity extends PO 
              This class is a helper class for  MWFActivity 
              public class MWFActivity extends X_AD_WF_Activity implements Runnable 


              Beans and PO together realize  persistency in ADempiere: 
                     Beans keep   data 
                     PO supplies   mechanism for persistency 


                              Page 8  



页面 9-----------------------

Developer’s Guide The ADempiere Bazaar


Persistency-Engine


org.compiere.model.PO


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

model validators.


public abstract class PO implements Serializable, Comparator, Evaluatee;

        Serializable 
        For streaming out objects. 
        Comparator 
         methods compare() and equals() are implemented by PO. 
        Evaluate 
        Implements the method get_ValueAsString(), which is also called by 
        org.compiere.util.Evaluator. evaluateLogicTuple() . The tupels @xxxx@ defined in AD, are 
        evaluated. 


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

PO :

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


Important constructors:

        the root-constructor is called at last: 
        public PO (Properties ctx, int ID, String trxName, ResultSet rs) 
                To create a new instance: if the parameter ID ist set with value 0. 
                If the transaction is set as null, a new one will be created. 
                org.compiere.util.Trx controlls      transactions with static methods 
                         When new transactions are created, Trx sets a random name. 
                         Trx.createTrxName() 
                         Create a new transaction, e.g. 
                         Trx.createTrxName("Cost") 
                         Name of the    current transaction will be got 
                         Trx.get(trxName, True) 
                         A new transaction will be created if  transaction       trxName  does not exist and the 
                         second parameter is True. 
                         Trx.get(trxName, True) 
                         In Trx.get a new will be created: 
                         { 
                          : 
                          retValue = new Trx (trxName) 
                         : 
                         } 
                         Setting the transaction name assures that all DB savings with equal names are 
                         handled as one transaction in commit and rollback contextes. 


                                            Page 9  



页面 10-----------------------

Developer’s Guide The ADempiere Bazaar


       The root-constructor calls: 




                { 
                 : 
                load (int ID, String trxName) 
                : 
                } 


                If  ID and transaction are known 
                        ID > 0 
                        The columns are being retrieved via SQL and         instance is filled with values. 
                        ID <= 0 
                        Creation of new object. 
                        Private class m_createNew  gets the value true, so that       object knows that it is a 
                        new creation. 
                         Columns processing, processed, Created , CreatedBy, etc.          are created with 
                        calling loadDefaults(). 


                loadComplete (boolean success) Can be defined by the subclasses if needed, but it is 
                currently not used anywhere. 


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


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


Additional methods of PO

       public final Object get_Value (String columnName) 
                It checks whether    column is active 
                If active, get_Value (int index) is called, which returns m_newValues[index]. 
                See Properties. 


       set_ValueNoCheck (String ColumnName, Object Value) 
        The column ColumnName gets a value without checking. 
        Value can be cut off. That is the reason why sometimes values are typed in into the program and 
       the programm saves them shortened. They are shorted to the length defined in Adempiere's AD. 


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


                                        Page 10  



页面 11-----------------------

Developer’s Guide The ADempiere Bazaar


       delete() 
       An object is deleted. 
       Under certain conditions, events are validated with ModelValidationEngine. 
       delete_Tree() 
       ID-Trees are deleted 
       save() 
       Thr saving of subclasses' instances done here. 
       Some checks are done: new object, organisation. 


       In save() other methods are called: 
              beforeSave() 
              Is often implemented by business classes like MInvoice, MProduct and so on. 
              saveNew() 
              Saving of a new object 
              saveUpdate() 
              Saving of an existing object. 


              Change tracking 
                      A session variable is created. 
                      MSession session = MSession.get (p_ctx, false) 
                      If a value change occurs, the table MChangeLog saves the state before and after 
                      saving: 
                      MChangeLog cLog = session.changeLog 
                      In order to achieve this, configuration must be done in Adempiere   A table can 
                      be defined for logging there. If this is done, Adempiere shows the change log in 
                      the window, where the table is displayed, by double-clicking on the bottom rigth 
                      of the window (click right mouse button on the bottom right corner of the table 
                      window). 
                      The following is shown: 
                             Tablename 
                             When and by whom the record was created 
                             When and by whom the record was modified 
                             change log 


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


       Methods beforeSave() and  afterSave() are often implemented by business classes like 
      MInvoice, MProduct etc. . 


Properties of PO (among others)

       POInfo p_info (Column info: Table-ID, Table Name, Access Level, etc). 
       Information about columns is provided by p_info. 
       This property is being taken from the root-constructor with 
      p_info = initPO(ctx); 
       Subclasses implement initPO(). See class POInfo. 


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


                              Page 11  



页面 12-----------------------

Developer’s Guide The ADempiere Bazaar


      (or m_oldValues[index], when m_newValues==null) 
      protected transient  CLogger   log = CLogger.getCLogger (getClass()) 
      To show   the subclass-output of the system consoleprivate static Clogger s_log = 
      CLogger.getCLogger (PO.class); 
       To show the PO-output in the system console 
      private Doc m_doc 
      private Object[] m_IDs = new Object[] {I_ZERO} // Ids of the data sets 
      private Object[] m_oldValues = null; // old values 
      private Object[] m_newValues = null; // new values 
      private Mattachment m_attachment = null; 
      etc. 


                         Page 12  



页面 13-----------------------

Developer’s Guide The ADempiere Bazaar


class POInfo


package org.compiere.model

public class POInfo implements Serializable




The class provides the access to POInfoColumn-instances


                Contains information about     columns    of the business object 
                is serializeable (for exporting functionality) 
                Properties (incomplete list) 
                    *   m_AD_Table_ID 
                    *   m_TableName 
                    *   m_AccessLevel 
                    *   POInfoColumn[] m_columns 
                    *  private Properties    m_ctx = null 
                Methods    (incomplete list) 
                Refer mostly to properties of m_column using the index. 
                    *   The Constructor 
                        POInfo (Properties ctx, int AD_Table_ID, boolean baseLanguageOnly) 
                        calls loadInfo(), who in turn 
                            *   uses a SQL-Statement      which comprises the tables 
                                    *   AD_Table t 
                                    *   AD_Column c 
                                    *   AD_Val_Rule vr  and 
                                    *   AD_Element e 
                                (Parameter for SQL:m_AD_Table_ID), 
                            *   Creates for each column an instance of  POInfoColumn and           fills the 
                                instance with the SQL data 
                                    *   t.TableName 
                                    *   c.ColumnName 
                                    *   c.AD_Reference_ID 
                                    *   c.IsMandatory 
                                    *   c.IsUpdateable 
                                    *   c.DefaultValue 
                                    *   e.Name,e.Description 
                                    *   c.AD_Column_ID 
                                    *   c.IsKey 
                                    *   c.IsParent 
                                    *   c.AD_Reference_Value_ID 
                                    *   vr.Code 
                                    *   c.FieldLength 
                                    *   c.ValueMin 
                                    *   c.ValueMax 
                                    *   c.IsTranslated 
                                    *   t.AccessLevel 
                                    *   c.ColumnSQL 


                                      Page 13  



页面 14-----------------------

Developer’s Guide The ADempiere Bazaar


                                  *   c.IsEncrypted 
                          *    Information of all columns are copied to   m_columns 
                          *   for Translations  table AD_Element_TRL is used and      the language is 
                              selected 
                  *   public static POInfo getPOInfo (Properties ctx, int AD_Table_ID) 
                      Calls the constructor. 
                      getPOInfo() itself is called in initPO() , within the Java Beans for the related 
                      table ID.  initPO()  is called inside the PO-Constructor. 
                  *   getColumnName (int index) 
                  *   isColumnMandatory (int index) 
                  *   String getColumnDescription (int index) 
                  *   getColumnCount() 
                  *   Class getColumnClass (int index) 


                                 Page 14  



页面 15-----------------------

Developer’s Guide The ADempiere Bazaar


class POInfoColumn


package org.compiere.model

public class POInfoColumn implements Serializable


              Contains Information about a column of the business Object 
              Properties (all public) 
                  *  AD_Column_ID 
                  *   ColumnName 
                  *   ColumnSQL 
                  *  DisplayType 
                  *   ColumnClass 
                  *  IsMandatory 
                  *  DefaultLogic 
                  *  IsUpdateable 
                  *   ColumnLabel 
                  *   ColumnDescription 
                  *  AD_Reference_Value_ID 
                  *   ValidationCode 
                  *   FieldLength 
                  *   ValueMin 
                  *   ValueMax 
                  *   ValueMin_BD 
                  *   ValueMax_BD 
          *   methods 
                  *  IsKey 
                  *  IsParent 
                  *  IsTranslated 
                  *   isUpdateable 
                  *   toString 
                  *  IsEncrypted 


          *   Relationship between POInfoColumn, PO       and beans (X_-classs) 
                  *   POInfoColumn instances contain information about     properties the business 
                      object (=columns) 
                  *   Beans contain  data 
                  *   PO manages data fetching and data  saving 


                             Page 15  



页面 16-----------------------

Developer’s Guide The ADempiere Bazaar


Business Object Classes


package org.compiere.model


There are two kind of business objects

    *   Workflow-Business-Objects 
        They are called inside a workflow. They must fulfill the requirements of the workflow 
        additionaly to their BO responsibilities. 
            *    e.g:  MInvoice, MOrder 
            *    class definition 
                public class MInvoice extends X_C_Invoice implements DocAction  
                 With DocAction the class implements the methods that are needed to realize a workflow. 
                 These methods are triggered by buttons, which can be defined at the window 
                 Table&Columns in the Application Dictionary of Adempiere.                Depending of the state 
                 and the next DocAction,      the corresponding method is called. 


                 For more information see the Workflow Chapter. 


                 The following methods query states and set actions. 
                     *   prepareIt() 
                         Events are validated with ModelValidationEngine. 
                          Class ModelValidationEngine can be used, to realize custom business logic. 
                         Here listened events are caught and executed,          referenced document states and 
                         document     types are defined and other methods         are called. 
                         An custom class      can be defined in the client window, Field validation class. 
                         An example for an own validation class can be found in Libero. 
                     *   completeIt() 
                         Events are validated amonst others with ModelValidationEngine 
                     *   approveIt() 
                     *   etc. 
            *    Document workflow classses define  the property private String m_processMsg, which 
                 shows messages as "PeriodClosed" in the bottom left of the coresponding window. 


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


    *   Master data of business objects, for example in MProduct 
            *   public class MProduct extends X_M_Product 
            *    no implement-part 
            *    Methods that realize the interaction      of  actual business logic , for example in MProduct 
                     *   isProductStocked() 
                     *   isOneAssetPerUOM() 
                     *   getAttributeSet() 


                                             Page 16  



页面 17-----------------------

Developer’s Guide The ADempiere Bazaar


                    *   etc. 


   *    properties depend on the purpose of the class . 


   *    All BOs implement trigger methods 
            *   beforeSave() 
                Actions before  Data is saved. 
            *   afterSave() 
                Actions after  data is saved. 
            *   beforeDelete() 
                Actions before  data is deleted 
            *   afterDelete() 
                 Actions after  data is deleted 


   *    Business-classes implement different constructors depending on their use. 
        Examples: 
            *   Standard Constructor 
                MInvoiceLine (Properties ctx, int C_InvoiceLine_ID, String trxName) 
                MInvoice to = new MInvoice (from.getCtx(), 0, null); 
            *   public MProduct (X_I_Product impP) 
                Constructor of the MProduct class for the product import. Properties of  an instantiated 
                product are set with the value  of the object impP . 
                The behavior of the import can be influenced with the class ImportProduct and 
                X_I_Product or with ValidateModel. 


   *    Business classes often implement       static-method get(), that returns an array of objects of its own 
        class. 
        Example: public static MProduct[] get(Properties ctx, String whereClause, String trxName) 


   *    Business classes implement a property  to log events in the system console 
       private static Clogger s_log = CLogger.getCLogger (Mproduct.class); 


   *    Business classes implement an object cache 
            *   private static CCache<Integer,MInvoice>            s_cache= new 
                CCache<Integer,MInvoice>("C_Invoice", 20, 2);               // 2 minutes 
            *    Thr number of objects in cache (here 20) and        residence duration (here 2 minutes) are 
                different for each object. 
            *   org.compiere.util.CacheMgt manages the Cache via the Application Server. 


   *    Business logik 
        Functionality is explained here based on Minvoice.prepareIt(). 
        Steps: 
            *   Model Validation (if there is one) 
                See chapter on ModelValidation. 
            *   Gets an instance of DocumentType on basis of  "C_DocTypeTarget_ID" 
            *   Checks whether a period is open based on the DocBaseTyp 


                                          Page 17  



页面 18-----------------------

Developer’s Guide The ADempiere Bazaar


           *   line validation 
           *   Checking of cashbooks 
           *   Doctype checking or setting 
           *  BOM expanding 
           *   Tax calculation 
           *   Landed Costs per line 
                  *   First all costs are added and then distributed to the rows 
                  *   In a row all costs are allocated to the product 
                  *   MinvoiceLine allocateLandedCosts() calls 
                      MinvoiceLine getBase() 
                      Here is  calculation of LANDEDCOSTDISTRIBUTION_Costs not implemented 
           *   Validation 
           *   Set Docaction 


                                 Page 18  



页面 19-----------------------

Developer’s Guide The ADempiere Bazaar


Interface DocAction


package org.compiere.process

public interface DocAction


            *   WF business objects implement methods defined            inDocAction,  like  in 
                public class MInvoice extends X_C_Invoice implements DocAction. 
                A BO that implements DocAction is called a Document. 
                The final shape depends on the object 
                    *    approveIt() 
                         Sets  property isApproved to value true 
                    *    closeIt() 
                         There is no action possible after this 
                    *    invalidateIt() 
                    *    prepareIt() 
                         Logic before execution 
                    *   processIt() 
                          Exceution of the business logic 
                    *    reActivateIt() 
                         after calling closeIt()  document can be reactivated. 
                         I depends on the object: an order can be reactivated, but not an invoice 
                    *    rejectIt() 
                    *    reverseAccrualIt() 
                    *    reverseCorrectIt() 
                         Generates the entries in accounting. In order to accomplish this, a copy of the 
                         original document is taken. 
                    *    unlockIt() 
                         A method lockIt() does not exist 
                    *    voidIt() 
                Defines static final Properties (String constants) the  actions and states 
                    *   ACTION_Complete 
                    *   ACTION_Close 
                    *    STATUS_Drafted 
                    *    STATUS_Completed 
                    *    etc. 
                Miscellaneous methods 
                    *    getApprovalAmt() 
                    *    getCtx() 
                         ID of record 
                    *    getDocAction() 
                    *    getDocStatus() 
                         Return sthe state defined as static final Property 
                    *    getDocumentInfo() 
                         Name of document type and document number. 
                    *    getDocumentNo() 
                    *    getDoc_User_ID() 


                                          Page 19  



页面 20-----------------------

Developer’s Guide The ADempiere Bazaar


                       Controls  sequence 
                       e.g. Selling- Purchase- Payment-No. 
                   *   getprocessMsg () 
                       Returns the value of the property m_processMsg 
                   *   get_TrxName() 
                   *   save() 
                   *   setDocStatus () 
                       Set a state that is defined as static final Property 
                   *   and many more 


                                   Page 20  



页面 21-----------------------

Developer’s Guide The ADempiere Bazaar


class MDocType


package org.compiere.model

public class MDocType extends X_C_DocType


            *   Representss a document typ 
                Is instantiated by BOs with dcument types (in methods like prepare() or 
                getDocumentInfo() ) like in 
                    *   MCash 
                    *   MInOut 
                    *   MInventory 
                    *   MInvoice 
                    *   MJournal 
                    *   MMovement 
                    *   MOrder 
                    *   MPayment 
                    *   MPeriod 
                    *   MRequisition 
                    *   etc. 
            *   Has few methods and no public properties 
            *   methods 
                    *   getOfDocBaseType() 
                        returns  the base document 
                    *   static public MDocType get (Properties ctx, int C_DocType_ID) 
                        returns an instance of MDocType from the cache and instanciates an object 
                    *   isOffer() 
                        Using properties of X_C_DocType it checks whether the BO is a subtype of 
                        Offer. Abstract: 
                        DOCSUBTYPESO_Proposal.equals(getDocSubTypeSO()) 
                    *   isProposal() and 
                        isQuotation() behave similar to isOffer(). 


                                      Page 21  



页面 22-----------------------

Developer’s Guide The ADempiere Bazaar


class X_C_DocType

Seems also to be generated by GenerateModel .


package org.compiere.model

public class X_C_DocType extends PO


    Properties 
          *  public static final int C_DOCTYPEINVOICE_ID_AD_Reference_ID=170; 
               ID_AD_Reference_ID 
          *  public static final String COLUMNNAME_AD_PrintFormat_ID = 
              "AD_PrintFormat_ID" 
          *  public static final String COLUMNNAME_C_DocType_ID  = "C_DocType_ID"; 
          *  public static final String COLUMNNAME_C_DocTypeInvoice_ID = 
              "C_DocTypeInvoice_ID" 
          *  public static final String COLUMNNAME_C_DocTypeShipment_ID = 
              "C_DocTypeShipment_ID" 
          *  public static final String COLUMNNAME_IsDefault = "IsDefault" 


          *  public static final String DOCSUBTYPESO_Proposal =  "ON" 
          *  public static final int DOCBASETYPE_AD_Reference_ID =183; 
              /** AP Credit Memo = APC */ 
          *  public static final String DOCBASETYPE_APCreditMemo = "APC"; 
              ** AP Invoice = API */ 
          *  public static final String DOCBASETYPE_APInvoice =  "API"; 
              /** AP Payment = APP */ 
          *   usw. für Bank Statement (CMB), GL Document(GLD), Material Movement (MMM), GL 
              Journal (GLJ) etc. 
   *   methods 
          *   many getter methods call PO-methods like e.g. 
                 *   getDocSubTypeSO(), which calls get_Value("DocSubTypeSO") in PO 
                 *   getName(), which calls get_Value("Name") in PO 
          *   also setter methods 


                          Page 22  



页面 23-----------------------

Developer’s Guide The ADempiere Bazaar


Documents Workflow


class DocumentEngine


Package: org.compiere.process

public class DocumentEngine implements DocAction


Caveat: the business object m_document is called ,document“ here, because it implements methods of

DocAction.


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

excecutes this action of the business object if validation succeeds.

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

done by the DocumentEngine.

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


DocumentEngine:

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


        and 


        on the other hand it is instantiated so that a BO changes its state by calling a method of the same 
        name that is implemented in the BO: processIt() of BO executes processIt() of 
        DocumentEngine, that is followed by a call of a method like complete() of the BO. 


            *   Properties 
                    *   DocAction m_document  // A business object          casted   to DocAction 
                        DocumentEngine implements also the interface DocAction and has a Property 
                        DocAction 
                    *   String m_status (Default: STATUS_Drafted, defined in DocAction) 
                    *   String m_message 
                    *   String m_action 
            *   get and set methods 
                    *   setDocStatus(String ignored) does nothing 
                    *   isDrafted() return STATUS_Drafted.equals(m_status); 
                        // STATUS_Drafted, defined in DocAction 
                    *   isInvalid() return STATUS_Invalid.equals(m_status); 
                    *   isInProgress() return STATUS_InProgress.equals(m_status); 
                    *   isApproved()return STATUS_Approved.equals(m_status) 
                    *   etc... 
            *   Methods 
                 The methods process      business object (document) 
                    *   Constructor 


                                         Page 23  



页面 24-----------------------

Developer’s Guide The ADempiere Bazaar


                         DocumentEngine (DocAction po, String docStatus) 
                          property m_document is set. 
                    *    processIt() 
                    *    completeIt() 
                    *    isValidAction (String action) 
                         Check with  public String[] getActionOptions() whether  the action is valid with 
                         of the current state of the BO. 
                         Example: When the current        state=STATUS_Drafted, then the following actions 
                         are possible: {ACTION_Prepare, ACTION_Invalidate, ACTION_Complete, 
                        ACTION_Unlock, ACTION_Void } (These actions are defined in DocAction). 
                         After that the the current state of the BO defines the potential aktions. 


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


                Typical use of DocumentEngine 
                e.g. in MInvoice processIt(String processAction): 
                    *    DocumentEngine engine = new DocumentEngine (this, getDocStatus()); 
                         An instance of DocumentEngine is created for the current BO with its current 
                         state. 
                         return engine.processIt(processAction, getDocAction()); 
                          processAction is  expected action of the WF and getDocAction()             action desired 
                         by the user. 
                     DocumentEngine: processIt(processAction, docAction) // own logic 
                         Validations are performed, depending on         whether WF-Action ist valid, 
                         If WF-Action ist not valid, whether the user action is valid, m_action is set. 
                         See isValidAction(). 
                    *    After that processIt( m_action) is called. 
                    *    DocumentEngine:  processIt(String action)  // Implementation of DocAction 
                         Depending on  m_Action  the related (custom) method is called, in which 
                             *   the state is changed. 
                             *   th method of the business objects with the same name is called: 
                                     *    if (ACTION_Unlock.equals(m_action)) return unlockIt(); 
                                          calls m_document.approveIt() 
                                     *    if (ACTION_Invalidate.equals(m_action)) return invalidateIt(); 
                                          Sets m_document.setDocStatus(STATUS_Invalid) 
                                     *    if (ACTION_Complete.equals(m_action) ....) completeIt(), which 
                                          calls 
                                          m_document.completeIt() of BO. 
                                     *    etc. 
                                     *    The new state of the BO is set after the execution of this methods. 
                                     *    Some DocAction methods like completeIt() of the 
                                         DocumentEngine  check the BO method for validity of the action 
                                          isValidAction (String action). 
                                     *    A special action is  ACTION_Complete, because depending on the 
                                          state, it not only does call completeIt(), but also 
                                          m_document.save() and postIt() . 


                                           Page 24  



页面 25-----------------------

Developer’s Guide The ADempiere Bazaar


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


                              Page 25  



页面 26-----------------------

Developer’s Guide The ADempiere Bazaar


Class MWFActivity (Workflow Activity)


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

interface.


Package: org.compiere.wf

public class MWFActivity extends X_AD_WF_Activity implements Runnable

public class X_AD_WF_Activity extends PO


It does not implement the DocAction interface


           *   Definition 
               public class MWFActivity extends X_AD_WF_Activity implements Runnable 
                   *   Inherits from   X_AD_WF_Activity 
                   *   X_AD_WF_Activity is generated by GenerateModel and inherits from PO 
           *   Properties of MWFActivity 
                   *   private m_po   // Reference to BO, which executes this activity 
                       Extracted with getPO (Trx trx) 
                   *   m_docStatus 
                       gets it value during run() 
                       or performWork() at DocActions: 
                           *   doc = (DocAction) m_po; //     business object instance is taken 
                               success = doc.processIt (m_node.getDocAction());// action is processed 
                               setTextMsg(doc.getSummary()); 
                               processMsg = doc.getprocessMsg(); 
                               m_docStatus = doc.getDocStatus(); 
                   *   private Trx m_trx 
                   *   private MWFNode m_node  // Node  to which action is referenced to 
                   *   private StateEngine m_state = null; 
                   *   private MWFprocess m_process = null; 
                   *   private DocAction m_postImmediate = null; 


           *   Methods 
                   *   Constructors 
                           *   MWFActivity (MWFprocess process, int AD_WF_Node_ID) 
                               Here take place the calls of the implemented methods of the interface 
                               X_AD_WF_Activity. 
                           *   There are more constructors 
                           *   constructor is called in 
                                   *   Workflowprocessor for  servers 
                                   *   WFActivity seem to be for clients 
                                   *   VDocAction for grids 
                                   *   WebInfo 
                                   *   other classes 


                   *   getPO (Trx trx) 
                       Gets a reference to a business object 
                       Here interact MWFActivity with MTable and PO. 


                                   Page 26  



页面 27-----------------------

Developer’s Guide The ADempiere Bazaar


                   *   getAD_Table_ID() from X_AD_WF_Activity returns the ID of the object in the 
                        table AD_Table. 
                        For this X_AD_WF_Activity calls  the method get_Value ("AD_Table_ID") of 
                       PO, which calculates the value. 
                            *   With the ID, the table of the business object can be obtained 
                            *   getRecord_ID()  returns the ID of the business object 
                                To do this X_AD_WF_Activity        calls the method get_Value("Record_ID") 
                                of PO ,  which calculates the value. 
                            *   After this the reference to  PO is queried. 
                   *    run() 
                        Is called in FormFrame class, startBatch(), which is in turn called by class 
                        VSetup, method actionPerformed()  after any kind of events in grids, editors and 
                        so on. 
                        Calls performWork() 
                        It is called in MWFprocess.startNext(), when a button was triggered.         See 
                       beneath. 
                   *   performWork() 
                        Calls 
                        success = doc.processIt (m_node.getDocAction()) 
                   *   getActiveInfo (Properties ctx, int AD_Table_ID, int Record_ID) 
                        Prepares content and and actions for combo boxes. 
                   *   public void setWFState (String WFState) 
                        See the following example. 


                   *    example MinOut.processIt() 


                        Abstract: The workflow processor, which was activated by the server, looks for 
                        all activities that needs to be processed. Each activitythat is checked for validity 
                        and handed over to the MWF process, which considers all activities of a process 
                        and triggers the next valid for execution. The next valid activity calls finally 
                       processIt() . 


                        In DocumentEngine it was already explained what happens afterwards 
                        ( processIt() of the BO calls the constructor of DocumentEngine and then 
                        documentEgine.processIt() etc.) 


                        The following call chain is executed: 
                        A) 
                            *  AdempiereServer: public void run() 
                                    *   in a loop doWork()  is called after a certain time. 
                            *   Workflowprocessor: protected void doWork() 
                                    *   calls Workflowprocessor.wakeup() 
                            *   Workflowprocessor: private void wakeup() 
                                    *   All interrupted, not processed activities of workflow nodes with 
                                        inactive actions are identified via a SQL statement. 
                                        Tables: AD_WORFLOW->AD_WF_NODE-> AD_WF_ACTIVITY 
                                        As a workflow is triggered by a process, the activity can be traced 


                                      Page 27  



页面 28-----------------------

Developer’s Guide The ADempiere Bazaar


                                      to a node, the node can be traced to a workflow and the workflow 
                                      to a process. 
                                  *   For each activity 
                                      activity.setWFState (String WFState) 
                           *  MWFActivity: public void setWFState (String WFState) 
                                  *   it checks whether the new state WFState is valid. 
                                  *   if yes, the process for Ctx  is taken and 
                                  *   checkActivities(String trxName) is called 
                           *  MWFprocess: public void.checkActivities(String trxName) 
                                  *   All activities of the process, which the current activity belongs to, 
                                      are scanned: 
                                       MWFprocess.getActivities(),  which contains 
                                      SELECT * FROM AD_WF_Activity 
                                      WHERE AD_WF_process_ID=? . 
                                  *   The first activity after one that is marked as “completed” , is given 
                                      as parameter to MWFprocess.startNext(). 
                                  *   With all other activities the state is managed 
                           *  MWFprocess: private boolean.startNext() 
                                  *   Set last Activity to processed and call save() 
                                  *   next Activity is taken 
                                  *   processing the logical operators (AND / XOR) 
                                  *   Check whether Activity t is next in chain. 
                                  *   Start the Activity using a thread 
                                      new Thread(new WFActivity(....) ).start(); 
                           *  public synchronized void Thread( ).start(); 
                               Comment in code: “it calls the run() Method of this thread” 
                           *  MWFActivity: public void run() 
                                  *   Calls performWork() 
                                  *   Parameter: m_trx 
                                  *   Comment in code: 
                                      Feedback to process via setWFState -> checkActivities” 
                           *  MWFActivity: private boolean performWork() 
                                  *   Fetched from property m_node, which gets the action: 
                                      String action = m_node.getAction() 
                                  *   Possible actions: Document Action, Report,process ,  Email, Set 
                                      variable, User Choice. 
                                      Actions like Task, Sub Workflow, User Workbench, User Form 
                                      and User Windows are offered, but not implemented. 
                                  *   Is the action about to be excecuted equal Action, then calls 
                                      doc.processIt(DocAction of node) the DocAction for the 
                                      implementing BO. 
                                      Parameter is m_node.getDocAction(). 


                                      Reminder: Actions are Report,process , Document Action etc.; 
                                      whereas DocActions are prepare , complete and so on. 


                                  *   Sometimes a “post immediate”is prepared. 


                                  Page 28  



页面 29-----------------------

Developer’s Guide The ADempiere Bazaar


              Other calls of setWFState (String WFState): 


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


                          *   MWFprocess: setWFState (String WFState) 
                                  *   Sets process state and refreshes all actions 
                                  *   calls MWFActivity.setWFState (String WFState) 
                                  *   seems to be made for Status=closed 
                                  *   i do not track this(Comment of the author) 


                           InOutGenerate.completeShipment() 


                                 Page 29  



页面 30-----------------------

Developer’s Guide The ADempiere Bazaar


class MWorkflow


Package: org.compiere.wf

public class MWorkflow extends X_AD_Workflow

public class X_AD_Workflow extends PO


See explaination in class processModalDialog, where MWorkflow is instantiated.


Properties

     m_nodes: Array of MWFNode-elements 
     private static Ccache<String,MWorkflow[]>  s_cacheDocValue 


methods

     loadNodes() 
       Depending on Workflow_ID all nodes of table AD_WF_Node are read into m_nodes . 
     public MWFNode[] getNodes() 
        nodes saved in m_nodes are returned as MWFNode[] 
     public MWFNode[] getNextNodes (int AD_WF_Node_ID, int AD_Client_ID) 
       Identify the  nodes about to be executed 
     public MWFprocess start (processInfo pi) 
        Starts a workflow 
             An instance of MWFprocess is created: 
                retValue = new MWFprocess (this, pi) 
             is saved 
             startWork() of this instance is executed. 
                Here a validation takes place whether the workflow can be started with its first node. 
                The first node's activity is identified and executed. 


                 See here also the context of the description of a call of actionButton() in class 
               processModalDialog. 
             public int getPrevious (int AD_WF_Node_ID, int AD_Client_ID)  
                Take the previous node from m_nodes. 
     aftterSave() 
       All nodes are saved under some circunstances. 
     some get-methods 


                                      Page 30  



页面 31-----------------------

Developer’s Guide The ADempiere Bazaar


Class ModelValidationEngine

Package org.compiere.model

public class ModelValidationEngine


           *   It is mostly used in calls inside of business classes (in the methods prepareIt(), 
               completIt(), closeIt() and so on) as following: 
               ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_ 
               CLOSE); 
           *    method get() yields (and sometimes creates) an instance of ModelValidationEngine 
           *   The result of get(). fireDocValidate (a String) is assigned to the property m_processMsg 
               of the business-class . 
           *   has a property s_engine, which contains an instance of its own class (!) 
               It therefore seems that there is just one  instance of ModelValidationEngine 
           *   In the constructor of ModelValidationEngine       one ModelValidator is instantiated: 
               ModelValidator validator = (ModelValidator)clazz.newInstance(); 
               It seems to be a problem here, because an interface is instantiated here, but interfaces are 
               implemented and not instantiated. 
               Solution: In client window a Client Validator Class can be set, which includes a client 
               validation. If is not there, the validation is ignored 
               Example of a validator-class: compiere.model. ModelValidator, at /extend. 
               public class MyValidator implements ModelValidator . 
               Afterwards, initialize() in Validator is called. 
           *   fireDocValidate() 
               The method docValidate() is called for every validator. 


   *   interface ModelValidator 
       Package org.compiere.model 
       public interface ModelValidator 


       ModelValidator     is an interface, which can be implemented by the developer. 


       If the class is registered in Adempiere, on every change of a record or document a specific 
       method can be called. Inside this method developers can programm their own actions as e.g. 
       booking to another account or change booking logic 


       If accounting rules are changed in Adempiere, RUN_setup must be rerun, so that the application 
       server can see the changes. 


       ModelValidator is used to program external logic to adempiere core. 


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


           *   is used inside the constructor ModelValidationEngine 


                                   Page 31  



页面 32-----------------------

Developer’s Guide The ADempiere Bazaar


                 {... 
                 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 offireDocValidate  calls. 
            *    declares methods like 
                     *   public String docValidate (PO po, int timing) 
                     *   public void initialize (ModelValidationEngine engine, MClient client) 
                     *   public String modelChange (PO po, int type) throws Exception 
            *    used to implement custom validations. 
                 Example of a validation-class: compiere.model. ModelValidator, under /extend. 


                                              Page 32  



页面 33-----------------------

Developer’s Guide The ADempiere Bazaar


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) 
                        RuftjbInit() and dynInit(Record_ID)auf.. 
                    *   jbInit() 
                        Is called by the constructor. 
                        Initializes  window. 
                        The property ActionLabel gets the correct value. 
                    *   dynInit()Is called by the constructor. 
                        Identifies the valid actions based on the state of the business object. (documents) 
                        The workflow state is detected: 
                        wfStatus=MWFActivity.getActiveInfo() 


                        Calls DocumentEngine.getValidActions(), which realizes the following: 
                            *    First the actions are identified depending on the DocState e.g. 
                                 in status_NotApproved --> Action_Prepare and Action_Void 
                            *    Then depending on table and state, additional Actions are added.The 
                                 tables are: Order, MinOut (Shipment), Invoice, Payment, GL-Journal, 


                                         Page 33  



页面 34-----------------------

Developer’s Guide The ADempiere Bazaar


                             Allocation, Bank Statement, Inventory Movement, Physical Inventory. 
                         *   At least the combo box with shortforms is created: CO, CL, DR, etc. 
                  *  actionPerformed() 
                     does some query 
                  *  save() 
                     Causes via a complicated mechanism that this command triggers a saving on the 
                     database: 
                     m_mTab.setValue("DocAction", s_Value[index]) 


                             Page 34  



页面 35-----------------------

Developer’s Guide The ADempiere Bazaar


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 calledframe  of the class AWindow : 
        frame.initWindow(AD_Window_ID, null) 


        The third parameter is an optional query, which is needed for the final window.                     Here the third 
        parameter is null. 


        org.compiere.apps 
        AWindow.initWindow (int AD_Window_ID, MQuery query) 
        In the Awindow constructor (last method) the class variable m_APanel was initialized. 
        In initWindow() the method m_APanel.initPanel (0, AD_Window_ID, query) is called. 


                 Side-explanation begin 
                  How can a window be called from an arbitrary place in code. 


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


                :  




                 : 
                 MQuery my_query = new       MQuery ("C_BPartner"); // e.g.. C_BPartner 
                 my_query.setRecordCount(1); // if only one BP is to be shown 
                 my_query.addRestriction("C_BPartner_ID", "=", 1000598);          // BP ID 


                 AWindow frame = new AWindow(); 
                 boolean OK = false; 
                 OK = frame.initWindow(123, my_query); // 123=BP window 


                 frame.validate(); 
                   AEnv.showCenterScreen(frame); 


                   When table, restriction and window nr. do not match, a window with a new record will     be shown. 
                   Problem: MQuery is a class. 


                                                   Page 35  



页面 36-----------------------

Developer’s Guide The ADempiere Bazaar


                Side-explanation end 


       org.compiere.apps 
       APanel.initPanel(int AD_Workbench_ID, int AD_Window_ID, MQuery query) 
       Here it can be discovered that the workbench alternative  was not implemented. 
        So this is just one window. Tabs will be generated, if there are any. 
       If there is query in the tab, it will also be defined there: 
       query = initialQuery (query, gTab) 


       MQuery APanel.initialQuery (MQuery query, GridTab mTab) 
       If MQuery exists, is active and has <10 objects, it follows no other query: 
        if (query != null && query.isActive() && query.getRecordCount() < 10) 
                return query; 


        Otherwise it is a so called volume table and a filter dialog appears. 
       Find find = new Find (Env.getFrame(this), m_curWindowNo, mTab.getName(), 
                mTab.getAD_Table_ID(), mTab.getTableName(), 
                 where.toString(), findFields, 10); 


       It takes care of the dialog of volume tables. 


                                         Page 36  



页面 37-----------------------

Developer’s Guide The ADempiere Bazaar


Class APanel


package org.compiere.apps


public final class APanel extends CPanel

implements DataStatusListener, ChangeListener, ActionListener, ASyncprocess


           *  actionPerformed() 
                  *   Command dispatcher depending on the triggered icon in the window (Save, 
                      Print, Attachment, Forward, Backward, etc.) 
                  *   Actions are processed in private methods. 
                      Some use processCtl: process() or m_curTab.dataSave(manualCmd) with 
                      m_curTab  as a grid. 
           *  actionButton() 
                  *   Creates an instance of VDocAction: VDocAction vda 
                      Shows the dialog with   vda.setVisible(true). 
                      In the dialog the action can be selected (Complete, Void, etc.). 
                  *   calls processModalDialog dialog = new processModalDialog() 
                  *   Shows the last called window via 
                      aenv.showCenterWindow(Env.getWindow(m_curWindowNo), dialog) 


                               Page 37  



页面 38-----------------------

Developer’s Guide The ADempiere Bazaar


Class processModalDialog


package org.compiere.apps

public class processModalDialog extends Cdialog implements ActionListener


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

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

Finally, the activity is executed with processIt() .


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


           *   actionPerformed(actionEvent e) 
               actionPerformed(actionEvent e) is triggered by events from graphical components. 
               Calls processCtl: process() 
           *   processCtl: process(m_ASyncprocess, m_WindowNo, parameterPanel, m_pi, null) 
                   *   processCtl: public static method process() for synchronized or unsynchronized 
                       processes 
                   *   m_pi is of the class processInfo 
                   *   Depending on m_pi.AD_process_ID and m_p.Record_ID, an instance of 
                       MPInstance is acquired. 
                   *   In the static method a processCtl is instantiated: 
                       processCtl worker = new processCtl(parent, WindowNo, pi, trx); 
                   *   With unsynchronized processes: worker.start() -> starts a new thread 
                           *   start() calls new Thread(this).start(). 
                           *   finally processCtl.run()is called. 
                   *   With synchronized processes: worker.run() -> starts the WF,       a bit more 
                       complicated. 


           *   processCtl:public boolean run() 
                   *   The process info is fetched with a complicated SQL-query: 11 columns of the 
                       table AD_process-AD_PInstance : 
                           *   column1: processname 
                           *   column2: Procedurename 
                           *   column3: Classname 
                           *   column4:  AD_process_ID 
                           *   column5: isReport 
                           *   column6: isDirectPrint 
                           *   column7:   AD_ReportView_ID 
                           *   column8:  AD_Workflow_ID 
                           *   column9: static case-value 
                           *   column10: isServerprocess 
                           *   column11:jasperReport 
                   *   processes, Workflows, Jasper Reports, Reports and processes are managed here. 
                       processes call e.g. pi.setPrintPreview(!IsDirectPrint), which ends up      in 
                       ReportCtl.start(), where processes for Order, Invoice, Shipment, Project, 


                                   Page 38  



页面 39-----------------------

Developer’s Guide The ADempiere Bazaar


                        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 inprocessIt() of 
                        the referring BO. 


           *   MWFprocess: private MWorkflow getWorkflow() 
                    *   Call of 
                        MWorkflow.get (getCtx(), getAD_Workflow_ID()) 
           *   MWorkflow: public static MWorkflow get (Properties ctx, int AD_Workflow_ID) 
                    *   a workflow is instantiated 
                        new MWorkflow (ctx, AD_Workflow_ID, null) 
           *   MWorkflow: Constructor 
               public MWorkflow (Properties ctx, int AD_Workflow_ID, String trxName) 
                    *   see description of the class 
                    *   Properties are set 
               Nodes are loaded: 
               loadNodes()  


                                       Page 39  



页面 40-----------------------

Developer’s Guide The ADempiere Bazaar


Class MWFprocess


package org.compiere.wf

public class MWFprocess extends X_AD_WF_process

public class X_AD_WF_process extends PO // This class has persistancy functionality too.


Properties

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


Methods

     checkActivities() 
        The next Activity after the first completed Activity is started: 
       startNext (activity, activities) 
     public boolean startWork() 
       Here is validated, whether the workflow can be started with its first node, whose activity is 
        detected and executed. 
     public void setAD_WF_Responsible_ID () 
       The ID of the WF-Responsible is set. 


                                      Page 40  



页面 41-----------------------

Developer’s Guide The ADempiere Bazaar


Class Workflowprocessor

package org.compiere.server

public class Workflowprocessor extends AdempiereServer


Properties

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


methods

    Constructor 
       public Workflowprocessor (MWorkflowprocessor model) 
       m_model gesetzt; m_Client is set using the model 
    doWork() 
       called by AdempiereServer 
       Calls wakeUp(). 
     wakeUp() 
           *   All interrupted, not processed activities of workflow nodes with inactive action are 
               instantiated using an SQL-query 
           *   For each of these activities: 
               activity.setWFState (String WFState) 
           *   private int sendAlertToResponsible (MWFResponsible responsible, 
               ArrayList<Integer> list, MWFprocess process, 
               String subject, String message, File pdf) 
           *   private int sendEmail (MWFActivity activity, String AD_Message, 
               boolean toprocess, boolean toSupervisor) 


                                  Page 41  



页面 42-----------------------

Developer’s Guide The ADempiere Bazaar


Class MWFNode

org.compiere.wf

public class MWFNode extends X_AD_WF_Node

public class X_AD_WF_Node extends PO // This class has also persistancy functionality


Properties

   *   private ArrayList<MWFNodeNext> m_next                          // next node 
   *   private MColumn                        m_column = null;        // column description 
   *   private MWFNodePara[]                  m_paras = null;          // process parameter 


methods

   *   public String getActionInfo() 
       called by toString() 
           *   Action is fetched (goes until PO) 
           *   In cases that Action is an Application process, called for information only 
               return "process:AD_process_ID=" + getAD_process_ID() 
                   *   ( 
                       Possible actions (public static final Strings, defined in X_AD_WF_Node) 
                           *   ACTION_UserWorkbench = "B" 
                           *  ACTION_UserChoice = "C"; 
                           *  ACTION_DocumentAction = "D"; 
                           *  ACTION_SubWorkflow = "F"; 
                           *  ACTION_EMail = "M"; 
                           *  ACTION_Appsprocess = "P"; 
                           *  ACTION_AppsReport = "R"; 
                           *  ACTION_AppsTask = "T"; 
                           *  ACTION_SetVariable = "V"; 
                           *  ACTION_UserWindow = "W"; 
                           *  ACTION_UserForm = "X"; 
                           *  ACTION_WaitSleep = "Z"; 
                               ) 
                       These values must match the values of the Combobox for actions in the node of a 
                       workflow. 
           *   public MWFNodePara[] getParameters() 
               node parameter was fetched: 
               m_paras = MWFNodePara.getParameters(getCtx(), getAD_WF_Node_ID()) 
               getAD_Workflow_ID()get the WF-ID using X_AD_WF_Node  until PO 
           *   public MWorkflow getWorkflow() 
               Mit MWorkflow.get(getCtx(), getAD_Workflow_ID()) 
           *   public boolean isUserApproval() 
                   *   Is  Action=ACTION_UserChoice?: 
                       ACTION_UserChoice.equals(getAction()) 
                   *   It is query, whether it was accepted: 
                       "IsApproved".equals(getColumn().getColumnName()) 


                                  Page 42  



页面 43-----------------------

Developer’s Guide The ADempiere Bazaar


Class processCtl

package org.compiere.apps (Client-Projekt)

public class processCtl implements Runnable


Manages Report&process. See its description


Properties

   *   ASyncprocess m_parent; 
   *   processInfo m_pi; // Properties and methods for process (transaction-ame, table-ID, 
       AD_process_ID, AD_Workflow_ID, parameter, Record-ID, etc) 
   *   private Trx m_trx; 
   *   private Waiting m_waiting; 
   *   private boolean m_IsServerprocess = false; 


Methods

   *   run() 
        11 columns   of  AD_process are read using a SQL-query  and m_pi is filled: 
           *   column1: processname 
           *   column2: Procedurename 
           *   column3: Classname 
           *   column4:  AD_process_ID 
           *   column5: isReport 
           *   column6: isDirectPrint 
           *   column7:   AD_ReportView_ID 
           *   column8:  AD_Workflow_ID 
           *   column9: static case-value 
           *   column10: isServerprocess 
           *   column11:jasperReport 


       Depends on the resulting m_pi-values different methods are executed: if e.g. 
       AD_Workflow_ID>0, a workflow will be started: 
       startWorkflow (AD_Workflow_ID) , a private method, which starts        WF: 
       processUtil.startWorkFlow(...). 


       Alternatively it also could be started: 
           *    Java-classes 
               Starting with the method run() in processCtl, which calls startprocess(), the methods 
               prepare() and doIt() of the class ImportInventory are called, which implement the 
               behavior. 
               In the report description of  AD, this relation is explained more accurately 
           *   Jasper Reports 
               startprocess() 
           *   normal Reports 
               ReportCtl.start() 
           *   Oracle-DB-Procedures 
               startDBprocess() 


                                  Page 43  



页面 44-----------------------

Developer’s Guide The ADempiere Bazaar


   *   Constructor 
       public static processCtl process(ASyncprocess parent, int WindowNo, IprocessParameter 
       parameter, processInfo pi, Trx trx) 
           *   An instance of MPInstance  is created: 
               MPInstance instance (mithilfe von pi) 
           *   Parameters are read ( save() ) 
           *   With synchronized processes the process is executed immediately run() 


Class processUtil


org.adempiere.util

public final class processUtil


No important properties (just the Logger)


Just 3 methods

   *   public static boolean startDatabaseProcedure(processInfo processInfo, String ProcedureName, 
       Trx trx) 
        Procedure is executed 
   *   public static boolean startJavaprocess(processInfo pi, Trx trx) 
       the class name is read from pi , then the class is obtained. Finally the class will be instantiated as 
       process. 
   *   public static MWFprocess startWorkFlow(Properties ctx, processInfo pi, int AD_Workflow_ID) 
           *   a workflow is instantiated: 
               wf = MWorkflow.get (ctx, AD_Workflow_ID) 
           *    WF is started: wf.start(pi) 


                                  Page 44  



页面 45-----------------------

Developer’s Guide The ADempiere Bazaar


Invoice-Preview

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


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


Values in Table of a invoice previews

AD_TABLE_ID=318 ( C_INVOICE)

AD_process_ID: 116 (Rpt C_Invoice)

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

table n C_Invoice_Header_v, which represents a invoice print view


Calling sequence to show the Invoice-Preview

APanel.actionPerformed()

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

The process is executed:

processCtl.run:

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


         back in getPrintData() 
                loadPrintData() is called, where the SQL-query for data acquisition is executed 
   backin  ReportEngine.get() 
        ReportEngine of the calling  method startDocumentPrint() is returned 
  backin startDocumentPrint(): 
   ReportCtl.CreateOutput() 
     The report is shown, considering whether direct printing or preview is selected. 
     In the latter case the Code der Viewer takes over: 
     ReportCtl.preview() 
                ReportViewerProvider provider = getReportViewerProvider(); 
                provider.openViewer(re); 
      SwingViewerProvider. openViewer()            -- SwingViewerProvider inherited from 
                                                                                  ReportViewerProvider 
       Viewer-Constructor() 
        ReportEngine.getView() 


                                        Page 45  



页面 46-----------------------

Developer’s Guide The ADempiere Bazaar


        ReportEngine.layout() 
        LayoutEngine-Constructor() 
          LayoutEngine.layout() 
                       Different handling, depending on whether PrintForm is form or table 
            LayoutEngine.layoutForm() 
                      read configuration of Print Format. 
                      Position, max. Width, max. Hight, etc. of each Element is identified. 
                       The local variable element contains Print Format. 


        In viewer, the button Print triggers: 
       Viewer.actionPerformed () 
        Viewer.cmd_print() 
        ReportEngine.print()          -- the print is started 


                                  Page 46  



页面 47-----------------------

Developer’s Guide The ADempiere Bazaar


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


THE BUTTON PRINT PREVIEW IN INVOICE WAS PRESSED HERE


Apanel.actionPerformed(): PrintPreview - 16


Apanel.cmd_print()

         ID=116 [12] 
         AD_TABLE_ID=318 ( C_INVOICE) 
         AD_process_ID= 116 (Rpt C_Invoice) 
         processInfo pi: 
                pi.m_Title=Invoice (Customer)       SuperUser@Company Name 
                pi.m_AD_process_ID=116 
                pi.m_Record_ID=1004868 


processCtl.process()

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

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

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

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


ReportCtl.start: start()

         processInfo[Invoice Print ,process_ID=116,AD_PInstance_ID=1004057,Record_ID=1004772, 
         Error=false, Summary=, Log=0] [42] 
         unsynchronized process 
         AD_PInstance_ID changes always 
         in AD_PInstance the process ID (here 116) and        Record ID (here 1004868) are saved, so the 
         value is not lost on reload. 


Thread is executed


processCtl.run()

         with a SQL query on AD_PInstance the configuration of AD_process is read in: 
         Name, ProcedureName,ClassName, AD_process_ID, isReport (=Y), isDirectPint (=Y), 
         ReportView-ID (=keine), Workflow-ID (=keine), isServerprocess(=N) and JasperReport-Name 
         (none). 
        These values are assigned to the classvariable m_pi. 
         Client- and User-ID do already have the correct values 


         After that, it checks what is about to be executed (Workflow, Report, process). 
         As print invoice is a report, the next methode is called. 


ReportCtl.start()

         Hard-coded dependancy: depending on the process_id ,          a method will be called. 
         For standard reports the the call is: startStandardReport(pi). A Report-Engine instance is c 
         reated and CreateOutput(re, pi.isPrintPreview()) is called.  ReportEngine-Instance is 


                                        Page 47  



页面 48-----------------------

Developer’s Guide The ADempiere Bazaar


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


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


ReportCtl.startDocumentPrint()

         calls immediately ReportEngine.get() . 


         In ReportEngine.get() a SQL query is executed depending on the type (Check, Dunning, 
        Remmitance,       Project, RfQ,     Order/Invoice/Shipment). 
         Here the SQL query for an invoice is executed, because the type invoice (=2) was passed 
         through since ReportCtl.start(). 


        Side explanation Print Format begin 
         There are 3 places in Adempiere where a Print Format can be defined: 
                 1.- At Business Partner 
                 2.- In register "Client" a Print Format can be set 
                 3.-Window Document Type: 
                  Table c_doctype has besides the Doc Base Type a Print Format. 


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

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

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

“MyCustomer”.

         This ID is in table AD_PRINT_FORMAT       "Invoice_Header", its column ad_table_id refers to table called 
        C_Invoice_Header_v, which represents an Invoice Print View . 


         If neither business partner nor document type is declared, it will default to ad_printform. 
         Side explanation Print Format end 


         Is the printing of an order required and there is already an invoice, then this invoice is printed 
         (else the order is printed). ReportEngine.getDocumentWhat() is called for this. It changes the 
         type and Record-ID to Invoice. The rest is done in            ReportEngine.get(). 


         Neededdocument information for printing is fetched with a SQL query. 
         TheSQL query contains a Join of tables c_invoice, ad_printform, ad_client, c_doctype and 
        c_bpartner, where  Print Format of Order, Shipment, Projekt, Remmitance and Invoice is 
         identified. 
         In the case of an Invoice: Priority # 1:       BPartner; Priority # 2: DocType: Priority #3: 
         PrintFormat (ad_printform). 
         Additionally the number of copies (either from Business Partner or Document Type), 
         Document-No. of Invoice and BP-ID are fetched: 


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


                                                 Page 48  



页面 49-----------------------

Developer’s Guide The ADempiere Bazaar


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


        Result (for example) is a line with following values: 
               ORDER_PRINTFORMAT_ID                           1000069 
               SHIPMENT_PRINTFORMAT_ID                        1000073 
               PRINTFORMAT_ID                                 1000100 
               PROJECT_PRINTFORMAT_ID 
               REMITTANCE_PRINTFORMAT_ID                       1000077 
               ISMULTILINGUALDOCUMENT                         Y 
               AD_LANGUAGE 
               DOCUMENTCOPIES                                 2 
               AD_PRINTFORMAT_ID 
               C_BPARTNER_ID                                  1000317 
               DOCUMENTNO                                     VCF_633487_2007 


        Ad_printformat_id, c_bpartner_id, beleg-Nr and number of copies (if empty, it is set to 1) are 
        extracted using the SQL and saved in variables. This is info about the print, not printable data 
        (yet). 


        An instance of each MPrintFormat, MQuery and PrintInfo is created. 
        The local variable query assigned to the constructor. I can e.g. be 
        “C_Invoice_Header_v.C_Invoice_ID=1004868" and is directly created using the invoice type 
        (Result: C_Invoice_Header_v at Invoice) and the document type + -ID (Result: 
        “C_Invoice_ID=1004868”). 


       Side explanation for DOC_TABLES begin 
                The local variable query uses an indexing of DOC_TABLES for finding the table : 
               DOC_TABLES[type]. 
                By defining the variables TABLES the table name can be found: 
               DOC_TABLES = new String[] 
                {"C_Order_Header_v",  "M_InOut_Header_v", "C_Invoice_Header_v", 
                "C_Project_Header_v","C_RfQResponse_v","C_PaySelection_Check_v", 
                "C_PaySelection_Check_v",  "C_DunningRunEntry_v" }; 


               So, if  type==2 (Invoice),  the resulting table becomes C_Invoice_Header_v. 
        Side explanation for DOC_TABLES end 


        At the end, a ReportEngine is instantiated in ReportEngine.get(),    where class variables are 
        assigned to the extracted instances of the classes MPrintFormat, MQuery and PrintInfo . 
        The instantiated ReportEngine is returned to the calling method. 


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


                                  Page 49  



页面 50-----------------------

Developer’s Guide The ADempiere Bazaar


        Example: 
        ReportEngine.<init>(Constructor): 
        MPrintFormat[ID=1000071,Name=Invoice_Header,Language=Language=[Espanol (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 variablefinalSQL . 
        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=[Espanol (El Salvador), Locale=es_SV, 
                        AD_Language=es_SV, DatePattern=DD/MM/YYYY, DecimalPoint=true], 
                        Items=56] 


        Column order AD_Column_ID=7483 


        A SQL is executed with ad_printformat_id as parameter (extracted from variable 
       Format). Columns of Print Format, PrintFormatItem, AD_Column etc. are extracted so a SQL 
        like this is built: 


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


                                        Page 50  



页面 51-----------------------

Developer’s Guide The ADempiere Bazaar


                        + "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 variablefinalSQL looks  e.g. like 
        this: 
        SELECT 
        (SELECT C_Order.DocumentNo||' - '||TRIM( TO_CHAR( C_Order.DateOrdered, 


                                        Page 51  



页面 52-----------------------

Developer’s Guide The ADempiere Bazaar


         'DD/MM/YYYY')) 
         FROM C_Order 
         WHERE C_Invoice_Header_vt.C_Order_ID=C_Order.C_Order_ID) AS AC_Order_ID, 
         // This was the first column, called AC_Order_ID 
         C_Invoice_Header_vt.C_Order_ID, 
         // The last columns are just displayed instead of c_order_id; see explanation beneath 
         C_Invoice_Header_vt.DateInvoiced, 
         C_Invoice_Header_vt.Name, 
         B.City||'.' AS Baddress,                     // see explanation 
         C_Invoice_Header_vt.C_Location_ID,  // see explanation 
         C_Invoice_Header_vt.PaymentTerm, 
                                   // for invoice, see explanation 
         (SELECT M_PriceList.Name FROM M_PriceList 
         WHERE C_Invoice_Header_vt.M_PriceList_ID=M_PriceList.M_PriceList_ID) 
                 AS CM_PriceList_ID, 
         // This is another column, called    CM_PriceList_ID 
         C_Invoice_Header_vt.M_PriceList_ID 
         FROM C_Invoice_Header_vt 
         LEFT OUTER JOIN C_Location B ON 
                 (C_Invoice_Header_vt.C_Location_ID=B.C_Location_ID) 
             WHERE C_Invoice_Header_vt.C_Invoice_ID=1006390 
             AND C_Invoice_Header_vt.AD_Language='es_SV' 
             AND C_Invoice_Header_vt.AD_Client_ID IN (0,1000001) 
             AND B.C_Location_ID NOT IN 
                 ( SELECT Record_ID FROM AD_Private_Access WHERE AD_Table_ID = 162 AND 
         AD_User_ID <> 100 AND IsActive = 'Y' ) 
         ORDER BY C_Invoice_Header_vt.DocumentNo 


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


        Side explanation begin 
         Which field will be shown  if there are IDs in Print Format? 
         A) As example: c_order_id in invoice print. 
         Each column is described in the AD among other things with the field isIdentifier. All columns     having this 
         field=”Y” are displayed when the ID field is present. 
         This way, isIdentifier is used as an information source for the whole object,  because just the ID itself is not very 
         useful. 


         In getPrintDataInfo() it is queried during the column analysis, whether the column is a search     field 
         (ad_reference_id=30). If true, the following will be called:  MlookupFactory.getLookup_TableDirEmbed(), 

where a SQL query is called:


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


         A column like AC_Order_ID is built with this. In c_order, the columns  DocumentNo and DateOrdered are 
         marked  as identifier ( isIdentifier =”Y”), which yields this output: 
        SELECT C_Order.DocumentNo ||' - 
         '||TRIM( TO_CHAR( C_Order.DateOrdered, 'DD/MM/YYYY')) 
         FROM C_Order 


                                                  Page 52  



页面 53-----------------------

Developer’s Guide The ADempiere Bazaar


        WHERE C_Invoice_Header_vt.C_Order_ID=C_Order.C_Order_ID. 


        Next, the “normal” SQL code is created: 
        C_Invoice_Header_vt.C_Order_ID. 


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


        C) If the reference type of the column = Location, then the SQL in the programm is changed in a way that 
       B.City||'.' AS Baddress is returned. Next the “normal” SQL-Code is created: 
       C_Invoice_Header_vt.C_Location_ID. Something similar will happen if the reference type the column = 
       Account, Locator oder PAttribute. 
        Side explanation end. 


End of DataEngine.getPrintDataInfo().


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


DataEngine.loadPrintData()

        The result of the SQL is a single or multiple lines. Example with a single line: 
            BPVALUE                           0191 (Key) 
            AC_ORDER_ID                       701517 
            C_ORDER_ID                        1004302 
            DATEINVOICED                      22-AUG-07 
            NAME                              NAME OF A COMPANY 
            NAME2                             FARMACIA DEL PUEBLO 
            DUNS                              126615-2 
            BADDRESS                          SANTA ROSA DE LIMA. 
            C_LOCATION_ID                     1000300 
            TAXID 
            DESCRIPTION                       COMPRA/VENTA DE MEDICINAS 
            SALESREP_NAME                     TORRES CARBALLO, RIGOBERTO 
            PAYMENTTERMNOTE                    30 DIAS 
            CC_INVOICE_ID                     VCF_633487_2007 - 22/08/2007 - 302.98 
            C_INVOICE_ID                      1005618 
            TOTALLINES                        268.12 


       In loadPrintData() all lines are read in a big loop with all lines and there the columns are 
       analyzed singly within a small loop . 


       The variable rowNo counts the rows, counter the columns (the last references to the next 
       column). 
       Small loop: 


                                  Page 53  



页面 54-----------------------

Developer’s Guide The ADempiere Bazaar


                The variable pdc (PrintDataColumn) contains infos about the columns that needs to be 
                edited (using indexing of the input parameter pd). 
                        m_ad_Column_id is the definition of the column in ad_column, within the table 
                        C_Invoice_Header_v. 
                        m_alias is the name of the column in SQL, e.g.        AC_Order_ID 
                        Alias is only used if the column is concatenated, caused by the combination ID- 
                        identifier. 
                        Using aliases the next column is fetched immediately, because it follows suit in 
                        the SQL (see SQL of getPrintDataInfo()) . 
                        m_column_name: is the column that can be found in Print Format, e.g. 
                        C_Order_ID. 
                        the actual column depends amongst other things whether it is a ID and also 
                        which fields are called as identifier. 
                Boolean values, long texts, DateTime-values, strings and numbers) are managed 
                At the end of the small loop an object pde (PrintDataElement) is created, which contains 
                the name of the column, content, whether it contains a key or page break. The object pde 
                is added to pd and added     to the variable m_group. 
                        m_column_name: is the column, that is set in Print format, e.g.: C_Order_ID 
                        m_display_type: = AD_REFERENCE_ID (see previously in text) 
                        m_value 
                        Contains the pair in concatenated      columns (otherwise just m_value) 
                                m_key e.g. 1000300 
                                The ID of the related table. For c_invoice_id it is c_invoice, at 
                                c_location_id it is table c_location, etc. 
                                m_value, e.g. “SANTA ROSA DE LIMA.. ” 
        In loadPrintData() the lines are counted programmatic (starting with the line, with the comment: 
        //       Add Total Lines 


        The calculation of functions like Count, Mean, Sum, Minimum, Maximum, Derivation  etc. are 
        started at “//   Check last Group Change “ in loadPrintData() with the call m_group.getValue() 
        PrintDataGroup.getValue (): 
       PrintDataFunction.getValue(char function). 


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


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

printing or preview.

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

       ReportViewerProvider provider = getReportViewerProvider(); 
        provider.openViewer(re) 

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

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


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


        Side explanation begin 
        LayoutEngine 


                                        Page 54  



页面 55-----------------------

Developer’s Guide The ADempiere Bazaar


        The class LayoutEngine has 3 methods that are important for printing 
           layout() 
           In the constructor of LayoutEngine the parameter Data that is used amongst other things for 
           data extraction is submitted and the  m_data is saved. m_data contains m_sql, which is used 
           for data extraction 
           The SQL for the aging report is: 
           SELECT 
           T_Aging.DueAmt, 
           A.Value||'-'||A.Name AS Aname, 
           T_Aging.SalesRep_ID, 
           (SELECT C_SalesRegion.Name FROM C_SalesRegion WHERE 
           T_Aging.C_SalesRegion_ID=C_SalesRegion.C_SalesRegion_ID) AS BC_SalesRegion_ID, 
           T_Aging.C_SalesRegion_ID, 
           T_Aging.BPValue, 
           T_Aging.SalesRep_Name, 
           T_Aging.SalesRegionName, 
           T_Aging.DateInvoiced, 
           (SELECT C_BPartner_Location.Name FROM C_BPartner_Location WHERE 
           T_Aging.C_BPartner_Location_ID=C_BPartner_Location.C_BPartner_Location_ID) AS 
           CC_BPartner_Location_ID, 
           T_Aging.C_BPartner_Location_ID, 
           T_Aging.LocationName, 
           (SELECT C_BPartner.Value||' - '||C_BPartner.Name FROM C_BPartner WHERE 
           T_Aging.C_BPartner_ID=C_BPartner.C_BPartner_ID) AS DC_BPartner_ID, 
           T_Aging.C_BPartner_ID, 
           SELECT C_BP_Group.Name FROM C_BP_Group WHERE 
           T_Aging.C_BP_Group_ID=C_BP_Group.C_BP_Group_ID) AS EC_BP_Group_ID, 
           T_Aging.C_BP_Group_ID 


           FROM T_Aging 


           LEFT OUTER JOIN AD_User A ON (T_Aging.SalesRep_ID=A.AD_User_ID) WHERE 
           T_Aging.AD_PInstance_ID=1017227 
           AND T_Aging.C_BPartner_ID=1000598.0 
           AND T_Aging.IsListInvoices='N' 
           AND T_Aging.IsSOTrx='Y' 
           AND T_Aging.AD_Client_ID IN (0,1000001) 
           AND A.AD_User_ID NOT IN ( SELECT Record_ID FROM AD_Private_Access WHERE 
           AD_Table_ID = 114 AND AD_User_ID <> 100 AND IsActive = 'Y' ) 


           Also if the condition is C_BPartner_ID=1000598.0 the correct value is returned.  




           layoutForm() 
           layoutTable() 
        Side explanation end 


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


                                  Page 55  



页面 56-----------------------

Developer’s Guide The ADempiere Bazaar


LayoutEngine.layout() 
 LayoutEngine.layoutForm(), 
   LayoutEngine.includeFormat() 
        DataEngine.getPrintData() is called (see above) 
        Variable PrintData includedData contains data for printing. 
        Call of LayoutEngine.layoutTable() -- e.g. using tables 
              The data are analysed here. 
   If it is a field, the method LayoutEngine.createFieldElement()is called in 
       LayoutEngine.layoutForm(). 


       LayoutEngine.createFieldElement() 
        Here the output is done 
              Conversion into string 
              Display of ID elements 
              Numbers are called as words at output: 
              Msg.getAmtInWords (m_format.getLanguage(), stringContent) 
              This depends on the selected language. For Spanish the method 
              AmtInWords_ES.getAmtInWords() is called. 
              Color 


                               Page 56  



页面 57-----------------------

Developer’s Guide The ADempiere Bazaar


PrintFormat


Table AD_PRINT_FORMAT

      contains all entries to fill the equal-named window 


                          Page 57  



页面 58-----------------------

Developer’s Guide The ADempiere Bazaar


class PrintData


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


        class variables 
               PrintDataColumn[] m_column_info 
               An array of elements, which describe the columns that are printed 
               PrintDataColumn is also a container with only get methods, a constuctor and the 
               following class variables: 
                       private int            m_AD_Column_ID; 
                       private String         m_columnName; 
                       private int            m_displayType; 
                       private int            m_columnSize; 
                       private String         m_alias; 
                       private boolean         m_pageBreak; 


               Properties m_ctx         -- Context 
               String          m_name:                -- Name of the print format 


               ArrayList<Object>        m_nodes        -- Array of PrintDataElement Arrays 
               Extra lines as sum. 
               PrintDataElement Klassenvariablen: 
                       private String         m_columnName; 
                       private int            m_displayType; 
                       private boolean         m_isPageBreak; 
                       private boolean         m_isPKey; 
                                              m_value; 


               ArrayList<ArrayList<Object>>  mrows             -- Array of PrintDataElement arrays 
               Contains the real data as rows and columns. 


               m_sql 
               Data is extracted with this e.g. 


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


                                   Page 58  



页面 59-----------------------

Developer’s Guide The ADempiere Bazaar


             ORDER BY C_Invoice_LineTax_vt.Line 


             m_TableName, e.g. C_Invoice_LineTax_vt 


                    Page 59  



页面 60-----------------------

Developer’s Guide The ADempiere Bazaar


Workflow (WF) im AD


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

    Login as System Admin 
    Menu, Window Workflow 
    Zoom at field Window 
    In Window, Tab & Field with name Workflow, Tab Workflow, Field       Workflow Type zoom to 
      column WorkflowType. 
    Table&Column with name AD_Workflow to Tab Column, zoom at Field Reference  Key          (is 
      AD_Workflow Type) 
     Select Reference with namen  AD_Workflow Type, Tab List Validation in window. 
       values General, Document process and Document Value can be found there. 


There are three WF-Types in Adempiere 
    General (common process)       G 
      WFs, that can be seen and used as normal user 
           Accounting Setup 
           BP Setup 
           Price List Setup 
           Product Setup 
           Request Setup 
           Requisition Setup 
           Sales Setup 
           Tax Setup 
           etc. 


    Document process                      P 
           process_Cash 
           process_Inventory 
           process_Invoice 
           process_Journal 
           process_Journal Batch 
           process_Movement 
           process_Order 
           process_Payment 
           process_Requisition 
           etc. 


    Document Value                        V 
      No entries (maybe in Garden World?) 


                         Page 60  



页面 61-----------------------

Developer’s Guide The ADempiere Bazaar


Workflow-Window

    Tab Workflow 
           Workflow Type (see above) 
           Data Access Level (All, Organisation, Client, etc) 
           Start Node 
           Workflow processor (it is empty in all entries; there is just only one: System Workflow 
              processor) 
    Tab Node 
           Combo Box WF Responsible ( Invoker, Organisation) 
           Combo Box Start Mode (Automatic, Manual) 
           Combo Box Join Element (XOR, AND) 
           Combo Box Split Element (XOR, AND) 
           Combo Box Action (Apps process , Apps Report, Apps Task, Document Action, Email, 
              Set Variable, Sub Workflow, User Choice, User Form, User Window, Wait (Sleep). 
              They seem equal to the possibilities in  MWFActivity: performWork(). 
              Task, Sub Workflow, User Workbench, User Form and User Window are offered, but not 
              implemented. 


              Fields are offered depending on the selected Action : in User Window  it is a combo box 
              window with all possible windows; whereas in Document Action,       it is a combo box 
              with entries from the reference _Document Action (Approve, Close, Complete, 
              Invalidate, Post, Prepare, Void, Unlock etc. They are equal to DocAction-methods in 
              source code of BOs). 
    Tab Transition 
           Next Node: next node to execute 
           There are multiple node possible to be the next. 
              One of them is Standard. 
               Operation of the node defines when which node is executable. 
    Tab Condition 
       rarely used 
           And/Or 
           column 
           Operation (+, -, etc.) 
           Value 


Conclusion:

In Tab Workflow is defined which node is started.

The Node defines for actions, which action is executed.

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

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

Transition defines which node is next.


ERM

    Static 
       Is all what is defined in the AD 
           An AD_WORKFLOW can have multiple AD_WF_NODEs. 


                              Page 61  



页面 62-----------------------

Developer’s Guide The ADempiere Bazaar


    Dynamic 
      processes are created during the execution 
           an AD_WF_ACTIVITYs can have a AD_WF_NODE 
           an AD_WORKFLOW can have multiple AD_WF_processs 
           an AD_WF_process can have multiple AD_WF_ACTIVITYs 


Actions in Tab Node:

    Reference: WF_Action 
           List Validation                 Search Key 
           Apps process                    P 
           Apps Report                     R 
           Apps Task                       T 
           Document Action                 D 
           Email                           M 
           Set Variable                    V 
           Sub Workflow                    F 
           User Choice                     C 
            User Form                      X 
            User Window                    W 
           User Workbench                  B (inactive) 
           Wait (Sleep)                    Z 


Workflow documentation for BOs:

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

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

So it can be seen where the workflow is.


Loading:

    APanel : actionPerformed (ActionEvent e) is the button dispatcher. 
    For the workflow button AEnv :public static void startWorkflowprocess (int AD_Table_ID, int 
      Record_ID) is called. 
    A dataset of AD_WF_process is read in here (for the current Tabelle and its current dataset). 


Saving:

Object X_AD_WF_process, father class of MWFprocess is a X_AD_WF_process.

But X_AD_WF_process is subclass of PO too, so that intances of it are saved.

Somewhere in the constructor it is saved and refreshed.


                            Page 62  



页面 63-----------------------

Developer’s Guide The ADempiere Bazaar


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 nameprocessing 
    Field Reference is a button; Field process references to C_Order process. 
       Zoom into this: 
     feld Workflow has the content Process_Order (one of the defined general workflows) 


Window Workflow Editor

    Reads      nodes of a workflow,   transition to the next node, join constraints and split constrains 
       and presents it in a graphical visualisation. 
    XOR       as a join-condition means that the first possible action is selected. 


                                  Page 63  



页面 64-----------------------

Developer’s Guide The ADempiere Bazaar


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 process Call 
                Especially Svrprocess implements          method startprocess() of processCall . 
            *   So Svrprocess  implements startprocess(). 
                The following happens inside:        private method process() is called, where  immediately 
                and after each other prepare() and doIt() is called. 
                Both are definined as abstract in Svrprocess, so         both must be defined in the subclass. 
                This happens in ImportInventory, and so these  methods are executed by 
                ImportInventory. 


            *   protected void prepare() 
                Reads the calling parameter from process info variable            m_pi and assigns Properties with 
                it. 
            *   protected String doIt() 
                The process is implemented. 
                Returns a String, which is used for among other things fault handling and messaging. 


        This way AD and programm code interact during java class-calls . 
        For more explaination, see description of the class processCtl . 


                                             Page 64  



页面 65-----------------------

Developer’s Guide The ADempiere Bazaar


   *   Oracle-Procedure 
   *   Workflow 
   *   Report 
   *   Jasper Report 

Call chain for parameter displaying in Report&Process calls

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

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


       AMenuStartItem.startprocess()                            org.compiere.apps 


       processDialog.init()                                     org.compiere.apps 
       Fetches the process info 


       processParameterPanel.init()                             org.compiere.apps 
       Fertches all call parameter of the process 
               SELECT p.Name, p.Description, p.Help, p.AD_Reference_ID, p.AD_process_Para_ID, p.FieldLength, 
               p.IsMandatory, p.IsRange, p.ColumnName, p.DefaultValue, p.DefaultValue2, p.VFormat, p.ValueMin, 
               p.ValueMax, p.SeqNo, p.AD_Reference_Value_ID, vr.Code AS ValidationCode 
               FROM AD_process_Para p 
               LEFT OUTER JOIN AD_Val_Rule vr ON (p.AD_Val_Rule_ID=vr.AD_Val_Rule_ID) 
               WHERE p.AD_process_ID=? AND p.IsActive='Y'      // z.B.238 
                ORDER BY SeqNo 
       createField() is called for each parameter. 


       processParameterPanel.createField()                      org.compiere.apps 
       GridFieldVO.createParameter() is called first. 
       Next, the constructor new GridField (voF) is called, where loadLookup() is called. 
       Variable vot.AD_Column_ID references to          ID of the Parameter in the table AD_process_Para 
       and not  ID in AD_Column ! 


       GridFieldVO.createParameter()                            org.compiere.model 
       A constructor for GridFieldVO is called. 
       Data like ColumnName, Name, Description, AD_Reference_ID, FieldLength, etc. are read from 
       the parameter.   Class variable lookupInfo contains possible lookup information. 


       GridFieldVO.initFinish()                                 org.compiere.model 
       When    field Reference of report&process-parameters equals List, Table, TableDirect or Search: 
       Lookup-handling. 
       Field Reference is extracted from table AD_Reference, where e.g. “List“ and ,Search“have 
       IDs 17 and 30. 
       Calling getLookupInfo().     The result is saved in MLookupInfo lookupInfo (just a variable, no 
       data), a class variable of GridFieldVO. 


       MLookupFactory.getLookupInfo()                           org.compiere.model 
       Different handling, depending on List, Table, TableDirect or Search. 
       Lists: getLookup_List(). 
       Table or Search and  field Reference Value with valid value: getLookup_Table(). 
       The field Reference Value is extracted from table AD_Reference, where e.g. “AD_User – 


                                      Page 65  



页面 66-----------------------

Developer’s Guide The ADempiere Bazaar


       SalesRep“ has ID 190. 


       Otherwise (including TableDirect), the call is getLookup_TableDir(). 


       The SQL is completed furthermore after the lookup call (e.g. to add       Client_ID condition and 
       the role condition). An object with the information of this parameter is returned to initFinish(). 


               MLookupFactory.getLookup_Table()                       org.compiere.model 
               A SQL query is sent. 
                       SELECT t.TableName,ck.ColumnName AS KeyColumn,cd.ColumnName AS 
                        DisplayColumn,rt.IsValueDisplayed, 
                       cd.IsTranslated,rt.WhereClause,rt.OrderByClause,t.AD_Window_ID,t.PO_Window_ID, 
                       t.AD_Table_ID 
                       FROM AD_Ref_Table rt 
                       INNER JOIN AD_Table t ON (rt.AD_Table_ID=t.AD_Table_ID) 
                       INNER JOIN AD_Column ck ON (rt.AD_Key=ck.AD_Column_ID) 
                       INNER JOIN AD_Column cd ON (rt.AD_Display=cd.AD_Column_ID) 
                       WHERE rt.AD_Reference_ID=190 -- "AD_User - SalesRep " 
                       AND rt.IsActive='Y' 
                       AND t.IsActive='Y' 


                        result is a line with e.g. the following content of the AD-column SalesRep, 
                       which is saved in variables: 
                       TABLENAME                      AD_User 
                       KEYCOLUMN                      AD_User_ID 
                       DISPLAYCOLUMN                  Name 
                       ISVALUEDISPLAYED                Y 
                       ISTRANSLATED                   N 
                     WHERECLAUSE                      EXISTS (SELECT * FROM C_BPartner bp 
                                                      WHERE 
                                                      AD_User.C_BPartner_ID=bp.C_BPartner_ID AND 
                                                      bp.IsSalesRep='Y') 
                       This where-Clause will not work correctly, if it is executed in SQL Developer. 
                       Within the SQL query, which is build later it causes like the following: 
                       SELECT * FROM C_BPartner bp 
                       inner join AD_User adu on (adu.C_BPartner_ID=bp.C_BPartner_ID) 
                       WHERE bp.IsSalesRep='Y' 


                       In this example all users are shown, which are marked as SalesRep in the 
                       C_BPartner-table . 


                       ORDERBYCLAUSE                  -- 
                       AD_WINDOW_ID                   108      (the same asWindow Namens Task) 
                       PO_WINDOW_ID                   -- 
                       AD_TABLE_ID                    114      (the same as table Namens User/Contact) 


               AD_RefTable contains attributes of instances of the table AD_Reference, specially the 
               table referred to, their key- and display columns, as well as a constrain asSQL, which 
               must be valid for this reference. These Feld can be found in Adempiere in tab Table 
               Validation of window Reference. 
               This makes the result of the last query understandable. 


                                   Page 66  



页面 67-----------------------

Developer’s Guide The ADempiere Bazaar


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


                A SQL query is created with this values: 
                SELECT AD_User.AD_User_ID, 
                 NULL, 
                 AD_User.Value || '-' || AD_User.Name, 
                 AD_User.IsActive 
                 FROM AD_User 
                 WHERE EXISTS (SELECT * FROM C_BPartner bp WHERE 
                 AD_User.C_BPartner_ID=bp.C_BPartner_ID AND bp.IsSalesRep='Y') 
                ORDER BY 3 


                Parameters with a reference=Table must be defined as reference with the attributes 
                Table   Name, Key Column, Displayname, SQL-Constrain. 


                 An instance of MLookupInfo is created with values (TABLENAME, 
                 KEYCOLUMN, etc) and         SQL. 


                Result of this SQL would be (shortened) 
                AD_USER_ID     NULL AD_USER.VALUE||'-'||AD_USER.NAME               ISACTIVE 
                 1000000                         -VidesAdmin                              Y 
                 1000001                         -VidesUser                               Y 
                 1000808                         05-TORRES                                Y 
                 1000311                         15-TOLENTINO                             Y 
                 101                             gardenad-GardenAdmin                     Y 
                 102                             gardenusr-GardenUser                     Y 
                 1000002                         labvides-LabVIDESAdmin                   Y 
                 1000003                         labvides-LabVIDESUser                    Y 
                 1000004                         lsaravia-LSaravia                        Y 
                 1001142                         wbeltran-W.BELTRAN                       Y 
                 etc. 


                MLookupFactory.getLookup_TableDir()                      org.compiere.model 
                At TableDirect the field muss be named TabellenName_ID! Otherwise it will result in 
                an error. 
                Displaying of the columns, that are marked as Identifier . 


                getLookup_List() 
                Depending on the selected field in Reference Key a list is shown here. 
                An empty list is shown when there  is no value in the field Reference Key. 


                                        Page 67  



页面 68-----------------------

Developer’s Guide The ADempiere Bazaar


Parameter analysis at report call

      Parameters are analyzed, after they were typed in. 


              Parameter are saved with (org.compiere.apps) 
              processParameterPanel.saveParameters () in    table AD_PInstancePara. 
              Depending on the data type the parameters are saved in different columns. 


              The parameters are read from the table AD_PInstancePara during the execution of the 
              report . To achieve this, the method (org.compiere.model) MQuery.get() is called in 
              (org.compiere.print) ReportEngine.get() 


              Then it goes on as described in paragraph Printing 


                            Page 68  



页面 69-----------------------

Developer’s Guide The ADempiere Bazaar


Callouts

From Adempiere-Wiki + own research:

   *   What is or what mean "callout"? 
           *   A Callout is a java method that is executed after the focus has changeg away from a 
               field and the field's Value is changed?. 
           *   Callout is a java method which is executed when a field in Adempiere window is 
               modified. A callout class (extends CalloutEngine) groups different methods that are 
               called when the column is changed using the UI. For a column (see AD_Column.Callout 
               database column and Table and Column tab), you may specify a list of fully qualified 
               methods (separated by ";"). 
           *   Callouts are not for data validation - use dynamic validation (AD) instead 
           *   Callouts can   read the field or other fields. It is used for data entry consequences like 
               calculate totals that need direct feedback at the GUI. 
           *   Callouts are deployed in the package org.compiere.model, as well as CalloutInvoice and 
               CalloutEngine 
           *   If you do calculations in callouts you have to repeat them in the related PO classes 
               (beforeSave()) to allow the acces via a different UI like HTML interface. 
   *   Callouts in AD 
           *   System Admin 
           *   Window Table&Column, Tab Column 
           *   Field Callout contains method, e.g. the contents of Table C_Order, Column Target 
               Document Type, Field Callout is org.compiere.model.CalloutOrder.docType 
   *   Callouts in Code 
           *   Callout-Hierarchy in an example 
                   *   public class CalloutOrder extends CalloutEngine 
                       Package: org.compiere.model 
                   *   public class CalloutEngine implements Callout 
                           *   The implemented method start() parses the text and invokes the Method 
                               with parameters : (String) method.invoke(this, args). 
                               start() is called by ???? 
                           *   Method (e.g. docType)is called and executed. 
                           *   How it proceeds to call the specific callout I can not explain. 
                   *   public interface Callout 


           *   Callout signature 
               Every callout has the same signature. 
               public String my_callout_method       (Properties ctx, int WindowNo, GridTab mTab, 
               GridField mField, Object Value) 
               Example in class CalloutOrder: 
               public String docType (Properties ctx, int WindowNo, GridTab mTab, GridField 
               mField, Object Value) 
                   *   Properties ctx 
                       Used in cases like 
                       MPriceList.getStandardPrecision(ctx, M_PriceList_ID) 
                       Env.setContext(ctx, WindowNo, "OrderType", DocSubTypeSO) 
                       MWorkflow.get (ctx, AD_Workflow_ID) 
                   *   int WindowNo 
                       Used mostly with org.compiere.util.Env class: 


                                    Page 69  



页面 70-----------------------

Developer’s Guide The ADempiere Bazaar


                      Env.setContext(ctx, WindowNo, "OrderType", DocSubTypeSO) 
                      Env.getContextAsInt(ctx, WindowNo, "C_BankAccount_ID") 
                      Env.getContext(ctx, WindowNo, "DiscountSchema") 
                      Env.getContextAsDate(ctx, WindowNo, "PayDate") 
                  *   GridTab mTab 
                      It represents all columns within a record. 
                      Used in cases like 
                      (BigDecimal)mTab.getValue("QtyEntered") 
                      mTab.setValue ("M_Product_ID", new Integer (M_Product_ID)) 
                      mTab.setValue("DateAcct", Value) 
                  *   GridField mField 
                      The methods of the Grid Field Model class GridField can be used 
                      (getAD_Tab_ID(), getValue(), isDisplayed(), getAD_Column_ID() etc.).  
                      Example: String colName = mField.getColumnName(); 
                  *   Object Value 
                      Value represents the Value of the field and has to be cast to the class required. It 
                      depends on the data type of the underlying field. 
                      Examples: 
                      ((Integer)Value).intValue() 
                      (Timestamp)Value 
                      (BigDecimal)Value 


           *   Functionality 
                  *   using getValue() and setValue() to interpret and change logic. 
                  *   To avoid loops (not always used): 
                      at the beginning of a callout: setCalloutActive(true); 
                      at the end of a callout: setCalloutActive(false) 


                                 Page 70  



页面 71-----------------------

Developer’s Guide The ADempiere Bazaar


Validation of context values


       Configuration in AD 
              Display 
               Window, Tab&Field of a windows 
              Any tab, field Display Logic contains commands 
                      @IsEmployee@=N 
                      1=2    // wird also nie angezeigt 
                      @$Element_U2@=Y 
                      @processed@=Y & @#ShowAcct@=Y 
                      @IsCustomer@='Y' 
                      etc 
              Read Only 
              Table&Column of a table 
              Any  column, field Dread Only Logic contains commands like 
                      @OrderType@='WP' 
                      @IsDropShip@=Y 
                      @AD_OrgBP_ID@!0 
                      @ProductType@=R | @ProductType@=E | @ProductType@=O 
                      @CostingMethod@!x & @CostingMethod@!S               // was is this? 
                      (in MCost, column Current Cost Price) 
                      etc. 
              Mandatory 
              Table&Column of a table 
              Checkbox Mandatory 
       Code I 
              APanel: initPanel() calls m_curTab.getTableModel().setChanged(false) 
                      m_cur_tab is the current tab at initialization 
                      (initPanel() is called by initWorkbench()  in AWindow ) 
                      Initializes panel. 
              public class GridTab implements DataStatusListener, Evaluatee, Serializable 
                      query() and getTableModel() call initTab(). 
                      initTab() in the class GridTab calls loadTab() 
                      GridTab: protected boolean loadTab() 
                              calls loadFields() 
                              does order by 
                      GridTab: private boolean loadFields() 
                      Gets all fields separated as Standard fields and content fields ( 
                      Standard fields like Created, CreatedBy etc.) 
                      Calls  list of the column names  this field depends on: 
                      field.getDependentOn() 
              public class GridField implements Serializable, Evaluatee 
              GridField: public ArrayList<String> getDependentOn(): 
              { 
              : 
              Evaluator.parseDepends(list, m_vo.DisplayLogic); 


                                Page 71  



页面 72-----------------------

Developer’s Guide The ADempiere Bazaar


                Evaluator.parseDepends(list, m_vo.ReadOnlyLogic); 
                Evaluator.parseDepends(list, m_vo.MandatoryLogic); 
                : 
                Evaluator.parseDepends(list, m_lookup.getValidation()) 
                : 
                } 
                Evaluator 
                public static void parseDepends (ArrayList<String> list, String parseString) 
                Parses the corresponding string: everything between @. 
        Code II 
        Evaluate-class 
        evaluateLogic() is also used in methods of GridTab ( isReadOnly() ) and Grid Field ( 
        isMandatory() ). 
        These calls are often done within the program. 
         And analyse 
        Code III 
        Evaluate class 
        There is also isAllVaraiblesDefined() in Evaluate 


                                           Page 72  



页面 73-----------------------

Developer’s Guide The ADempiere Bazaar


Accounting processor


Comments by Karsten Thiemann in the forum:

The Accounting processor works with all document tables (all documents that have a post button) and

creates the entries of Fact_Acct. See Acctprocessor.java for details. Especially the postSession()

method. The posting is done by the Doc_* classes (Line 119 String error = doc.post(false, false);)


Class Doc


       package org.compiere.acct 
       public abstract class Doc 
       Classes with accounting logic as Doc_GLJournal, Doc_Cash, Doc_Bank, Doc_Invoice, 
       Doc_Inventory,     Doc_InOut,    Doc_Order,     Doc_Payment,     Doc_Requisition inherit from Doc. 
       this 
       Accounting logic is implemented in these classes. 
       Each BO, which inherites from PO (as the Bos discussed above), has a reference to a Doc 
       instance, which is the document of the business object: 
               private Doc m_doc ,    with its access methods 
               public Doc getDoc() 
               public void setDoc(Doc doc) 
       example: public class Doc_GLJournal extends Doc 
       Some methods as loadDocumentDetails(), createFacts(), getBalance() must be overwritten with 
       their own logic by the subclasses. 




       Properties 
               All IDs of the Table, which process documents (postings, documents) 
               public static int[] documentsTableID 
               Now: C_Invoice, C_Allocation, C_Cash, C_BankStatement, C_Order, C_Payment, 
               M_InOut, M_Inventory, M_Movement, M_Production, GL_Journal, M_MatchInv, 
               M_MatchPO, C_ProjectIssue, M_Requisition 
               All names of  tables which process documents 
               public static String[] documentsTableName 
               Constants for document types 
                        C_Invoice: ARI, ARC, ARF, API, APC 
                       e.g. for Accounts Payable Invoices 
                       public static final String DOCTYPE_APInvoice = "API "; 
                        C_Payment: ARP, APP 
                        C_Order: SOO, POO 
                        Transaction: MMI, MMM, MMS, MMR 
                        C_BankStatement: CMB 
                        C_Cash: CMC 
                        C_Allocation: CMA 
                        GL_Journal: GLJ 
                        and so on 
               Constants for posting state 
                       Notposted: N 


                                     Page 73  



页面 74-----------------------

Developer’s Guide The ADempiere Bazaar


                       e.g.: 
                      public static final String STATUS_NotPosted = "N"; 
                       NotBalanced b 
                       NotConvertible  c 
                       PeriodClosed p 
                       InvalidAccount i 
                       PostPrepared y 
                       Posted Y 
                       Error E 
               Account Type-Constants 
                       Invoice – Charge 
                       e.g. public static final int   ACCTTYPE_Charge = 0; 
                       Invoice - AR 
                       Invoice - AP 
                      AP Service 
                      AR Service 
                       etc. 
               Other properties 
                       Accounting Schema 
                      private MAcctSchema[]m_ass = null; 
                       Properties 
                      private Properties m_ctx = null; 
                        “document” BO, derived from PO 
                      protected PO p_po = null; 
                       Doc Lines 
                      protected DocLine[] p_lines; 
                       Debit and Credit is accounted here 
                       Facts 
                      private ArrayList<Fact> m_fact = null; 
                        Furthermore 
                       Document type, -state, -No, description, GL-Category, period,      accounting date, 
                       document date, business partner, bank account, currency 
       Methods 
               public final String post(boolean force, boolean repost) 
               I did not find an other callers except postImmediate() (Comment of the author). 
                       Check validity of document state 
                       Checks whether accounting schema an BO refers to the same Client. 
                       Locks the data set. 
                       loadDocumentDetails() is called (is scaffolded by each Doc class with its own 
                       logic) 
                       Example Doc_Invoice: 
                              MInvoice is instantiated 
                               document date is calculated 
                               Quantities are calculated 
                                      getGrandTotal() 
                                      getTotalLines() 
                                      getChargetAmt() 
                                      Steuern ermittelt 


                                  Page 74  



页面 75-----------------------

Developer’s Guide The ADempiere Bazaar


                                      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 BigDecima l getBalance() 


                                 Page 75  



页面 76-----------------------

Developer’s Guide The ADempiere Bazaar


Class Fact


       package org.compiere.acct 
       public final class Fact 
       In the end there is a class Balance 




       Properties 
               private Doc m_doc = null; 
               private MacctSchema m_acctSchema = null; 
               private ArrayList<FactLine> m_lines = new ArrayList <FactLine>() 
                 Lines in Fact_Act 
       Methods 
                Constructor 
               public Fact (Doc document, MAcctSchema acctSchema, String defaultPostingType) 
                 properties are set 
               public FactLine creatLine(DocLine docLine, MAccount account, int C_Currency_ID, 
                BigDecimal debitAmt, BigDecimal creditAmt) 
                Defines an instance of FactLine. 
                More createLine-methods 
               public FactLine[] getLines() 
                 FactLines are returned as array 
               public boolean save (String trxName) 
                All lines are saved. 
                etc. 


                                        Page 76  



页面 77-----------------------

Developer’s Guide The ADempiere Bazaar


Class FactLine

       package org.compiere.acct 
       public final class FactLine extends X_Fact_Acct 
       public class X_Fact_Acct extends PO 
       FactLine has persistancy too 
       Realizes functionality of table Fact_Acct 
       Is often used by createFacts in the accounting classes 




       Properties 
               private Maccount m_acct = null 
               private MacctSchema m_acctSchema = null 
               private Doc m_doc = null; 
               Document Header 
               private DocLine m_docLine = null; 


       Methods 
               public void setDocumentInfo(Doc doc, DocLine docLine) 
                Client. Date Acct, Period, Tax, Product, Quantity, BP, Project, Campain, Activity, usw. 
               public void setLocationFromBPartner (int C_BPartner_Location_ID, boolean isFrom) 
               public BigDecimal getSourceBalance() 
               The line 
               public BigDecimal getAcctBalance() 
               Accounted Balance 
               beforeSave() 
               because of PO 
               createRevenueRecognition() 
               Called by FactLine.save() 
               public boolean updateReverseLine (int AD_Table_ID, int Record_ID, int Line_ID, 
               BigDecimal multiplier) 
               Caller: Doc_MatchInv 
               etc. 


                                      Page 77  



页面 78-----------------------

Developer’s Guide The ADempiere Bazaar


DocTypes and DocBaseTypes

        DocBase Types 
       are defined in AD : There is an AD reference called C_DocType DocBaseType as List 
       Validation (=Combo Box) with the following parameters 
              DocBase Type           Search Key 
              AP Credit Memo         APC 
              AP Invoice            API 
              AP Payment            APP 
              AR Credit Memo         ARC 
              AR Invoice            ARI 
              GL Journal             GLJ 
              Material Movement      MMM 
              Material Receipt       MMR 
              etc. (at all 23) 
       DocTypes 
       All accounting actions are processed with the Document Type. 
       All possible document types in accounting  are defined at application level. 
       Additionally to DocBase Type  there can be defined in Document Type the following: Print 
       Format (selectively), document count, number of copies etc. 


       Example in an application (Spanish): 
              DocType                       DocBase Type 
              CXP Nota de Crédito           AP Credit Memo 
              CxP Retención de IVA           AP Credit Memo 
              CxP Crédito Fiscal            AP Invoice 
              CxP Factura                   AP Invoice 
              CxC Crédito Fiscal            AR Invoice 
              Return Material               Sales Order 
              CxP Pedido                    Purchase Order 
              etc. (at all 44 in in the application) 


                              Page 78  



页面 79-----------------------

Developer’s Guide The ADempiere Bazaar


Application Dictionary: Financial Report


A window appears clicking to Adempiere-Menue/Performance Analysys/Financial Reporting/Financial

Report.

What is this button for and where is the executing place in Adempiere?


Answer

       Login as System Administrator 


       Application Dictionary/Menue 
       Select path to Financial Reporting window 


       Zoom (right mouse button) into the window of the report (has content Financial Report) 


       Select feld Create Report (which is defined as button) 


       Zoom at the column processing-process Now 


       The column processing of the table PA_Report_Financial Report appears. 


       In the column process FinReport is set. 


       In Application Dictionary/Report & process is actually FinReport the content of the column 
       Search Key of the report Create Report (column Description: Create Financial Report). 


       Column Classname of report Create Report is set to org.compiere.report.FinReport. 
       Starting Eclipse/Netbeans, file  FinReport.java, which contains   class FinReport, can be found 
       at /adempiere_trunk/base/src/org/compiere/report . 
       =>This class is executed. There are database activities. 


        column ReportView of reports Create Report has an entry T_Report. 
       Zomming the T_Report leads to Temporary Reporting Table, which has a predefined number of 
       columns (29)   with their format. The report  uses them and fills them the corresponding value 
       into it. I assume that other reports use T_Report(Author). 


                               Page 79  



页面 80-----------------------

Developer’s Guide The ADempiere Bazaar


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 ofprocessUtil.startJavaprocess() 
               processUtil.startJavaprocess() : Call ofprocess.startprocess() 
               LandedCostDistribute (Svrprocess): startprocess() calls process() . 
               LandedCostDistribute (Svrprocess):process() calls prepare() and doIt(). 
               LandedCostDistribute (Svrprocess): doIt() creates an instance of  Landed Cost and calls 
               m_lc.allocateCosts(). 
               As this is a doIt()-method, this must be a AD-process that is called somewhere. 


                                    Page 80  



页面 81-----------------------

Developer’s Guide The ADempiere Bazaar


               Log entry as: 
               LandedCostDistribute.doIt: 
               MLandedCost[1000000,CostDistribution=Q,M_CostElement_ID=1000006,M_InOut_I 
               D=1000039] 
               MLandedCost: allocateCosts(): calls invoiceLine.allocateLandedCosts(). 
               MInvoiceLine: allocateLandedCosts() does: 
                       All defined Landed Costs for the invoice line are read from table C_LandedCost 
                       and instantiated as MLandedCost array. 
                       All entries of the table C_LandedCostAllocation of  corresponding invoice line 
                       are deleted, if there are any. 
                       it is distinguished between single and multiple lines 
                       One single line: 
                       The shipmentand a list of the shipment's lines is set using the calculated Landed 
                       Cost. 
                       The complete sum is calculated 
                       Etc.. 


                                   Page 81  



页面 82-----------------------

Developer’s Guide The ADempiere Bazaar


Price lists


     Each product is part of a Product Category. 


     Each product can have multiple Price List versions. They differ in                in List Price, Standard Price 
        and Limit Price. 


     There is a Price List Schema (table: M_DiscountSchema) at Material Management/Material 
        Management Rules. For each Price List Schema             fields Valid From, Discount Type and the 
        button Renumber can be seleted here; additionally multiple conditions               for each Price List 
        schema can be defined as schema line (table: M_DiscountSchemaLine). For each condition can 
        be defined with which BO it is valid: Business Partner, Product Category or Product.  Schema 
        lines have a sequence(number) which defines the order to be processed: starting with the small 
        an ending with the big sequence numbers. Steps of 10 are defined as default.               Conditions are 
        mostly prices and discounts (List Price Discount in %, List Price Min Margin, List Price Max 
        Margin etc.). 


     There is a Price List (table: M_PriceList) in Material Management/Material Management 
        Rules. Price lists and its versions can be defined here (table: M_PriceListVersion). It is more 
        important that product prices can be set in register version for each price list. Price List Schema 
        (see above) and one or none aktive Price List Version (in the programm mistakengly called 
        Base Price List !!) are needed for this. In      the combo box all active Price List Versions are 
        displayed for selection. 
        Finally a price list can be defined based on a Price List Schema and Price List Version. Click 
        button Create Price List for this. 


        This can also be followed using the database: The Feld M_PRICELIST_VERSION_BASE_ID, 
        that refers to a dataset of table M_PRICELIST_VERSION, is in the table 
        M_PRICELIST_VERSION . Paradoxically this field is defined as foreign key. 


     Summarized: 


                                             Page 82  



页面 83-----------------------

Developer’s Guide The ADempiere Bazaar


                                                                                                                   M_PriceList 


 M_DiscountSchemaLine 


   M_DiscountSchema_ID 


                                                                                                             M_PriceListVersion 


                                                                                                            M_Pricelist_ID 
                                                       M_DiscountSchema                                     M_DiscountSchema_ID 


           Ein Price List Schema (M_DiscounSchema) hat mehrere Schema Lines                    (M_DiscounSchemaLine).  
           Eine Price List hat mehere Price List Versions. 
           Die Price List Version Verweist auf ein Price List Schema. 
          Wenn man also aus eine Price List Version die Preise kalkulieren will, greift Adempiere auf die im Price List  
           Schema definierten Schema Lines zurück 


                                                          Page 83  



页面 84-----------------------

Developer’s Guide The ADempiere Bazaar


Price calculation


               M_ProductCategory 


                                                                                                                    M_PriceList 


                                                                                                              M_PriceListVersion 


                                                             M_ProductPrice                                     M_PriceList_ID 
                 M_Product 


                                                           M_PriceListVersion_ID 
            M_ProductCategory_ID                           M_Product_ID 


    Ein Product gehoert zu einer Product Category.  
    Eine Price List hat mehere Price List Versions. 
    Ein Product und eine Price List Version verweisen auf einen Satz von Product Price. 
    Dazu kommen in M_ProductPrice die Spalten PriceList, PriceStd, PriceLimit, die für Listenpreis, Standardpreis und  
    Mindestpreis stehen. 
     In M_ProductPrice wird das Ergebnis der Preiskalkulation festgehalten. 
    Im Fenster Price List, Register Product Price werden die Werte für Listenpreis, Standardpreis und Mindestpreis  
    angezeigt. 


                                                       Page 84  



页面 85-----------------------

Developer’s Guide The ADempiere Bazaar


Payment Term


                    Page 85  



页面 86-----------------------

Developer’s Guide The ADempiere Bazaar


Views RV_C_INVOICE, RV_OPENITEM  


RV_OPENITEM is a view, that is called when Menue/Open Items/Open Items is selected.

View RV_C_INVOICE is used in RV_OPENITEM ; so it is inspected first.


1.- In RV_C_INVOICE the table C_INVOICE is conected to the tables C_DocType, C_BPartner,

C_BPartner_Location and C_Location using an inner join. The result contains all lines that have

reference to this table . So there can not be more lines as in C_INVOICE.


Semantics: probably just completed invoices are processed.


Almost all columns of the view RV_C_INVOICE (52 columns) derive from table C_INVOICE (60

columns) excepted C_CountryID, C_Region_ID, Postal and City, which derive from table C_Location.


There is a column DocStatus in the table C_INVOICE; its values consist of 2 letters. CO -> completed,

DR -> Drafted. See other sections in this documentation for a complete list.


Columns ChargeAmt, Totallines and GrandTotal of the table C_INVOICE can switch to negative

values while creating the view, if Multiplier gets the value-1, when the third letter of the column is

DocBaseType equals ,C“.


DocBaseType is a column of the table , and has values like:

    CMC (Cash Journal), 
    APC (Vendor Credit Memo), 
    ARC (Credit Memo), 
    MMR (Vendor Delivery), 
    POO (purchase Order), 
    SOO (Order Confirmation, Proposal, Quotation, etc). 


CMC , APC and ARC are the only values, that have a ,C“ in third position. It seems that only these

document types are used to create the view RV_C_INVOICE.


The function charAt() read the n-th letter in a string (the code is simple: RETURN SUBSTR(p_string,

p_pos, 1) ).


2.- view RV_C_INVOICE is used in View RV_OPENITEM.

This is a bit more complicated.

   *    view RV_C_INVOICE consists of a union 
       Remember: 
          data type of the selected table columns    must be equal in both tables during the union. 
           A simple union returns non-repeating lines, which means same lines are filtered. 
           A UNION ALL returns repeating lines. 


       Because of this the column names of the second part of the UNION and and their order are 


                                Page 86  



页面 87-----------------------

Developer’s Guide The ADempiere Bazaar


       equal to the first of this part 


   *   First set fo union: 
            First an inner join between the View RV_C_INVOICE            and the table C_PaymentTerm is 
            created. 
            Explaination: A Payment Term can have multiple schedules. The Payment Term is 
            calculated in register Invoice during the creation of an invoice. 
            The values for DueDate, DaysDue, DiscountDate, DiscountAmt, PaidAmt, OpenAmt are 
            calculated from selected Payment Term during this first join. 
            Calcuations are made using funtions as paymentTermDueDate(), which calls the method 
            org.compiere.sqlj.PaymentTerm.dueDate(i.C_PaymentTerm_ID, i.DateInvoiced). This 
            method calculates the days to maturity using the invoiced date and the payment conditions. 
            Other Oracle functions as addDays(i.DateInvoiced,p.DiscountDays) are used. 
           DiscountDays from the table C_PaymentTerm is fetched here. This function is solved as 
            following: RETURN TRUNC(p_date) + p_days. 
            Some columns of view RV_OPENITEM are defined new in the view: DueDate, DaysDue, 
           DiscountDate, DiscountAmt, PaidAmt, OpenAmt. Their values are calculated using 
            functions.  Calculation of these columns are the difference to the 2. join. 
            Almost all other columns columns are from the view RV_C_INVOICE. As almost all 
            columns of the view RV_C_INVOICE derive from table C_INVOICE, is can be said that 
            view RV_OPENITEM is created using the table C_INVOICE adding some calculated fields. 
            where-condition defines that amog other things that lines are selected where payment has 
            not been done (i.IsPayScheduleValid<>'Y') and         document should not have state ,Draft“ 
            (i.DocStatus<>'DR'). 
   *   2. set of union 
            An inner join is done between the View RV_C_INVOICE              and the table 
            C_InvoicePaySchedule. 
            Info: table C_InvoicePaySchedule is displayed inwindow Invoice (Customer), displayed as 
            4. tab (named Payment Schedule). A Payment Schedule and some parameter as maturity 
            date and discount date are selected here.  Payment Schedule depends on Payment Term, 
            which is selected in register Invoice. 


           Window Invoice (Customer) is executed in Menue/Quote-to-Invoice/Sales Invoices/Invoice 
            (Customer). 
            The same columns as during the first join are used; just the selection ofcolumns as DueDate, 
           DaysDue, DiscountDate, DiscountAmt, PaidAmt, OpenAmt is different. 
          where-condition has an additionally component: wheter  payschedule is valid. 


So what does this view? It selects the mature invoices for the set of invoices and calculates the current

day and the discounts for every invoice. I dont know, why this value are calculated twice (once via

Payment Term and once via Schedules) (Author).


                                       Page 87  



页面 88-----------------------

Developer’s Guide The ADempiere Bazaar


Interestingly this view is saved into temporary table T_Aging (i think it is because of performance

reasons).

The dictionary results as following: 
    The Report&Process RV_T_Aging uses          Report View T_Aging, 
    RV_T_Aging uses org.compiere.process.Aging as classname, where amongst other  view 
       RV_OpenItem    is called. 
    T_Aging defined as table (and not as view like in other reports). 


The relations between the table are shown in the diagram:


Comments: As example the table C_InvoicePaySchedule and the register definition of Payment

Schedule in dictionary it can be seen that not all columns, which are defined in the table, are

accessable via the dictionary. There are 17 columns in the table definition of C_InvoicePaySchedule. In

Application Dictonary just 13 columns can be selected for register definition of Payment Schedule.

The columns created, createdBy, updated and updatedBy are not accessable via the dictionary.


                               Page 88  



页面 89-----------------------

Developer’s Guide The ADempiere Bazaar


Explanation of the warehouse structure in the DB

          warehouse is found in Adempiere in  Material Management/Material Management 
            Rules/Warehouse and Locator.        Here the warehouse, it(s) locator(s) and the storage per 
            locator can be seen. 


         In the table m_warehouse the warehouses are saved. 
            Each warehouse can have multiple Locators. The are registered in the table m_locator. 
            Locators have 
            ■   Search value (for fast lookup). A format-proposal for the value field is for        3- 
                dimensional warehouses xx-yy-zz, where 08-13-06 defines the 8. Aisle,             13.Din and dort 
                6. level. 
            ■   Aisle oder "X" 
            ■   Bin oder "Y" 
            ■   Level oder "Z" 
                A lot of locators for any kind of 3-dimensional warehouse can be modeled this way. 
                Each Locator and Product be addressed to a storage. 


                It is best practice to select this format for Search Key: warehouse xx-yy-zz. In reports it 
                would be displayed as: “Movement from central warehouse 23-44-3 to distribution 
                warehouse 21-44-4“. 


         Storages are saved in the table m_storage and have on hand quantity, reserverd quantity, 
            ordered quantity, last inventory count, etc as parameters. 


            It is allowed to use different storages for each product and locator,      so that a 3-dimensional 
            storage is not needed. 
            This is useful because e.g. with charges the same product can have different charge numbers 
            and warranty dates. 


            Eentries of m_storage  can not be deleted (delete from m_strorage where 
            ad_client_id=1000001 is useless too). If there is a wrong m_storage-entry, ad_client_id, 
            m_product or m_locator must be changed. 


                                        Page 89  



页面 90-----------------------

Developer’s Guide The ADempiere Bazaar


        SQL query to find the relation locator-warehouse 
           select w.m_warehouse_id, w.name, l.m_locator_id 
           FROM m_warehouse w 
           INNER JOIN m_locator l ON (w.m_warehouse_ID=l.m_warehouse_ID) 
           where w.ad_client_id=xxxxx 


           Pay attention that ad_client_id is correct. 


                                 Page 90  



页面 91-----------------------

Developer’s Guide The ADempiere Bazaar


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) 


                                  Page 91