# Epicor BPM Custom Code Block Generator (C# for Method Directives)

> Source: https://netray.co/tools/epicor-bpm-custom-code-block-generator
> Publisher: Netray (netray.co), AI and integration engineering for ERP and manufacturing systems.
> Section: Free Tools

## Short answer

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.

## Category

epicor

## Erp

Epicor Kinetic

## Audience

- Epicor developer
- Epicor BPM author

## Tool

**Kind:** template

**Output:** code

**Language:** csharp

**Fields:**

#### Temp table name (dataset table)

**Key:** tt

**Type:** text

**Default:** ttOrderHed

#### Temp table collection

**Key:** ttset

**Type:** text

**Default:** ttOrderHeds

#### Field to validate

**Key:** field

**Type:** text

**Default:** PONum

#### Error message

**Key:** msg

**Type:** text

**Default:** PO number is required for this customer.

#### Db table for lookup

**Key:** lookup_table

**Type:** text

**Default:** Customer

#### Db field to read

**Key:** lookup_field

**Type:** text

**Default:** CustID

#### Key on the temp table

**Key:** key_field

**Type:** text

**Default:** CustNum

**Blocks:**

#### Validate new or changed rows (pre-processing)

**Body:** foreach (var {{tt}} in {{ttset}}.Where(r => r.RowMod == "A" || r.RowMod == "U"))
{
    if (string.IsNullOrEmpty({{tt}}.{{field}}))
    {
        throw new Ice.BLException("{{msg}}");
    }
}

#### Lookup against the database

**Body:** foreach (var {{tt}} in {{ttset}}.Where(r => r.RowMod == "A" || r.RowMod == "U"))
{
    var rec = Db.{{lookup_table}}
        .Where(x => x.Company == Session.CompanyID && x.{{key_field}} == {{tt}}.{{key_field}})
        .FirstOrDefault();

    if (rec != null)
    {
        string value = rec.{{lookup_field}};
        // Use value here
    }
}

#### Information message instead of a hard stop

**Body:** this.PublishInfoMessage(
    "{{msg}}",
    Ice.Common.BusinessObjectMessageType.Information,
    Ice.Bpm.InfoMessageDisplayMode.Individual,
    "{{tt}}",
    "{{field}}");

**Notes:** Syntax can vary between Epicor 10 and Kinetic releases, and the Db context and temp table names depend on the business object the directive is attached to. Confirm field names and types in the BPM Designer's variable list and test in a pilot company before enabling in production. RowMod values: A added, U updated, D deleted.

## Content

**Intro:** Most Epicor BPM logic can be built with widgets, but real rules eventually need a custom code block. This generator gives you correct C# skeletons for the three patterns developers write most: validating temp table rows, looking up database records, and showing messages.

**Sections:**

#### How to use the generator

**Body:** Open your method directive in the BPM Designer, add a Custom Code block, and paste the relevant snippet after replacing names using the fields above. Set the temp table and collection to match the business object, for example ttOrderHeds on SalesOrder.Update. Use pre-processing directives for validation that must stop the save, and post-processing for work that should happen after the data is written.

#### Understanding the patterns

**Body:** Epicor passes changed data to a directive as temp tables. The RowMod column shows the change: A for added, U for updated, D for deleted. Looping only those rows avoids processing untouched records. Db gives LINQ access to tables, and Session.CompanyID scopes queries to the current company. Ice.BLException stops the transaction and shows the text to the user, while PublishInfoMessage warns without blocking.

**Bullets:**

- Throw Ice.BLException to block the save
- PublishInfoMessage to inform without stopping
- Always filter Db queries on Company
- Check RowMod so you handle only the rows that changed

#### Common mistakes

**Body:** Developers often loop all rows including unchanged ones, throw exceptions in post-processing after data is saved, or query the database inside nested loops. Forgetting the Company filter returns the wrong record in multi-company systems. Keep logic small, comment why the rule exists, and move reusable code into the function library. Test with add, update and delete paths, and with API and UI callers.

**Cta:** Netray's AI agents can review BPM and customization code and help plan Epicor upgrades, with private on-prem models available.

**Faq:**

#### Should validation be pre- or post-processing?

**Answer:** Use pre-processing for validation you want to block the save, since nothing is written yet. Post-processing runs after the base method, so exceptions there can leave a confusing state. Use post-processing for follow-up actions.

#### What does RowMod mean?

**Answer:** RowMod marks the type of change on a temp table row. A is an added row, U is updated, D is deleted, and blank means unchanged. Filter on it so your rule runs only for rows the user actually touched.

#### Can I call this code from several directives?

**Answer:** Not directly. For reuse, put the logic in a function in the Function Library and call it from each directive. That keeps rules in one place and makes upgrades easier to test.

#### Why does my code compile in Designer but fail at run time?

**Answer:** Common causes are null values from lookups, wrong field names on the temp table, or a Db query that returns nothing. Add null checks and test with realistic data in a pilot company.

## Search terms

epicor bpm custom code, epicor bpm c# example, epicor ttorderhed rowmod, epicor bpm raise exception code, epicor bpm db query linq, epicor ice blexception, epicor bpm publishinfomessage, epicor method directive code block

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