Passed
Pull Request — main (#57)
by Thierry
15:28 queued 13s
created

SavingService::getSavingSum()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
rs 10
c 1
b 0
f 0
1
<?php
2
3
namespace Siak\Tontine\Service\Meeting\Saving;
4
5
use Closure;
6
use Illuminate\Database\Eloquent\Builder;
7
use Illuminate\Database\Eloquent\Relations\Relation;
8
use Illuminate\Support\Arr;
9
use Illuminate\Support\Collection;
10
use Illuminate\Support\Facades\DB;
11
use Siak\Tontine\Exception\MessageException;
12
use Siak\Tontine\Model\Fund;
13
use Siak\Tontine\Model\Member;
14
use Siak\Tontine\Model\Saving;
15
use Siak\Tontine\Model\Session;
16
use Siak\Tontine\Service\LocaleService;
17
use Siak\Tontine\Service\TenantService;
18
use Siak\Tontine\Service\Tontine\FundService;
19
20
use function count;
21
use function trans;
22
23
class SavingService
24
{
25
    /**
26
     * @param LocaleService $localeService
27
     * @param TenantService $tenantService
28
     * @param FundService $fundService
29
     */
30
    public function __construct(private LocaleService $localeService,
31
        private TenantService $tenantService, private FundService $fundService)
32
    {}
33
34
    /**
35
     * @param Session $session
36
     * @param int $fundId
37
     *
38
     * @return Builder|Relation
39
     */
40
    private function getSavingQuery(Session $session, int $fundId): Builder|Relation
41
    {
42
        // $fundId < 0 => all the savings
43
        // $fundId === 0 => savings of the default fund
44
        // $fundId > 0 => savings of the corresponding fund
45
        return $session->savings()
46
            ->when($fundId === 0, function(Builder $query) {
47
                $query->whereNull('fund_id');
48
            })
49
            ->when($fundId > 0, function(Builder $query) use($fundId) {
50
                $query->where('fund_id', $fundId);
51
            });
52
    }
53
54
    /**
55
     * Count the savings for a given session.
56
     *
57
     * @param Session $session
58
     * @param int $fundId
59
     *
60
     * @return int
61
     */
62
    public function getSavingCount(Session $session, int $fundId): int
63
    {
64
        return $this->getSavingQuery($session, $fundId)->count();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getSavingQ...sion, $fundId)->count() could return the type Illuminate\Database\Eloq...uent\Relations\Relation which is incompatible with the type-hinted return integer. Consider adding an additional type-check to rule them out.
Loading history...
65
    }
66
67
    /**
68
     * Get the savings sum for a given session.
69
     *
70
     * @param Session $session
71
     * @param int $fundId
72
     *
73
     * @return int
74
     */
75
    public function getSavingTotal(Session $session, int $fundId): int
76
    {
77
        return $this->getSavingQuery($session, $fundId)->sum('amount');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getSavingQ...$fundId)->sum('amount') could return the type Illuminate\Database\Eloq...uent\Relations\Relation which is incompatible with the type-hinted return integer. Consider adding an additional type-check to rule them out.
Loading history...
78
    }
79
80
    /**
81
     * Get the savings for a given session.
82
     *
83
     * @param Session $session
84
     * @param int $fundId
85
     * @param int $page
86
     *
87
     * @return Collection
88
     */
89
    public function getSavings(Session $session, int $fundId, int $page = 0): Collection
90
    {
91
        return $this->getSavingQuery($session, $fundId)
92
            ->select(DB::raw('savings.*, members.name as member'))
93
            ->join('members', 'members.id', '=', 'savings.member_id')
94
            ->with(['fund'])
95
            ->page($page, $this->tenantService->getLimit())
96
            ->orderBy('members.name')
97
            ->get();
98
    }
99
100
    /**
101
     * Get a saving for a given session.
102
     *
103
     * @param Session $session
104
     * @param int $savingId
105
     *
106
     * @return Saving|null
107
     */
108
    public function getSaving(Session $session, int $savingId): ?Saving
109
    {
110
        return $session->savings()->with(['fund'])->find($savingId);
111
    }
112
113
    /**
114
     * Find a member saving for a given session.
115
     *
116
     * @param Session $session
117
     * @param int $fundId
118
     * @param int $memberId
119
     *
120
     * @return Saving|null
121
     */
122
    public function findSaving(Session $session, int $fundId, int $memberId): ?Saving
123
    {
124
        return $this->getSavingQuery($session, $fundId)
125
            ->where('member_id', $memberId)
126
            ->first();
127
    }
128
129
    /**
130
     * @param Session $session
131
     * @param Member $member
132
     * @param Fund|null $fund
133
     * @param Saving $saving
134
     * @param int $amount
135
     *
136
     * @return void
137
     */
138
    private function persistSaving(Session $session, Member $member,
139
        ?Fund $fund, Saving $saving, int $amount)
