Skip to main content
Back to DevlogsEngineering field notes
Domain LogicPricingTypeScriptProduct Engineering

Pricing After Midnight

Building a promotion engine where overlapping rules, overnight windows, and existing discounts always resolve to one explainable price.

Published on September 1, 2026
~3 min read

Discounts Need an Order

A restaurant can run a venue-wide happy hour, discount an entire category, and promote one item at the same time. All three rules may be valid at 17:30. Without an explicit hierarchy, the price depends on query order—an invisible implementation detail that neither staff nor guests can reason about.

Sofreh resolves specificity first: item rules beat category rules, and category rules beat establishment-wide rules. The engine selects one dynamic rule, then compares its percentage reduction with the item's existing fixed discount and applies whichever saves the guest more. One item, one final price, one reason.

Midnight Breaks the Obvious Condition

A same-day window is straightforward: the current time must fall between start and end. A late-night offer from 23:00 to 02:00 reverses that ordering. Treating every window as start <= now <= end makes the promotion disappear precisely when midnight arrives.

The evaluator detects wrapped windows. Normal ranges use an AND condition; overnight ranges use an OR condition—after the start on the first side of midnight, or before the end on the second. Times are normalized to zero-padded HH:mm strings, making comparisons deterministic and easy to test with an injected date.

Pricing Belongs in One Service

The dangerous version of dynamic pricing is implemented separately in the menu card, basket, checkout, and receipt. Those copies drift. A promotion can look active in the menu while checkout charges the static amount, or a receipt can describe a rule that was never applied.

Sofreh keeps rule evaluation in a domain service. It accepts establishment context, the item's stable identifiers and base price, and a date; it returns original price, discounted price, discount amount, the selected rule, and whether happy hour is active. Presentation surfaces consume a decision instead of reimplementing one.

Explainability Is Part of Correctness

A mathematically correct price can still be a product failure if staff cannot explain it. The selected rule travels with the result so the interface can name the promotion and show how the amount changed. When no dynamic rule applies, the same result shape describes the item's static discount instead of forcing consumers into a separate code path.

Determinism is not only about getting the same number. It is about getting the same reason.

That principle keeps support conversations short, receipts trustworthy, and future rules composable. Before adding more promotion types, the engine has a stable contract: precedence is explicit, time is injectable, prices never fall below zero, and every outcome can be traced.