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
Comments
Comment #2
owens-d commentedComment #3
afagioliComment #6
afagioliComment #7
afagioliSuggested fix is available into current dev and 11.1.0-rc11
Comment #8
afagioli