PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 1.2.20
Booking for Appointments and Events Calendar – Amelia v1.2.20
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
599 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 ($bookingsPayments->keyExists($booking->getId()->getValue()) &&
174 $bookingsPayments->getItem($booking->getId()->getValue())
175 ) {
176 $booking->getPayments()->addItem($bookingsPayments->getItem($booking->getId()->getValue()));
177 }
178 }
179 }
180
181 /** @var ProviderRepository $providerRepository */
182 $providerRepository = $this->container->get('domain.users.providers.repository');
183 /** @var ServiceRepository $serviceRepository */
184 $serviceRepository = $this->container->get('domain.bookable.service.repository');
185 /** @var ProviderService $providerService */
186 $providerService = $this->container->get('domain.user.provider.service');
187
188 /** @var Collection $services */
189 $services = $serviceRepository->getAllArrayIndexedById();
190
191 /** @var Collection $providers */
192 $providers = $providerRepository->getWithSchedule(['dates' => $params['dates']]);
193
194 /** @var Provider $provider */
195 foreach ($providers->getItems() as $provider) {
196 $providerService->setProviderServices($provider, $services, true);
197 }
198
199 /** @var ProviderApplicationService $providerApplicationService */
200 $providerApplicationService = $this->container->get('application.user.provider.service');
201
202 /** @var AbstractPackageApplicationService $packageApplicationService */
203 $packageApplicationService = $this->container->get('application.bookable.package');
204
205 $packageDatesData = $packageApplicationService->getPackageStatsData($params);
206
207 /** @var Collection $appointmentsPackageCustomerServices */
208 $appointmentsPackageCustomerServices = $packageApplicationService->getPackageCustomerServicesForAppointments(
209 $appointments
210 );
211
212 $stats = [];
213
214 $statsPeriod = new \DatePeriod(
215 DateTimeService::getCustomDateTimeObject($params['dates'][0]),
216 new \DateInterval('P1D'),
217 DateTimeService::getCustomDateTimeObject($params['dates'][1])
218 );
219
220 /** @var DateTime $date */
221 foreach ($statsPeriod as $date) {
222 $stats[$date->format('Y-m-d')] = null;
223 }
224
225 $weekDaysData = [];
226
227 $specialDatesData = [];
228
229 $providersDaysOff = [];
230
231 /** @var Provider $provider */
232 foreach ($providers->getItems() as $provider) {
233 $providerId = $provider->getId()->getValue();
234
235 $providersDaysOff[$providerId] = [];
236
237 /** @var DayOff $daysOff */
238 foreach ($provider->getDayOffList()->getItems() as $daysOff) {
239 $daysOffPeriod = new \DatePeriod(
240 $daysOff->getStartDate()->getValue(),
241 new \DateInterval('P1D'),
242 DateTimeService::getCustomDateTimeObject(
243 $daysOff->getEndDate()->getValue()->format('Y-m-d H:i:s')
244 )->modify('+1 days')
245 );
246
247 /** @var DateTime $date */
248 foreach ($daysOffPeriod as $date) {
249 $providersDaysOff[$providerId][] = $date->format('Y-m-d');
250 }
251 }
252
253 // get provider week day available time
254 /** @var WeekDay $weekDay */
255 foreach ($provider->getWeekDayList()->getItems() as $weekDay) {
256 $dayIndex = $weekDay->getDayIndex()->getValue();
257
258 if (!array_key_exists($dayIndex, $weekDaysData)) {
259 $weekDaysData[$dayIndex] = [];
260 }
261
262 if ($weekDay->getPeriodList()->length() === 0) {
263 $weekDay->getPeriodList()->addItem(
264 PeriodFactory::create(
265 [
266 'startTime' => $weekDay->getStartTime()->getValue()->format('H:i:s'),
267 'endTime' => $weekDay->getEndTime()->getValue()->format('H:i:s'),
268 'periodServiceList' => [],
269 'periodLocationList' => [],
270 ]
271 )
272 );
273 }
274
275 $weekDaysData[$dayIndex][$providerId] = $providerApplicationService->getProviderScheduleIntervals(
276 $weekDay->getPeriodList(),
277 $weekDay->getTimeOutList()
278 );
279 }
280
281 // get provider special day available time
282 /** @var SpecialDay $specialDay */
283 foreach ($provider->getSpecialDayList()->getItems() as $specialDay) {
284 $specialDaysPeriod = new \DatePeriod(
285 $specialDay->getStartDate()->getValue(),
286 new \DateInterval('P1D'),
287 DateTimeService::getCustomDateTimeObject(
288 $specialDay->getEndDate()->getValue()->format('Y-m-d H:i:s')
289 )->modify('+1 days')
290 );
291
292 $specialDayExist = false;
293
294 foreach ($specialDaysPeriod as $date) {
295 if (array_key_exists($date->format('Y-m-d'), $stats)) {
296 $specialDayExist = true;
297 continue;
298 }
299 }
300
301 if ($specialDayExist) {
302 $providerSpecialDaysIntervals = $providerApplicationService->getProviderScheduleIntervals(
303 $specialDay->getPeriodList(),
304 new Collection()
305 );
306
307 /** @var DateTime $date */
308 foreach ($specialDaysPeriod as $date) {
309 $dateString = $date->format('Y-m-d');
310
311 if (array_key_exists($dateString, $stats)) {
312 if (!array_key_exists($dateString, $specialDatesData)) {
313 $specialDatesData[$dateString] = [];
314 }
315
316 $specialDatesData[$dateString][$providerId] = $providerSpecialDaysIntervals;
317 }
318 }
319 }
320 }
321 }
322
323 $appointmentDatesData = [];
324
325
326
327 /** @var Appointment $appointment */
328 foreach ($appointments->getItems() as $appointment) {
329 $date = $appointment->getBookingStart()->getValue()->format('Y-m-d');
330
331 $providerId = $appointment->getProviderId()->getValue();
332
333 $serviceId = $appointment->getServiceId()->getValue();
334
335 $appointmentDuration = $appointment->getBookingEnd()->getValue()->diff(
336 $appointment->getBookingStart()->getValue()
337 );
338
339 if (!array_key_exists($date, $appointmentDatesData)) {
340 $appointmentDatesData[$date] = [
341 'providers' => [],
342 'services' => []
343 ];
344 }
345
346 if (!array_key_exists($providerId, $appointmentDatesData[$date]['providers'])) {
347 $appointmentDatesData[$date]['providers'][$providerId] = [
348 'count' => 0,
349 'occupied' => 0,
350 'revenue' => 0
351 ];
352 }
353
354 if (!array_key_exists($serviceId, $appointmentDatesData[$date]['services'])) {
355 $appointmentDatesData[$date]['services'][$serviceId] = [
356 'count' => 0,
357 'occupied' => 0,
358 'revenue' => 0
359 ];
360 }
361
362 $occupiedDuration = $appointmentDuration->h * 60 + $appointmentDuration->i;
363
364 $appointmentDatesData[$date]['providers'][$providerId]['count']++;
365 $appointmentDatesData[$date]['providers'][$providerId]['occupied'] += $occupiedDuration;
366
367 $appointmentDatesData[$date]['services'][$serviceId]['count']++;
368 $appointmentDatesData[$date]['services'][$serviceId]['occupied'] += $occupiedDuration;
369
370 /** @var CustomerBooking $booking */
371 foreach ($appointment->getBookings()->getItems() as $booking) {
372 if ($booking->getPackageCustomerService()) {
373 $packageApplicationService->updatePackageStatsData(
374 $packageDatesData,
375 $appointmentsPackageCustomerServices,
376 $booking->getPackageCustomerService()->getId()->getValue(),
377 $date,
378 $occupiedDuration
379 );
380 } else {
381 /** @var Payment $payment */
382 foreach ($booking->getPayments()->getItems() as $payment) {
383 $appointmentDatesData[$date]['providers'][$providerId]['revenue'] +=
384 $payment->getAmount()->getValue();
385
386 $appointmentDatesData[$date]['services'][$serviceId]['revenue'] +=
387 $payment->getAmount()->getValue();
388 }
389 }
390 }
391 }
392
393 foreach ($stats as $dateKey => $dateStats) {
394 $dayIndex = DateTimeService::getCustomDateTimeObject($dateKey)->format('N');
395
396 // parse week day for provider
397 if (array_key_exists($dayIndex, $weekDaysData)) {
398 foreach ($weekDaysData[$dayIndex] as $providerKey => $weekDayData) {
399 if (!in_array($dateKey, $providersDaysOff[$providerKey], true)) {
400 $stats[$dateKey]['providers'][$providerKey] = [
401 'count' => 0,
402 'occupied' => 0,
403 'revenue' => 0,
404 'intervals' => $weekDayData
405 ];
406 }
407 }
408 }
409
410 // parse special day for provider
411 if (array_key_exists($dateKey, $specialDatesData)) {
412 foreach ($specialDatesData[$dateKey] as $providerKey => $specialDayData) {
413 if (!in_array($dateKey, $providersDaysOff[$providerKey], true)) {
414 $stats[$dateKey]['providers'][$providerKey] = [
415 'count' => 0,
416 'occupied' => 0,
417 'revenue' => 0,
418 'intervals' => $specialDayData
419 ];
420 }
421 }
422 }
423
424 if (array_key_exists($dateKey, $appointmentDatesData)) {
425 foreach ($appointmentDatesData[$dateKey]['providers'] as $providerKey => $appointmentStatsData) {
426 if (empty($stats[$dateKey]['providers']) ||
427 !array_key_exists($providerKey, $stats[$dateKey]['providers'])
428 ) {
429 $stats[$dateKey]['providers'][$providerKey] = [
430 'intervals' => []
431 ];
432 }
433
434 $stats[$dateKey]['providers'][$providerKey]['count'] = $appointmentStatsData['count'];
435
436 $stats[$dateKey]['providers'][$providerKey]['occupied'] = $appointmentStatsData['occupied'];
437
438 $stats[$dateKey]['providers'][$providerKey]['revenue'] = $appointmentStatsData['revenue'];
439 }
440
441 foreach ($appointmentDatesData[$dateKey]['services'] as $serviceKey => $appointmentStatsData) {
442 $stats[$dateKey]['services'][$serviceKey]['count'] = $appointmentStatsData['count'];
443
444 $stats[$dateKey]['services'][$serviceKey]['occupied'] = $appointmentStatsData['occupied'];
445
446 $stats[$dateKey]['services'][$serviceKey]['revenue'] = $appointmentStatsData['revenue'];
447 }
448 }
449
450 if (array_key_exists($dateKey, $packageDatesData)) {
451 foreach ($packageDatesData[$dateKey] as $packageKey => $packageStatsData) {
452 $stats[$dateKey]['packages'][$packageKey]['count'] = $packageStatsData['count'];
453
454 $stats[$dateKey]['packages'][$packageKey]['purchased'] = $packageStatsData['purchased'];
455
456 $stats[$dateKey]['packages'][$packageKey]['occupied'] = $packageStatsData['occupied'];
457
458 $stats[$dateKey]['packages'][$packageKey]['revenue'] = $packageStatsData['revenue'];
459
460 $stats[$dateKey]['packages'][$packageKey]['intervals'] = [];
461 }
462 }
463 }
464
465 return $stats;
466 }
467
468 /**
469 * @param $params
470 *
471 * @return array
472 * @throws ContainerValueNotFoundException
473 * @throws InvalidArgumentException
474 * @throws QueryExecutionException
475 */
476 public function getEmployeesStats($params)
477 {
478 /** @var ProviderRepository $providerRepository */
479 $providerRepository = $this->container->get('domain.users.providers.repository');
480
481 $appointments = $providerRepository->getAllNumberOfAppointments($params);
482
483 $views = $providerRepository->getAllNumberOfViews($params);
484
485 return array_values(array_replace_recursive($appointments, $views));
486 }
487
488 /**
489 * @param $providerId
490 *
491 * @return bool
492 * @throws ContainerValueNotFoundException
493 * @throws QueryExecutionException
494 */
495 public function addEmployeesViewsStats($providerId)
496 {
497 /** @var ProviderRepository $providerRepository */
498 $providerRepository = $this->container->get('domain.users.providers.repository');
499
500 $providerRepository->beginTransaction();
501
502 if (!$providerRepository->addViewStats($providerId)) {
503 $providerRepository->rollback();
504
505 return false;
506 }
507
508 return $providerRepository->commit();
509 }
510
511 /**
512 * @param $params
513 *
514 * @return array
515 * @throws ContainerValueNotFoundException
516 * @throws InvalidArgumentException
517 * @throws QueryExecutionException
518 */
519 public function getServicesStats($params)
520 {
521 /** @var ServiceRepository $serviceRepository */
522 $serviceRepository = $this->container->get('domain.bookable.service.repository');
523
524 $appointments = $serviceRepository->getAllNumberOfAppointments($params);
525
526 $views = $serviceRepository->getAllNumberOfViews($params);
527
528 return array_values(array_replace_recursive($appointments, $views));
529 }
530
531 /**
532 * @param $serviceId
533 *
534 * @return bool
535 * @throws ContainerValueNotFoundException
536 * @throws QueryExecutionException
537 */
538 public function addServicesViewsStats($serviceId)
539 {
540 /** @var ServiceRepository $serviceRepository */
541 $serviceRepository = $this->container->get('domain.bookable.service.repository');
542
543 $serviceRepository->beginTransaction();
544
545 if (!$serviceRepository->addViewStats($serviceId)) {
546 $serviceRepository->rollback();
547
548 return false;
549 }
550
551 return $serviceRepository->commit();
552 }
553
554 /**
555 * @param $params
556 *
557 * @return array
558 * @throws ContainerValueNotFoundException
559 * @throws InvalidArgumentException
560 * @throws QueryExecutionException
561 */
562 public function getLocationsStats($params)
563 {
564 /** @var LocationRepository $locationRepository */
565 $locationRepository = $this->container->get('domain.locations.repository');
566
567 $appointments = $locationRepository->getAllNumberOfAppointments($params);
568
569 $views = $locationRepository->getAllNumberOfViews($params);
570
571 return array_values(array_replace_recursive($appointments, $views));
572 }
573
574 /**
575 * @param $locationId
576 *
577 * @return bool
578 * @throws ContainerValueNotFoundException
579 * @throws QueryExecutionException
580 */
581 public function addLocationsViewsStats($locationId)
582 {
583 /** @var LocationRepository $locationRepository */
584
585 if ($locationId) {
586 $locationRepository = $this->container->get('domain.locations.repository');
587 $locationRepository->beginTransaction();
588 if (!$locationRepository->addViewStats($locationId)) {
589 $locationRepository->rollback();
590
591 return false;
592 }
593 return $locationRepository->commit();
594 }
595
596 return false;
597 }
598 }
599