How-toEpicor KineticBPM / Custom Code

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

Question
epicor bpm custom code examples

Also searched as

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

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.

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

How to add a Custom Code block to a directive

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

Set a field value in the incoming data

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.

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

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.

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

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.

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);
});

Try it here, free

Epicor BPM Custom Code Block Generator

Open full tool

Generates a C# custom code block for an Epicor method directive that validates a field on a temp table and raises an error or message.

Common 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.

How an ERP-grounded AI assistant handles this

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.

Frequently asked questions

What language does Epicor BPM custom code use?

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?

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?

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?

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.

Related

How-to

How to Create a BPM Method Directive in Epicor Kinetic

In Epicor Kinetic, open Business Process Management > Method Directive Maintenance (the BPM Workbench), pick the business object and method, choose Pre-Processing, Base Processing or Post-Processing, then add a condition and actions such as Show Message or Set Field. Enable it and test in a pilot company before production.

Advanced

How to create and call an Epicor Function

Open Function Studio from the main menu (Customization > Function Studio) or from inside a BPM designer's Call Context widget, define a Library and Function with typed inputs/outputs, write the logic in the C# widget, then compile and test with the built-in test harness before calling it from a BPM directive, a dashboard, or another function.

Error fix

Epicor BPM Runtime Error: Where to Look and How to Fix It

A BPM runtime error is an exception thrown while a directive runs, so the user's save or action fails. Find the directive name and line in the client trace or server log, then check the usual causes: a null reference in custom code, a directive that no longer compiles after a change, conflicting directive order, or a lock or validation error from a nested update.

How-to

Epicor BPM: Update a UD Field, a PO Cost or a Table, and Update vs UpdateExt

From a BPM you can update data three ways: change the incoming temp table row (best for the record being saved), call a business object Update or UpdateExt on another record, or write through Db and call Validate. UpdateExt returns errors in the dataset instead of throwing. A record in process error means the record is already being saved in the same call chain.

Error fix

Fixing Epicor BusinessObjectException: BPM Directive Errors

A BusinessObjectException that says "A Business Process Management (BPM) directive has raised the following error" means a directive on that business object stopped the transaction, either deliberately via a Raise Exception widget or accidentally via an unhandled .NET error in Custom Code. Expand the InnerException on the error dialog to see the real message and the directive name, then open BPM Designer for that object and method to find the widget that fired.

How-to

Fixing a Slow Epicor BAQ (Business Activity Query)

A slow BAQ in Epicor is usually caused by unindexed join columns, a subquery or calculated field forcing a table scan, or the BAQ pulling far more rows than the dashboard actually displays before filtering client-side. Fix it by reading the SQL Server execution plan Epicor generates, moving filters into the BAQ criteria instead of the dashboard filter panel, and replacing subqueries with joins where possible.

AI for ERP

AI for Epicor Kinetic, Beyond What Prism Covers

Add AI to Epicor Kinetic beyond Prism: private LLM over BAQs, BPM data, and REST v2, on-prem or private cloud, with honest guidance on when Prism already covers you.

Stuck on Epicor Kinetic?

Talk to engineers who work inside Epicor Kinetic every week, and who build private AI that answers these questions from your own ERP data.

Keep exploring

Free tools

ERP fixes and how-tos

AI on your ERP

Integrations

Comparisons

Software