PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 1.2.31
Booking for Appointments and Events Calendar – Amelia v1.2.31
2.4.9 2.4.8 2.4.7 2.4.6 2.4.5 2.4.4 2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / src / Application / Services / Stats / StatsService.php
ameliabooking / src / Application / Services / Stats Last commit date
StatsService.php 1 year ago
StatsService.php
600 lines
1 <?php
2
3 namespace AmeliaBooking\Application\Services\Stats;
4
5 use AmeliaBooking\Application\Services\Bookable\AbstractPackageApplicationService;
6 use AmeliaBooking\Application\Services\User\ProviderApplicationService;
7 use AmeliaBooking\Domain\Collection\Collection;
8 use AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException;
9 use AmeliaBooking\Domain\Entity\Booking\Appointment\Appointment;
10 use AmeliaBooking\Domain\Entity\Booking\Appointment\CustomerBooking;
11 use AmeliaBooking\Domain\Entity\Payment\Payment;
12 use AmeliaBooking\Domain\Entity\Schedule\DayOff;
13 use AmeliaBooking\Domain\Entity\Schedule\SpecialDay;
14 use AmeliaBooking\Domain\Entity\Schedule\WeekDay;
15 use AmeliaBooking\Domain\Entity\User\Provider;
16 use AmeliaBooking\Domain\Factory\Schedule\PeriodFactory;
17 use AmeliaBooking\Domain\Services\DateTime\DateTimeService;
18 use AmeliaBooking\Domain\Services\User\ProviderService;
19 use AmeliaBooking\Infrastructure\Common\Container;
20 use AmeliaBooking\Infrastructure\Common\Exceptions\QueryExecutionException;
21 use AmeliaBooking\Infrastructure\Repository\Bookable\Service\ServiceRepository;
22 use AmeliaBooking\Infrastructure\Repository\Booking\Appointment\AppointmentRepository;
23 use AmeliaBooking\Infrastructure\Repository\Booking\Appointment\CustomerBookingRepository;
24 use AmeliaBooking\Infrastructure\Repository\Location\LocationRepository;
25 use AmeliaBooking\Infrastructure\Repository\Payment\PaymentRepository;
26 use AmeliaBooking\Infrastructure\Repository\User\ProviderRepository;
27 use DateTime;
28 use Exception;
29 use Interop\Container\Exception\ContainerException;
30 use Slim\Exception\ContainerValueNotFoundException;
31
32 /**
33 * Class StatsService
34 *
35 * @package AmeliaBooking\Application\Services\Stats
36 */
37 class StatsService
38 {
39 private $container;
40
41 /**
42 * StatsService constructor.
43 *
44 * @param Container $container
45 */
46 public function __construct(Container $container)
47 {
48 $this->container = $container;
49 }
50
51 /**
52 * @param $params
53 *
54 * @return array
55 * @throws ContainerValueNotFoundException
56 * @throws InvalidArgumentException
57 * @throws QueryExecutionException
58 */
59 public function getCustomersStats($params)
60 {
61 /** @var CustomerBookingRepository $bookingRepository */
62 $bookingRepository = $this->container->get('domain.booking.customerBooking.repository');
63
64 /** @var array $returningCustomers */
65 $returningCustomers = array_column($bookingRepository->getReturningCustomers($params), 'customerId');
66
67 /** @var array $bookings */
68 $bookings = array_column($bookingRepository->getFilteredDistinctCustomersIds($params), 'customerId');
69
70 // Calculate number of customers in past period.
71 // E.g. If in a date filter is selected current week, calculate it for past week.
72 $dateFrom = DateTimeService::getCustomDateTimeObject($params['dates'][0]);
73
74 $dateTo = DateTimeService::getCustomDateTimeObject($params['dates'][1]);
75
76 $diff = (int)$dateTo->diff($dateFrom)->format('%a') + 1;
77
78 $dateFrom->modify('-' . $diff . 'days');
79 $dateTo->modify('-' . $diff . 'days');
80
81 $paramsPast = ['dates' => [$dateFrom->format('Y-m-d H:i:s'), $dateTo->format('Y-m-d H:i:s')]];
82
83 $bookingsPast = array_column($bookingRepository->getFilteredDistinctCustomersIds($paramsPast), 'customerId');
84
85 $pastPeriodCount = count($bookingsPast);
86
87 $returningCount = count(array_intersect($returningCustomers, $bookings));
88
89 $newCount = count($bookings) - $returningCount;
90
91 return [
92 'newCustomersCount' => $newCount,
93 'returningCustomersCount' => $returningCount,
94 'totalPastPeriodCustomers' => $pastPeriodCount
95 ];
96 }
97
98 /**
99 * @param array $params
100 * @return array
101 * @throws QueryExecutionException
102 * @throws ContainerValueNotFoundException
103 * @throws InvalidArgumentException
104 * @throws Exception
105 * @throws ContainerException
106 */
107 public function getRangeStatisticsData($params)
108 {
109 /** @var AppointmentRepository $appointmentRepository */
110 $appointmentRepository = $this->container->get('domain.booking.appointment.repository');
111
112 /** @var Collection $appointments */
113 $appointments = $appointmentRepository->getFiltered(
114 array_merge(
115 $params,
116 [
117 'skipServices' => true,
118 'skipProviders' => true,
119 'skipCustomers' => true,
120 'skipPayments' => true,
121 'skipExtras' => true,
122 'skipCoupons' => true,
123 'skipBookings' => true,
124 ]
125 )
126 );
127
128 /** @var CustomerBookingRepository $bookingRepository */
129 $bookingRepository = $this->container->get('domain.booking.customerBooking.repository');
130
131 /** @var Collection $bookings */
132 $bookings = $appointments->length()
133 ? $bookingRepository->getByCriteria(['appointmentIds' => $appointments->keys()])
134 : new Collection();
135
136 /** @var CustomerBooking $booking */
137 foreach ($bookings->getItems() as $booking) {
138 /** @var Appointment $appointment */
139 $appointment = $appointments->getItem($booking->getAppointmentId()->getValue());
140
141 $appointment->getBookings()->addItem($booking, $booking->getId()->getValue());
142 }
143
144 /** @var Collection $bookingsPayments */
145 $bookingsPayments = new Collection();
146
147 /** @var Appointment $appointment */
148 foreach ($appointments->getItems() as $appointment) {
149 /** @var CustomerBooking $booking */
150 foreach ($appointment->getBookings()->getItems() as $booking) {
151 $bookingsPayments->addItem(null, $booking->getId()->getValue());
152 }
153 }
154
155 /** @var PaymentRepository $paymentRepository */
156 $paymentRepository = $this->container->get('domain.payment.repository');
157
158 /** @var Collection $payments */
159 $payments = $bookingsPayments->length() ?
160 $paymentRepository->getByCriteria(['bookingIds' => $bookingsPayments->keys()]) : new Collection();
161
162 /** @var Payment $payment */
163 foreach ($payments->getItems() as $payment) {
164 if ($payment->getCustomerBookingId()) {
165 $bookingsPayments->placeItem($payment, $payment->getCustomerBookingId()->getValue(), true);
166 }
167 }
168
169 /** @var Appointment $appointment */
170 foreach ($appointments->getItems() as $appointment) {
171 /** @var CustomerBooking $booking */
172 foreach ($appointment->getBookings()->getItems() as $booking) {
173 if (
174 $bookingsPayments->keyExists($booking->getId()->getValue()) &&
175 $bookingsPayments->getItem($booking->getId()->getValue())
176 ) {
177 $booking->getPayments()->addItem($bookingsPayments->getItem($booking->getId()->getValue()));
178 }
179 }
180 }
181
182 /** @var ProviderRepository $providerRepository */
183 $providerRepository = $this->container->get('domain.users.providers.repository');
184 /** @var ServiceRepository $serviceRepository */
185 $serviceRepository = $this->container->get('domain.bookable.service.repository');
186 /** @var ProviderService $providerService */
187 $providerService = $this->container->get('domain.user.provider.service');
188
189 /** @var Collection $services */
190 $services = $serviceRepository->getAllArrayIndexedById();
191
192 /** @var Collection $providers */
193 $providers = $providerRepository->getWithSchedule(['dates' => $params['dates']]);
194
195 /** @var Provider $provider */
196 foreach ($providers->getItems() as $provider) {
197 $providerService->setProviderServices($provider, $services, true);
198 }
199
200 /** @var ProviderApplicationService $providerApplicationService */
201 $providerApplicationService = $this->container->get('application.user.provider.service');
202
203 /** @var AbstractPackageApplicationService $packageApplicationService */
204 $packageApplicationService = $this->container->get('application.bookable.package');
205
206 $packageDatesData = $packageApplicationService->getPackageStatsData($params);
207
208 /** @var Collection $appointmentsPackageCustomerServices */
209 $appointmentsPackageCustomerServices = $packageApplicationService->getPackageCustomerServicesForAppointments(
210 $appointments
211 );
212
213 $stats = [];
214
215 $statsPeriod = new \DatePeriod(
216 DateTimeService::getCustomDateTimeObject($params['dates'][0]),
217 new \DateInterval('P1D'),
218 DateTimeService::getCustomDateTimeObject($params['dates'][1])
219 );
220
221 /** @var DateTime $date */
222 foreach ($statsPeriod as $date) {
223 $stats[$date->format('Y-m-d')] = null;
224 }
225
226 $weekDaysData = [];
227
228 $specialDatesData = [];
229
230 $providersDaysOff = [];
231
232 /** @var Provider $provider */
233 foreach ($providers->getItems() as $provider) {
234 $providerId = $provider->getId()->getValue();
235
236 $providersDaysOff[$providerId] = [];
237
238 /** @var DayOff $daysOff */
239 foreach ($provider->getDayOffList()->getItems() as $daysOff) {
240 $daysOffPeriod = new \DatePeriod(
241 $daysOff->getStartDate()->getValue(),
242 new \DateInterval('P1D'),
243 DateTimeService::getCustomDateTimeObject(
244 $daysOff->getEndDate()->getValue()->format('Y-m-d H:i:s')
245 )->modify('+1 days')
246 );
247
248 /** @var DateTime $date */
249 foreach ($daysOffPeriod as $date) {
250 $providersDaysOff[$providerId][] = $date->format('Y-m-d');
251 }
252 }
253
254 // get provider week day available time
255 /** @var WeekDay $weekDay */
256 foreach ($provider->getWeekDayList()->getItems() as $weekDay) {
257 $dayIndex = $weekDay->getDayIndex()->getValue();
258
259 if (!array_key_exists($dayIndex, $weekDaysData)) {
260 $weekDaysData[$dayIndex] = [];
261 }
262
263 if ($weekDay->getPeriodList()->length() === 0) {
264 $weekDay->getPeriodList()->addItem(
265 PeriodFactory::create(
266 [
267 'startTime' => $weekDay->getStartTime()->getValue()->format('H:i:s'),
268 'endTime' => $weekDay->getEndTime()->getValue()->format('H:i:s'),
269 'periodServiceList' => [],
270 'periodLocationList' => [],
271 ]
272 )
273 );
274 }
275
276 $weekDaysData[$dayIndex][$providerId] = $providerApplicationService->getProviderScheduleIntervals(
277 $weekDay->getPeriodList(),
278 $weekDay->getTimeOutList()
279 );
280 }
281
282 // get provider special day available time
283 /** @var SpecialDay $specialDay */
284 foreach ($provider->getSpecialDayList()->getItems() as $specialDay) {
285 $specialDaysPeriod = new \DatePeriod(
286 $specialDay->getStartDate()->getValue(),
287 new \DateInterval('P1D'),
288 DateTimeService::getCustomDateTimeObject(
289 $specialDay->getEndDate()->getValue()->format('Y-m-d H:i:s')
290 )->modify('+1 days')
291 );
292
293 $specialDayExist = false;
294
295 foreach ($specialDaysPeriod as $date) {
296 if (array_key_exists($date->format('Y-m-d'), $stats)) {
297 $specialDayExist = true;
298 continue;
299 }
300 }
301
302 if ($specialDayExist) {
303 $providerSpecialDaysIntervals = $providerApplicationService->getProviderScheduleIntervals(
304 $specialDay->getPeriodList(),
305 new Collection()
306 );
307
308 /** @var DateTime $date */
309 foreach ($specialDaysPeriod as $date) {
310 $dateString = $date->format('Y-m-d');
311
312 if (array_key_exists($dateString, $stats)) {
313 if (!array_key_exists($dateString, $specialDatesData)) {
314 $specialDatesData[$dateString] = [];
315 }
316
317 $specialDatesData[$dateString][$providerId] = $providerSpecialDaysIntervals;
318 }
319 }
320 }
321 }
322 }
323
324 $appointmentDatesData = [];
325
326
327
328 /** @var Appointment $appointment */
329 foreach ($appointments->getItems() as $appointment) {
330 $date = $appointment->getBookingStart()->getValue()->format('Y-m-d');
331
332 $providerId = $appointment->getProviderId()->getValue();
333
334 $serviceId = $appointment->getServiceId()->getValue();
335
336 $appointmentDuration = $appointment->getBookingEnd()->getValue()->diff(
337 $appointment->getBookingStart()->getValue()
338 );
339
340 if (!array_key_exists($date, $appointmentDatesData)) {
341 $appointmentDatesData[$date] = [
342 'providers' => [],
343 'services' => []
344 ];
345 }
346
347 if (!array_key_exists($providerId, $appointmentDatesData[$date]['providers'])) {
348 $appointmentDatesData[$date]['providers'][$providerId] = [
349 'count' => 0,
350 'occupied' => 0,
351 'revenue' => 0
352 ];
353 }
354
355 if (!array_key_exists($serviceId, $appointmentDatesData[$date]['services'])) {
356 $appointmentDatesData[$date]['services'][$serviceId] = [
357 'count' => 0,
358 'occupied' => 0,
359 'revenue' => 0
360 ];
361 }
362
363 $occupiedDuration = $appointmentDuration->h * 60 + $appointmentDuration->i;
364
365 $appointmentDatesData[$date]['providers'][$providerId]['count']++;
366 $appointmentDatesData[$date]['providers'][$providerId]['occupied'] += $occupiedDuration;
367
368 $appointmentDatesData[$date]['services'][$serviceId]['count']++;
369 $appointmentDatesData[$date]['services'][$serviceId]['occupied'] += $occupiedDuration;
370
371 /** @var CustomerBooking $booking */
372 foreach ($appointment->getBookings()->getItems() as $booking) {
373 if ($booking->getPackageCustomerService()) {
374 $packageApplicationService->updatePackageStatsData(
375 $packageDatesData,
376 $appointmentsPackageCustomerServices,
377 $booking->getPackageCustomerService()->getId()->getValue(),
378 $date,
379 $occupiedDuration
380 );
381 } else {
382 /** @var Payment $payment */
383 foreach ($booking->getPayments()->getItems() as $payment) {
384 $appointmentDatesData[$date]['providers'][$providerId]['revenue'] +=
385 $payment->getAmount()->getValue();
386
387 $appointmentDatesData[$date]['services'][$serviceId]['revenue'] +=
388 $payment->getAmount()->getValue();
389 }
390 }
391 }
392 }
393
394 foreach ($stats as $dateKey => $dateStats) {
395 $dayIndex = DateTimeService::getCustomDateTimeObject($dateKey)->format('N');
396
397 // parse week day for provider
398 if (array_key_exists($dayIndex, $weekDaysData)) {
399 foreach ($weekDaysData[$dayIndex] as $providerKey => $weekDayData) {
400 if (!in_array($dateKey, $providersDaysOff[$providerKey], true)) {
401 $stats[$dateKey]['providers'][$providerKey] = [
402 'count' => 0,
403 'occupied' => 0,
404 'revenue' => 0,
405 'intervals' => $weekDayData
406 ];
407 }
408 }
409 }
410
411 // parse special day for provider
412 if (array_key_exists($dateKey, $specialDatesData)) {
413 foreach ($specialDatesData[$dateKey] as $providerKey => $specialDayData) {
414 if (!in_array($dateKey, $providersDaysOff[$providerKey], true)) {
415 $stats[$dateKey]['providers'][$providerKey] = [
416 'count' => 0,
417 'occupied' => 0,
418 'revenue' => 0,
419 'intervals' => $specialDayData
420 ];
421 }
422 }
423 }
424
425 if (array_key_exists($dateKey, $appointmentDatesData)) {
426 foreach ($appointmentDatesData[$dateKey]['providers'] as $providerKey => $appointmentStatsData) {
427 if (
428 empty($stats[$dateKey]['providers']) ||
429 !array_key_exists($providerKey, $stats[$dateKey]['providers'])
430 ) {
431 $stats[$dateKey]['providers'][$providerKey] = [
432 'intervals' => []
433 ];
434 }
435
436 $stats[$dateKey]['providers'][$providerKey]['count'] = $appointmentStatsData['count'];
437
438 $stats[$dateKey]['providers'][$providerKey]['occupied'] = $appointmentStatsData['occupied'];
439
440 $stats[$dateKey]['providers'][$providerKey]['revenue'] = $appointmentStatsData['revenue'];
441 }
442
443 foreach ($appointmentDatesData[$dateKey]['services'] as $serviceKey => $appointmentStatsData) {
444 $stats[$dateKey]['services'][$serviceKey]['count'] = $appointmentStatsData['count'];
445
446 $stats[$dateKey]['services'][$serviceKey]['occupied'] = $appointmentStatsData['occupied'];
447
448 $stats[$dateKey]['services'][$serviceKey]['revenue'] = $appointmentStatsData['revenue'];
449 }
450 }
451
452 if (array_key_exists($dateKey, $packageDatesData)) {
453 foreach ($packageDatesData[$dateKey] as $packageKey => $packageStatsData) {
454 $stats[$dateKey]['packages'][$packageKey]['count'] = $packageStatsData['count'];
455
456 $stats[$dateKey]['packages'][$packageKey]['purchased'] = $packageStatsData['purchased'];
457
458 $stats[$dateKey]['packages'][$packageKey]['occupied'] = $packageStatsData['occupied'];
459
460 $stats[$dateKey]['packages'][$packageKey]['revenue'] = $packageStatsData['revenue'];
461
462 $stats[$dateKey]['packages'][$packageKey]['intervals'] = [];
463 }
464 }
465 }
466
467 return $stats;
468 }
469
470 /**
471 * @param $params
472 *
473 * @return array
474 * @throws ContainerValueNotFoundException
475 * @throws InvalidArgumentException
476 * @throws QueryExecutionException
477 */
478 public function getEmployeesStats($params)
479 {
480 /** @var ProviderRepository $providerRepository */
481 $providerRepository = $this->container->get('domain.users.providers.repository');
482
483 $appointments = $providerRepository->getAllNumberOfAppointments($params);
484
485 $views = $providerRepository->getAllNumberOfViews($params);
486
487 return array_values(array_replace_recursive($appointments, $views));
488 }
489
490 /**
491 * @param $providerId
492 *
493 * @return bool
494 * @throws ContainerValueNotFoundException
495 * @throws QueryExecutionException
496 */
497 public function addEmployeesViewsStats($providerId)
498 {
499 /** @var ProviderRepository $providerRepository */
500 $providerRepository = $this->container->get('domain.users.providers.repository');
501
502 $providerRepository->beginTransaction();
503
504 if (!$providerRepository->addViewStats($providerId)) {
505 $providerRepository->rollback();
506
507 return false;
508 }
509
510 return $providerRepository->commit();
511 }
512
513 /**
514 * @param $params
515 *
516 * @return array
517 * @throws ContainerValueNotFoundException
518 * @throws InvalidArgumentException
519 * @throws QueryExecutionException
520 */
521 public function getServicesStats($params)
522 {
523 /** @var ServiceRepository $serviceRepository */
524 $serviceRepository = $this->container->get('domain.bookable.service.repository');
525
526 $appointments = $serviceRepository->getAllNumberOfAppointments($params);
527
528 $views = $serviceRepository->getAllNumberOfViews($params);
529
530 return array_values(array_replace_recursive($appointments, $views));
531 }
532
533 /**
534 * @param $serviceId
535 *
536 * @return bool
537 * @throws ContainerValueNotFoundException
538 * @throws QueryExecutionException
539 */
540 public function addServicesViewsStats($serviceId)
541 {
542 /** @var ServiceRepository $serviceRepository */
543 $serviceRepository = $this->container->get('domain.bookable.service.repository');
544
545 $serviceRepository->beginTransaction();
546
547 if (!$serviceRepository->addViewStats($serviceId)) {
548 $serviceRepository->rollback();
549
550 return false;
551 }
552
553 return $serviceRepository->commit();
554 }
555
556 /**
557 * @param $params
558 *
559 * @return array
560 * @throws ContainerValueNotFoundException
561 * @throws InvalidArgumentException
562 * @throws QueryExecutionException
563 */
564 public function getLocationsStats($params)
565 {
566 /** @var LocationRepository $locationRepository */
567 $locationRepository = $this->container->get('domain.locations.repository');
568
569 $appointments = $locationRepository->getAllNumberOfAppointments($params);
570
571 $views = $locationRepository->getAllNumberOfViews($params);
572
573 return array_values(array_replace_recursive($appointments, $views));
574 }
575
576 /**
577 * @param $locationId
578 *
579 * @return bool
580 * @throws ContainerValueNotFoundException
581 * @throws QueryExecutionException
582 */
583 public function addLocationsViewsStats($locationId)
584 {
585 if ($locationId) {
586 /** @var LocationRepository $locationRepository */
587 $locationRepository = $this->container->get('domain.locations.repository');
588 $locationRepository->beginTransaction();
589 if (!$locationRepository->addViewStats($locationId)) {
590 $locationRepository->rollback();
591
592 return false;
593 }
594 return $locationRepository->commit();
595 }
596
597 return false;
598 }
599 }
600