PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / trunk
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution vtrunk
2.4.0 2.3.0 2.2.5 2.2.0 2.1.2 2.1.1 trunk 1.10.0 1.10.01 1.10.02 1.5.0 1.5.01 1.5.02 1.5.1 1.5.10 1.5.20 1.5.21 1.5.22 1.5.23 1.5.24 1.5.25 1.6.0 1.7.0 1.7.1 1.7.2 All 33 releases
← All changes | app/Http/Controllers/ReportController.php +71 -90 2.1.2trunk View file →
@@ -3,8 +3,9 @@
3 3 namespace FluentBooking\App\Http\Controllers;
4 4
5 5 use FluentBooking\App\Models\Booking;
6 6 use FluentBooking\App\Models\BookingActivity;
7 +use FluentBooking\App\Services\BookingReportService;
7 8 use FluentBooking\App\Services\ReportingHelperTrait;
8 9 use FluentBooking\Framework\Http\Request\Request;
9 10 use FluentBooking\Framework\Support\Arr;
10 11 use FluentBooking\App\Services\DateTimeHelper;
@@ -116,22 +117,15 @@
116 117
117 118 list($groupBy, $orderBy) = $this->getGroupAndOrder($frequency);
118 119
119 120 // Define a function to fetch booking data based on status
120 - $fetchBookingsByStatus = function ($status) use ($period, $groupBy, $orderBy, $frequency, $from, $to) {
121 -
122 - if (!PermissionManager::userCanSeeAllBookings()) {
123 -
124 - return Booking::select($this->prepareSelect($frequency))
125 - ->where('status', $status)
126 - ->whereBetween('created_at', [$from->format('Y-m-d'), $to->format('Y-m-d')])
127 - ->where('host_user_id', get_current_user_id())
128 - ->groupBy($groupBy)
129 - ->orderBy($orderBy, 'ASC')
130 - ->get();
131 - }
132 -
133 - return Booking::select($this->prepareSelect($frequency))
121 + // Scoped through BookingReportService so this graph counts the same
122 + // bookings as the widgets above it and the schedules list beside it.
123 + // It previously scoped on the host_user_id column alone, which hid
124 + // bookings a limited host was a secondary host on.
125 + $fetchBookingsByStatus = function ($status) use ($groupBy, $orderBy, $frequency, $from, $to) {
126 + return BookingReportService::scoped()
127 + ->select($this->prepareSelect($frequency))
134 128 ->where('status', $status)
135 129 ->whereBetween('created_at', [$from->format('Y-m-d'), $to->format('Y-m-d')])
136 130 ->groupBy($groupBy)
137 131 ->orderBy($orderBy, 'ASC')
@@ -160,36 +154,28 @@
160 154 $lastMonthStartTime = gmdate('Y-m-d H:i:s', strtotime("$startTime - $differenceInDays days")); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
161 155
162 156 $bookingStats = $this->getBookingStats($startTime, $endTime, $lastMonthStartTime, $startTime);
163 157
164 - $bookingStats['bookedComparison'] = $this->getComparisonMessage($bookingStats['bookedStat']);
165 - $bookingStats['completedComparison'] = $this->getComparisonMessage($bookingStats['completedStat']);
166 - $bookingStats['cancelledComparison'] = $this->getComparisonMessage($bookingStats['cancelledStat']);
167 - $bookingStats['guestComparison'] = $this->getComparisonMessage($bookingStats['guestStat']);
158 + $comparison = $this->getComparisonLabel($differenceInDays);
168 159
160 + $bookingStats['bookedComparison'] = $comparison;
161 + $bookingStats['completedComparison'] = $comparison;
162 + $bookingStats['cancelledComparison'] = $comparison;
163 + $bookingStats['guestComparison'] = $comparison;
164 +
169 165 return $bookingStats;
170 166 }
171 167
172 168 private function getBookingStats($currentMonthStart, $currentMonthEnd, $lastMonthStart, $lastMonthEnd)
173 169 {
174 - $scopeByUser = !PermissionManager::userCanSeeAllBookings();
175 -
176 - $scopeToUser = function ($query) {
177 - $query->whereHas('hosts', function ($hostQuery) {
178 - $hostQuery->where('user_id', get_current_user_id());
179 - });
170 + $createdBetween = function ($start, $end) {
171 + return BookingReportService::scoped()->whereBetween('created_at', [$start, $end]);
180 172 };
181 173
182 - $createdBetween = function ($start, $end) use ($scopeByUser, $scopeToUser) {
183 - return Booking::whereBetween('created_at', [$start, $end])
184 - ->when($scopeByUser, $scopeToUser);
174 + $endTimeBetween = function ($start, $end) {
175 + return BookingReportService::scoped()->whereBetween('end_time', [$start, $end]);
185 176 };
186 177
187 - $endTimeBetween = function ($start, $end) use ($scopeByUser, $scopeToUser) {
188 - return Booking::whereBetween('end_time', [$start, $end])
189 - ->when($scopeByUser, $scopeToUser);
190 - };
191 -
192 178 // Bookings and guests based on 'created_at'
193 179 $totalBookedCurrentMonth = $createdBetween($currentMonthStart, $currentMonthEnd)->count();
194 180 $totalBookedLastMonth = $createdBetween($lastMonthStart, $lastMonthEnd)->count();
195 181
@@ -214,34 +200,29 @@
214 200 private function getPercentage($currentMonthTotal, $lastMonthTotal)
215 201 {
216 202 if ($lastMonthTotal > 0) {
217 203 return round((($currentMonthTotal - $lastMonthTotal) / $lastMonthTotal) * 100, 2);
218 - } else if (!$lastMonthTotal) {
219 - return 100;
220 204 }
221 - return 0;
205 +
206 + // Nothing before and nothing now is no change, not a rise from nothing - which is what
207 + // every tile on a fresh install used to claim.
208 + if (!$currentMonthTotal) {
209 + return 0;
210 + }
211 +
212 + return 100;
222 213 }
223 214
224 215 private function getBookingWidgetNumbers($startTime, $endTime)
225 216 {
226 - $scopeByUser = !PermissionManager::userCanSeeAllBookings();
227 -
228 - $scopeToUser = function ($query) {
229 - $query->whereHas('hosts', function ($hostQuery) {
230 - $hostQuery->where('user_id', get_current_user_id());
231 - });
217 + $createdBetween = function () use ($startTime, $endTime) {
218 + return BookingReportService::scoped()->whereBetween('created_at', [$startTime, $endTime]);
232 219 };
233 220
234 - $createdBetween = function () use ($startTime, $endTime, $scopeByUser, $scopeToUser) {
235 - return Booking::whereBetween('created_at', [$startTime, $endTime])
236 - ->when($scopeByUser, $scopeToUser);
221 + $endTimeBetween = function () use ($startTime, $endTime) {
222 + return BookingReportService::scoped()->whereBetween('end_time', [$startTime, $endTime]);
237 223 };
238 224
239 - $endTimeBetween = function () use ($startTime, $endTime, $scopeByUser, $scopeToUser) {
240 - return Booking::whereBetween('end_time', [$startTime, $endTime])
241 - ->when($scopeByUser, $scopeToUser);
242 - };
243 -
244 225 $totalBooked = $createdBetween()->count();
245 226 $totalGuests = $createdBetween()->distinct()->count('email');
246 227
247 228 $bookingCompleted = $endTimeBetween()->where('status', 'completed')->count();
@@ -256,58 +237,62 @@
256 237 }
257 238
258 239 private function getAllBookingWidgetNumbers()
259 240 {
260 - $scopeByUser = !PermissionManager::userCanSeeAllBookings();
241 + // Four undated counts, so four full reads of the bookings table on
242 + // every dashboard load. They are lifetime totals; five minutes stale
243 + // is invisible.
244 + $cacheKey = 'fcal_report_all_widgets_' . get_current_blog_id() . '_' . get_current_user_id();
261 245
262 - $userId = get_current_user_id();
246 + $cached = get_transient($cacheKey);
263 247
264 - $totalBooked = Booking::when($scopeByUser, function($q) use ($userId) {
265 - $q->whereHas('hosts', function ($hostQuery) use ($userId) {
266 - $hostQuery->where('user_id', $userId);
267 - });
268 - })->count();
248 + if (is_array($cached)) {
249 + return $cached;
250 + }
269 251
270 - $bookingCompleted = Booking::where('status', 'completed')
271 - ->when($scopeByUser, function($q) use ($userId) {
272 - $q->whereHas('hosts', function ($hostQuery) use ($userId) {
273 - $hostQuery->where('user_id', $userId);
274 - });
275 - })->count();
252 + $totalBooked = BookingReportService::scoped()->count();
276 253
277 - $bookingCancelled = Booking::where('status', 'cancelled')
278 - ->when($scopeByUser, function($q) use ($userId) {
279 - $q->whereHas('hosts', function ($hostQuery) use ($userId) {
280 - $hostQuery->where('user_id', $userId);
281 - });
282 - })->count();
254 + $bookingCompleted = BookingReportService::scoped()->where('status', 'completed')->count();
283 255
284 - $totalGuests = Booking::distinct()
285 - ->when($scopeByUser, function($q) use ($userId) {
286 - $q->whereHas('hosts', function ($hostQuery) use ($userId) {
287 - $hostQuery->where('user_id', $userId);
288 - });
289 - })->count('email');
256 + $bookingCancelled = BookingReportService::scoped()->where('status', 'cancelled')->count();
290 257
291 - return [
258 + $totalGuests = BookingReportService::scoped()->distinct()->count('email');
259 +
260 + $numbers = [
292 261 'totalBooked' => $totalBooked,
293 262 'totalGuests' => $totalGuests,
294 263 'bookingCompleted' => $bookingCompleted,
295 264 'bookingCancelled' => $bookingCancelled
296 265 ];
266 +
267 + set_transient($cacheKey, $numbers, 300);
268 +
269 + return $numbers;
297 270 }
298 271
299 - private function getComparisonMessage($change)
272 + /**
273 + * The window the percentage beside each number is measured against.
274 + *
275 + * This used to describe the direction of the change - "More than last month" - which is
276 + * what the percentage next to it already says, so the tile stated one fact twice and left
277 + * the window it was comparing against unstated. It was also wrong whenever a custom range
278 + * was picked, since the comparison is always against a preceding window of the same
279 + * length, not against a calendar month.
280 + *
281 + * @param float $days Length of the reporting window, in days.
282 + *
283 + * @return string
284 + */
285 + private function getComparisonLabel($days)
300 286 {
301 - if ($change > 0) {
302 - return __('More than last month', 'fluent-booking');
287 + $days = max(1, (int) round($days));
288 +
289 + if ($days === 1) {
290 + return __('vs. previous day', 'fluent-booking');
303 291 }
304 - if ($change < 0) {
305 - return __('Less than last month', 'fluent-booking');
306 - }
307 292
308 - return __('Same as last month', 'fluent-booking');
309 -
293 + /* translators: %d - the number of days being compared against */
294 + return sprintf(__('vs. previous %d days', 'fluent-booking'), $days);
310 295 }
311 296
312 297 private function getPaymentWidgets($startTime, $endTime)
313 298 {
@@ -362,9 +347,9 @@
362 347 ->total;
363 348
364 349 $paymentPercentage = $this->getPercentage($currentMonthTotal, $lastMonthTotal);
365 350
366 - $paymentComparison = $this->getComparisonMessage($paymentPercentage);
351 + $paymentComparison = $this->getComparisonLabel($differenceInDays);
367 352
368 353 $paymentStats['totalPayment'] = intval($currentMonthTotal);
369 354 $paymentStats['paymentComparison'] = $paymentComparison;
370 355 $paymentStats['paymentStat'] = $paymentPercentage;
@@ -379,11 +364,9 @@
379 364 ->upcoming()
380 365 ->orderBy('start_time', 'ASC');
381 366
382 367 if (!PermissionManager::userCanSeeAllBookings()) {
383 - $bookingQuery->whereHas('calendar', function ($q) {
384 - $q->where('user_id', get_current_user_id());
385 - });
368 + $bookingQuery->whereHostAccess(get_current_user_id());
386 369 }
387 370
388 371 $nextMeetings = $bookingQuery->limit(50)->get()
389 372 ->unique('group_id')
@@ -414,11 +397,9 @@
414 397 {
415 398 $bookingQuery = Booking::whereIn('status', ['pending', 'scheduled', 'completed']);
416 399
417 400 if (!PermissionManager::userCanSeeAllBookings()) {
418 - $bookingQuery->whereHas('calendar', function ($q) {
419 - $q->where('user_id', get_current_user_id());
420 - });
401 + $bookingQuery->whereHostAccess(get_current_user_id());
421 402 }
422 403
423 404 return $bookingQuery->latest()->take(5)->get();
424 405 }