140
    {
141
        $saving->amount = $amount;
142
        $saving->member()->associate($member);
143
        $saving->session()->associate($session);
144
        if($fund !== null)
145
        {
146
            $saving->fund()->associate($fund);
147
        }
148
        else
149
        {
150
            $saving->fund()->dissociate();
151
        }
152
        $saving->save();
153
    }
154
155
    /**
156
     * Create a saving.
157
     *
158
     * @param Session $session The session
159
     * @param array $values
160
     *
161
     * @return void
162
     */
163
    public function createSaving(Session $session, array $values): void
164
    {
165
        if(!($member = $this->getMember($values['member'])))
166
        {
167
            throw new MessageException(trans('tontine.member.errors.not_found'));
168
        }
169
        $fund = !$values['fund'] ? null : $this->fundService->getFund($values['fund']);
170
171
        $saving = new Saving();
172
        $this->persistSaving($session, $member, $fund, $saving, $values['amount']);
173
    }
174
175
    /**
176
     * Update a saving.
177
     *
178
     * @param Session $session The session
179
     * @param int $savingId
180
     * @param array $values
181
     *
182
     * @return void
183
     */
184
    public function updateSaving(Session $session, int $savingId, array $values): void
185
    {
186
        if(!($member = $this->getMember($values['member'])))
187
        {
188
            throw new MessageException(trans('tontine.member.errors.not_found'));
189
        }
190
        $saving = $session->savings()->find($savingId);
191
        if(!$saving)
192
        {
193
            throw new MessageException(trans('meeting.saving.errors.not_found'));
194
        }
195
        $fund = !$values['fund'] ? null : $this->fundService->getFund($values['fund']);
196
197
        $this->persistSaving($session, $member, $fund, $saving, $values['amount']);
198
    }
199
200
    /**
201
     * Create or update a saving.
202
     *
203
     * @param Session $session The session
204
     * @param int $fundId
205
     * @param int $memberId
206
     * @param int $amount
207
     *
208
     * @return void
209
     */
210
    public function saveSaving(Session $session, int $fundId, int $memberId, int $amount): void
211
    {
212
        if(!($member = $this->getMember($memberId)))
213
        {
214
            throw new MessageException(trans('tontine.member.errors.not_found'));
215
        }
216
        $fund = !$fundId ? null : $this->fundService->getFund($fundId);
217
        $saving = $this->findSaving($session, !$fund ? 0 : $fund->id, $member->id);
218
        if(!$saving)
219
        {
220
            $saving = new Saving();
221
        }
222
223
        $this->persistSaving($session, $member, $fund, $saving, $amount);
224
    }
225
226
    /**
227
     * Delete a saving.
228
     *
229
     * @param Session $session The session
230
     * @param int $savingId
231
     * @param int $memberId
232
     *
233
     * @return void
234
     */
235
    public function deleteSaving(Session $session, int $savingId, int $memberId = 0): void
236
    {
237
        $savingId > 0 ?
238
            $session->savings()->where('id', $savingId)->delete() :
239
            $session->savings()->where('member_id', $memberId)->delete();
240
    }
241
242
    /**
243
     * @param Session $session
244
     * @param int $fundId
245
     * @param string $search
246
     * @param bool|null $filter
247
     *
248
     * @return Closure
249
     */
250
    private function getMemberSavingsFilter(Session $session, int $fundId): Closure
251
    {
252
        return function(/*Builder|Relation*/ $query) use($session, $fundId) {
253
            $query->where('session_id', $session->id)
254
                ->where(function(Builder $query) use($fundId) {
255
                    $fundId > 0 ?
256
                        $query->where('fund_id', $fundId) :
257
                        $query->whereNull('fund_id');
258
                });
259
        };
260
    }
261
262
    /**
263
     * @param Session $session
264
     * @param int $fundId
265
     * @param string $search
266
     * @param bool|null $filter
267
     *
268
     * @return Builder|Relation
269
     */
270
    private function getMembersQuery(Session $session, int $fundId,
271
        string $search, ?bool $filter): Builder|Relation
272
    {
273
        $savingsFilter = $this->getMemberSavingsFilter($session, $fundId);
274
        return $this->tenantService->tontine()->members()->active()
275
            ->when($search !== '', function(Builder $query) use($search) {
276
                $search = '%' . strtolower($search) . '%';
277
                return $query->where(DB::raw('lower(name)'), 'like', $search);
278
            })
279
            ->when($filter === true, function(Builder $query) use($savingsFilter) {
280
                $query->whereHas('savings', $savingsFilter);
281
            })
282
            ->when($filter === false, function(Builder $query) use($savingsFilter) {
283
                $query->whereDoesntHave('savings', $savingsFilter);
284
            });
285
    }
286
287
    /**
288
     * Get a paginated list of members.
289
     *
290
     * @param Session $session
291
     * @param int $fundId
292
     * @param string $search
293
     * @param bool|null $filter
294
     * @param int $page
295
     *
296
     * @return Collection
297
     */
298
    public function getMembers(Session $session, int $fundId, string $search,
299
        ?bool $filter, int $page = 0): Collection
