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

Caller Inventory — find_available_dates_without_start_time & find_available_dates

Branch: refactor/move-bulk-precomputer-into-inv-checker Date: 2026-07-06 Scope: Every reachable caller of the two InvCheckerHungryHubService methods that the follow-up refactor will touch.


TL;DR

MethodDirect callersTransitive reachVendor‑V1‑specific cap needed?
find_available_dates_without_start_time5 (4 in app/, 1 in bin/)1 (Vendor V1, via VendorAvailability::ReadService)Yes — only Vendor V1
find_available_dates6 (5 in app/, 1 in bin/)1 (Vendor V1, via VendorAvailability::ReadService)Yes — only Vendor V1 (the method already applies its own caps internally)

Risk summary:

  • High: 2 (Vendor V1, both methods — go through VendorAvailability::ReadService)
  • Medium: 0
  • Low: 9

The refactor is safe for every direct caller as long as the vendor‑V1‑specific caps (expiry_date = min(restaurant.expiry_date, local_rps.map(&:end_date)) and exceeds_max_seat?) stay in a thin wrapper (e.g., the existing VendorAvailability::ReadService or a future controller‑level wrapper). The risk is concentrated entirely in the Vendor V1 read path.


1. Callers of find_available_dates_without_start_time

1.1 app/controllers/api/v5/restaurants_controller.rb:258 — V5 (active restaurant, has packages)

  • Calling class / context: Api::V5::RestaurantsController#find_available_dates (the public V5 REST endpoint GET /api/v5/restaurants/:id/find_available_dates.json).
  • Branch taken when: restaurant.restaurant_packages.present? (active restaurant with at least one package).
  • What it does with the result: Returns the array directly as the JSON data payload (line 310–314). No post‑processing, no re‑mapping of keys.
  • Equivalent vendor‑V1 caps? No. This caller does NOT apply any equivalent of the expiry_date cap or exceeds_max_seat? cap. The existing method already only caps by restaurant.expiry_date (not rp.end_date), and it does not call exceeds_max_seat?.
  • Performance sensitivity: High. Public customer‑facing endpoint, 20‑day window, high traffic (drives the reservation funnel).
  • Assumptions about output shape: Expects raw per‑date payload: date, availability, seat_left, min_seat, max_seat, booked_seat, quantity_available. Matches the current find_available_dates_without_start_time shape exactly.
  • Vendor‑V1 assumption map: A — no vendor‑V1‑specific assumptions. Drop‑in.
  • Risk: Low.
  • Test coverage: spec/controllers/api/v5/restaurants_controller_spec.rb exists but does not exercise this branch (no find_available_dates examples in the grep above). Risk is amplified by the lack of direct test coverage, but the shape contract is exercised by validate_inventory_consistency.rb which compares this exact output to the API.
  • Notes: The V5 controller does its own date > today filtering and 20‑day clamp BEFORE calling the method, so the method’s internal past‑date filter is redundant but harmless.

1.2 app/controllers/api/v5/restaurants_controller.rb:346 — V5 old_find_available_dates (widget web)

  • Calling class / context: Api::V5::RestaurantsController#old_find_available_dates (the legacy widget path, taken when params['ignore_end_date'] == 'true').
  • Branch taken when: params.fetch('ignore_end_date', true).to_s == 'true'. Used by the widget web (see app/assets/javascripts/widgets/book-table.js:105).
  • What it does with the result: Returns the array directly as the JSON data payload (line 349–353).
  • Equivalent vendor‑V1 caps? No. Same as 1.1.
  • Performance sensitivity: High. Widget web is customer‑facing, called on the restaurant detail page. Window is today..today+30.days (or days_in_advance for restaurant id 1590).
  • Assumptions about output shape: Same raw per‑date payload as 1.1.
  • Vendor‑V1 assumption map: A — drop‑in.
  • Risk: Low.
  • Test coverage: None directly. Only indirect via validate_inventory_consistency.rb.
  • Notes: This is the ignore_end_date=true path. The 1.1 path is the default.

