SummaryService::getPoolsBalance()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 11
rs 10
1
<?php
2
3
namespace Siak\Tontine\Service\Meeting\Session;
4
5
use Illuminate\Support\Collection;
6
use Illuminate\Support\Facades\DB;
7
use Siak\Tontine\Model\Fund;
8
use Siak\Tontine\Model\Pool;
9
use Siak\Tontine\Model\Round;
10
use Siak\Tontine\Model\Session;
11
use Siak\Tontine\Service\Payment\BalanceCalculator;
12
use Siak\Tontine\Service\Planning\PoolService;
13
use Siak\Tontine\Service\TenantService;
14
use Siak\Tontine\Service\Traits\ReportTrait;
15
use stdClass;
16
17
use function collect;
18
use function compact;
19
20
class SummaryService
21
{
22
    use ReportTrait;
0 ignored issues
show
introduced by
The trait Siak\Tontine\Service\Traits\ReportTrait requires some properties which are not provided by Siak\Tontine\Service\Mee...\Session\SummaryService: $deposit_fixed, $id
Loading history...
23
24
    /**
25
     * @var Collection
26
     */
27
    private $deposits;
28
29
    /**
30
     * @var Collection
31
     */
32
    private $remitments;
33
34
    /**
35
     * @var Collection
36
     */
37
    private $auctions;
38
39
    /**
40
     * @param BalanceCalculator $balanceCalculator
41
     * @param TenantService $tenantService
42
     * @param PoolService $poolService
43
     */
44
    public function __construct(protected BalanceCalculator $balanceCalculator,
45
        protected TenantService $tenantService, PoolService $poolService)
46
    {
47
        $this->poolService = $poolService;
48
    }
49
50
    /**
51
     * @param Pool $pool
52
     * @param int $cashier
53
     * @param stdClass|null $deposit
54
     * @param stdClass|null $remitment
55
     *
56
     * @return stdClass
57
     */
58
    private function getSessionFigures(Pool $pool, int $cashier,
59
        ?stdClass $deposit, ?stdClass $remitment): stdClass
60
    {
61
        $figures = $this->makeFigures(0);
62
        $figures->cashier->start = $cashier;
63
        $figures->cashier->recv = $cashier;
64
65
        $depositCount = $deposit?->count ?? 0;
66
        $depositAmount = $deposit?->amount ?? 0;
67
68
        $figures->deposit->amount += $depositAmount;
69
        $figures->cashier->recv += $depositAmount;
70
        $figures->deposit->count = $depositCount;
71
72
        $sessionCount = $pool->sessions->count();
73
        $remitmentCount = $remitment?->count ?? 0;
74
        $remitmentAmount = $pool->deposit_fixed ?
75
            $pool->def->amount * $sessionCount * $remitmentCount :
76
            ($remitmentCount > 0 ? $depositAmount : 0);
77
78
        $figures->cashier->end = $figures->cashier->recv;
79
        $figures->remitment->amount += $remitmentAmount;
80
        $figures->cashier->end -= $remitmentAmount;
81
        $figures->remitment->count += $remitmentCount;
82
83
        return $figures;
84
    }
85
86
    /**
87
     * @param Pool $pool
88
     * @param Session|null $session
89
     * @param Collection $sessions
90
     *
91
     * @return array
92
     */
93
    private function getCollectedFigures(Pool $pool, Collection $sessions): array
94
    {
95
        $cashier = 0;
96
        $collectedFigures = [];
97
        foreach($sessions as $session)
98
        {
99
            $deposit = $this->deposits[$pool->id][$session->id] ?? null;
100
            $remitment = $this->remitments[$pool->id][$session->id] ?? null;
101
            $figures = $this->getSessionFigures($pool, $cashier, $deposit, $remitment);
102
            if($pool->remit_auction)
103
            {
104
                // Add the auctions amount to the cash amount.
105
                $figures->cashier->end += $this->auctions[$pool->id][$session->id]?->amount ?? 0;
106
            }
107
            $cashier = $figures->cashier->end;
108
            $collectedFigures[$session->id] = $figures;
109
        }
110
111
        return $collectedFigures;
112
    }
113
114
    /**
115
     * @param Round $round
116
     * @param Collection $poolIds
117
     *
118
     * @return void
119
     */
120
    private function getDeposits(Round $round, Session|null $session, Collection $poolIds): void
121
    {
122
        $this->deposits = DB::table('v_deposits')
123
            ->select('subscriptions.pool_id', 'v_deposits.session_id',
124
                DB::raw('count(*) as count'), DB::raw('sum(v_deposits.amount) as amount'))
125
            ->join('sessions', 'v_deposits.session_id', '=', 'sessions.id')
126
            ->join('receivables', 'receivables.id', '=', 'v_deposits.receivable_id')
127
            ->join('subscriptions', 'receivables.subscription_id', '=', 'subscriptions.id')
128
            ->whereIn('subscriptions.pool_id', $poolIds)
129
            ->where('sessions.round_id', $round->id)
130
            ->when($session !== null, fn($qs) =>
131
                $qs->where('sessions.day_date', '<=', $session->day_date))
132
            ->groupBy(['subscriptions.pool_id', 'v_deposits.session_id'])
133
            ->get()
134
            // Group the data by pool id and session id.
135
            ->groupBy('pool_id')
136
            ->map(fn($poolDeposits) => $poolDeposits->groupBy('session_id')
137
                ->map(fn($array) => $array->first()));
138
    }
139
140
    /**
141
     * @param Round $round
142
     * @param Session|null $session
143
     * @param Collection $poolIds
144
     *
145
     * @return void
146
     */
147
    private function getRemitments(Round $round, Session|null $session, Collection $poolIds): void
148
    {
149
        $this->remitments = DB::table('remitments')
150
            ->select('subscriptions.pool_id', 'payables.session_id', DB::raw('count(*) as count'))
151
            ->join('payables', 'payables.id', '=', 'remitments.payable_id')
152
            ->join('subscriptions', 'payables.subscription_id', '=', 'subscriptions.id')
153
            ->join('sessions', 'payables.session_id', '=', 'sessions.id')
154
            ->whereIn('subscriptions.pool_id', $poolIds)
155
            ->where('sessions.round_id', $round->id)
156
            ->when($session !== null, fn($qs) =>
157
                $qs->where('sessions.day_date', '<=', $session->day_date))
158
            ->groupBy(['subscriptions.pool_id', 'payables.session_id'])
159
            ->get()
160
            // Group the data by pool id and session id.
161
            ->groupBy('pool_id')
162
            ->map(fn($poolRemitments) => $poolRemitments->groupBy('session_id')
163
                ->map(fn($array) => $array->first()));
164
    }
165
166
    /**
167
     * @param Round $round
168
     * @param Session|null $session
169
     * @param Collection $poolIds
170
     *
171
     * @return void
172
     */
173
    private function getAuctions(Round $round, Session|null $session, Collection $poolIds): void
174
    {
175
        $this->auctions = DB::table('auctions')
176
            ->select('subscriptions.pool_id', 'auctions.session_id',
177
                DB::raw('count(*) as count'), DB::raw('sum(amount) as amount'))
178
            ->join('remitments', 'auctions.remitment_id', '=', 'remitments.id')
179
            ->join('payables', 'remitments.payable_id', '=', 'payables.id')
180
            ->join('subscriptions', 'payables.subscription_id', '=', 'subscriptions.id')
181
            ->join('sessions', 'payables.session_id', '=', 'sessions.id')
182
            ->where('paid', true)
183
            ->whereIn('subscriptions.pool_id', $poolIds)
184
            ->where('sessions.round_id', $round->id)
185
            ->when($session !== null, fn($qs) =>
186
                $qs->where('sessions.day_date', '<=', $session->day_date))
187
            ->groupBy(['subscriptions.pool_id', 'auctions.session_id'])
188
            ->get()
189
            // Group the data by pool id and session id.
190
            ->groupBy('pool_id')
191
            ->map(fn($poolAuctions) => $poolAuctions->groupBy('session_id')
192
                ->map(fn($array) => $array->first()));
193
    }
194
195
    /**
196
     * @param Pool $pool
197
     *
198
     * @return array
199
     */
200
    private function getPoolFigures(Pool $pool): array
201
    {
202
        $sessions = $this->poolService->getActiveSessions($pool);
203
        $figures = new stdClass();
204
        if($pool->remit_planned)
205
        {
206
            $figures->expected = $this->getExpectedFigures($pool, $sessions);
207
        }
208
        $figures->collected = $this->getCollectedFigures($pool, $sessions);
209
        if($pool->remit_auction)
210
        {
211
            $figures->auctions = $this->auctions[$pool->id] ?? collect();
212
        }
213
214
        return compact('pool', 'figures', 'sessions');
215
    }
216
217
    /**
218
     * Get the receivables of a given pool.
219
     *
220
     * @param Round $round
221
     * @param Session|null $session
222
     * @param int $poolId
223
     *
224
     * @return Collection
225
     */
226
    public function getFigures(Round $round, Session|null $session = null,
227
        int $poolId = 0): Collection
228
    {
229
        $pools = $round->pools()
230
            ->with(['round.guild', 'sessions'])
231
            ->whereHas('subscriptions')
232
            ->when($poolId > 0, fn($query) => $query->where('pools.id', $poolId))
233
            ->get();
234
        if($pools->count() === 0)
235
        {
236
            return collect();
237
        }
238
239
        $poolIds = $pools->pluck('id');
240
        $this->getDeposits($round, $session, $poolIds);
241
        $this->getRemitments($round, $session, $poolIds);
242
        $this->getAuctions($round, $session, $poolIds);
243
244
        return $pools->map(fn(Pool $pool) => $this->getPoolFigures($pool));
245
    }
246
247
    /**
248
     * @param Collection $figures
249
     *
250
     * @return array
251
     */
252
    public function getPoolsBalance(Collection $figures): array
253
    {
254
        $pools = [];
255
        foreach($figures as $poolData)
256
        {
257
            foreach($poolData['figures']->collected as $sessionId => $collected)
258
            {
259
                $pools[$sessionId] = ($pools[$sessionId] ?? 0) + $collected->cashier->end;
260
            }
261
        }
262
        return $pools;
263
    }
264
265
    /**
266
     * Get the funds.
267
     *
268
     * @param Round $round
269
     *
270
     * @return Collection
271
     */
272
    public function getFunds(Round $round): Collection
273
    {
274
        return Fund::ofRound($round)->get()
275
            ->filter(fn($fund) => $fund->start_amount > 0 ||
276
                $fund->end_amount > 0 || $fund->profit_amount > 0);
277
    }
278
279
    /**
280
     * @param Pool $pool
281
     * @param Session $session
282
     *
283
     * @return int
284
     */
285
    public function getSessionRemitmentCount(Pool $pool, Session $session): int
286
    {
287
        if(!$pool->remit_planned)
288
        {
289
            return -1;
290
        }
291
        if(!$pool->deposit_fixed)
292
        {
293
            return 1;
294
        }
295
296
        $sessions = $this->poolService->getActiveSessions($pool);
297
        $position = $sessions->filter(
298
            fn($_session) => $_session->day_date->lt($session->day_date)
299
        )->count();
300
301
        return $this->getRemitmentCount($sessions->count(),
302
            $pool->subscriptions()->count(), $position);
303
    }
304
}
305