How to create a Module that Overrides Accounting
Languages: |
English | Translate this article... |
About this document
This document is part of the Localization Guide for Openbravo and describes the process of creating a module that overrides the default accounting behaviour implemented in Core, allowing the developer to modify the way entries are generated when posting documents.
Recommended articles
Before reading this howto it is important to have a clear understanding about the Modularity Concepts, specially the process of creating and packing modules.
During the development of this module it will be necessary to create a dataset that includes the accounting template configuration. In case you haven't created yet a dataset, it is recommended to firstly read the How to create a Dataset article to get a general idea before moving forward.
From the functional side you should have a general idea about accounting configuration in Openbravo, specially the role of the General Ledger Configuration, and how the configuration defined at the Active Tables and Documents tabs affects the accounting engine.
The development is based on the concept of Accounting Templates, so it would be recommended to read its project documentation.
As you can imagine, the logic for overriding the default accounting entries must be implemented using Java. It is required to have good level in Java for developing this module.
Finally it is highly recommended to use Eclipse IDE. Please refer to the How to setup Eclipse IDE article if you haven't done it yet.
Estimated effort
The Openbravo accounting engine provides an infrastructure that easily allows the implementation of code that overrides its behaviour. The effort for developing this kind of modules highly depends on the complexity required for implementing the logic that generates the new accounting entries.
The flow for developing this logic usually consists on copying the original code in charge of creating the entries, which is distributed inside Core under the Openbravo Public License, and adapting it to the particular requirements.
As a rough estimation, the whole process of creating the module, testing and publishing in the Forge shouldn't take more than 4 days for an average developer.
Introduction
The accounting entries generated by Openbravo are usually fine in most scenarios, however there could be the case where these entries don't fulfill the legal requirements for a concrete country, making it necessary to generate them in a different way.
To support this kind of requirements, Openbravo allows to override the code that generates the accounting entries through what is called Accounting Templates. Each time a document is posted, the accounting engine checks whether there is defined any Accounting Template for the associated table or for the concrete document and execute it instead of the default Core's code.
This is a powerful feature that must be used with caution. The code that generates the accounting entries must be deeply tested before deploying it in a real instance.
Creating the module definition
Each time we plan to develop a new module, the first step must always be to create the module's definition and to register it in the Central Repository.
These are the special considerations for our module:
- You should try to follow the Naming guidelines for modules
- The flag Has reference data must be set because our module will contain a dataset with the Accounting Template configuration. Remember to write any useful information inside the Reference Data Description field.
- Define the mandatory dependency to Core
- Include a DB Prefix for your module in case it's necessary. For example, your module will probably define new messages that will require a DB Prefix.
- If your module has UI elements that can be translated (like messages), set the Translation Required checkbox and specify your Module Language.
- Remember to register the module in case you want to publish it in the Forge
Now you should export the database to generate the file structure inside the module folder
ant export.database
Finally, inside the modules/<accounting template java package> directory create the src folder, where we will store the Java class that implements the logic for generating the new accounting entries.
Setting up Eclipse
In case you are using Eclipse, you must now add the modules' src directory in the Openbravo project Build path. Right click on the openbravo project and select Build Path > Configure Build Path
Now, inside the Source tab, press Add Folder... button and select your module src directory.
In case the src directory is not shown, you may first need to refresh the openbravo project in Eclipse.
Creating the Accounting Template Configuration
The Java class that implements the generation of the new accounting entries is defined into the Accounting Templates window. The definition is quite simple and only requires a name, the Java class name, which must be obviously inside the java package of our module, and the table for which we want to override its accounting entries (for example, C_Invoice for Invoices, M_InOut for goods shipments/receipts, etc.)
This Accounting Template will be later on associated to the Active Tables or a Document. So in this step is important to define as many Java classes as Active Tables or Document to override their accounting behavior.
For example, we can define an unique java class for overriding the accounting behavior of all the invoices (sales invoices, purchase invoices, purchase/sales credit memo, etc.), or alternatively define a class for overriding only the accounting entries for Purchase Invoices (AP Invoice) and keep the default behavior to the rest of the invoices.
In this how to we are going to create just one template to override the Invoice table, but it's up to you to create as many templates as necessary inside the same module.
Dataset definition
Dataset definition is a key step in this process. A wrong dataset definition can waste all our previous work, so it is important to follow all these considerations:
- The dataset must belong to your accounting template module
- Try to avoid strange characters in the dataset's name. This string is used for generating the XML file name that stores the dataset.
- The Data Access Level must be set to System/Client, which means we allow users to apply the configuration only at Client level (Organization *).
- The Export allowed flag must be set.
- Inside the Table tab we must include the AD_CreateFact_template table, which is the one that stores the Accounting Template configuration.
- The HQL/SQL Where clause is a very important field, because it allows us to filter the records we want to include into the dataset. In the example we have filtered all the records that are inside our module java package name.
The dataset definition is ready, so we just need to export it to a file pressing the Export Reference Data button. This process queries the previous tables and gets all the records that fulfill the HQL/SQL Where clause, generating a XML file inside the module's referencedata/standard directory. As a fast check, you can open this file using any plain text editor and you can verify that it contains several lines.
In case the file is empty, you should double check the dataset definition, specially the HQL/SQL Where clause used for each table.
Testing the Dataset
The real test to ensure the taxes dataset is OK can be done inside our development instance. The test consists on creating a new client running the Initial Client Setup and select our new dummy accounting template dataset.
If the data inside the dataset are consistent, the Initial Client Setup Process should be completed successfully, otherwise it will fail giving a description about the error.
After a successful Initial Client Setup, we just need to login into the new client, go to the Accounting Templates window and check our record is there.
Implementing the accounting logic
Up to now we have just created the module definition and the required configuration that is included into a dataset. It is time now to develop the logic for creating the new accounting entries.
Dummy accounting entries example
As an example for this article we are going to create an accounting template for Invoices that generates exactly the same accounting entries but multiplied by 2.
Example: we have registered a sales invoice that originally generates this accounting entries:
If we now unpost and post the same invoice using our dummy accounting template, all the entries will have a double amount.
Creating the Java Package
Inside the Creating Accounting Template Configuration we set org.openbravo.localizationguide.accountingtemplate.DummyAccountingTemplate as the Java Class Name that will implement the new logic for the accounting entries.
Let's first create the org.openbravo.localizationguide.accountingtemplate package inside our module src directory. If you are using Eclipse you can easily create (Ctrl-N) the new package inside the modules/<your module>/src folder
Creating the Java Class
A bit of theory
At the org.openbravo.erpCommon.ad_forms package there is a set of classes which start by Doc and that implement the default accounting behaviour in Core. Examples of this classes are DocInvoice.java, DocAmortization.java or DocInOut.java that implement the accounting logic for Invoices, Amortizations and Goods shipments/receipts respectively. As you can see, all these classes extend from the Accounting Server class, which means they implement accounting logic.
You have probably noticed that, apart from these classes, we have other classes with the same name pattern but ending in Template, like DocInvoiceTemplate.java. This kind of classes are the ones that allow overriding the accounting behaviour of a concrete class. Let's see how they look like:
public abstract class DocInvoiceTemplate { private static final long serialVersionUID = 1L; public DocInvoiceTemplate() {} public abstract Fact createFact(DocInvoice docInvoice, AcctSchema as, ConnectionProvider conn, Connection con, VariablesSecureApp vars) throws ServletException; }
The above code belongs to the DocInvoiceTemplate.java file, but it can be extrapolated to the rest of the Doc*Template classes. You can see it is an abstract class that just defines the abstract method createFact(), which is the one in charge of creating the accounting entries.
As you are probably imaging, the Java class that will override the default accounting behaviour must extend from this kind of Doc*Template classes. All the logic for creating the entries, which at the end is just inserting the records in the Fact_Acct table, must be developed inside the createFact() method.
Notice that the signature of this createFact() method is almost the same as the defined into the Accounting Server's createFact() abstract method; the only difference is that it has the new parameter DocInvoice docInvoice. This object is the instance of the original DocInvoice object, which already contains all necessary information for implementing our accounting logic (document type, lines, amounts...).
Implementing the createFact() method is for sure the most difficult part in the creation of this module. The good news for the developer is that he can take as a base the original code and adapt it inside the new class with just a few considerations:
- The logic for overriding the accounting logic is embedded at the beginning of the original createFact() method. This logic must not be inserted again in our createFact() method
String strClassname = AcctServerData .selectTemplateDoc(conn, as.m_C_AcctSchema_ID, DocumentType); if (strClassname.equals("")) strClassname = AcctServerData.selectTemplate(conn, as.m_C_AcctSchema_ID, AD_Table_ID); if (!strClassname.equals("")) { try { DocInvoiceTemplate newTemplate = (DocInvoiceTemplate) Class.forName(strClassname) .newInstance(); return newTemplate.createFact(this, as, conn, con, vars); } catch (Exception e) { log4j.error("Error while creating new instance for DocInvoiceTemplate - " + e); } }
- All the references to the object this inside the original code must be replaced by the docInvoice object passed as parameter. Example:
Fact fact = new Fact(this, as, Fact.POST_Actual);
will be transformed to:
Fact fact = new Fact(docInvoice, as, Fact.POST_Actual);
- Some references to objects, attributes or methods must be now obtained through the docInvoice object. Moreover the access to attributes must be sometimes wrapped by the correspondent getter method. Example:
getConvertedAmt(m_debt_payments[i].Amount, m_debt_payments[i].C_Currency_ID_From, this.C_Currency_ID, DateAcct, "", AD_Client_ID, AD_Org_ID, conn)
will be transformed to:
docInvoice.getConvertedAmt(docInvoice.m_debt_payments[i].getAmount(), docInvoice.m_debt_payments[i].getC_Currency_ID_From(), docInvoice.C_Currency_ID, docInvoice.DateAcct, "", docInvoice.AD_Client_ID, docInvoice.AD_Org_ID, conn)
A bit of Java knowledge background and the help provided by Eclipse should be enough to properly implement your own createFact() method.
After this theoretical explanation, let's now continue with our dummy example for Invoices...
Implementing the logic
Inside the recently created package, we must now create the Java Class that will be in charge of generating the new accounting entries. This class, that in our example is called DummyAccountingTemplate, must extend from the org.openbravo.erpCommon.ad_forms.DocInvoiceTemplate class:
Extending from the DocInvoiceTemplate class implies we must implement the createFact(DocInvoice docInvoice, AcctSchema as, ConnectionProvider conn, Connection con, VariablesSecureApp vars) method.
package org.openbravo.localizationguide.accountingtemplate; import java.sql.Connection; import javax.servlet.ServletException; import org.openbravo.base.secureApp.VariablesSecureApp; import org.openbravo.database.ConnectionProvider; import org.openbravo.erpCommon.ad_forms.AcctSchema; import org.openbravo.erpCommon.ad_forms.DocInvoice; import org.openbravo.erpCommon.ad_forms.DocInvoiceTemplate; import org.openbravo.erpCommon.ad_forms.Fact; public class DummyAccountingTemplate extends DocInvoiceTemplate { @Override public Fact createFact(DocInvoice docInvoice, AcctSchema as, ConnectionProvider conn, Connection con, VariablesSecureApp vars) throws ServletException { } }
As we have seen in the previous chapter, we can get the original createFact method from the DocInvoice class as a base and adapt it to our needs.
If you remember, the objective of our DummyAccountingTemplate was to multiply by 2 the accounting entries amounts. Let's see an example of how to do it:
public class DummyAccountingTemplate extends DocInvoiceTemplate { @Override public Fact createFact(DocInvoice docInvoice, AcctSchema as, ConnectionProvider conn, Connection con, VariablesSecureApp vars) throws ServletException { // create Fact Header Fact fact = new Fact(docInvoice, as, Fact.POST_Actual); String Fact_Acct_Group_ID = SequenceIdData.getUUID(); // <some stuff hidden for simplicity> // accounting entry fact.createLine(docInvoice.p_lines[i], ((DocLine_Invoice) docInvoice.p_lines[i]).getAccount(ProductInfo.ACCTTYPE_P_Revenue, as, conn), docInvoice.C_Currency_ID, "", new BigDecimal(docInvoice.p_lines[i].getAmount()).multiply(new BigDecimal("2")).toString(), // Notice that the amount is multiplied by 2 Fact_Acct_Group_ID, docInvoice.nextSeqNo(docInvoice.getSeqNo()), docInvoice.DocumentType, conn); // <more stuff hidden for simplicity> return fact; } }
The fact.createLine() method is in charge of creating the entry that will be later on inserted into the Fact_Acct table. For our development we have used the original code found in the createFact() method of the DocInvoice class, adapting it to our needs. Apart from the usual required changes explained in the previous chapter, we have just multiplied by 2 the accounting entry amount.
The same logic should be applied for the rest of the createLine() occurrences to complete our DummyAccountingTemplate class.
Remember to compile and to restart tomcat after finishing the code development.
General Ledger configuration
The final step to override the default accounting behavior is manually done in the General Ledger Configuration window. The end-user must specify the General Ledgers(s) for which he wants to use the new Accounting Template. This configuration can be done either at Active Tables or at Documents level.
In our example we want to override the accounting behavior for all the Invoices, that's why we will define the template at Active Table level.
We will use the F&B sample client distributed with Openbravo, because it already contains documents ready to be posted. Remember to apply our configuration dataset to this client in case your Accounting Template is not available.
Important: Take into account that the end-user must manually perform this configuration in his instance. You should probably include this critical step into the manual configuration for your module. Alternatively, to make your end-user's life easier, you can develop a module script that transparently configures all the General Ledgers available in the instance.
Testing the code
After updating the General Ledger configuration everything is ready, so it's time to test it.
The test is quite simple. First of all we should check the accounting entry of any Invoice that we have previously posted using the original accounting logic. We can get them by pressing the Unpost button without checking the Delete accounting entry option.
Once we have written down the amounts, we move back to the same invoice and we unpost it, but this time checking the Delete accounting entry option. After that we post it again (now the system will use our Accounting Template), and we check the new amounts. If everything is OK, all the amounts are multiplied by 2
Packaging the module
To generate the obx file we should follow the standard steps from the command line:
- Export the database:
ant export.database
- Package the module:
ant package.module -Dmodule=<accounting template module java package>
- We can now publish the module in the Central Repository