1.3 app/services/vendor_availability/read_service.rb:140 — Vendor V1 Read Service

  • Calling class / context: VendorAvailability::ReadService#available_dates_without_start_time. This is the Vendor V1 entry point — the one being refactored.
  • Branch taken when: Vendor V1 find_available_dates controller is called WITHOUT a restaurant_package_id param AND the restaurant has packages.
  • What it does with the result: Applies the vendor‑V1‑specific caps (see read_service.rb:161expiry_date = restaurant.expiry_date, then merges the per‑date payload). Returns the array to the controller which JSON‑encodes it.
  • Equivalent vendor‑V1 caps? Yes — but in the WRAPPER, not the inv_checker method. The ReadService adds: (a) expiry_date = restaurant.expiry_date cap (line 146), and the result is then post‑processed by the controller’s response shape. The exceeds_max_seat? cap is NOT applied here (that’s only in available_dates, not available_dates_without_start_time).
  • Performance sensitivity: Very high. Vendor V1 is the integration used by all third‑party partners (Klook, GetYourGuide, etc.). This is the endpoint the entire PR #8274 perf overhaul is targeting.
  • Assumptions about output shape: Expects the raw per‑date payload from find_available_date(date, slug) (one row per date, with the standard keys).
  • Vendor‑V1 assumption map: C — relies on the caps being applied SOMEWHERE in the read path. If the caps move into the generic find_available_dates_without_start_time, every other caller (1.1, 1.2, 1.4, 1.5) will silently change behavior. If the caps are not preserved in the wrapper, Vendor V1 will silently return wrong results.
  • Risk: HIGH.
  • Test coverage:
    • spec/requests/api/vendor/v1/restaurants_availability_contract_spec.rb:49 — contract test for the no‑package‑id branch.
    • spec/services/vendor_availability/read_service_spec.rb — direct unit test of the ReadService.
  • Notes: This is the call site that the refactor is specifically trying to fold into the generic method. The wrapper must continue to add the caps (or they must be added to the generic method with a feature flag, which the task says is NOT the plan).

1.4 app/workers/vendors/getyourguide/notify_worker.rb:103 — GetYourGuide notify worker

  • Calling class / context: Vendors::Getyourguide::NotifyWorker#check_availability. Called from processed_active_restaurants (line 96) for each active restaurant in the worker’s batch.
  • Branch taken when: Always (when restaurant.restaurant_packages.present?).
  • What it does with the result: Iterates availabilities (the return value), maps each to a date string, and only considers dates where the time‑slot has zero seats left (line 56–58). Effectively, it only cares about availability and date keys.
  • Equivalent vendor‑V1 caps? No. The worker does not apply any equivalent of the vendor‑V1 caps. It only reads availability and date.
  • Performance sensitivity: Medium. Background worker, runs hourly (or on demand), processes a batch of restaurants. Window is today..today+30.days. Not customer‑facing.
  • Assumptions about output shape: Only uses date (via availability['date']) and availability. Tolerant of the raw payload shape.
  • Vendor‑V1 assumption map: A — drop‑in.
  • Risk: Low.
  • Test coverage: None directly. The worker has no spec for check_availability.
  • Notes: Background job, so the perf benefit of the batched warm‑up is real but not customer‑visible. Safe.

1.5 bin/inventory_system/validate_inventory_consistency.rb:186 — Ops consistency script

  • Calling class / context: InventoryConsistencyValidator#compare_restaurant_dates. Standalone Ruby script run via bundle exec rails runner.
  • Branch taken when: Always (when validating a restaurant with packages).
  • What it does with the result: Compares the output to the V5 API output field‑by‑field (availability, seat_left, quantity_available, min_seat, max_seat, booked_seat). Reports any mismatch as a failed consistency check.
  • Equivalent vendor‑V1 caps? No. The script compares raw fields, no caps.
  • Performance sensitivity: Low. Ops script, run on demand.
  • Assumptions about output shape: Expects the exact raw per‑date payload, byte‑for‑byte match with the V5 API. This script is the closest thing to a behavioral contract test for this method.
  • Vendor‑V1 assumption map: A — drop‑in. Actually, this is the strongest signal that the method must remain a drop‑in: if the enhanced method changes the shape, this script will fail.
  • Risk: Low (for the refactor itself) but high signal value: any change to the output shape or field semantics will be caught here.
  • Test coverage: N/A (this IS the test for production).
  • Notes: This script compares the inv_checker output to the V5 API output. If the refactor changes the inv_checker output to match Vendor V1’s expected shape, the V5 comparison will fail. The refactor MUST keep the inv_checker method’s output byte‑identical to today’s output for the non‑vendor‑V1 path.