300
    {
301
        return $this->getMembersQuery($session, $fundId, $search, $filter)
302
            ->page($page, $this->tenantService->getLimit())
303
            ->with('savings', $this->getMemberSavingsFilter($session, $fundId))
304
            ->orderBy('name', 'asc')
305
            ->get()
306
            ->each(function($member) {
307
                $member->saving = $member->savings->first();
308
            });
309
    }
310
311
    /**
312
     * Get the number of members.
313
     *
314
     * @param Session $session
315
     * @param int $fundId
316
     * @param string $search
317
     * @param bool|null $filter
318
     *
319
     * @return int
320
     */
321
    public function getMemberCount(Session $session, int $fundId, string $search, ?bool $filter): int
322
    {
323
        return $this->getMembersQuery($session, $fundId, $search, $filter)->count();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getMembers...arch, $filter)->count() could return the type Illuminate\Database\Eloq...uent\Relations\Relation which is incompatible with the type-hinted return integer. Consider adding an additional type-check to rule them out.
Loading history...
324
    }
325
326
    /**
327
     * Get a single member.
328
     *
329
     * @param int $id       The member id
330
     *
331
     * @return Member|null
332
     */
333
    public function getMember(int $id): ?Member
334
    {
335
        return $this->tenantService->tontine()->members()->active()->find($id);
336
    }
337
338
    /**
339
     * Get all closings for a given fund.
340
     *
341
     * @param int $fundId
342
     *
343
     * @return array
344
     */
345
    public function getFundClosings(int $fundId): array
346
    {
347
        $closings = $this->tenantService->tontine()->properties['closings'] ?? [];
0 ignored issues
show
Bug introduced by
The property properties does not seem to exist on Siak\Tontine\Model\Tontine. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
348
        $closings = Arr::where($closings, fn($closing) => isset($closing[$fundId]));
349
        return Arr::map($closings, fn($closing) => $closing[$fundId]);
350
    }
351
352
    /**
353
     * Save the given session as closing for the fund.
354
     *
355
     * @param Session $session
356
     * @param int $fundId
357
     * @param int $profitAmount
358
     *
359
     * @return void
360
     */
361
    public function saveFundClosing(Session $session, int $fundId, int $profitAmount)
362
    {
363
        $tontine = $this->tenantService->tontine();
364
        $properties = $tontine->properties;
0 ignored issues
show
Bug introduced by
The property properties does not seem to exist on Siak\Tontine\Model\Tontine. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
365
        $properties['closings'][$session->id][$fundId] = $profitAmount;
366
        $tontine->saveProperties($properties);
367
    }
368
369
    /**
370
     * Check if the given session is closing the fund.
371
     *
372
     * @param Session $session
373
     * @param int $fundId
374
     *
375
     * @return bool
376
     */
377
    public function hasFundClosing(Session $session, int $fundId): bool
378
    {
379
        $properties = $this->tenantService->tontine()->properties;
0 ignored issues
show
Bug introduced by
The property properties does not seem to exist on Siak\Tontine\Model\Tontine. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
380
        return isset($properties['closings'][$session->id][$fundId]);
381
    }
382
383
    /**
384
     * Set the given session as closing the fund.
385
     *
386
     * @param Session $session
387
     * @param int $fundId
388
     *
389
     * @return void
390
     */
391
    public function deleteFundClosing(Session $session, int $fundId)
392
    {
393
        $tontine = $this->tenantService->tontine();
394
        $properties = $tontine->properties;
0 ignored issues
show
Bug introduced by
The property properties does not seem to exist on Siak\Tontine\Model\Tontine. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
395
        if(isset($properties['closings'][$session->id][$fundId]))
396
        {
397
            unset($properties['closings'][$session->id][$fundId]);
398
            if(count($properties['closings'][$session->id]) == 0)
399
            {
400
                unset($properties['closings'][$session->id]);
401
            }
402
        }
403
        $tontine->saveProperties($properties);
404
    }
405
406
    /**
407
     * Get the profit amount saved on a given session.
408
     *
409
     * @param Session $session
410
     * @param int $fundId
411
     *
412
     * @return int
413
     */
414
    public function getProfitAmount(Session $session, int $fundId): int
415
    {
416
        $tontine = $this->tenantService->tontine();
417
        return $tontine->properties['closings'][$session->id][$fundId] ?? 0;
0 ignored issues
show
Bug introduced by
The property properties does not seem to exist on Siak\Tontine\Model\Tontine. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
418
    }
419
420
    /**
421
     * Get all the fund closings on a given session.
422
     *
423
     * @param Session $session
424
     *
425
     * @return array
426
     */
427
    public function getSessionClosings(Session $session): array
428
    {
429
        $tontine = $this->tenantService->tontine();
430
        return $tontine->properties['closings'][$session->id] ?? [];
0 ignored issues
show
Bug introduced by
The property properties does not seem to exist on Siak\Tontine\Model\Tontine. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
431
    }
432
}
433