BlogAnton Ignashev

Custom fields in enova365: the schema change nobody tells you about

Custom fields in enova365: the schema change nobody tells you about

An enova365 administrator can add a field to the contractor record in about five minutes. No ticket, no deployment, no approval from anybody, and nothing in the system objects. From inside enova365 it barely registers as an event.

For every integration reading that data, those same five minutes are a schema change.

That asymmetry is the whole story of custom fields in enova365 projects, and almost every failure I've been called in to look at traces back to it. Not a hard failure — a quiet one. The kind where the data has been wrong since March and everybody finds out in June.

Three different things get called "pola własne"

The first practical problem is vocabulary. Ask a client whether they have custom fields and you'll get a yes. That yes covers at least three unrelated mechanisms.

Cechy. The no-code route. An administrator defines them through the interface, attaches them to an object type — kontrahent, towar, a document type — and picks a data type: text, number, date, boolean, a dictionary list, a relation to another object. The values live in a generic structure, not in a column on the object's own table. This is the mechanism most offices actually use, because it needs nobody technical.

Fields added through Konfigurator. Still no code, but a heavier tool, and what it lets you change depends on your licence tier. Don't take a tier map from me or from any other blog post — open your own licence and check what you're entitled to. I've watched a scope get written around a mechanism the client didn't own, and that is not a pleasant conversation to have in week three.

Fields compiled in by an add-on. Industry dodatki built against the SDK add real, typed properties to business objects. They behave like native fields in every respect. Convenient — right up to the moment you upgrade the add-on and the property changes shape.

The three behave differently over the API, so "we have custom fields" isn't an answer you can scope from. You need to know which kind, on which object, from which add-on, at which version. Four questions, and the client can usually answer all of them in one email.

What comes through WebAPI, and what quietly doesn't

Fields that an add-on compiled into a business object come through dynamic controllers like anything else. Nothing special to do.

Cechy are the ones that catch people. They aren't properties on the object — they're a collection of definition-and-value pairs hanging off it. A client that reads a flat property list gets a perfectly valid response: no error, no warning, and no cechy in it. The general mechanics of dynamic controllers, licensing and JWT handling are in the WebAPI developer guide; this is the part of them people discover last, usually in production.

Then there's the knock-on effect, which is arguably worse. A cecha of type text will hold whatever anybody typed into it. I've seen a single "data podpisania umowy" field containing 15.03.2026, 2026-03-15, marzec 2026 and podpisana — all four in the same field, all four perfectly valid text. The definition said text, so no validation ever ran. Nobody in the office was inconvenienced by this for years. Your parser is the first thing in the chain that will ever care, which makes it the first thing that breaks.

The inventory, and the only column that matters

Before you quote anything, get three artefacts from the administrator. They take under an hour to produce.

  1. Custom field definitions per object type, with the type of each.
  2. Installed add-ons with version numbers.
  3. The fill count per field over the last twelve months, plus the number of distinct values.

The third one is the one nobody offers, and the one that decides the scope. In the installations I've opened, a contractor and product card between them carry somewhere in the region of thirty to fifty defined cechy — and fewer than a dozen have any meaningful fill rate. The rest is archaeology. Somebody's 2019 idea, a migration leftover, a field created for a campaign that ran once.

Sort every field into three buckets and treat them differently.

Dead. Near-zero fill. Don't map it, don't test it, don't let it into the estimate. Say so in writing, because the field list is what a client reads as "the size of the job" — and you want that conversation on the record before anybody starts comparing your number with somebody else's.

Real. High fill, low count of distinct values. This is a category the business runs on: a segment, a service tier, an assigned account manager. Map it, and pin it to identifiers rather than text.

Free-text swamp. High fill, nearly as many distinct values as there are records. This is a notes field that quietly grew into a business process. It is not integration input. At best it's input for a model that extracts something out of it — a separate project, with its own conversation about accuracy, not a line item in a mapping table.