2. Callers of find_available_dates (with restaurant_package_id)

2.1 app/controllers/api/v5/concerns/inventories_v4.rb:30 — V4 packages path

  • Calling class / context: Api::V5::Concerns::InventoriesV4#available_dates_based_on_packages_v4. This is the V5 endpoint GET /api/v5/restaurants/:id/available_dates_based_on_packages.json (minor_version = 4 only).
  • Branch taken when: params[:restaurant_package_ids] is present and the request is V5 minor_version 4.
  • What it does with the result: Returns the array directly as the JSON data payload.
  • Equivalent vendor‑V1 caps? No. This caller does not apply any equivalent of the vendor‑V1 caps. It relies on the method itself to apply them.
  • Performance sensitivity: Medium. V4 packages path, lower traffic than the main find_available_dates endpoint. 20‑day window.
  • Assumptions about output shape: Expects raw per‑date payload with the standard keys (date, availability, seat_left, min_seat, max_seat, booked_seat, quantity_available).
  • Vendor‑V1 assumption map: A — no vendor‑V1‑specific assumptions. Drop‑in.
  • Risk: Low.
  • Test coverage: No direct spec for this concern. The endpoint is exercised by bin/inventory_system/validate_inventory_consistency.rb which compares this output to the API (see compare_package_dates at line 238).
  • Notes: The create_inv_checker_instance helper at line 14 pre‑configures the inv_checker with the specific restaurant_package_ids, so the method receives a package‑scoped inv_checker.

2.2 app/controllers/api/v5/restaurants_controller.rb:303 — V5 (inactive restaurant, widget)

  • Calling class / context: Api::V5::RestaurantsController#find_available_dates, the else branch (restaurant has no packages). Used by the widget when the restaurant is inactive or has no packages.
  • Branch taken when: restaurant.restaurant_packages.blank? (no packages).
  • What it does with the result: Concatenates with dates_find_by_start_time (the today‑only special case) and returns the combined array as JSON data.
  • Equivalent vendor‑V1 caps? No. The controller does not apply any equivalent of the vendor‑V1 caps. However, the method itself (find_available_dates in inv_checker_hungry_hub_service.rb:1013) already applies the caps internally (expiry_date from rp.end_date at line 1022–1027, exceeds_max_seat? at line 1095).
  • Performance sensitivity: High. Widget web, customer‑facing, called on the restaurant detail page. 20‑day window.
  • Assumptions about output shape: Expects the same raw per‑date payload as 2.1.
  • Vendor‑V1 assumption map: B — the method already has its own equivalent of the caps. The enhanced method (with batched warm‑up) is a drop‑in.
  • Risk: Low.
  • Test coverage: No direct spec.
  • Notes: This is the “no packages” branch of the V5 endpoint. The method’s internal caps are a no‑op when there are no packages (the has_packages check at line 1020 short‑circuits the rp.end_date logic).
  • Calling class / context: Admin::RestaurantAvailabilitiesController#search. Admin UI for searching restaurants by availability.
  • Branch taken when: params['pick_date'] is anything other than 'with_time' or 'single' (i.e., a date range).
  • What it does with the result: Checks if any date in the range has availability == true (line 83). Returns a simple status: true/false JSON.
  • Equivalent vendor‑V1 caps? No. The controller only reads availability.
  • Performance sensitivity: Low. Admin UI, low traffic. Date range is whatever the admin enters (unbounded, but typically short).
  • Assumptions about output shape: Only uses availability key. Tolerant.
  • Vendor‑V1 assumption map: A — drop‑in.
  • Risk: Low.
  • Test coverage: spec/controllers/admin/restaurant_availabilities_controller_spec.rb (not directly verified for this method, but the file exists).
  • Notes: Admin‑facing, so a behavioral change would be noticed quickly by the team.

