SIGNALS Documentation
API Reference

Deal Price Validator Registry

Contract for registering validators that run before proportional deal-price distribution.

Overview

App\Services\Pricing\DealPriceValidatorRegistry stores validation steps for proportional deal-price distribution.

Core binds the registry as a singleton in AppServiceProvider, but currently ships with no validators. The extension seam is consumed by DealPriceProportionalDistributor before it calculates any line allocations.

Public surface

Method Purpose
register() Store one validator under its key()
get() Return the validator for a key or throw
has() Check whether a validator key is registered
all() Return the complete keyed validator map

Accepted contract and identity

Every registration implements App\Contracts\Pricing\DealPriceValidator:

public function key(): string;

public function validate(
    Rental $rental,
    int $dealTotalMinor,
    ?RentalItem $groupItem = null,
): void;

The validator's key() is the registry identity. validate() receives the rental, requested deal total in minor units, and an optional group item when distribution is scoped to one revenue group.

Validators communicate rejection by throwing. The contract does not prescribe one exception type, return validation metadata, or declare validators pure or side-effect-free.

Collisions and lookup failures

register() assigns directly into an associative map. Registering another validator with the same key replaces the earlier object; duplicate keys are not rejected.

get($key) throws InvalidArgumentException with Unknown deal price validator: {key} for an unregistered key. Use has() when a lookup is optional.

The normal distributor does not call get(): it consumes every value returned by all().

Core registrations and execution order

The framework currently ships with no validators. Consequently, the core registry starts as an empty map and deal-price distribution proceeds directly to its built-in calculations and validation.

The registry has no priority field or sorting API. DealPriceProportionalDistributor iterates all() directly, so registered validators run in registry iteration order and the first thrown exception stops the operation. No broader ordering or error-aggregation guarantee is provided.

After registered validators pass, the distributor enforces its own invariants, including that the requested total cannot fall below non-discountable or manually discounted line totals.

Runtime consumer

DealPriceProportionalDistributor::distribute() invokes every registered validator before loading the scoped lines and calculating proportional discounts. A validator therefore sees the requested input before the distributor derives baselines or applies its largest-remainder allocation.

The same call shape serves both rental-wide and group-scoped deal prices. A null group item means the whole rental; a non-null group item narrows the later distribution, and is passed unchanged to every validator.

Worked example

This is the current consumption loop:

foreach ($validators->all() as $validator) {
    $validator->validate($rental, $dealTotalMinor, $groupItem);
}

With the core empty registry, the loop performs no calls. When a validator is registered, returning normally allows the next validator or the distributor to continue; throwing aborts distribution.