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