2.4 app/services/vendor_availability/read_service.rb:55 — Vendor V1 Read Service

  • Calling class / context: VendorAvailability::ReadService#available_dates. This is the Vendor V1 entry point when restaurant_package_id IS provided.
  • Branch taken when: Vendor V1 find_available_dates controller is called WITH a restaurant_package_id param.
  • What it does with the result: Applies the vendor‑V1‑specific caps: (a) expiry_date = min(restaurant.expiry_date, local_rps.map(&:end_date)) (line 63–68), (b) exceeds_max_seat?(adult, slug) cap (line 108), (c) re‑derives min_seat/max_seat from the package table (line 97–105). Returns the array to the controller.
  • Equivalent vendor‑V1 caps? Yes — but in the WRAPPER, not the inv_checker method. The ReadService adds BOTH the expiry_date cap (using rp.end_date) and the exceeds_max_seat? cap. The inv_checker method ALSO applies these caps internally (see inv_checker_hungry_hub_service.rb:1022–1095), so the ReadService’s caps are redundant for the inv_checker method’s output but the ReadService still applies them defensively.
  • Performance sensitivity: Very high. Vendor V1, the endpoint the entire PR #8274 perf overhaul is targeting. 20‑day window.
  • Assumptions about output shape: Expects the raw per‑date payload from find_available_date(date, slug).
  • Vendor‑V1 assumption map: C — relies on the caps being applied in the read path. The inv_checker method already applies them, so the ReadService’s caps are belt‑and‑suspenders. If the refactor changes the inv_checker method to NOT apply the caps, the ReadService’s own caps will catch it. This is the one method where the ReadService’s caps are a true safety net.
  • Risk: HIGH (but mitigated — the ReadService already applies the caps itself).
  • Test coverage:
    • spec/requests/api/vendor/v1/restaurants_availability_contract_spec.rb:25 — contract test for the with‑package‑id branch.
    • spec/services/vendor_availability/read_service_spec.rb:35 — direct unit test.
  • Notes: This is the second call site that the refactor is trying to fold into the generic method. The wrapper must continue to apply the caps (or the inv_checker method must continue to apply them, which it already does for find_available_dates).

2.5 app/services/vendors_service/klook/availability_service.rb:74 — Klook integration

  • Calling class / context: VendorsService::Klook::AvailabilityService#calendar_dates_availability_data. Called by the Klook webhook/controller when Klook asks for calendar availability.
  • Branch taken when: Klook sends a start_date + end_date payload (date range).
  • What it does with the result: Maps each date to a Klook‑shaped hash (local_date, available, vacancies, capacity). Divides by per_pack_qty if the package uses per‑pack pricing.
  • Equivalent vendor‑V1 caps? No. The service does not apply any equivalent of the vendor‑V1 caps. It only reads date, seat_left, quantity_available, and availability.
  • Performance sensitivity: Medium. Klook webhook, called on availability updates. Date range is whatever Klook sends.
  • Assumptions about output shape: Expects the raw per‑date payload with the standard keys. Uses date (as string), seat_left, quantity_available, availability.
  • Vendor‑V1 assumption map: A — drop‑in.
  • Risk: Low.
  • Test coverage: spec/services/vendors_service/klook/availability_service_spec.rb:83 — direct unit test. Stubs the inv_checker.
  • Notes: Klook is a third‑party integration, so a behavioral change would manifest as wrong availability data sent to Klook, which would be caught by Klook’s downstream consumers.

