Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
54 / 54
100.00% covered (success)
100.00%
1 / 1
CRAP
100.00% covered (success)
100.00%
1 / 1
GetOwnerDashboardQuery
100.00% covered (success)
100.00%
54 / 54
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
1 / 1
 execute
100.00% covered (success)
100.00%
54 / 54
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace ImovelZapAi\Owners\Application\Queries;
6
7use ImovelZapAi\Owners\Domain\Enums\ListingAgreementStatus;
8use ImovelZapAi\Owners\Domain\Enums\ListingAgreementType;
9use ImovelZapAi\Owners\Domain\Enums\ListingStageKey;
10use ImovelZapAi\Owners\Infrastructure\Persistence\Eloquent\ListingAgreementModel;
11use ImovelZapAi\Owners\Infrastructure\Persistence\Eloquent\ListingOpportunityModel;
12
13final class GetOwnerDashboardQuery
14{
15    /**
16     * @return array<string, mixed>
17     */
18    public function execute(string $tenantId): array
19    {
20        $openCapture = ListingOpportunityModel::query()
21            ->where('tenant_id', $tenantId)
22            ->whereNull('closed_at')
23            ->count();
24
25        $expiringDays = (int) config('owners.agreement_expiry_alert_days', 30);
26        $expiringAgreements = ListingAgreementModel::query()
27            ->where('tenant_id', $tenantId)
28            ->where('status', ListingAgreementStatus::Active)
29            ->whereDate('ends_on', '<=', now()->addDays($expiringDays)->toDateString())
30            ->whereDate('ends_on', '>=', now()->toDateString())
31            ->count();
32
33        $exclusives = ListingAgreementModel::query()
34            ->where('tenant_id', $tenantId)
35            ->where('status', ListingAgreementStatus::Active)
36            ->where('type', ListingAgreementType::Exclusive)
37            ->count();
38
39        $awaitingPublish = ListingOpportunityModel::query()
40            ->where('tenant_id', $tenantId)
41            ->whereNull('closed_at')
42            ->whereHas('stage', fn ($q) => $q->where('key', ListingStageKey::Captured->value))
43            ->count();
44
45        $published = ListingOpportunityModel::query()
46            ->where('tenant_id', $tenantId)
47            ->whereHas('stage', fn ($q) => $q->where('key', ListingStageKey::Published->value))
48            ->count();
49
50        $lost = ListingOpportunityModel::query()
51            ->where('tenant_id', $tenantId)
52            ->whereNotNull('lost_at')
53            ->count();
54
55        $closed = $published + $lost;
56        $conversion = $closed > 0 ? round(($published / $closed) * 100, 1) : null;
57
58        $byBroker = ListingOpportunityModel::query()
59            ->where('tenant_id', $tenantId)
60            ->whereNotNull('responsible_user_id')
61            ->selectRaw('responsible_user_id, count(*) as total')
62            ->groupBy('responsible_user_id')
63            ->orderByDesc('total')
64            ->limit(10)
65            ->get()
66            ->map(fn ($row) => [
67                'responsible_user_id' => $row->responsible_user_id,
68                'total' => (int) $row->total,
69            ])
70            ->all();
71
72        return [
73            'open_captures' => $openCapture,
74            'expiring_agreements' => $expiringAgreements,
75            'exclusive_agreements' => $exclusives,
76            'awaiting_publish' => $awaitingPublish,
77            'published' => $published,
78            'lost' => $lost,
79            'conversion_rate' => $conversion,
80            'by_broker' => $byBroker,
81        ];
82    }
83}