ZuloOne
English
Sign in
Menu

Write an event handler

Updated

Every dictionary and document has a module Events/<Name>EventHandler.*. Empty templates are normal — fill them, do not recreate them. Class names are unique across the whole workspace: the first handler is <Name>EventHandler; the next link in the chain uses a suffix (<Name>ExtEventHandler, <Name>Tx_<Model>).

Note

The owner — the handler in the object's own model — is link 0 and does not call next. A handler on somebody else's object must declare [ExtensionOf("ObjectName")] and call next from every override. See Chain of Command.

How to#

public partial class CustomerEventHandler : TypedDictionaryEventHandler<Customer>
{
    public override Task<EventResult> OnBeforeSaveAsync(
        Customer record, bool isNew, EventContext context)
    {
        if (string.IsNullOrEmpty(record.Name))
            return Task.FromResult(EventResult.Cancel(Res.NameRequired));
        return Task.FromResult(EventResult.Ok());
    }
}

The pair is the envelope (*.script.json) plus the user partial (*.cs). Commit both. Override only the hooks you need; an extra override is a dead line, not a template.

Hook When
OnValidateField / OnFieldChanged A field on the card, no Save. Fill only empty dependents — Save runs the same hooks
OnBeforeSave / OnAfterSave Persist
OnBeforePost / OnAfterPost A document subtype transition (and unpost)

On a table part, the header is Owner<Document>(context).

Text the user will read is a resource on this script (Res.NameRequired), not a C# literal. Identifiers (subtype values, analytic names, cache keys) stay strings in code.

Do not read a second table in a create event. Put that lookup on a command. OnAfterPostAsync can run twice (repost, and a totals driver that posts under the same doc id). Anything outside the document — a spawned child, a tax submission, an outbound call — must be idempotent. Movements of the document itself are replaced, not added.

Tip

A script cannot open files, sockets, processes, or reflect around the platform. Outbound I/O goes through IOutboundGateway / IOutboundCall by channel name. See what a script can do and outbound integrations.

Platform services are available in every script: IDocumentManager, IDictionaryManager, IDataEntityManager, IDataService, IRegisterMovementService, IInformationRegisterService, IDocumentPostingService, IMetadataService, IConstantResolver, plus your own I<Name>.

What's not there yet#

Event handlers do not write register movements. That is a posting script.

Where next#

Was this page helpful?