# Epicor BPM Custom Code Examples: Set, Query, Validate, Call and Refresh

> Source: https://netray.co/erp-help/epicor-bpm-custom-code-examples
> Publisher: Netray (netray.co), AI and integration engineering for ERP and manufacturing systems.
> Section: ERP Help

## Short answer

A BPM Custom Code block is a C# snippet that runs inside a Method or Data directive and can read the incoming temp tables (ttOrderHed, ttOrderDtl and so on), query the database through Db, and call business objects. Below are tested-pattern examples for setting a field, looking up a record, validating and raising an error, and calling a service, plus how to make the client see data you changed.

## Kind

how-to

## System

epicor

## Erp

Epicor Kinetic

## Module

BPM / Custom Code

## Query

epicor bpm custom code examples

## Alternate Queries

- epicor bpm custom code refresh
- epicor bpm c# code block examples
- epicor bpm set field value custom code
- epicor bpm query database custom code

## Applies To

Epicor Kinetic (2021 and later) and Epicor 10.x BPM Designer, on-prem and cloud. Cloud tenants may restrict some namespaces.

## Steps

### How to add a Custom Code block to a directive

**Items:**

- Open Menu > System Management > Business Process Management > Method Directive Maintenance (or Data Directive Maintenance for table-level triggers).
- Pick the business object and method (for example SalesOrder.GetNewOrderDtl or Update) and create a Pre-Processing, Base Processing or Post-Processing directive.
- Open the Designer and drag a Custom Code widget onto the canvas between Start and End.
- Write the C# in the code editor. Use the Insert Variable / Insert Code pane to see the available temp tables for that method.
- Click the Validate / Check Syntax action so compile errors show before you save.
- Save the directive, tick Enabled, and use the Generate / Deploy step so the server compiles it.
- Test from the client with tracing on, and read the server log if the directive throws.

## Sections

### Set a field value in the incoming data

**Body:**

The simplest custom code touches the temp table rows the client sent. In a Pre-Processing directive on SalesOrder.Update, ttOrderHed holds the header and ttOrderDtl the lines. Always filter on RowMod, because a row with RowMod empty is only there as context, and changing it does nothing useful.

For a field that exists on the table, assign it directly. For a UD field, use the UDField helpers on the row. In most releases a plain widget called Set Field/Argument does this without code, so use code only when you need a condition or a loop.

**Code:**

foreach (var hed in ttOrderHed.Where(r => r.Updated() || r.Added()))
{
    if (string.IsNullOrEmpty(hed.PONum))
    {
        hed.PONum = "NOPO-" + hed.OrderNum.ToString();
    }
}

### Query the database and validate

**Body:**

To look up related data, use Db, the Entity Framework style context for the Erp schema. Always filter by Company, because a missing company filter returns rows from other companies or runs slowly. Use FirstOrDefault and test for null, since a null reference is the most common BPM runtime error.

To stop the save with a message, use the Raise Exception widget or throw a BusinessObjectException style error from code. Info messages that do not stop the save use PublishInfoMessage.

**Code:**

foreach (var dtl in ttOrderDtl.Where(r => r.Added()))
{
    var part = Db.Part.Where(p => p.Company == Session.CompanyID && p.PartNum == dtl.PartNum).FirstOrDefault();
    if (part != null && part.InActive)
    {
        throw new Ice.Common.BusinessObjectException("Part " + dtl.PartNum + " is inactive.");
    }
}

this.PublishInfoMessage("Credit hold check passed", Ice.Common.BusinessObjectMessageType.Information, Ice.Bpm.InfoMessageDisplayMode.Individual, "", "");

### Call a service and make the client refresh

**Body:**

To call another business object from custom code, use CallService with the contract interface, then fetch a dataset, change it and call its Update. If you only need a computed value, an Epicor Function called from a Call Function widget is cleaner and reusable. See the related page on creating Epicor Functions.

Refresh is a common source of confusion. Changes made to the ttTables inside a Pre or Post-Processing directive travel back to the client with the response, so the screen shows them. Changes you write straight to the database with Db or a second service are not pushed to a form that is already open. The user sees them after a refresh or re-query, or the Application Studio app has to reload the data view after the call completes. Also remember that edits to the BPM itself only take effect after you save and deploy the directive; an open client does not need a restart, but the server recompiles.

**Code:**

this.CallService<Erp.Contracts.SalesOrderSvc>(svc =>
{
    var ds = svc.GetByID(orderNum);
    ds.OrderHed[0].RowMod = "U";
    ds.OrderHed[0]["Character01"] = "Reviewed";
    svc.Update(ref ds);
});

## Pitfalls

- Writing to a table you also have in the incoming dataset can be overwritten by the client's copy on return. Change the temp table row when you can.
- Forgetting the Company filter in a Db query returns the wrong rows and scans the whole table.
- Calling Update on the same business object from inside its own directive can recurse or hit a record-in-process error. See the update vs UpdateExt page.
- Heavy queries in a Pre-Processing directive on a frequently called method slow every user, so put expensive lookups behind a condition.
- Hard-coded IDs in code block hide logic. Prefer UD codes or a BAQ so the business can change them.

## Ai Angle

An assistant grounded in your Kinetic environment can read the existing directives, the table and field names in your schema, and draft the C# for a rule you describe in plain language. The draft is reviewed by your developer and tested in a pilot company before enabling. With a private on-prem model, the code and schema never leave your network.

## Faq

### What language does Epicor BPM custom code use?

**Answer:** C#, compiled on the server when the directive is deployed. You can use LINQ against temp tables and against Db. Cloud tenants can restrict some namespaces and assemblies, so test in your pilot environment before relying on anything unusual.

### Why does my code block change not show on screen?

**Answer:** Changes to the temp tables return to the client. Changes written straight to the database do not update an open form. Refresh or re-query the screen, or write through the temp table where the design allows.

### Can I use a BAQ inside custom code?

**Answer:** Yes. The Execute BAQ widget or a service call to the dynamic query service returns a dataset you can loop over. It is easier to maintain than hard-coded Db joins, and the BAQ can be tested alone.

### How do I debug a custom code block?

**Answer:** Use Validate to catch compile errors, turn on client tracing, and add PublishInfoMessage lines during testing. For server-only failures read the server log. Remove the debug messages before go-live.

## Search terms

epicor bpm custom code examples, epicor bpm custom code refresh, epicor bpm c# example, epicor method directive code, epicor bpm Db query, epicor ttOrderHed custom code, epicor bpm publishinfomessage, epicor kinetic bpm

---
Talk to an engineer: https://netray.co/contact?intent=erp_ai | Machine catalog: https://netray.co/agent/catalog.json
