0063

Override Behavior with an IsHandled Event

integration-event
event-subscriber
ishandled
bindsubscription

Override Behavior with an IsHandled Event

Your freight calculator ships inside a base extension that other teams build on. Marketing wants a free-freight promotion for large orders — but they must not edit your calculator, and you must not hardcode their campaign into it. The Business Central answer is the OnBefore + IsHandled pattern: the calculator raises an integration event before doing its own work, and any subscriber may take over the calculation entirely. The starter already declares the event publisher; wiring it into the calculation and honouring IsHandled is your job.

Requirements

Complete the codeunit named "Freight Charge Calculator" with its public procedure:

procedure CalculateFreight(Amount: Decimal): Decimal
  1. By default, the freight charge is 10% of Amount, rounded to the nearest cent (0.01).
  2. Before the default runs, the calculator raises the integration event the starter already declares, with exactly this name and signature (the grading tests subscribe to it, so name, parameter order, and var modifiers must stay character for character as provided):
OnBeforeCalculateFreight(Amount: Decimal; var Freight: Decimal; var IsHandled: Boolean)
  1. If a subscriber sets IsHandled to true, CalculateFreight returns whatever that subscriber left in Freight — even when that value is 0 — and the default calculation must not run.
  2. If no subscriber sets IsHandled — even if one wrote something into Freight — the default calculation decides the result. Only IsHandled may make that decision.

Then create a second codeunit named "Free Freight Promotion" that subscribes to OnBeforeCalculateFreight:

  1. For an Amount of 1000 or more, it grants free freight: the returned charge is 0 and the default must not run.
  2. Below 1000 it leaves the event untouched, so the default charge applies.
  3. The promotion participates only while a caller has explicitly bound it with BindSubscription. When nothing has bound it, CalculateFreight must return the default charge even for an order of 5000.

What the tests check

The tests call CalculateFreight with generated amounts and compare against the 10% formula computed independently, so a hardcoded result fails. A test subscriber overrides Freight with a random value plus IsHandled and expects exactly that value back; another overrides it to exactly 0; a third writes a junk Freight without touching IsHandled and expects the default. The promotion is graded bound (free at exactly 1000, free above 1000, default below 1000) and unbound (default even at a large amount) — the tests bind it with BindSubscription, which errors unless the codeunit is built for manual binding.

Learn More

Hint 1
The starter's OnBeforeCalculateFreight is an integration event: a procedure with an empty body and the [IntegrationEvent(false, false)] attribute. Declaring it does nothing by itself — calling it from CalculateFreight is what raises the event, and every subscriber gets to write to its var parameters before your next statement runs.
Hint 2
In CalculateFreight, raise the event first and run the default formula only when IsHandled is still false afterwards. Base that decision on IsHandled alone: one test writes a junk Freight without setting IsHandled, another overrides Freight to exactly 0.
Hint 3
A subscriber fires for every call in the system unless its codeunit declares EventSubscriberInstance = Manual; — then it participates only while someone holds it bound via BindSubscription. The promotion's subscriber is [EventSubscriber(ObjectType::Codeunit, Codeunit::"Freight Charge Calculator", 'OnBeforeCalculateFreight', '', false, false)], and at 1000 or more it must set both Freight := 0 and IsHandled := true.
ALBusiness Central 28.5
Press Compile to check your code compiles — Submit runs the tests.
The code editor is desktop-only
Open this problem on a computer to write and run code. Reading the description, tests and discussion works fine here.