OutflowService::updateOutflow()   A
last analyzed

Complexity

Conditions 5
Paths 6

Size

Total Lines 37
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 22
c 0
b 0
f 0
nc 6
nop 4
dl 0
loc 37
rs 9.2568
1
<?php
2
3
namespace Siak\Tontine\Service\Meeting\Cash;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Support\Collection;
7
use Siak\Tontine\Exception\MessageException;
8
use Siak\Tontine\Model\Category;
9
use Siak\Tontine\Model\Charge;
10
use Siak\Tontine\Model\Guild;
11
use Siak\Tontine\Model\Outflow;
12
use Siak\Tontine\Model\Member;
13
use Siak\Tontine\Model\Round;
14
use Siak\Tontine\Model\Session;
15
use Siak\Tontine\Service\Payment\BalanceCalculator;
16
use Siak\Tontine\Service\LocaleService;
17
use Siak\Tontine\Service\TenantService;
18
19
use function trans;
20
use function trim;
21
22
class OutflowService
23
{
24
    /**
25
     * @param LocaleService $localeService
26
     * @param TenantService $tenantService
27
     * @param BalanceCalculator $balanceCalculator
28
     */
29
    public function __construct(protected LocaleService $localeService,
30
        protected TenantService $tenantService, protected BalanceCalculator $balanceCalculator)
31
    {}
32
33
    /**
34
     * Get a list of members for the dropdown select component.
35
     *
36
     * @param Round $round
37
     *
38
     * @return Collection
39
     */
40
    public function getMembers(Round $round): Collection
41
    {
42
        return $round->members()
43
            ->orderBy('name', 'asc')
44
            ->get()
45
            ->pluck('name', 'id')
46
            ->prepend('', 0);
47
    }
48
49
    /**
50
     * Find a member.
51
     *
52
     * @param Round $round
53
     * @param int $memberId
54
     *
55
     * @return Member|null
56
     */
57
    public function getMember(Round $round, int $memberId): ?Member
58
    {
59
        return $round->members()->find($memberId);
60
    }
61
62
    /**
63
     * Get a list of charges for the dropdown select component.
64
     *
65
     * @param Round $round
66
     *
67
     * @return Collection
68
     */
69
    public function getCharges(Round $round): Collection
70
    {
71
        return $round->charges()->fee()
72
            ->orderBy('name', 'asc')
73
            ->get()
74
            ->pluck('name', 'id')
75
            ->prepend('', 0);
76
    }
77
78
    /**
79
     * Find a charge.
80
     *
81
     * @param Round $round
82
     * @param int $chargeId
83
     *
84
     * @return Charge|null
85
     */
86
    public function getCharge(Round $round, int $chargeId): ?Charge
87
    {
88
        return $round->charges()->fee()->find($chargeId);
89
    }
90
91
    /**
92
     * Get the outflow categories for the dropdown select component.
93
     *
94
     * @param Guild $guild
95
     *
96
     * @return Builder
97
     */
98
    private function getAccountQuery(Guild $guild): Builder
99
    {
100
        return Category::outflow()->active()
0 ignored issues
show
Bug Best Practice introduced by
The expression return Siak\Tontine\Mode...ion(...) { /* ... */ }) could return the type Illuminate\Database\Eloq...gHasThroughRelationship which is incompatible with the type-hinted return Illuminate\Database\Eloquent\Builder. Consider adding an additional type-check to rule them out.
Loading history...
101
            ->where(fn($query) => $query->global()->orWhere('guild_id', $guild->id));
102
    }
103
104
    /**
105
     * Get the outflow categories for the dropdown select component.
106
     *
107
     * @param Guild $guild
108
     *
109
     * @return Collection
110
     */
111
    public function getAccounts(Guild $guild): Collection
112
    {
113
        // It is important to call get() before pluck() so the name field is translated.
114
        $categories = $this->getAccountQuery($guild)->orderBy('id')->get();
115
        // We need to move the "other" category to the end of the list.
116
        // getAttributes()['name'] returns the name field, without calling the getter.
117
        [$otherCategory, $categories] = $categories
118
            ->partition(fn($category) => $category->is_other);
119
120
        return $categories->concat($otherCategory)->pluck('name', 'id');
121
    }
122
123
    /**
124
     * Find a cash outflow category.
125
     *
126
     * @param Guild $guild
127
     * @param int $categoryId
128
     *
129
     * @return Category|null
130
     */
131
    public function getAccount(Guild $guild, int $categoryId): ?Category
