Robust Work With Epicor Functions

Robust Work With Epicor Functions

Epicor Kinetic is incredibly customisable. Really, you can do almost anything if you know the tools and mechanics well enough.

But Epicor themselves, and almost all consultants, will tell you – don’t do it. Or only a bare minimum.

Why?

Because there are few guarantees about non-standard workings. Not that they’ll keep working as intended, not that they’ll upgrade cleanly, not even that your processes will stay consistent. If you do things the way the standard ERP does them, you have the backup of the ERP’s creator. Within reason.

But my job is customising. So what can I do?

Here I am, having built my business around customising and automating Epicor Kinetic. So, below, I’ll describe my most recent way of working.

Not only do I meddle with the standard system, I also can’t reckon on being around in the future. I work on a project, and that might be it. It had better work, it had better be reliable, and it had better be clear for anyone in the future to deal with.

Almost all the work I do is for companies who have very capable Epicor admins in-house. Far from trying to keep my professional secrets, I can only succeed if I’m transparent and create things they can use for themselves. Because they’re still supporting the system after my project is done.

Standard good practices like clear specifications and documentation help, of course. Everybody knows that, even if they don’t actually follow those practices.

But Kinetic itself can be used in better ways.

Keep things where they belong

Epicor has done a good job, in recent years, of creating tools with distinct purposes for customising, even if they do discourage their use.

There are BAQs for data access. Customisations (“layers” in Kinetic-speak) for interface. User Codes and UD tables for custom data. BPMs, method and data directives, for triggering actions. SSRS and routing for reports. Functions for … well, that’s where it gets interesting.

Leaving functions aside for a minute, the key to a maintainable change to Epicor Kinetic is to only use each of these for its core purpose. No business logic in the interface, ideally no logic in BPMs even, but cleanly separate out what needs to happen and do it in the right place.

Keep things basic

When I say “basic”, I don’t necessarily mean “simple”. Simple is best where possible, but it’s even more important to get to the root of the workings, and work with what is least likely to change.

I find, for example, that widget-based workflows have a habit of breaking, because they have to depend on high-level assumptions about how the system is going to keep working, and upgrades often change things. Code that carefully uses the core Business Objects that the system exposes, in ways standard for the platform it’s built on, is more robust.

I sometimes talk to clients about working “with the grain” – finding out where the system works most naturally similar to what they want to achieve, and leveraging it, rather than trying to override anything.

Where something is completely different and the system doesn’t cover it at all, it’s often better separated out completely.

And, do things in a repeatable and predictable way. Don’t create solutions to be clever, create solutions that are, as far as possible, just what anyone competent would expect to see. Not least yourself when you come back to it.

Use Functions

“Epicor Functions” have changed the way custom logic in Kinetic should be approached. Because they’re isolated and reusable, they allow custom logic to be kept in one place.

In general, what you can do in a BPM, you should normally do in a function and call from a BPM, unless it’s trivial.

This is not about the system itself, or any efficiency or capability, it’s all about the humans responsible for the system.

So this is what I do

When I have a project for a company, most types work like this.

Create a Function Library for the core workings.

A whole series of posts could be written about how this is done in itself, but the point is that all the logic is in functions in this library, and ideally nothing repeated. And nothing should need changing unless the logic needs to.

If there are company-specific details that need to be used by those functions, and might change, they are stored in a dedicated set of User Codes. Never in the code itself. So, for example, with a REST integration, the endpoints and access details are in User Codes.

If the function assembles non-trivial data from the system to put somewhere, either pass it into the function from outside, or call it in via a BAQ. This is controversial to some, because it’s less efficient (slightly). But it has the massive advantage that other Epicor users and admins can access and adjust that BAQ, and therefore the data used, without needing to know anything about the logic or the code so can leave the functions alone.

If it’s an integration, either calling on an external system or callable from an external system, make all calls via the function library.

Where needed, use method and data directives to call the functions when triggered by system events.

Where needed, show new and different data to users in the interface and allow them to interact with it.

If something really radically outside the system scope is required, build it elsewhere with an API and call on it from functions.

What does this achieve?

What I’ve realised is that when I, as an outsider, create a solution for a company’s Epicor system, success depends not just on the end users. My real customer, usually, is the in-house expert who has to maintain everything.

By creating a modular solution, I give them additional functionality, not just a tick in a list of things they’re asked to provide.

A good function-based system can be tweaked, re-used and extended, because the functions are usable alone. And if architected like this, they’ll be robust and flexible enough for it. So it continues to be a win beyond the initial scope, long after it’s been signed off.

It takes a different kind of approach to build for maintainability, and I have yet to see much discussion about it from developers.

Another of these posts can go into more detail about how the components are structured.