Passed
Push — main ( 2e4b10...1f2a96 )
by Thierry
05:42
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 trans;
21
22
class SavingService
23
{
24
    /**
25
     * @param LocaleService $localeService
26
     * @param TenantService $tenantService
27
     * @param FundService $fundService
28
     * @param MemberService $memberService
29
     */
30
    public function __construct(private LocaleService $localeService,
31
        private TenantService $tenantService, private FundService $fundService,
32
        private MemberService $memberService)
0 ignored issues
show
Bug introduced by
The type Siak\Tontine\Service\Meeting\Saving\MemberService was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
33
    {}
34
35
    /**
36
     * @param Session $session
37
     * @param int $fundId
38
     *
39
     * @return Builder|Relation
40
     */
41
    private function getSavingQuery(Session $session, int $fundId): Builder|Relation
42
    {
43
        // $fundId < 0 => all the savings
44
        // $fundId === 0 => savings of the default fund
45
        // $fundId > 0 => savings of the corresponding fund
46
        return $session->savings()
47
            ->when($fundId === 0, function(Builder $query) {
48
                $query->whereNull('fund_id');
49
            })
50
            ->when($fundId > 0, function(Builder $query) use($fundId) {
51
                $query->where('fund_id', $fundId);
52
            });
53
    }
54
55
    /**
56
     * Count the savings for a given session.
57
     *
58
     * @param Session $session
59
     * @param int $fundId
60
     *
61
     * @return int
62
     */
63
    public function getSavingCount(Session $session, int $fundId): int
64
    {
65
        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...
66
    }
67
68
    /**
69
     * Get the savings sum for a given session.
70
     *
71
     * @param Session $session
72
     * @param int $fundId
73
     *
74
     * @return int
75
     */
76
    public function getSavingSum(Session $session, int $fundId): int
77
    {
78
        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...
79
    }
80
81
    /**
82
     * Get the savings for a given session.
83
     *
84
     * @param Session $session
85
     * @param int $fundId
86
     * @param int $page
87
     *
88
     * @return Collection
89
     */
90
    public function getSavings(Session $session, int $fundId, int $page = 0): Collection
91
    {
92
        return $this->getSavingQuery($session, $fundId)
93
            ->select(DB::raw('savings.*, members.name as member'))
94
            ->join('members', 'members.id', '=', 'savings.member_id')
95
            ->with(['fund'])
96
            ->page($page, $this->tenantService->getLimit())
97
            ->orderBy('members.name')
98
            ->get();
99
    }
100
101
    /**
102
     * Get a saving for a given session.
103
     *
104
     * @param Session $session
105
     * @param int $savingId
106
     *
107
     * @return Saving|null
108
     */
109
    public function getSaving(Session $session, int $savingId): ?Saving
110
    {
111
        return $session->savings()->with(['fund'])->find($savingId);
112
    }
113
114
    /**
115
     * Find a member saving for a given session.
116
     *
117
     * @param Session $session
118
     * @param int $fundId
119
     * @param int $memberId
120
     *
121
     * @return Saving|null
122
     */
123
    public function findSaving(Session $session, int $fundId, int $memberId): ?Saving
124
    {
125
        return $this->getSavingQuery($session, $fundId)
126
            ->where('member_id', $memberId)
127
            ->first();
128
    }
129
130
    /**
131
     * @param Session $session
132
     * @param Member $member
133
     * @param Fund|null $fund
134
     * @param Saving $saving
135
     * @param int $amount
136
     *
137
     * @return void
138
     */
139
    private function persistSaving(Session $session, Member $member,
140
        ?Fund $fund, Saving $saving, int $amount)
141
    {
142
        $saving->amount = $amount;
143
        $saving->member()->associate($member);
144
        $saving->session()->associate($session);
145
        if($fund !== null)
146
        {
147
            $saving->fund()->associate($fund);
148
        }
149
        else
150
        {
151
            $saving->fund()->dissociate();
152
        }
153
        $saving->save();
154
    }
155
156
    /**
157
     * Create a saving.
158
     *
159
     * @param Session $session The session
160
     * @param array $values
161
     *
162
     * @return void
163
     */
164
    public function createSaving(Session $session, array $values): void
165
    {
166
        if(!($member = $this->memberService->getMember($values['member'])))
167
        {
168
            throw new MessageException(trans('tontine.member.errors.not_found'));
169
        }
170
        $fund = !$values['fund'] ? null : $this->fundService->getFund($values['fund']);
171
172
        $saving = new Saving();
173
        $this->persistSaving($session, $member, $fund, $saving, $values['amount']);
174
    }
175
176
    /**
177
     * Update a saving.
178
     *
179
     * @param Session $session The session
180
     * @param int $savingId
181
     * @param array $values
182
     *
183
     * @return void
184
     */
185
    public function updateSaving(Session $session, int $savingId, array $values): void
186
    {
187
        if(!($member = $this->memberService->getMember($values['member'])))
188
        {
189
            throw new MessageException(trans('tontine.member.errors.not_found'));
190
        }
191
        $saving = $session->savings()->find($savingId);
192
        if(!$saving)
193
        {
194
            throw new MessageException(trans('meeting.saving.errors.not_found'));
195
        }
196
        $fund = !$values['fund'] ? null : $this->fundService->getFund($values['fund']);
197
198
        $this->persistSaving($session, $member, $fund, $saving, $values['amount']);
199
    }
200
201
    /**
202
     * Create or update a saving.
203
     *
204
     * @param Session $session The session
205
     * @param int $fundId
206
     * @param int $memberId
207
     * @param int $amount
208
     *
209
     * @return void
210
     */
211
    public function saveSaving(Session $session, int $fundId, int $memberId, int $amount): void
212
    {
213
        if(!($member = $this->memberService->getMember($memberId)))
214
        {
215
            throw new MessageException(trans('tontine.member.errors.not_found'));
216
        }
217
        $fund = !$fundId ? null : $this->fundService->getFund($fundId);
218
        $saving = $this->findSaving($session, !$fund ? 0 : $fund->id, $member->id);
219
        if(!$saving)
220
        {
221
            $saving = new Saving();
222
        }
223
224
        $this->persistSaving($session, $member, $fund, $saving, $amount);
225
    }
226
227
    /**
228
     * Delete a saving.
229
     *
230
     * @param Session $session The session
231
     * @param int $savingId
232
     * @param int $memberId
233
     *
234
     * @return void
235
     */
236
    public function deleteSaving(Session $session, int $savingId, int $memberId = 0): void
237
    {
238
        $savingId > 0 ?
239
            $session->savings()->where('id', $savingId)->delete() :
240
            $session->savings()->where('member_id', $memberId)->delete();
241
    }
242
243
    /**
244
     * @param Session $session
245
     * @param int $fundId
246
     * @param string $search
247
     * @param bool|null $filter
248
     *
249
     * @return Closure
250
     */
251
    private function getMemberSavingsFilter(Session $session, int $fundId): Closure
252
    {
253
        return function(/*Builder|Relation*/ $query) use($session, $fundId) {
254
            $query->where('session_id', $session->id)
255
                ->where(function(Builder $query) use($fundId) {
256
                    $fundId > 0 ?
257
                        $query->where('fund_id', $fundId) :
258
                        $query->whereNull('fund_id');
259
                });
260
        };
261
    }
262
263
    /**
264
     * @param Session $session
265
     * @param int $fundId
266
     * @param string $search
267
     * @param bool|null $filter
268
     *
269
     * @return Builder|Relation
270
     */
271
    private function getMembersQuery(Session $session, int $fundId,
272
        string $search, ?bool $filter): Builder|Relation
273
    {
274
        $savingsFilter = $this->getMemberSavingsFilter($session, $fundId);
275
        return $this->tenantService->tontine()->members()->active()
276
            ->when($search !== '', function(Builder $query) use($search) {
277
                $search = '%' . strtolower($search) . '%';
278
                return $query->where(DB::raw('lower(name)'), 'like', $search);
279
            })
280
            ->when($filter === true, function(Builder $query) use($savingsFilter) {
281
                $query->whereHas('savings', $savingsFilter);
282
            })
283
            ->when($filter === false, function(Builder $query) use($savingsFilter) {
284
                $query->whereDoesntHave('savings', $savingsFilter);
285
            });
286
    }
287
288
    /**
289
     * Get a paginated list of members.
290
     *
291
     * @param Session $session
292
     * @param int $fundId
293
     * @param string $search
294
     * @param bool|null $filter
295
     * @param int $page
296
     *
297
     * @return Collection
298
     */
299
    public function getMembers(Session $session, int $fundId, string $search,
300
        ?bool $filter, int $page = 0): Collection
301
    {
302
        return $this->getMembersQuery($session, $fundId, $search, $filter)
303
            ->page($page, $this->tenantService->getLimit())
304
            ->with('savings', $this->getMemberSavingsFilter($session, $fundId))
305
            ->orderBy('name', 'asc')
306
            ->get()
307
            ->each(function($member) {
308
                $member->saving = $member->savings->first();
309
            });
310
    }
311
312
    /**
313
     * Get the number of members.
314
     *
315
     * @param Session $session
316
     * @param int $fundId
317
     * @param string $search
318
     * @param bool|null $filter
319
     *
320
     * @return int
321
     */
322
    public function getMemberCount(Session $session, int $fundId, string $search, ?bool $filter): int
323
    {
324
        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...
325
    }
326
327
    /**
328
     * Get a single member.
329
     *
330
     * @param int $id       The member id
331
     *
332
     * @return Member|null
333
     */
334
    public function getMember(int $id): ?Member
335
    {
336
        return $this->tenantService->tontine()->members()->active()->find($id);
337
    }
338
339
    /**
340
     * Get all closings for a given fund.
341
     *
342
     * @param int $fundId
343
     *
344
     * @return array
345
     */
346
    public function getFundClosings(int $fundId): array
347
    {
348
        $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...
349
        $closings = Arr::where($closings, fn($closing) => isset($closing[$fundId]));
350
        return Arr::map($closings, fn($closing) => $closing[$fundId]);
351
    }
352
353
    /**
354
     * Save the given session as closing for the fund.
355
     *
356
     * @param Session $session
357
     * @param int $fundId
358
     * @param int $profitAmount
359
     *
360
     * @return void
361
     */
362
    public function saveFundClosing(Session $session, int $fundId, int $profitAmount)
363
    {
364
        $tontine = $this->tenantService->tontine();
365
        $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...
366
        $properties['closings'][$session->id][$fundId] = $profitAmount;
367
        $tontine->saveProperties($properties);
368
    }
369
370
    /**
371
     * Check if the given session is closing the fund.
372
     *
373
     * @param Session $session
374
     * @param int $fundId
375
     *
376
     * @return bool
377
     */
378
    public function hasFundClosing(Session $session, int $fundId): bool
379
    {
380
        $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...
381
        return isset($properties['closings'][$session->id][$fundId]);
382
    }
383
384
    /**
385
     * Set the given session as closing the fund.
386
     *
387
     * @param Session $session
388
     * @param int $fundId
389
     *
390
     * @return void
391
     */
392
    public function deleteFundClosing(Session $session, int $fundId)
393
    {
394
        $tontine = $this->tenantService->tontine();
395
        $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...
396
        if(isset($properties['closings'][$session->id][$fundId]))
397
        {
398
            unset($properties['closings'][$session->id][$fundId]);
399
            if(count($properties['closings'][$session->id]) == 0)
400
            {
401
                unset($properties['closings'][$session->id]);
402
            }
403
        }
404
        $tontine->saveProperties($properties);
405
    }
406
407
    /**
408
     * Get the profit amount saved on a given session.
409
     *
410
     * @param Session $session
411
     * @param int $fundId
412
     *
413
     * @return int
414
     */
415
    public function getProfitAmount(Session $session, int $fundId): int
416
    {
417
        $tontine = $this->tenantService->tontine();
418
        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...
419
    }
420
421
    /**
422
     * Get all the fund closings on a given session.
423
     *
424
     * @param Session $session
425
     *
426
     * @return array
427
     */
428
    public function getSessionClosings(Session $session): array
429
    {
430
        $tontine = $this->tenantService->tontine();
431
        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...
432
    }
433
}
434