2.6 bin/inventory_system/validate_inventory_consistency.rb:256 — Ops consistency script

  • Calling class / context: InventoryConsistencyValidator#compare_package_dates. Standalone Ruby script.
  • Branch taken when: Always (when validating a restaurant with packages and --package-ids is provided).
  • What it does with the result: Compares the output to the V5 API output (available_dates_based_on_packages.json) field‑by‑field.
  • Equivalent vendor‑V1 caps? No.
  • Performance sensitivity: Low. Ops script.
  • Assumptions about output shape: Expects byte‑for‑byte match with the V5 API.
  • Vendor‑V1 assumption map: A — drop‑in. Strong signal that the method must remain a drop‑in for the non‑vendor‑V1 path.
  • Risk: Low (for the refactor) but high signal value.
  • Test coverage: N/A (this IS the test).
  • Notes: Same as 1.5 — the script will catch any output shape change.

3. Callers of InvCheckerFactory / create_inv_checker_service (transitive reach)

These callers create an inv_checker instance but do NOT directly call find_available_dates_without_start_time or find_available_dates. They are listed for completeness because the refactor changes the internals of the inv_checker methods, which could transitively affect them. None of these are at risk from the refactor because they don’t call the two methods being changed.

3.1 Reachable from public APIs (vendor V1, V5, partner, admin)

FileLineWhat it calls on inv_checkerRisk
app/controllers/api/vendor/v1/restaurants_controller.rb120available_packages (for find_available_packages)None — different method
app/controllers/api/vendor/v1/restaurants_controller.rb428restaurant_package_ids= (for find_available_start_times[_v4])None — different method
app/controllers/api/v5/restaurants_controller.rb234, 323, 459, 479, 517, 838, 1110, 1167find_available_dates_without_start_time, find_available_dates, find_available_start_times[_v4], bookable?, recommend_dateSee §1.1, §1.2, §2.2
app/controllers/api/v5/restaurant_packages_controller.rb137, 179, 214recommend_date, seat_lefts (TODO), otherNone — different methods
app/controllers/api/v5/concerns/inventories_v4.rb14 (via create_inv_checker_instance)find_available_dates, find_available_start_times_v4See §2.1
app/controllers/api/partner/v1/reservations_controller.rb260bookable?None — different method
app/controllers/api/dashboard/reservations_controller.rb165bookable?None — different method
app/controllers/admin/restaurant_availabilities_controller.rb53bookable?, find_available_single_date, find_available_datesSee §2.3
app/controllers/admin/restaurants_controller.rb109, 110, 115, 1507bookable?None — different method

3.2 Internal (workers, presenters, services)

FileLineWhat it calls on inv_checkerRisk
app/services/vendor_availability/read_service.rb192find_available_date (per‑date), exceeds_max_seat? (via send)See §1.3, §2.4
app/workers/vendors/getyourguide/notify_worker.rb46, 121find_available_dates_without_start_time, find_available_start_times, get_inv_by_dateSee §1.4
app/workers/vendors/restaurant_search_availability_check_worker.rb(HTTP to V5 API, not direct inv_checker)n/a — calls V5 API, which calls the inv_checkerIndirect — see §1.1
app/workers/inventory/generate_summary_worker.rb56unknown (not investigated in detail)None expected
app/workers/inventory_reporter_worker.rb25seat_lefts (TODO)None
app/workers/check_booking_availability_worker.rb11unknownNone expected
app/services/reservation_service/init/hungry_hub.rb386, 416, 611unknownNone expected
app/services/report_service/dashboard.rb40unknownNone expected
app/services/partner_service/inventories/get_inventory_with_time_service.rb224unknownNone expected
app/services/partner_service/reports/branch_covers_service.rb48unknownNone expected
app/services/partner_service/reports/base_service.rb106unknownNone expected
app/services/partner_service/reports/sold_out_service.rb70unknownNone expected
app/services/event_driven_services/hh_search/schemas/restaurant_availability_schema.rb57find_available_start_times_v4None
app/services/event_driven_services/hh_search/producers/restaurants/availability_producer.rb150, 181inventories (private, via method(:inventories))None
app/presenters/admin/bookability_presenter.rb896find_available_date (per‑date, via ReadService for the new cache)See §4
app/my_lib/modules/owners/reports.rb40unknownNone expected
app/my_lib/package_booking/users/update.rb246unknownNone expected
app/my_lib/modules/owners/reports/sold_out_report.rb44unknownNone expected
app/my_lib/modules/owners/reports/branch_covers_report.rb17unknownNone expected
app/my_lib/agents/base.rb145unknownNone expected
app/models/restaurant.rb292, 314, 751create_inv_checkers, inv_checker_service_classNone
app/mailers/staff_mailer.rb570unknownNone expected
app/controllers/external_reservations_controller.rb109unknownNone expected
app/controllers/dashboard/v2/inventories_controller.rb105unknownNone expected
app/controllers/dashboard/v2/reservations_controller.rb383bookable?None
app/controllers/dashboard/v2/group/reservations_controller.rb47bookable?None
app/controllers/api/vendor/v1/google_reserve/availabilities_controller.rb100restaurant.create_inv_checkersNone
app/controllers/api/vendor/v1/getyourguide/availabilities_controller.rb27restaurant.create_inv_checkers.firstNone

