|
|
@@ -60,6 +60,7 @@ async def diff(
|
|
|
_timed('prices', _diff_prices(start_date, end_date, api), timings),
|
|
|
_timed('closed', _diff_closed(start_date, end_date, api), timings),
|
|
|
_timed('reservations', _diff_reservations(start_date, end_date, api), timings),
|
|
|
+ _timed('double_bookings', _diff_double_bookings(start_date, end_date, api), timings),
|
|
|
_timed('user_cal_closed', _diff_user_cal_closed(start_date, end_date), timings),
|
|
|
_timed('ical_feeds_closed', _diff_ical_feeds_closed(start_date, end_date), timings),
|
|
|
_timed('rule_discounts', _diff_rule_discounts(start_date, end_date), timings),
|
|
|
@@ -68,7 +69,7 @@ async def diff(
|
|
|
_timed('orphans', _diff_orphans(start_date, end_date), timings),
|
|
|
)
|
|
|
timings['_total'] = round(time.perf_counter() - t0, 2)
|
|
|
- keys = ['prices', 'closed', 'reservations', 'user_cal_closed', 'ical_feeds_closed', 'rule_discounts', 'rule_closed', 'rule_prices', 'orphans']
|
|
|
+ keys = ['prices', 'closed', 'reservations', 'double_bookings', 'user_cal_closed', 'ical_feeds_closed', 'rule_discounts', 'rule_closed', 'rule_prices', 'orphans']
|
|
|
return {**dict(zip(keys, results)), '_timings': timings}
|
|
|
|
|
|
|
|
|
@@ -244,6 +245,49 @@ async def _diff_reservations(
|
|
|
return diffs
|
|
|
|
|
|
|
|
|
+# ── double bookings ──────────────────────────────────────────────────────────
|
|
|
+
|
|
|
+async def _diff_double_bookings(
|
|
|
+ start_date: date,
|
|
|
+ end_date: date,
|
|
|
+ api: 'ServiceAPI',
|
|
|
+) -> list[dict]:
|
|
|
+ """Service API reservations that overlap in date range (double bookings).
|
|
|
+
|
|
|
+ Each row: {'a': {id, guest_name, service, checkin_date, checkout_date},
|
|
|
+ 'b': {id, guest_name, service, checkin_date, checkout_date},
|
|
|
+ 'overlap_start', 'overlap_end'}
|
|
|
+ Sorted by overlap_start.
|
|
|
+ """
|
|
|
+ reservations = await api.get_reservations(start_date, end_date)
|
|
|
+ results: list[dict] = []
|
|
|
+ for i, a in enumerate(reservations):
|
|
|
+ for b in reservations[i + 1:]:
|
|
|
+ overlap_start = max(a['start_date'], b['start_date'])
|
|
|
+ overlap_end = min(a['end_date'], b['end_date'])
|
|
|
+ if overlap_start < overlap_end:
|
|
|
+ results.append({
|
|
|
+ 'a': {
|
|
|
+ 'id': a['id'],
|
|
|
+ 'guest_name': a['guest_name'],
|
|
|
+ 'service': a['service'],
|
|
|
+ 'checkin_date': a['start_date'],
|
|
|
+ 'checkout_date': a['end_date'],
|
|
|
+ },
|
|
|
+ 'b': {
|
|
|
+ 'id': b['id'],
|
|
|
+ 'guest_name': b['guest_name'],
|
|
|
+ 'service': b['service'],
|
|
|
+ 'checkin_date': b['start_date'],
|
|
|
+ 'checkout_date': b['end_date'],
|
|
|
+ },
|
|
|
+ 'overlap_start': overlap_start,
|
|
|
+ 'overlap_end': overlap_end,
|
|
|
+ })
|
|
|
+ results.sort(key=lambda x: x['overlap_start'])
|
|
|
+ return results
|
|
|
+
|
|
|
+
|
|
|
# ── closed (user_cal) ─────────────────────────────────────────────────────────
|
|
|
|
|
|
async def _diff_user_cal_closed(
|