Problem/Motivation
`AbstractCalendar::getEventsItemized()` contains a loop that is supposed to
produce mock (default-state) itemized events for any unit that has no rows in
the BAT calendar state tables. A single line in that loop was commented out,
which means units with no DB rows are silently dropped from the returned
`$events` array. Downstream callers such as `bat_event_get_matching_units()`
therefore never see those units, and availability checks always return an empty
set for them — regardless of the unit's configured default state.
Steps to reproduce
1. Install BAT + BEE on a fresh Drupal site.
2. Create a "Generally available" daily bookable content type via BEE and create
a node of that type (this creates a BAT unit with a default state of
`bee_daily_available`, but writes no rows to the calendar state table).
3. Navigate to the node's **Add Reservation** form and select any date range.
4. Submit the form.
**Expected:** The reservation succeeds — the unit is available by default.
**Actual:** The form reports no available units and refuses the booking.
The same applies to `UpdateAvailabilityForm`: writing the very first
availability event for a unit fails because `bat_event_get_matching_units()`
returns `[]` (see also drupal.org/project/bee issue #3576346).
## Root Cause
In `AbstractCalendar::getEventsItemized()`, after the main `foreach ($db_events
as $unit => $event)` loop fills in data for units that *do* have DB rows, a
second loop iterates over `$keyed_units` to handle units with *no* DB rows:
foreach ($keyed_units as $id => $unit) {
if ((isset($events[$id]) && count($events[$id]) == 0) || !isset($events[$id])) {
$empty_event = new Event($start_date, $end_date, $unit, $unit->getDefaultValue());
// Mar25: do we need empty events from mock??
//$events[$id] = $empty_event->itemize(new EventItemizer($empty_event, $granularity));
}
}The critical assignment line is commented out. As a result:
- The `$empty_event` object is created but never stored.
- `$events[$id]` is never populated for units without DB rows.
- Those units are absent from the return value of `getEventsItemized()`.
- `bat_event_get_matching_units()` (which calls `getEventsItemized()`) returns
an empty array for any type whose units have no calendar history.
The comment `// Mar25: do we need empty events from mock??` suggests the line
was commented out experimentally in March 2025. The answer to that question is
**yes** — the mock event mechanism is what makes default-state units visible to
all calendar queries.
Note: `itemizeDays()` (called inside the first loop) already handles the
"month exists in DB but a specific day has value 0" case by substituting the
unit's default value. The second loop handles the distinct case where the unit
has **no DB rows at all** for the queried period.
Proposed resolution
Restore the commented-out line:
foreach ($keyed_units as $id => $unit) {
if ((isset($events[$id]) && count($events[$id]) == 0) || !isset($events[$id])) {
$empty_event = new Event($start_date, $end_date, $unit, $unit->getDefaultValue());
$events[$id] = $empty_event->itemize(new EventItemizer($empty_event, $granularity));
}
}
**Why this is safe:**
- The condition `(isset($events[$id]) && count($events[$id]) == 0) || !isset($events[$id])`
ensures the mock is only applied to units that are missing or empty in
`$events` — i.e., units for which the DB returned no rows.
- Units *with* DB rows are processed by the first loop via `itemizeDays()` /
`itemizeHours()` / `itemizeMinutes()` as before; this loop does not touch
them.
- `$unit->getDefaultValue()` returns the state ID that was configured for the
unit (e.g., the `bee_daily_available` state ID) — exactly what should be
assumed for unset days.
- This restores the originally intended behavior that was in place before the
line was commented out.
## Impact
Any site using BAT with units that have no calendar events written for a given
period (including all fresh installs) will experience:
- `bat_event_get_matching_units()` returning `[]` for those units.
- BEE's `AddReservationForm` always refusing bookings as "no units available."
- BEE's `UpdateAvailabilityForm` failing to write the first availability event
for a unit (see also BEE issue #3576346, which has a partial workaround in
BEE itself, but this is the underlying root cause).
- Any custom code relying on `getEventsItemized()` or `getMatchingUnits()`
receiving incorrect results for default-state units.
Remaining tasks
I have tested this in my own system under ddev, further verification is required prior to merging
User interface changes
none
API changes
none
Data model changes
none
Sponsorship
Comments
Suggested fix is available into current dev and 11.1.0-rc11
Comment #1
owens-d created an issue. See original summary.