4. Admin::BookabilityPresenter — the admin diagnosis presenter

This is a special case. The presenter does NOT call find_available_dates_without_start_time or find_available_dates directly. It calls find_available_date (per‑date, singular) and VendorAvailability::ReadService#available_dates_without_start_time (the read service wrapper).

  • File: app/presenters/admin/bookability_presenter.rb
  • Relevant lines:
    • Line 232: legacy = safe_call({}) { checker.find_available_date(date, slug) } — reads the legacy per‑date cache.
    • Line 239: (svc.available_dates_without_start_time(adult: party, kids: 0, start_date: date.to_s, end_date: date.to_s).find { |row| row['date'].to_s == date.to_s }) || {} — reads the new cache via the ReadService, for a single date.
    • Line 516: dates.each { |d| checker.set_find_available_date(d, slug) } — writes the legacy cache (sanctioned write path).
    • Line 643: checker.send(:calc_find_available_date, date, slug) — reads the fresh DB truth (private method, reached read‑only via send).
  • What it does: Compares fresh DB truth vs. legacy cache vs. new cache to diagnose bookability issues for the admin CS team.
  • Vendor‑V1 assumption map: B — the presenter uses the ReadService wrapper, which applies the vendor‑V1 caps. The presenter does NOT need the caps to be in the generic inv_checker method.
  • Risk: Low. The presenter’s contract is with the ReadService, not the inv_checker method directly.
  • Test coverage: spec/presenters/admin/bookability_parity_spec.rb and spec/presenters/admin/bookability_processing_spec.rb — extensive coverage.
  • Notes: This is the most heavily tested caller. The presenter’s attribution logic (line 217–313) explicitly compares three sources: fresh, legacy, new. If the refactor changes the inv_checker method’s output, the attribution will show a divergence and the admin CS team will see a false CACHE BUG diagnosis.

5. Spec coverage map

CallerSpec fileCoverage
§1.1 V5 find_available_dates (line 258)None directNone — only via validate_inventory_consistency.rb
§1.2 V5 old_find_available_dates (line 346)NoneNone
§1.3 Vendor V1 ReadService (line 140)restaurants_availability_contract_spec.rb:49 + read_service_spec.rbGood
§1.4 GYG notify worker (line 103)NoneNone
§1.5 validate_inventory_consistency (line 186)N/A (this IS the test)Excellent (byte‑for‑byte)
§2.1 V4 packages path (line 30)None directNone — only via validate_inventory_consistency.rb
§2.2 V5 no‑packages branch (line 303)NoneNone
§2.3 Admin restaurant_availabilities (line 73)spec/controllers/admin/restaurant_availabilities_controller_spec.rb (not directly verified)Partial
§2.4 Vendor V1 ReadService (line 55)restaurants_availability_contract_spec.rb:25 + read_service_spec.rbGood
§2.5 Klook availability_service (line 74)spec/services/vendors_service/klook/availability_service_spec.rb:83Good
§2.6 validate_inventory_consistency (line 256)N/A (this IS the test)Excellent (byte‑for‑byte)
§4 BookabilityPresenterspec/presenters/admin/bookability_parity_spec.rb + spec/presenters/admin/bookability_processing_spec.rbExcellent

