# Extending ADempiere
## Introduction

* The great thing about Open Source ERP is that you can extend it. This is part of the overall course on Adempiere Technical Training.
* Note that the spelling for ADempiere in the title is proper as it is defined here.

## Extension architecture

* Callouts
* Model Validator
* Java Triggers
* Processes
* Views and Reports
* Forms
* Print Formats
* Import File Loader

## Common code
### Context variables

Context variables are like 'global' variables for the whole Adempiere, or for a specific window or tab.
You can see all context variables in Tools -> Preference -> Context
Context variables starting with # are defined at login level
Every field in a window has his own context variable, you can access it programatically.
Reading context variable AD_Role_ID

### Creating object (new record, update record, delete record)

You can get or create objects from the database using the model classes, for example to read an invoice from the database:
This is equivalent to issue a 'SELECT * FROM C_Invoice' and load in memory the data.
To create a new record in the database you call the same code with zero as the ID:
You then can make changes to columns using the setter methods of the object, i.e.:
After you make all the changes you save the record in the database calling the method save:
The method save will execute an INSERT if is a new record or an UPDATE if the record already exists. If you want to delete an existing record, you can call the method delete, i.e.
and after deleting you need to call the method save to execute the corresponding DELETE in the database

### Use messages for translations

To show messages within your programs, please use the i18n facility of Adempiere using the message table for this purpose, programatically you simply execute, i.e.:

### SQL

* Usage of embedded SQL – recommended to use UpperCase in SQL Keywords (for security sql parser)
** Specially this keywords: SELECT, FROM, WHERE, ON, AS, INNER, JOIN, LEFT, OUTER, FULL
* When using joins, security parser also requires that ON clause defining the joining columns must be enclosed in parenthesis
* use Oracle SQL standard syntax that can be translated to postgres – Don't use postgres specific syntax
If you want to issue a simple query returning just one column of the first record, you can use the DB.getSQLValue functions provided by Adempiere:
To execute a DML operation in the database (INSERT, DELETE or UPDATE) you can use the DB.executeUpdate method, i.e.:
To read several records from the database (cursor) you can execute

### Commit (not common – used with transactions)

Normally you don't need to execute directly a commit. If in your process you need strict control of the commit, please create a transaction:

## Callouts

Called after user entered value (for Strings is called every keystroke)
It can be used for direct data validation – but you need to validate again before saving
Better usage for data consequences: i.e. Filling other fields with lookup values, calculating totals You must repeat all calculations in persistence layer
You can have multiple callouts separated by ;
The processing of a callout must be quick (< 1 sec), if slow better create a button and process
Callout classes must extend CalloutEngine and implement the callout methods with 6 parameters (last optional)
* Context
* Window No
* Model Tab
* Model Field
* The new value
* The old value
* 'Returns' error message
There are also callouts for translation of strings on Import File Loader, these callouts have just one parameter:
* The value
* 'Returns' the translated value
Exercise: Create a Callout on the Name of Product Category – the name must be passed to the Description in upper case

## Model Classes (lookup)

Persistence
PO (Persistent Object) --> X_<table> classes --> M<shortTable>
Class X_ is looked up with complete Name (and case) of the table
Class M is looked up with table name without prefix (if prefix is <= 2 char), and without underscore symbols “_”
Precedence when looking up for M classes:
* Look for model package of the entity type (for non-dictionary tables) – first look for M and then X_
* look for model package in:
* look for adempiere.model.X_ class
* look for compiere.model.X_ class
* look for org.compiere.model.X_ class
Exceptions:
If can't find persistence class then it uses PO to save directly to the database. In Adempiere there is a GenericPO that can be used for programatically save tables with no model class.
X_ classes are generated automatically – don't change them Usage of GenerateModel: arguments:
* Output Directory - C:\srcAdempiere\trunk\base\src\org\compiere\model\
* Package - org.compiere.model
* EntityType - 'D'
* Optional table like - 'U_RoleMenu'

## Model Classes (triggers)

Insert/Update triggers: beforeSave and afterSave
Delete triggers: beforeDelete and afterDelete

## Model validator

Defined at client level
You can have multiple model validators separated by ;
Events:
* User Login – you know the user, role, client and organization – i.e. Useful for veto login
Events on table:
* BEFORE/AFTER
* NEW/ CHANGE/ DELETE
Events on documents:
* BEFORE/AFTER
* PREPARE/ VOID/ CLOSE/ REACTIVATE/ REVERSECORRECT/ REVERSEACCRUAL/ COMPLETE / POST

## Model Classes vs Validators

* Don't customize adempiere model classes – implement triggers for official tables in model validator
* Use model classes (or model validator if preferred) for your customized tables
* Don't generate X_ classes for official adempiere tables – use general getter and setter from PO for custom columns
Exercises:
* Create a ModelValidator that avoid users login twice in Adempiere
* Create a ModelValidator when Order change the business partner – add the previous bp to the description
* Create a ModelValidator to forbid GardenAdmin to complete an Invoice
* Create a ModelValidator to avoid completely posting from invoices

## Processes

Called from menu or buttons prepare method for getting the parameters into variables – Record_ID just work for buttons
doIt method for execution of the process
addLog to keep log of the executions / for auditing purposes and showed at the end of the process
return a message
for errors must throw exceptions

## Forms

Custom swing windows – special cases Not recommended – not supported for webUI clients
* Extend org.compiere.swing.CPanel
* Swing code – implement listeners
* Use javax.swing and org.compiere.swing components

## Import File Loader

* Stage table
* File definition
* Import of the file
* Import process to pass from the stage table to the definite tables
Exercise:
* Add POReference column to the I_Invoice stage table
* Change the ImportInvoice process to manage the import of this new column

## Other Resource

* ADempiere Wiki on New Window Creation