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
The PO (Persistent Object) class is the abstract base class for all persistent objects in iDempiere. It provides the foundation for database operations, value management, and the model-view-controller pattern implementation.
Package: org.compiere.model
Source: org.adempiere.base/src/org/compiere/model/PO.java
Key Features:
- CRUD operations (Create, Read, Update, Delete)
- Value getters and setters with type safety
- Transaction management
- Validation hooks (beforeSave, afterSave, beforeDelete, afterDelete)
- Context and tenant management
- UUID and multi-key support
Constructors
Create New Record
public PO(Properties ctx)
Context containing session and environment variables
Load by ID
public PO(Properties ctx, int ID, String trxName)
The unique ID of the record (0 for new record)
Transaction name (null for auto-commit)
Load by UUID
public PO(Properties ctx, String UUID, String trxName)
The unique UUID of the record (empty string "" for new record)
Load from ResultSet
public PO(Properties ctx, ResultSet rs, String trxName)
ResultSet positioned at current row to load
Value Management
Get Value by Column Name
public final Object get_Value(String columnName)
Current value of the column, or null if not set
Example:
MOrder order = new MOrder(ctx, 1000000, trxName);
String docNo = (String) order.get_Value("DocumentNo");
Integer bpID = (Integer) order.get_Value("C_BPartner_ID");
Get Typed Values
public int get_ValueAsInt(String columnName)
public String get_ValueAsString(String columnName)
public boolean get_ValueAsBoolean(String columnName)
Value converted to the specified type
Example:
MOrder order = new MOrder(ctx, 1000000, trxName);
int bpID = order.get_ValueAsInt("C_BPartner_ID");
String docNo = order.get_ValueAsString("DocumentNo");
boolean isSOTrx = order.get_ValueAsBoolean("IsSOTrx");
Get Old Value
public final Object get_ValueOld(String columnName)
public int get_ValueOldAsInt(String columnName)
Previous value before modification
Set Value
public final boolean set_Value(String columnName, Object value)
Value to set (must match column type)
true if value was set successfully, false if column is read-only or virtual
Example:
MOrder order = new MOrder(ctx, 0, trxName); // new record
order.set_Value("C_BPartner_ID", 1000001);
order.set_Value("DateOrdered", new Timestamp(System.currentTimeMillis()));
order.set_Value("IsSOTrx", true);
Set Value Without Checks
public final boolean set_ValueNoCheck(String columnName, Object value)
Use this method carefully - it bypasses standard validation and updateable checks. Primarily used for system fields like Created, CreatedBy, etc.
Save Operations
Save
true if save was successful, false otherwise
Saves the record to the database. For new records, performs INSERT. For existing records, performs UPDATE only if values changed.
Example:
MOrder order = new MOrder(ctx, 0, trxName);
order.setC_BPartner_ID(1000001);
order.setC_DocType_ID(1000000);
order.setDateOrdered(new Timestamp(System.currentTimeMillis()));
if (order.save()) {
log.info("Order saved: " + order.getDocumentNo());
} else {
log.severe("Failed to save order: " + CLogger.retrieveErrorString(""));
}
Save with Exception
public void saveEx() throws AdempiereException
Same as save() but throws exception on failure instead of returning false.
Example:
try {
MOrder order = new MOrder(ctx, 0, trxName);
order.setC_BPartner_ID(1000001);
order.setC_DocType_ID(1000000);
order.saveEx();
log.info("Order saved successfully");
} catch (AdempiereException e) {
log.severe("Save failed: " + e.getMessage());
throw e;
}
Save with Transaction
public boolean save(String trxName)
public void saveEx(String trxName) throws AdempiereException
Transaction name to use for this save operation
true if save was successful (save() version only)
Delete Operations
Delete
public boolean delete(boolean force)
If true, delete even if record is processed
true if delete was successful, false otherwise
Example:
MOrder order = new MOrder(ctx, 1000000, trxName);
if (order.delete(false)) {
log.info("Order deleted successfully");
} else {
log.severe("Failed to delete: " + CLogger.retrieveErrorString(""));
}
Delete with Exception
public void deleteEx(boolean force) throws AdempiereException
public void deleteEx(boolean force, String trxName) throws AdempiereException
If true, delete even if record is processed
Transaction name (optional)
Validation Hooks
Before Save
protected boolean beforeSave(boolean newRecord)
true if this is a new record being inserted
true to continue save, false to abort
Override this method to perform validation and business logic before saving.
Example:
@Override
protected boolean beforeSave(boolean newRecord) {
// Validate business partner
if (getC_BPartner_ID() <= 0) {
log.saveError("FillMandatory", Msg.getElement(getCtx(), "C_BPartner_ID"));
return false;
}
// Set default values for new records
if (newRecord) {
if (getDateOrdered() == null)
setDateOrdered(new Timestamp(System.currentTimeMillis()));
}
return true;
}
After Save
protected boolean afterSave(boolean newRecord, boolean success)
true if this was a new record insertion
true if save operation succeeded
true to commit, false to rollback
Example:
@Override
protected boolean afterSave(boolean newRecord, boolean success) {
if (!success)
return false;
// Create order lines for new order
if (newRecord) {
createDefaultLines();
}
// Update totals
if (!newRecord && (is_ValueChanged("GrandTotal") || is_ValueChanged("C_Currency_ID"))) {
updateBPartnerBalance();
}
return true;
}
Before Delete
protected boolean beforeDelete()
true to continue delete, false to abort
Example:
@Override
protected boolean beforeDelete() {
// Check if order has shipments
String sql = "SELECT COUNT(*) FROM M_InOut WHERE C_Order_ID=?";
int count = DB.getSQLValue(get_TrxName(), sql, getC_Order_ID());
if (count > 0) {
log.saveError("Error", "Cannot delete order with shipments");
return false;
}
return true;
}
After Delete
protected boolean afterDelete(boolean success)
true to commit, false to rollback
State Methods
Check if New
true if this is a new record not yet saved to database
Check if Changed
public boolean is_Changed()
public boolean is_ValueChanged(String columnName)
true if any value changed, or if specified column changed
Example:
if (order.is_ValueChanged("C_BPartner_ID")) {
// Business partner changed, update pricing
order.setPriceList();
}
Identity Methods
Get ID
The record ID, or 0 for new records
Get UUID
public String get_TableName()
public int get_Table_ID()
Context Methods
Get Context
public Properties getCtx()
Get Transaction
public String get_TrxName()
public void set_TrxName(String trxName)
Client/Org Methods
Get Client and Org
public final int getAD_Client_ID()
public final int getAD_Org_ID()
Client ID or Organization ID
Get Access Level
protected abstract int get_AccessLevel()
Access level constant (ACCESSLEVEL_SYSTEM, ACCESSLEVEL_CLIENT, ACCESSLEVEL_ORG, etc.)
Access levels:
ACCESSLEVEL_SYSTEM = 4 - System data
ACCESSLEVEL_CLIENT = 2 - Client data
ACCESSLEVEL_ORG = 1 - Organization data
ACCESSLEVEL_ALL = 7 - System shared data
ACCESSLEVEL_SYSTEMCLIENT = 6 - System/Client data
ACCESSLEVEL_CLIENTORG = 3 - Client/Org data
Query Timeout
Set Query Timeout
public void set_QueryTimeout(int seconds)
public int get_QueryTimeout()
Query timeout in seconds (default: 300)
Current query timeout in seconds
Complete Example
import java.sql.Timestamp;
import java.util.Properties;
import org.compiere.model.MOrder;
import org.compiere.util.Env;
import org.compiere.util.CLogger;
public class OrderExample {
public static void createOrder(Properties ctx, String trxName) {
// Create new sales order
MOrder order = new MOrder(ctx, 0, trxName);
// Set mandatory fields
order.setC_BPartner_ID(1000001);
order.setC_DocType_ID(1000000);
order.setDateOrdered(new Timestamp(System.currentTimeMillis()));
order.setDatePromised(new Timestamp(System.currentTimeMillis()));
order.setM_Warehouse_ID(Env.getContextAsInt(ctx, "#M_Warehouse_ID"));
order.setIsSOTrx(true);
// Save with exception handling
try {
order.saveEx();
CLogger.getCLogger(OrderExample.class).info(
"Created order: " + order.getDocumentNo() +
" (ID=" + order.get_ID() + ")"
);
} catch (Exception e) {
CLogger.getCLogger(OrderExample.class).severe(
"Failed to create order: " + e.getMessage()
);
throw e;
}
}
public static void updateOrder(Properties ctx, int orderId, String trxName) {
// Load existing order
MOrder order = new MOrder(ctx, orderId, trxName);
if (order.get_ID() == 0) {
throw new IllegalArgumentException("Order not found: " + orderId);
}
// Update description
String oldDesc = order.getDescription();
order.setDescription("Updated: " + new Timestamp(System.currentTimeMillis()));
// Save only if changed
if (order.is_Changed()) {
if (order.save()) {
CLogger.getCLogger(OrderExample.class).info(
"Updated order " + order.getDocumentNo() +
" - Old desc: " + oldDesc +
", New desc: " + order.getDescription()
);
}
}
}
}
See Also