Where the law caps how much a customer may purchase within a rolling period, the limit is a control, not a feature — and controls must fail closed, validate their own configuration, and prove they ran. This spec defines the entities, the enforcement rule, and the audit record that turns “we believe the limit was enforced” into evidence.
Entity model
Entity-relationship model
A customer holds licences and endorsements; a purchase records quantity against a category (e.g. a calibre); the rolling window is evaluated over prior purchases of the same category. Every enforcement produces one compliance-decision record.
Definition
Enforcement rule
The window is always applied — there is no code path where an unset or zero window skips the date filter. The prior total is summed over the fixed window, the cap is compared, and a purchase that would exceed it is refused. The control fails closed: if the window cannot be determined, the sale is denied.
public function withinLimit(Customer $c, Category $cat, int $qty): bool
{
$window = config('limits.window_days'); // validated at boot (below)
$prior = Purchase::where('customer_id', $c->id)
->where('category_id', $cat->id)
->where('sold_at', '>=', now()->subDays($window)) // ALWAYS applied
->sum('quantity');
return ($prior + $qty) <= $cat->cap;
}Contract
Configuration contract
The window length is validated at application boot. A missing, zero, or non-integer value is a fatal startup error, not a permissive fallback — the application refuses to run a limit it cannot enforce, the same way it refuses to run without database credentials.
public function boot(): void
{
$window = config('limits.window_days');
if (! is_int($window) || $window < 1) {
throw new \RuntimeException(
'limits.window_days must be an integer >= 1; '
.'refusing to start with an unenforceable statutory limit.'
);
}
}Definition
Compliance-decision log
Every enforcement writes one immutable decision record, in the same transaction as the purchase it gates, capturing the window, the prior total, the cap, and the outcome. This is the artefact an auditor requires: proof that the check ran, with the exact figures it ran on, for every transaction.
| Column | Type | Purpose |
|---|---|---|
| purchase_id | uuid FK | The gated purchase (1:1) |
| window_days | int | The window in force at decision time |
| prior_total | int | Summed quantity over the window |
| cap | int | The statutory limit compared against |
| allowed | bool | The decision outcome |
| decided_at | timestamptz | When the check ran |
Invariants this spec guarantees
- The rolling window is applied on every check; no configuration value can cause it to be skipped.
- An invalid limit configuration is a fatal boot error, never a permissive fallback.
- Every purchase has exactly one compliance-decision record, written in its transaction.
- The decision log is immutable and reconstructs, for any transaction, the exact figures the check used.