Callers with NO test coverage (riskiest to change):

  1. §1.1 V5 find_available_dates (line 258) — public customer‑facing endpoint
  2. §1.2 V5 old_find_available_dates (line 346) — widget web
  3. §1.4 GYG notify worker (line 103) — background worker
  4. §2.1 V4 packages path (line 30) — public V5 endpoint
  5. §2.2 V5 no‑packages branch (line 303) — widget web

These five are the riskiest because they have no direct spec AND they are all in category A (drop‑in). If the refactor accidentally changes the output shape, these will break in production with no test to catch it. The validate_inventory_consistency.rb script will catch it for §1.1 and §2.1, but not for §1.2, §1.4, §2.2.


6. Vendor‑V1‑specific assumption map (summary)

find_available_dates_without_start_time

CallerCategoryNotes
§1.3 Vendor V1 ReadServiceC (relies on caps in wrapper)The only caller that needs the caps
§1.1 V5 (active, has packages)A (drop‑in)Expects raw payload
§1.2 V5 old_find_available_datesA (drop‑in)Expects raw payload
§1.4 GYG notify workerA (drop‑in)Only reads date and availability
§1.5 validate_inventory_consistencyA (drop‑in)Compares raw fields

find_available_dates

CallerCategoryNotes
§2.4 Vendor V1 ReadServiceC (relies on caps in wrapper)The only caller that needs the caps (and the inv_checker method already applies them internally)
§2.1 V4 packages pathA (drop‑in)Expects raw payload
§2.2 V5 (no packages)B (method already has caps)The inv_checker method already applies expiry_date from rp.end_date and exceeds_max_seat?
§2.3 Admin restaurant_availabilitiesA (drop‑in)Only reads availability
§2.5 Klook availability_serviceA (drop‑in)Expects raw payload
§2.6 validate_inventory_consistencyA (drop‑in)Compares raw fields

7. Risk ranking (summary)

High risk (2)

  1. §1.3 — VendorAvailability::ReadService#available_dates_without_start_time (Vendor V1, no package_id)

    • Relies on the vendor‑V1 caps being applied in the read path.
    • If the caps are removed from the wrapper and not added to the generic method, Vendor V1 will silently return wrong availability data.
    • Mitigation: The refactor must preserve the caps in a thin wrapper (the existing ReadService, or a future controller‑level wrapper).
  2. §2.4 — VendorAvailability::ReadService#available_dates (Vendor V1, with package_id)

    • Relies on the vendor‑V1 caps being applied in the read path.
    • Mitigation already in place: The inv_checker method (find_available_dates at inv_checker_hungry_hub_service.rb:1013) already applies the caps internally (expiry_date from rp.end_date at line 1022–1027, exceeds_max_seat? at line 1095). The ReadService also applies them defensively (line 63–68, line 108). Even if the refactor changes the method, the ReadService is a safety net.
    • Risk is LOWER than §1.3 because of the double‑coverage, but still HIGH because the refactor might also touch the internal caps logic.

Medium risk (0)

None identified. Every direct caller is either A (drop‑in) or B (method already has caps).

Low risk (9)

  1. §1.1 V5 find_available_dates (line 258) — A
  2. §1.2 V5 old_find_available_dates (line 346) — A
  3. §1.4 GYG notify worker (line 103) — A
  4. §1.5 validate_inventory_consistency (line 186) — A
  5. §2.1 V4 packages path (line 30) — A
  6. §2.2 V5 no‑packages branch (line 303) — B
  7. §2.3 Admin restaurant_availabilities (line 73) — A
  8. §2.5 Klook availability_service (line 74) — A
  9. §2.6 validate_inventory_consistency (line 256) — A
  10. §4 BookabilityPresenter — B (uses ReadService)

