Projects:Accounting Templates/Developers Guide
Contents |
Objective
While developing a new accounting template, posting logic of the ERP is altered. The posting logic of any document can be overwritten, so system do not execute the posting logic in core, but an alternative one. This is really usefull, for example, if we want a module modifying the posting logic of a core document.
Accounting Engine Flow
When a document is posted (whether manually, or triggered by the background process), the accounting server executes several actions, such as stablishing accounting schemas where document must be posted in, checking whether the periods are open or not, etc. Every document has got a class that implements the posting logic of the document. These classes are always sub-classes of the accounting server (org.openbravo.erpCommon.ad_forms.AcctServer).
The method that actually writes the entries in the accounting table in database (Fact_Acct) is the createFact() one.
The Accounting Templates
An accounting template is composed of two things:
- A class that implements a createFact() method.
- An entry in an accounting schema configuration, configuring this class as accounting template of a document posting process, as described in the accounting section of the configuration guide.
Let's focus on the class that must be implemented. Let's take, as an example, the DocInvoice java class, in charged of posting the invoices. At the beginning of the createFact method, it is checked whether an accounting template exists for the document type being posted or not:
public Fact createFact(AcctSchema as, ConnectionProvider conn, Connection con, VariablesSecureApp vars) throws ServletException { // Select specific definition 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); } }
Please realize that, if a template is found, then no more code of the method is executed, but it returns the result of executing the createFact mehod of the accounting template. It's also important to notice that the createFact function in the accounting template has got exactly the same parameters, plus the DocInvoice class itself, that it's actually an extension (SubClass) of AcctServer class. This way, the accounting template receives all the necessary data to perform the posting.
One example
The storno invoice module implements two new document types Purchase/Sales Storno Invoice. These document types do have same GL Category than AP Invoices, AR Invoices, AP Credit Memos and AR Credit Memos; because of this, DocInvoice will be the class in charged of posting them.
The storno invoice module is installed on top of Core. Then, accounting schema is modified so, bellow Accounting Schema -> Tables -> C_Invoice, two new Document records are added: AP Storno Invoice and AR Storno Invoice. For these two documents, accounting template included in the module is selected. This way, when an invoice is posted, nothing will happen, DocInvoice logic will be executed; but when posting a storno invoice, then the accounting template code will be executed.
Let's compare both pieces of code. For example, in DocInvoice, in case a AR Invoice (Sales Invoice) is being posted, for each invoice line, a new entry in the Fact_Acct table is written, through this piece of code:
fact.createLine( p_lines[i], // a DocLine_Invoice object, that contains information about one invoice line ((DocLine_Invoice) p_lines[i]).getAccount(ProductInfo.ACCTTYPE_P_Revenue, as, conn), // account used in the entry line this.C_Currency_ID, // currency of the entry "", // amount to the debit (0 in this case) p_lines[i].getAmount(), // amount to the credit column (line net amount, actually) Fact_Acct_Group_ID, nextSeqNo(SeqNo), // entry line number DocumentType, // c_doctype_id conn);
as the purpose of a sales storno invoice is to create the opposite entry of a sales invoice, an equivalent to the previous line is found in the template:
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()).negate().toString(), // Notice that the amount is the opposite one in this case. Fact_Acct_Group_ID, docInvoice.nextSeqNo(SeqNo), docInvoice.DocumentType, conn);
Notice how, everytime the template needs to reference an attribute of DocInvoice, it's as simple as invoke it through the first parameter provided to the createFact method in the accounting template: the DocInvoice object itself.