Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 47
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
GetExecutiveKpisQuery
0.00% covered (danger)
0.00%
0 / 47
0.00% covered (danger)
0.00%
0 / 3
42
0.00% covered (danger)
0.00%
0 / 1
 execute
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 1
2
 period
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 conversionRate
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3declare(strict_types=1);
4
5namespace ImovelZapAi\Analytics\Application\Queries;
6
7use Illuminate\Support\Carbon;
8use Illuminate\Support\Facades\DB;
9use ImovelZapAi\Analytics\Domain\Enums\MetricKey;
10use ImovelZapAi\Analytics\Infrastructure\Persistence\Eloquent\AnalyticsDailyMetricModel;
11
12final class GetExecutiveKpisQuery
13{
14    /**
15     * @param  array{from?: string, to?: string, days?: int}  $filters
16     * @return array<string, mixed>
17     */
18    public function execute(string $tenantId, array $filters = []): array
19    {
20        [$from, $to] = $this->period($filters);
21
22        $totals = AnalyticsDailyMetricModel::query()
23            ->select('metric_key', DB::raw('SUM(value) as total'))
24            ->where('tenant_id', $tenantId)
25            ->whereNull('dimension_key')
26            ->whereBetween('metric_date', [$from->toDateString(), $to->toDateString()])
27            ->groupBy('metric_key')
28            ->pluck('total', 'metric_key');
29
30        $get = static fn (MetricKey $key): float => (float) ($totals[$key->value] ?? 0);
31
32        return [
33            'period' => [
34                'from' => $from->toDateString(),
35                'to' => $to->toDateString(),
36            ],
37            'comercial' => [
38                'people_opportunities_created' => $get(MetricKey::PeopleOpportunitiesCreated),
39                'deals_won' => $get(MetricKey::DealsWon),
40                'volume_negotiated' => $get(MetricKey::VolumeNegotiated),
41                'conversion_rate' => $this->conversionRate(
42                    $get(MetricKey::PeopleOpportunitiesCreated),
43                    $get(MetricKey::DealsWon),
44                ),
45            ],
46            'imoveis' => [
47                'properties_listed_stub' => $get(MetricKey::PropertiesListedStub),
48            ],
49            'financeiro' => [
50                'payments_received_count' => $get(MetricKey::PaymentsReceivedCount),
51                'payments_received_amount' => $get(MetricKey::PaymentsReceivedAmount),
52                'commissions_calculated' => $get(MetricKey::CommissionsCalculated),
53            ],
54            'operacional' => [
55                'visits_created' => $get(MetricKey::VisitsCreated),
56                'visits_completed' => $get(MetricKey::VisitsCompleted),
57                'signatures_completed' => $get(MetricKey::SignaturesCompleted),
58                'documents_created' => $get(MetricKey::DocumentsCreated),
59                'workflows_executed' => $get(MetricKey::WorkflowsExecuted),
60            ],
61        ];
62    }
63
64    /** @param array{from?: string, to?: string, days?: int} $filters */
65    /** @return array{0: Carbon, 1: Carbon} */
66    private function period(array $filters): array
67    {
68        $to = isset($filters['to']) ? Carbon::parse($filters['to'])->endOfDay() : now();
69        $from = isset($filters['from'])
70            ? Carbon::parse($filters['from'])->startOfDay()
71            : $to->copy()->subDays((int) ($filters['days'] ?? 30))->startOfDay();
72
73        return [$from, $to];
74    }
75
76    private function conversionRate(float $created, float $won): float
77    {
78        if ($created <= 0) {
79            return 0.0;
80        }
81
82        return round(($won / $created) * 100, 1);
83    }
84}