132
    {
133
        return $this->getAccountQuery($guild)->find($categoryId);
134
    }
135
136
    /**
137
     * Get the amount available for outflow.
138
     *
139
     * @param Session $session    The session
140
     *
141
     * @return int
142
     */
143
    public function getAmountAvailable(Session $session): int
144
    {
145
        return $this->balanceCalculator->getTotalBalance($session);
146
    }
147
148
    /**
149
     * Count the outflows for a given session.
150
     *
151
     * @param Session $session
152
     *
153
     * @return int
154
     */
155
    public function getSessionOutflowCount(Session $session): int
156
    {
157
        return $session->outflows()->count();
158
    }
159
160
    /**
161
     * Get the outflows for a given session.
162
     *
163
     * @param Session $session
164
     * @param int $page
165
     *
166
     * @return Collection
167
     */
168
    public function getSessionOutflows(Session $session, int $page = 0): Collection
169
    {
170
        return $session->outflows()
171
            ->with(['member', 'charge', 'category'])
172
            ->page($page, $this->tenantService->getLimit())
173
            ->get();
174
    }
175
176
    /**
177
     * Get a cash outflow for a given session.
178
     *
179
     * @param Session $session
180
     * @param int $outflowId
181
     *
182
     * @return Outflow|null
183
     */
184
    public function getSessionOutflow(Session $session, int $outflowId): ?Outflow
185
    {
186
        return $session->outflows()->find($outflowId);
187
    }
188
189
    /**
190
     * Create a cash outflow.
191
     *
192
     * @param Guild $guild
193
     * @param Session $session The session
194
     * @param array $values
195
     *
196
     * @return void
197
     */
198
    public function createOutflow(Guild $guild, Session $session, array $values): void
199
    {
200
        $member = $this->getMember($session->round, $values['member']);
201
        $charge = $this->getCharge($session->round, $values['charge']);
202
        $category = $this->getAccount($guild, $values['category']);
203
        if(!$category)
204
        {
205
            throw new MessageException(trans('meeting.category.errors.not_found'));
206
        }
207
208
        $outflow = new Outflow();
209
        $outflow->amount = $values['amount'];
210
        $outflow->comment = trim($values['comment']);
211
        if(($member))
212
        {
213
            $outflow->member()->associate($member);
214
        }
215
        if(($charge))
216
        {
217
            $outflow->charge_lendable = $charge->lendable;
218
            $outflow->charge()->associate($charge);
219
        }
220
        else
221
        {
222
            $outflow->charge_lendable = true;
223
        }
224
        $outflow->category()->associate($category);
225
        $outflow->session()->associate($session);
226
        $outflow->save();
227
    }
228
229
    /**
230
     * Update a cash outflow.
231
     *
232
     * @param Guild $guild
233
     * @param Session $session The session
234
     * @param int $outflowId
235
     * @param array $values
236
     *
237
     * @return void
238
     */
239
    public function updateOutflow(Guild $guild, Session $session, int $outflowId, array $values): void
240
    {
241
        $member = $this->getMember($session->round, $values['member']);
242
        $charge = $this->getCharge($session->round, $values['charge']);
243
        $category = $this->getAccount($guild, $values['category']);
244
        if(!$category)
245
        {
246
            throw new MessageException(trans('meeting.category.errors.not_found'));
247
        }
248
        $outflow = $session->outflows()->find($outflowId);
249
        if(!$outflow)
250
        {
251
            throw new MessageException(trans('meeting.outflow.errors.not_found'));
252
        }
253
254
        $outflow->amount = $values['amount'];
255
        $outflow->comment = trim($values['comment']);
256
        if(($member))
257
        {
258
            $outflow->member()->associate($member);
259
        }
260
        else
261
        {
262
            $outflow->member()->dissociate();
263
        }
264
        if(($charge))
265
        {
266
            $outflow->charge_lendable = $charge->lendable;
267
            $outflow->charge()->associate($charge);
268
        }
269
        else
270
        {
271
            $outflow->charge_lendable = true;
272
            $outflow->charge()->dissociate();
273
        }
274
        $outflow->category()->associate($category);
275
        $outflow->save();
276
    }
277
278
    /**
279
     * Delete a cash outflow.
280
     *
281
     * @param Session $session The session
282
     * @param int $outflowId
283
     *
284
     * @return void
285
     */
286
    public function deleteOutflow(Session $session, int $outflowId): void
287
    {
288
        $session->outflows()->where('id', $outflowId)->delete();
289
    }
290
}
291