8. “Needs investigation” items

  1. §2.2 V5 no‑packages branch (line 303) — This caller hits find_available_dates on a restaurant with NO packages. The method’s internal caps (expiry_date from rp.end_date, exceeds_max_seat?) are short‑circuited by the has_packages check (line 1020 in inv_checker_hungry_hub_service.rb). The refactor should preserve this short‑circuit. Low risk but worth verifying the refactored method doesn’t accidentally add the caps unconditionally.

  2. §1.4 GYG notify worker (line 103) — No test coverage. The worker is a background job, so a behavioral change would not be immediately visible. Worth adding a spec before the refactor ships.

  3. §1.1 and §1.2 V5 find_available_dates — No direct test coverage for the V5 controller. The validate_inventory_consistency.rb script is the only safety net. Worth adding a controller spec for the public V5 endpoint.

  4. §2.1 V4 packages path (line 30) — No direct test coverage. The concern is included in the V5 controller but has no dedicated spec. Worth adding one.

  5. Inventory::InvCheckerSupplierService#find_available_dates override — The supplier service overrides find_available_dates (line 36 of inv_checker_supplier_service.rb) to normalize kids into adult. The refactor must preserve this override. The supplier service is used by restaurants with third‑party inventory (SevenRooms, Tablecheck, etc.). Low risk because the override just delegates to super, but worth verifying the refactored method still works with the supplier service.


9. Patterns noticed

  1. All non‑vendor‑V1 callers are category A (drop‑in). Every direct caller of find_available_dates_without_start_time other than the ReadService expects the raw per‑date payload with no vendor‑V1‑specific caps. The refactor must preserve this.

  2. Vendor V1 is the ONLY caller that needs the caps. The ReadService is the single point of vendor‑V1‑specific behavior. This is good design — the caps are isolated in one wrapper.

  3. find_available_dates already has its own caps internally. The method at inv_checker_hungry_hub_service.rb:1013 already applies expiry_date from rp.end_date and exceeds_max_seat?. The ReadService’s caps are redundant for this method (belt‑and‑suspenders). The refactor should NOT remove the internal caps — they’re a safety net.

  4. find_available_dates_without_start_time does NOT have its own caps. The method at inv_checker_hungry_hub_service/find_available_dates.rb:14 only applies expiry_date = restaurant.expiry_date (not rp.end_date) and does NOT call exceeds_max_seat?. The ReadService is the ONLY place these caps are applied. This is the asymmetry that makes the refactor risky.

  5. validate_inventory_consistency.rb is the strongest behavioral contract. It compares the inv_checker output to the V5 API output byte‑for‑byte. Any shape change to the inv_checker methods will be caught here. This is the single most important safety net for the refactor.

  6. The admin BookabilityPresenter uses the ReadService, not the inv_checker method directly. This means the presenter’s contract is with the ReadService wrapper, not the inv_checker method. The refactor can change the inv_checker method’s internals (including the warm‑up) without affecting the presenter, AS LONG AS the ReadService’s output shape is preserved.

  7. The Klook integration is the only third‑party vendor that calls find_available_dates directly (not through the ReadService). All other third‑party integrations (GetYourGuide, Google Reserve) go through the vendor V1 endpoint. The Klook integration is also the only one with a direct unit test for find_available_dates.

  8. The V5 controller has TWO call sites for find_available_dates_without_start_time (line 258 and line 346). The second one (old_find_available_dates) is the legacy widget path. Both are category A.

  9. The find_available_date (singular, per‑date) method has a wider reach. It’s called by the ReadService, the BookabilityPresenter, the inv_checker’s own warm_up_cache, and the set_find_available_date write path. The refactor should NOT change the per‑date method’s behavior — only the loop methods (find_available_dates and find_available_dates_without_start_time).

  10. The ReadService’s available_dates (with package_id) has DOUBLE coverage of the caps. Both the inv_checker method AND the ReadService apply them. The ReadService’s available_dates_without_start_time (no package_id) has SINGLE coverage — only the ReadService applies the caps. This is why §1.3 is riskier than §2.4.