Passed
Push — main ( 0be37e...fc9941 )
by Thierry
06:01
created

SavingService::saveFundStartAmount()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 2
dl 0
loc 6
rs 10
c 0
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\Collection;
9
use Illuminate\Support\Facades\DB;
10
use Siak\Tontine\Model\Fund;
11
use Siak\Tontine\Model\Member;
12
use Siak\Tontine\Model\Saving;
13
use Siak\Tontine\Model\Session;
14
use Siak\Tontine\Service\Guild\FundService;
15
use Siak\Tontine\Service\LocaleService;
16
use Siak\Tontine\Service\TenantService;
17
use Siak\Tontine\Validation\SearchSanitizer;
18
19
class SavingService
20
{
21
    /**
22
     * @param LocaleService $localeService
23
     * @param TenantService $tenantService
24
     * @param FundService $fundService
25
     */
26
    public function __construct(private LocaleService $localeService,
27
        private TenantService $tenantService, private FundService $fundService,
28
        private SearchSanitizer $searchSanitizer)
29
    {}
30
31
    /**
32
     * Count the session funds.
33
     *
34
     * @param Session $session
35
     *
36
     * @return int
37
     */
38
    public function getFundCount(Session $session): int
39
    {
40
        return $session->funds()->real()->count();
41
    }
42
43
    /**
44
     * @param Session $session
45
     *
46
     * @return Relation
47
     */
48
    public function getFundQuery(Session $session): Relation
49
    {
50
        $sqlFrom = "savings s where s.fund_id=funds.id and s.session_id={$session->id}";
51
        return $session->funds()->real()
52
            ->addSelect([
53
                DB::raw("(select count(*) from $sqlFrom) as s_count"),
54
                DB::raw("(select sum(s.amount) from $sqlFrom) as s_amount"),
55
            ]);
56
    }
57
58
    /**
59
     * Get the session funds.
60
     *
61
     * @param Session $session
62
     * @param int $page
63
     *
64
     * @return Collection
65
     */
66
    public function getFunds(Session $session, int $page = 0): Collection
67
    {
68
        return $this->getFundQuery($session)
69
            ->page($page, $this->tenantService->getLimit())
70
            ->join('fund_defs', 'fund_defs.id', '=', 'funds.def_id')
71
            ->orderBy('fund_defs.type') // The default fund is first in the list.
72
            ->orderBy('funds.id')
73
            ->get();
74
    }
75
76
    /**
77
     * Get a session fund.
78
     *
79
     * @param Session $session
80
     * @param int $fundId
81
     *
82
     * @return Fund|null
83
     */
84
    public function getFund(Session $session, int $fundId): ?Fund
85
    {
86
        return $this->getFundQuery($session)->find($fundId);
87
    }
88
89
    /**
90
     * @param Session $session
91
     * @param Fund|null $fund
92
     *
93
     * @return Builder|Relation
94
     */
95
    private function getSavingQuery(Session $session, ?Fund $fund): Builder|Relation
96
    {
97
        return $session->savings()
98
            ->when($fund !== null, fn(Builder $query) => $query->where('fund_id', $fund->id));
99
    }
100
101
    /**
102
     * Count the savings for a given session.
103
     *
104
     * @param Session $session
105
     * @param Fund|null $fund
106
     *
107
     * @return int
108
     */
109
    public function getSavingCount(Session $session, ?Fund $fund): int
110
    {
111
        return $this->getSavingQuery($session, $fund)->count();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getSavingQ...ession, $fund)->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...
112
    }
113
114
    /**
115
     * Get the savings sum for a given session.
116
     *
117
     * @param Session $session
118
     * @param Fund|null $fund
119
     *
120
     * @return int
121
     */
122
    public function getSavingTotal(Session $session, ?Fund $fund): int
123
    {
124
        return $this->getSavingQuery($session, $fund)->sum('amount');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getSavingQ..., $fund)->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...
125
    }
126
127
    /**
128
     * Get the savings for a given session.
129
     *
130
     * @param Session $session
131
     * @param Fund|null $fund
132
     * @param int $page
133
     *
134
     * @return Collection
135
     */
