How to create a new document with specific accounting

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

Contributed By

  • Fabian Aguilar from OFB Consulting ltda

Steps To Create New Document Type

  • In order to generate a new document within ADempiere that has a specific accounting to be generated upon completion of the document, it is necessary to complete the following 6 steps. For this example it is assumed that the new document behaves similarly to an invoice but generates different accounting transactions:

1. Login with SuperUser/System and System Administrator's role. Go to Application Dictionary -> References and choose the C_DocType DocBaseType reference. Select the List Validation tab and add a new entry. For Search Key we need a 3 capital letter code that identifies our new document base. For our example we will add 'PTK'. So we will have:

     Search Key:PTK

2. Go to the Application Dictionary -> Validation Rules and select the C_DocType AR/AP Invoices and Credit Memos validation rule. Add 'PTK' to the list of valid choices so it becomes:

     C_DocType.DocBaseType IN ('ARI', 'API','ARC','APC','PTK') AND C_DocType.IsSOTrx='@IsSOTrx@'

3. Create a new document type. Login with your client's administrator's login. For GardenWorld this is GardenAdmin/GardenAdmin and GardenWorld Admin Role. Go to Performance Analysis->Accounting Rules->Document Type and create a new document type. For document base type choose DOCTYPE_PTCheck.

Assigning To Invoice Type

4. In the java class, ADempiere defines the documents supported by the application. We need to add the following statement:

     public static final String DOCTYPE_NInvoice = "PTK";

5. There is a class in ADempiere (lokated in base/src org.compiere.model) which contains all document types in the system. You need to regenerate that class in order to let Adempiere know about your new document type. To regenerate it simply run GenerateModel with the appropriate arguments. For example when the following arguments are passed to GenerateModel:

     "D:\\eclipseworkspace\\customl_340\\base\\src\\org\\compiere\\model\\" "org.compiere.model" "'D'" "'C_DocType'" "Client"
  • It will regenerate the X_C_DocType and I_C_DocType classes in the directory D:\\eclipseworkspace\\customl_340\\base\\src\\org\\compiere\\model\\ and in package org.compiere.model (Search the wiki for Create your customization environment for more information on customizing ADempiere).
  • If you omit this step at some point you may have troubles bcause certain functionality relies on having all document types specified in (closing periods I think is one of them).

6. Every document table has a class that defines how the accounting transactions are generated. For instance the tables C_Order and C_Invoice have the corresponding and related classes and Within these classes you will find a method named "createFacts". This method contains the logic for the document accounting generation.

Extending Document Type Class

  • In reviewing the method you will find the following structure for each document:
if (getDocumentType().equals("A_Type_of_Document")

else if (getDocumentType().equals("Another_Type_Of_Document") )


Create Facts For New Document Type

In order to add accounting for our newly added document (PTCheck) we need to add the following in the (since our document behaves similarly to an invoice):

else if (getDocumentType().equals(DOCTYPE_PTCheck)) //PTK
  BigDecimal grossAmt = getAmount(Doc.AMTTYPE_Gross);
  BigDecimal serviceAmt = Env.ZERO;
  //  Header Charge           CR
  BigDecimal amt = getAmount(Doc.AMTTYPE_Charge);
  if (amt != null && amt.signum() != 0)
   fact.createLine(null, getAccount(Doc.ACCTTYPE_Charge, as),
    getC_Currency_ID(), null, amt);
  //  TaxDue                  CR
  for (int i = 0; i < m_taxes.length; i++)
   amt = m_taxes[i].getAmount();
   if (amt != null && amt.signum() != 0)
    FactLine tl = fact.createLine(null, m_taxes[i].getAccount(DocTax.ACCTTYPE_TaxDue, as),
     getC_Currency_ID(), null, amt);
    if (tl != null)
  //  Revenue                 CR
  for (int i = 0; i < p_lines.length; i++)
   amt = p_lines[i].getAmtSource();
   BigDecimal dAmt = null;
   if (as.isTradeDiscountPosted())
    BigDecimal discount = p_lines[i].getDiscount();
    if (discount != null && discount.signum() != 0)
     amt = amt.add(discount);
     dAmt = discount;
   fact.createLine (p_lines[i],
    p_lines[i].getAccount(ProductCost.ACCTTYPE_P_Revenue, as),
    getC_Currency_ID(), dAmt, amt);
   if (!p_lines[i].isItem())
    grossAmt = grossAmt.subtract(amt);
    serviceAmt = serviceAmt.add(amt);
  //  Set Locations
  FactLine[] fLines = fact.getLines();
  for (int i = 0; i < fLines.length ; i++)
   if (fLines[i] != null)
    fLines[i].setLocationFromOrg(fLines[i].getAD_Org_ID(), true);      //  from Loc
    fLines[i].setLocationFromBPartner(getC_BPartner_Location_ID(), false);  //  to Loc
  //  Receivables     DR
  int receivables_ID = getValidCombination_ID(Doc.ACCTTYPE_BankAsset, as);
  int receivablesServices_ID = getValidCombination_ID (Doc.ACCTTYPE_BankAsset, as);
  if (m_allLinesItem || !as.isPostServices()
   || receivables_ID == receivablesServices_ID)
   grossAmt = getAmount(Doc.AMTTYPE_Gross);
   serviceAmt = Env.ZERO;
  else if (m_allLinesService)
   serviceAmt = getAmount(Doc.AMTTYPE_Gross);
   grossAmt = Env.ZERO;
  if (grossAmt.signum() != 0)
   fact.createLine(null, MAccount.get(getCtx(), receivables_ID),
    getC_Currency_ID(), grossAmt, null);
  if (serviceAmt.signum() != 0)
   fact.createLine(null, MAccount.get(getCtx(), receivablesServices_ID),
    getC_Currency_ID(), serviceAmt, null);

Double Entry Logic

The "Revenue CR" comment marks the code that searches the Credit account (P_Revenue in the example) and the "Receivables DR" comment marks the code that searches the Debit account (BankAsset in the example).

Create Fact Lines Logic

The fact.createLine statement uses a format like this:

fact.createLine (p_lines[i],p_lines[i].getAccount(ProductCost.ACCTTYPE_P_Revenue, as),getC_Currency_ID(), dAmt, amt);

where p_lines[i].getAccount(ProductCost.ACCTTYPE_P_Revenue, as) is the method that searches the account needed for posting. This method, in turn, calls the getValidCombination_ID method in the class