Documentation Index
Fetch the complete documentation index at: https://mintlify.com/idempiere/idempiere/llms.txt
Use this file to discover all available pages before exploring further.
Overview
iDempiere processes are server-side operations that execute business logic, data transformations, and batch operations. The process framework provides transaction management, parameter handling, and logging capabilities.
Base Package: org.compiere.process
Source Files:
org.adempiere.base/src/org/compiere/process/ProcessCall.java
org.adempiere.base/src/org/compiere/process/SvrProcess.java
ProcessCall Interface
The ProcessCall interface defines the contract for all executable processes.
Package: org.compiere.process
Interface Methods
startProcess
public boolean startProcess(Properties ctx, ProcessInfo pi, Trx trx)
Context containing session and environment variables
Process information including parameters, record selection, and results
Transaction to use (null for auto-created local transaction)
true if process completed successfully, false otherwise
This method is called by the framework to execute the process.
setProcessUI
public void setProcessUI(IProcessUI processUI)
User interface for process interaction (logging, asking questions)
Sets the UI interface for user interaction during process execution.
SvrProcess Base Class
SvrProcess is the abstract base class that implements ProcessCall and provides the standard framework for process development.
Package: org.compiere.process
Annotation: @org.adempiere.base.annotation.Process
Abstract Methods
You must implement these methods in your process class:
prepare
protected abstract void prepare()
Called before process execution to extract and validate parameters.
Example:
private int p_C_BPartner_ID = 0;
private Timestamp p_DateFrom = null;
private Timestamp p_DateTo = null;
private boolean p_IsSOTrx = true;
@Override
protected void prepare() {
ProcessInfoParameter[] para = getParameter();
for (int i = 0; i < para.length; i++) {
String name = para[i].getParameterName();
if (para[i].getParameter() == null) {
; // null parameter
}
else if (name.equals("C_BPartner_ID")) {
p_C_BPartner_ID = para[i].getParameterAsInt();
}
else if (name.equals("DateOrdered")) {
p_DateFrom = (Timestamp)para[i].getParameter();
p_DateTo = (Timestamp)para[i].getParameter_To();
}
else if (name.equals("IsSOTrx")) {
p_IsSOTrx = "Y".equals(para[i].getParameter());
}
else {
log.log(Level.SEVERE, "Unknown Parameter: " + name);
}
}
}
Starting from iDempiere 7.1, you can use the @Parameter annotation to automatically inject parameters as class fields, eliminating the need for manual parameter extraction.
Using @Parameter Annotation:
import org.adempiere.base.annotation.Parameter;
@Parameter(name = "C_BPartner_ID")
private int p_C_BPartner_ID;
@Parameter(name = "DateOrdered")
private Timestamp p_DateFrom;
@Parameter(name = "DateOrdered", isRange = true)
private Timestamp p_DateTo;
@Parameter(name = "IsSOTrx")
private boolean p_IsSOTrx;
@Override
protected void prepare() {
// Parameters are automatically injected - no manual extraction needed
// You can add additional validation here if needed
}
doIt
protected abstract String doIt() throws Exception
Success message to display to user (variables are parsed with Msg.parseTranslation)
Any exception to abort process and rollback transaction
Contains the main process logic.
Example:
@Override
protected String doIt() throws Exception {
if (p_C_BPartner_ID <= 0) {
throw new AdempiereUserError("@FillMandatory@ @C_BPartner_ID@");
}
int count = 0;
StringBuilder sql = new StringBuilder()
.append("SELECT C_Order_ID FROM C_Order ")
.append("WHERE C_BPartner_ID=? ")
.append("AND IsSOTrx=? ")
.append("AND DocStatus='CO'");
if (p_DateFrom != null && p_DateTo != null) {
sql.append(" AND DateOrdered BETWEEN ? AND ?");
}
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
pstmt = DB.prepareStatement(sql.toString(), get_TrxName());
pstmt.setInt(1, p_C_BPartner_ID);
pstmt.setString(2, p_IsSOTrx ? "Y" : "N");
if (p_DateFrom != null && p_DateTo != null) {
pstmt.setTimestamp(3, p_DateFrom);
pstmt.setTimestamp(4, p_DateTo);
}
rs = pstmt.executeQuery();
while (rs.next()) {
int orderId = rs.getInt(1);
processOrder(orderId);
count++;
}
} finally {
DB.close(rs, pstmt);
}
return "@Processed@ " + count + " @C_Order_ID@";
}
Optional Hook Methods
postProcess
protected void postProcess(boolean success)
true if the process completed successfully
Called after transaction commit/rollback, outside the transaction scope. Use this for operations that should not be part of the main transaction.
Example:
@Override
protected void postProcess(boolean success) {
if (success) {
// Send email notification (outside transaction)
sendNotificationEmail();
// Open a window to display results
// This is safe here since UI operations should be outside transaction
}
}
Inherited Methods
Get Context and Transaction
protected Properties getCtx()
protected String get_TrxName()
Current context or transaction name
Get Process Info
protected ProcessInfo getProcessInfo()
protected ProcessInfoParameter[] getParameter()
protected int getRecord_ID()
protected int getTable_ID()
return
ProcessInfo | ProcessInfoParameter[] | int
Process information, parameters, or identifiers
Transaction Control
protected void commitEx() throws SQLException
protected void rollback()
Manual transaction control methods.
In most cases, you should NOT manually commit or rollback. The framework handles this automatically. Only use these methods when you need explicit control over transaction boundaries.
Logging Methods
protected void addLog(String message)
protected void addLog(int id, Timestamp date, BigDecimal number, String msg)
protected void addLog(int id, Timestamp date, BigDecimal number, String msg,
int tableId, int recordId)
protected void addBufferLog(int id, Timestamp date, BigDecimal number, String msg,
int tableId, int recordId)
Numeric value (amount, quantity, etc.)
Add entries to the process log that will be displayed to the user.
Example:
@Override
protected String doIt() throws Exception {
int count = 0;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
pstmt = DB.prepareStatement(
"SELECT C_Order_ID, DocumentNo, GrandTotal FROM C_Order WHERE C_BPartner_ID=?",
get_TrxName()
);
pstmt.setInt(1, p_C_BPartner_ID);
rs = pstmt.executeQuery();
while (rs.next()) {
int orderId = rs.getInt(1);
String docNo = rs.getString(2);
BigDecimal total = rs.getBigDecimal(3);
processOrder(orderId);
// Add log entry with drill-down link
addLog(count, null, total, "Processed: " + docNo,
MOrder.Table_ID, orderId);
count++;
}
} finally {
DB.close(rs, pstmt);
}
return count + " orders processed";
}
Status Bar Updates
protected void statusUpdate(String message)
Status message to display in UI
Update the status bar during long-running processes.
Complete Process Example
package com.example.process;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.util.logging.Level;
import org.adempiere.exceptions.AdempiereUserError;
import org.compiere.model.MOrder;
import org.compiere.model.MOrderLine;
import org.compiere.process.ProcessInfoParameter;
import org.compiere.process.SvrProcess;
import org.compiere.util.DB;
/**
* Update Order Prices
*
* Process to update order line prices based on current price list
*/
public class UpdateOrderPrices extends SvrProcess {
/** Business Partner */
private int p_C_BPartner_ID = 0;
/** Sales Transaction */
private boolean p_IsSOTrx = true;
/** Date Range */
private Timestamp p_DateFrom = null;
private Timestamp p_DateTo = null;
/**
* Prepare - extract parameters
*/
@Override
protected void prepare() {
ProcessInfoParameter[] para = getParameter();
for (int i = 0; i < para.length; i++) {
String name = para[i].getParameterName();
if (para[i].getParameter() == null)
;
else if (name.equals("C_BPartner_ID"))
p_C_BPartner_ID = para[i].getParameterAsInt();
else if (name.equals("IsSOTrx"))
p_IsSOTrx = "Y".equals(para[i].getParameter());
else if (name.equals("DateOrdered")) {
p_DateFrom = (Timestamp)para[i].getParameter();
p_DateTo = (Timestamp)para[i].getParameter_To();
}
else
log.log(Level.SEVERE, "Unknown Parameter: " + name);
}
}
/**
* Process - update order prices
*/
@Override
protected String doIt() throws Exception {
// Validation
if (p_C_BPartner_ID <= 0)
throw new AdempiereUserError("@FillMandatory@ @C_BPartner_ID@");
int orderCount = 0;
int lineCount = 0;
// Find orders
StringBuilder sql = new StringBuilder()
.append("SELECT C_Order_ID, DocumentNo ")
.append("FROM C_Order ")
.append("WHERE C_BPartner_ID=? ")
.append("AND IsSOTrx=? ")
.append("AND DocStatus IN ('DR','IP') ");
if (p_DateFrom != null && p_DateTo != null)
sql.append("AND DateOrdered BETWEEN ? AND ? ");
sql.append("ORDER BY DateOrdered");
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
pstmt = DB.prepareStatement(sql.toString(), get_TrxName());
pstmt.setInt(1, p_C_BPartner_ID);
pstmt.setString(2, p_IsSOTrx ? "Y" : "N");
if (p_DateFrom != null && p_DateTo != null) {
pstmt.setTimestamp(3, p_DateFrom);
pstmt.setTimestamp(4, p_DateTo);
}
rs = pstmt.executeQuery();
while (rs.next()) {
int orderId = rs.getInt(1);
String docNo = rs.getString(2);
statusUpdate("Processing: " + docNo);
int lines = updateOrderLines(orderId);
if (lines > 0) {
addLog(orderCount, null, BigDecimal.valueOf(lines),
"Updated: " + docNo, MOrder.Table_ID, orderId);
orderCount++;
lineCount += lines;
}
}
} finally {
DB.close(rs, pstmt);
}
return "@Updated@ " + orderCount + " @C_Order_ID@, " +
lineCount + " @C_OrderLine_ID@";
}
/**
* Update prices for all lines in an order
*/
private int updateOrderLines(int orderId) throws Exception {
MOrder order = new MOrder(getCtx(), orderId, get_TrxName());
MOrderLine[] lines = order.getLines();
int count = 0;
for (MOrderLine line : lines) {
if (line.getM_Product_ID() > 0) {
BigDecimal oldPrice = line.getPriceActual();
// Recalculate price from price list
line.setPrice();
BigDecimal newPrice = line.getPriceActual();
if (oldPrice.compareTo(newPrice) != 0) {
line.saveEx();
count++;
log.fine("Updated line " + line.getLine() +
": " + oldPrice + " -> " + newPrice);
}
}
}
// Recalculate order totals if lines were updated
if (count > 0) {
order.saveEx();
}
return count;
}
/**
* Post process - send notifications
*/
@Override
protected void postProcess(boolean success) {
if (success) {
log.info("Price update completed successfully");
// Could send email notification here
}
}
}
Process Registration
To make your process available in iDempiere:
- Compile your process class and include it in a plugin/jar
- Register in Application Dictionary:
- Go to System Admin → General Rules → Process
- Create new process record
- Set Classname to your process class (e.g.,
com.example.process.UpdateOrderPrices)
- Define parameters in Process Parameter tab
- Add to Menu or assign to button/toolbar
Process Events
The process framework fires OSGi events at key points:
IEventTopics.BEFORE_PROCESS - Before prepare() and doIt()
IEventTopics.AFTER_PROCESS - After doIt() success
IEventTopics.POST_PROCESS - After transaction commit (corresponds to postProcess())
You can listen to these events using Event Handlers.
Best Practices
- Parameter Validation: Always validate parameters in
prepare() or at the start of doIt()
- Transaction Management: Let the framework handle transactions unless you have specific needs
- Logging: Use
addLog() for user-facing information, log for debugging
- Error Handling: Throw exceptions with user-friendly messages using
@Variable@ syntax
- Performance: For long operations, update status with
statusUpdate()
- Resource Cleanup: Always use try-finally blocks for JDBC resources
- Commit Scope: Keep transactions as short as possible
- Use @Parameter: For iDempiere 7.1+, use annotation-based parameter injection
See Also