136
    public function getSavings(Session $session, ?Fund $fund, int $page = 0): Collection
137
    {
138
        return $this->getSavingQuery($session, $fund)
139
            ->select(DB::raw('savings.*, members.name as member'))
140
            ->join('members', 'members.id', '=', 'savings.member_id')
141
            ->with(['fund'])
142
            ->page($page, $this->tenantService->getLimit())
143
            ->orderBy('members.name')
0 ignored issues
show
Bug introduced by
'members.name' of type string is incompatible with the type Closure|Illuminate\Datab...\Database\Query\Builder expected by parameter $column of Illuminate\Database\Query\Builder::orderBy(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

143
            ->orderBy(/** @scrutinizer ignore-type */ 'members.name')
Loading history...
144
            ->get();
145
    }
146
147
    /**
148
     * Get a saving for a given session.
149
     *
150
     * @param Session $session
151
     * @param int $savingId
152
     *
153
     * @return Saving|null
154
     */
155
    public function getSaving(Session $session, int $savingId): ?Saving
156
    {
157
        return $session->savings()->with(['fund'])->find($savingId);
158
    }
159
160
    /**
161
     * Find a member saving for a given session.
162
     *
163
     * @param Session $session
164
     * @param Fund $fund
165
     * @param Member $member
166
     *
167
     * @return Saving|null
168
     */
169
    public function findSaving(Session $session, Fund $fund, Member $member): ?Saving
170
    {
171
        return $this->getSavingQuery($session, $fund)
172
            ->where('member_id', $member->id)
173
            ->first();
174
    }
175
176
    /**
177
     * @param Session $session
178
     * @param Fund $fund
179
     * @param Member $member
180
     * @param Saving $saving
181
     * @param int $amount
182
     *
183
     * @return void
184
     */
185
    private function persistSaving(Session $session, Fund $fund, Member $member,
186
        Saving $saving, int $amount)
187
    {
188
        $saving->amount = $amount;
189
        $saving->session()->associate($session);
190
        $saving->fund()->associate($fund);
191
        $saving->member()->associate($member);
192
        $saving->save();
193
    }
194
195
    /**
196
     * Create a saving.
197
     *
198
     * @param Session $session The session
199
     * @param Fund $fund
200
     * @param Member $member
201
     * @param int $amount
202
     *
203
     * @return void
204
     */
205
    public function createSaving(Session $session, Fund $fund, Member $member, int $amount): void
206
    {
207
        $saving = new Saving();
208
        $this->persistSaving($session, $fund, $member, $saving, $amount);
209
    }
210
211
    /**
212
     * Update a saving.
213
     *
214
     * @param Session $session The session
215
     * @param Fund $fund
216
     * @param Member $member
217
     * @param Saving $saving
218
     * @param int $amount
219
     *
220
     * @return void
221
     */
222
    public function updateSaving(Session $session, Fund $fund, Member $member,
223
        Saving $saving, int $amount): void
224
    {
225
        $this->persistSaving($session, $fund, $member, $saving, $amount);
226
    }
227
228
    /**
229
     * Create or update a saving.
230
     *
231
     * @param Session $session The session
232
     * @param Fund $fund
233
     * @param Member $member
234
     * @param int $amount
235
     *
236
     * @return void
237
     */
238
    public function saveSaving(Session $session, Fund $fund, Member $member, int $amount): void
239
    {
240
        $saving = $this->findSaving($session, $fund, $member);
241
        if(!$saving)
242
        {
243
            $saving = new Saving();
244
        }
245
246
        $this->persistSaving($session, $fund, $member, $saving, $amount);
247
    }
248
249
    /**
250
     * Delete a saving.
251
     *
252
     * @param Session $session The session
253
     * @param int $savingId
254
     *
255
     * @return void
256
     */
257
    public function deleteSaving(Session $session, int $savingId): void
258
    {
259
        $session->savings()->where('id', $savingId)->delete();
260
    }
261
262
    /**
263
     * @param Session $session
264
     * @param Fund $fund
265
     *
266
     * @return Closure
267
     */
268
    private function getMemberSavingsFilter(Session $session, Fund $fund): Closure
269
    {
270
        return fn(/*Builder|Relation*/ $query) =>
271
            $query->where('session_id', $session->id)->where('fund_id', $fund->id);
272
    }
273
274
    /**
275
     * @param Session $session
276
     * @param Fund $fund
277
     * @param string $search
278
     * @param bool|null $filter
279
     *
280
     * @return Builder|Relation
281
     */
282
    private function getMembersQuery(Session $session, Fund $fund,
283
        string $search, ?bool $filter): Builder|Relation
284
    {
285
        $savingsFilter = $this->getMemberSavingsFilter($session, $fund);
286
        return $this->tenantService->guild()
287
            ->members()
288
            ->active()
289
            ->search($this->searchSanitizer->sanitize($search))
290
            ->when($filter === true,
291
                fn(Builder $query) => $query->whereHas('savings', $savingsFilter))
292
            ->when($filter === false,
293
                fn(Builder $query) => $query->whereDoesntHave('savings', $savingsFilter));
294
    }
295
296
    /**
297
     * Get a paginated list of members.
298
     *
299
     * @param Session $session
300
     * @param Fund $fund
301
     * @param string $search
302
     * @param bool|null $filter
303
     * @param int $page
304
     *
305
     * @return Collection
306
     */
307
    public function getMembers(Session $session, Fund $fund, string $search,
308
        ?bool $filter, int $page = 0): Collection
309
    {
310
        return $this->getMembersQuery($session, $fund, $search, $filter)
311
            ->page($page, $this->tenantService->getLimit())
312
            ->with('savings', $this->getMemberSavingsFilter($session, $fund))
313
            ->orderBy('name', 'asc')
314
            ->get()
315
            ->each(fn($member) => $member->saving = $member->savings->first());
316
    }
317
318
    /**
319
     * Get the number of members.
320
     *
321
     * @param Session $session
322
     * @param Fund $fund
323
     * @param string $search
324
     * @param bool|null $filter
325
     *
326
     * @return int
327
     */
328
    public function getMemberCount(Session $session, Fund $fund, string $search, ?bool $filter): int
329
    {
330
        return $this->getMembersQuery($session, $fund, $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...
331
    }
332
333
    /**
334
     * Delete a saving.
335
     *
336
     * @param Session $session The session
337
     * @param Fund $fund
338
     * @param Member $member
339
     *
340
     * @return void
341
     */
342
    public function deleteMemberSaving(Session $session, Fund $fund, Member $member): void
343
    {
344
        $session->savings()
345
            ->where('fund_id', $fund->id)
346
            ->where('member_id', $member->id)
347
            ->delete();
348
    }
349
350
    /**
351
     * Get a single member.
352
     *
353
     * @param int $id       The member id
354
     *
355
     * @return Member|null
356
     */
357
    public function getMember(int $id): ?Member
358
    {
359
        return $this->tenantService->guild()->members()->active()->find($id);
360
    }
361
362
    /**
363
     * @param Fund $fund
364
     * @param int $amount
365
     *
366
     * @return void
367
     */
368
    public function saveFundStartAmount(Fund $fund, int $amount): void
369
    {
370
        $options = $fund->options;
371
        $options['amount']['start'] = $amount;
372
        $fund->options = $options;
373
        $fund->save();
374
    }
375
376
    /**
377
     * @param Fund $fund
378
     * @param int $amount
379
     *
380
     * @return void
381
     */
382
    public function saveFundEndAmount(Fund $fund, int $amount): void
383
    {
384
        $options = $fund->options;
385
        $options['amount']['end'] = $amount;
386
        $fund->options = $options;
387
        $fund->save();
388
    }
389
390
    /**
391
     * @param Fund $fund
392
     * @param int $amount
393
     *
394
     * @return void
395
     */
396
    public function saveFundProfitAmount(Fund $fund, int $amount): void
397
    {
398
        $options = $fund->options;
399
        $options['amount']['profit'] = $amount;
400
        $fund->options = $options;
401
        $fund->save();
402
    }
403
}
404