That sort takes an afternoon. It routinely halves the field count anyone is arguing about.

Map on IDs. Always.

Renaming a field or a dictionary entry in enova365 is a cosmetic act. Nothing breaks. Reports keep working, users see a better label, everybody moves on with their day.

Your integration was the only thing that cared, and nobody knew to tell you.

So: match on the definition identifier, keep the label for display only, and treat a label change as a log line rather than an error. Same rule one level down — for a dictionary-type cecha, store the entry ID, never the entry's text. This is the same discipline that decides whether a bank format definition surfaces the subfields a matcher can see, or hands you one undifferentiated blob of description text. Structure that exists but isn't addressed is structure you don't have.

Absent and empty are not the same thing

Here's the failure mode that costs the most. It produces no error at any point.

A field disappears from your payload — renamed, retyped, moved by an add-on upgrade, dropped because a controller stopped exposing it. Downstream, your code reads a missing key and writes null. Which is exactly what it writes when the field is present and genuinely empty. The two collapse into one indistinguishable state, and from that moment the system reports "not filled in" for a field that has actually stopped being readable.

And nobody investigates a low fill rate. That's the trap.

The fix is small, and you have to decide on it early: at the boundary, distinguish absent from the schema from present and empty, and make the first one loud. An unmapped or vanished field goes to a log with its name in it. Never to null, never dropped in silence — the same principle behind the nightly read-only checks in the KSeF and JPK post, where the point of the job is to notice, not to fix.

While you're in there, add the drift alarm. Read the list of custom field definitions on the objects you touch, hash it, compare against yesterday's hash. One query, in a job you're probably already running for other reasons. When the hash moves, somebody gets a message naming the field. That's the difference between hearing about a new field the day it appears and hearing about it a quarter later, from a customer, in a meeting about numbers that don't add up.

What to ask a vendor, if you are on the buying side

A test that fits in one question, in the same spirit as asking a vendor when they need credentials: ask how they handle a custom field added after go-live.

"We'll add it to the mapping" means an invoice every time your business changes. It also means the field will be missing until somebody happens to notice.

"The definition list is read nightly, unmapped fields are logged rather than dropped, and you get a message when the schema moves" means they've been through this before.

The second answer costs almost nothing to build. It's a marker of experience, not of budget — which is exactly what makes it a useful question. Nobody can buy their way into that answer on the call.

The one-line version

A custom field is a schema change performed by somebody with no obligation to tell you, and your integration is the only party that will ever notice. Design for that and these projects are ordinary. Assume the field list is stable and you'll spend a quarter explaining a number nobody can reconcile.

Not sure what's actually sitting on your contractor and product cards? Write to me — send the definition list with fill counts and I'll tell you which fields are real work and which are archaeology, before anyone writes an estimate. Half an hour, no charge.

Let’s talk about your project

Free 30-minute consultation. We’ll figure out if and how I can help.

Book a Free 30-Minute Call

Select a date

August 2026
Mon
Tue
Wed
Thu
Fri
Sat
Sun
Back to Blog

Related Posts

Comarch Optima API: a developer's guide to integrating Optima
Blog

Comarch Optima API: a developer's guide to integrating Optima

The question is usually whether Optima has an API. The answer is that it has five different things by that name, each with a different licence, a different owner, and a different phone number to call when it stops working.

Read more
Which AI agent to build first against enova365
Blog

Which AI agent to build first against enova365

The most valuable agent is almost always the wrong first build. Not because it cannot be built, but because its first honest output arrives in week ten, and nothing holds a room's attention that long. Four questions that screen the candidates, and one number that predicts whether the project lands.

Read more
The enova365 service account — what read-only actually means
Blog

The enova365 service account — what read-only actually means

The integration gets the Administrator account, because scoping the rights would take an afternoon and nobody has the afternoon. Then it turns out the strongest read-only boundary in enova365 is not the permission tree at all. It is the licence.

Read more