Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Pricing Rules by Date & Time

Specific Date & Time-Based Rule

Restaurants adjust package prices according to specific date/time windows.

AspectBehavior
ApprovalAlways auto-approve
ConditionUses the rule’s own Date Type/Period Type configuration
ActionBy Percentage (%) or By Fixed Amount

Date Types

Implementation: DateTimeCondition#date_type (pricing-engine/app/models/date_time_condition.rb:66).

Date TypeDescriptionRequired fieldsUses slots?
everydayApplies every day within the rule periodNone (auto)No
specific_dateSpecific calendar datesConditionItem.start_date, end_dateYes
specific_timeSpecific time-of-day (uses rule’s start_date as the single day)ConditionItem.start_time, end_timeYes
specific_date_timeSpecific dates AND timesDate + time fieldsYes
day_of_weekSpecific weekdays recurringDateTimeCondition.days_of_week[]No
day_time_of_weekSpecific weekdays at specific timesdays_of_week[] + ConditionItem timesYes

Day-of-week encoding

days_of_week is stored as a string array of integers (0=Sunday … 6=Saturday). The frontend can send mon, tue, … or numeric strings — both normalize to the integer form via DateTimeCondition#normalize_day_to_number (date_time_condition.rb:432).


Period Modes

PricingRule#period_mode controls whether a rule is a single occurrence or a recurring range.

ModeBehavior
singleOne occurrence only. end_date must equal start_date; is_no_end_date/is_no_end_time must be false.
rangeRecurring window. Supports open-ended (is_no_end_date = true) and explicit date ranges.

When the parent rule is single, all conditions inherit single automatically (except recurring day_of_week / day_time_of_week, which keep their own period_mode since for them “single” means a single time-point, not a single date).


Condition Item (Slot)

Implementation: ConditionItem (pricing-engine/app/models/condition_item.rb).

A ConditionItem is one time-window where the rule’s adjustment applies. Multiple items = multiple windows in the same rule.

FieldTypeNotes
start_date / end_datedateFor slot types that use dates
start_time / end_timetimeFor slot types that use times
is_no_end_datebooleanOpen-ended date range (covers all future dates)
is_no_end_timebooleanOpen-ended time window (until 23:59)
is_anytimebooleanTime matches regardless of time-of-day

Validation rules

  • start_date ≤ end_date
  • start_time ≤ end_time (same day only; multi-day ranges skip time order check)
  • Cannot create an item that ends in the past (condition_item.rb:599)
  • Item range must fall within the parent rule’s range (condition_item.rb:615)
  • Two items in the same rule cannot overlap (condition_item.rb:530)
  • An ongoing item (is_no_end_date) must be the only item in the rule
  • single period mode forbids is_no_end_date, is_no_end_time, and multi-day items

Cross-condition overlap

DateTimeCondition#no_cross_condition_overlaps (date_time_condition.rb:557) catches overlaps across different DateTimeCondition records within the same PricingRule.


Evaluation Logic

Pricing::ConditionEvaluator (pricing-engine/app/services/pricing/condition_evaluator.rb) checks whether a target datetime matches a condition.

result = Pricing::ConditionEvaluator.call(
  date_time_condition: condition,
  target_datetime: Time.current,
  target_date: Date.current
)
result.success?  # true if any condition_item matches
result.data      # { matches:, matched_items: [ids], condition_id: }

Evaluation order:

  1. Day-of-week filter — if days_of_week is set, the target wday must be in the allowed list
  2. Item scan — iterate condition_items; return the IDs of items where:
    • item.active_on_date?(target_date) (start/end date range, or rule’s dates as fallback)
    • item.active_at_time?(target_datetime) (start/end time range, with is_anytime short-circuit)
    • item.applies_to_day?(target_date) (delegates to condition’s day-of-week check)

Period Type Combinations (UI Matrix)

The frontend uses a date × period matrix to drive which fields are shown. Here is the complete mapping:

CaseDate TypePeriod TypeChild Input FieldsUI Constraint
1everyday(N/A)NoneAuto-calculated; runs every day between parent Start and End
2specific_dateSingle DateDate pickerDisables dates outside parent boundary
3specific_dateRange DateStart Date, End Date, No End DateBoth pickers disable out-of-bounds dates; End Date also disabled before Start Date
4specific_timeSingle TimeTime (Start/End)No pickers; applies to every valid day within parent boundary
5specific_timeRange TimeStart Time, End Time, No End TimeNo pickers; No End Time = active until 23:59 of that day
6specific_date_timeSingle DateStart Date, Start Time, End Time, No End TimeStart Date picker disables out-of-bounds; Time fields set hours on that date
7specific_date_timeRange DateStart Date, End Date, Start/End Time, No End Date/TimeBoth date pickers disable out-of-bounds; End Date disables pre-Start
8day_of_week(N/A)Mon–Sun checkboxesAuto-calculated; runs only on checked days within parent timeline
9day_time_of_weekSingle TimeMon–Sun checkboxes + Time (Start/End)Auto-calculated; selected hours on checked days within parent timeline
10day_time_of_weekRange TimeMon–Sun checkboxes + Start/End Time + No End TimeSame as Case 9

Parent-Child Date/Time Limitation

When a Child condition’s date range falls inside a Parent rule’s General Setting period, these 3 global rules apply:

Rule 1 — Disabled Dates (Poka-yoke)

Child calendar pickers grey out and disable any date before Parent’s Apply From or after Parent’s Valid Until.

Rule 2 — Soft Cap (No End Date)

Child No End Date checkbox is always clickable, even if Parent has a fixed end date.

If checked:

  • UI shows helper text: “Caps at {Parent End Date} based on General Setting.”
  • Backend automatically stops the rule at 23:59 on Parent’s end date

Rule 3 — Post-Edit Conflict (Retroactive Change)

User shrinks Parent Date Range after creating a valid Child condition:

  • Do NOT delete the child rule
  • Outline Child Rule block in Red
  • Disable Save button
  • Show error: “This date is outside the main rule period. Please adjust.”

Backend Execution Date Logic

Parent (Valid Until)Child (End Date)Child UIFinal Execution End
31 Dec 202530 Nov 2025Normal30 Nov 2025
31 Dec 2025Ongoing (checked)Checkbox + “Caps at 31 Dec 2025…”31 Dec 2025
Ongoing30 Nov 2025Normal30 Nov 2025
OngoingOngoing (checked)NormalInfinite

Examples

Example 1 — Weekend Premium

  • Date type: day_of_week
  • days_of_week: ["fri", "sat"]
  • Action: Increase by 15%

Applies every Friday and Saturday within the rule’s date range.

Example 2 — Holiday Date Range

  • Date type: specific_date
  • Period: Range
  • start_date: 2026-12-20, end_date: 2026-12-31, is_no_end_date: false
  • Action: Increase by 20%

Example 3 — Happy Hour

  • Date type: day_time_of_week
  • days_of_week: ["mon", "tue", "wed", "thu", "fri"]
  • Time slot: 15:00–18:00
  • Action: Decrease by 10%

Example 4 — New Year’s Eve

  • Date type: specific_date_time
  • Period: Single Date
  • Date: 2026-12-31
  • Time: 20:00–02:00 (next day) — note: for cross-midnight, use Range Time with separate time fields, or split into two specific_date_time conditions