1 | <?php |
||
2 | |||
3 | namespace Siak\Tontine\Service\Planning; |
||
4 | |||
5 | use Illuminate\Database\Eloquent\Builder; |
||
6 | use Illuminate\Database\Eloquent\Relations\Relation; |
||
7 | use Illuminate\Support\Collection; |
||
8 | use Illuminate\Support\Facades\DB; |
||
9 | use Siak\Tontine\Exception\MessageException; |
||
10 | use Siak\Tontine\Model\Guild; |
||
11 | use Siak\Tontine\Model\Pool; |
||
12 | use Siak\Tontine\Model\Round; |
||
13 | use Siak\Tontine\Model\Session; |
||
14 | use Siak\Tontine\Model\Subscription; |
||
15 | use Siak\Tontine\Service\TenantService; |
||
16 | use Siak\Tontine\Validation\SearchSanitizer; |
||
17 | |||
18 | use function trans; |
||
19 | |||
20 | class SubscriptionService |
||
21 | { |
||
22 | /** |
||
23 | * @param TenantService $tenantService |
||
24 | * @param PoolSyncService $poolSyncService |
||
25 | * @param SearchSanitizer $searchSanitizer |
||
26 | */ |
||
27 | public function __construct(private TenantService $tenantService, |
||
28 | private PoolSyncService $poolSyncService, private SearchSanitizer $searchSanitizer) |
||
29 | {} |
||
30 | |||
31 | /** |
||
32 | * Get pools for the dropdown list. |
||
33 | * |
||
34 | * @param Round $round |
||
35 | * @param bool $pluck |
||
36 | * |
||
37 | * @return Collection |
||
38 | */ |
||
39 | public function getPools(Round $round, bool $pluck = true): Collection |
||
40 | { |
||
41 | $query = $round->pools() |
||
42 | ->with(['round.guild']) |
||
43 | ->whereHas('subscriptions'); |
||
44 | return $pluck ? $query->get()->pluck('title', 'id') : $query->get(); |
||
45 | } |
||
46 | |||
47 | /** |
||
48 | * @param Guild $guild |
||
49 | * @param Pool $pool |
||
50 | * @param string $search |
||
51 | * @param bool $filter|null |
||
52 | * |
||
53 | * @return Builder|Relation |
||
54 | */ |
||
55 | private function getQuery(Pool $pool, string $search, ?bool $filter): Builder|Relation |
||
56 | { |
||
57 | return $pool->round->members() |
||
58 | ->search($this->searchSanitizer->sanitize($search)) |
||
59 | ->when($filter === true, function(Builder $query) use($pool) { |
||
60 | // Return only members with subscription in this pool |
||
61 | return $query->whereHas('subscriptions', function(Builder $query) use($pool) { |
||
62 | $query->where('subscriptions.pool_id', $pool->id); |
||
63 | }); |
||
64 | }) |
||
65 | ->when($filter === false, function(Builder $query) use($pool) { |
||
66 | // Return only members without subscription in this pool |
||
67 | return $query->whereDoesntHave('subscriptions', function(Builder $query) use($pool) { |
||
68 | $query->where('subscriptions.pool_id', $pool->id); |
||
69 | }); |
||
70 | }); |
||
71 | } |
||
72 | |||
73 | /** |
||
74 | * Get a paginated list of members. |
||
75 | * |
||
76 | * @param Pool $pool |
||
77 | * @param string $search |
||
78 | * @param bool $filter|null |
||
79 | * @param int $page |
||
80 | * |
||
81 | * @return Collection |
||
82 | */ |
||
83 | public function getMembers(Pool $pool, string $search, ?bool $filter, int $page = 0): Collection |
||
84 | { |
||
85 | return $this->getQuery($pool, $search, $filter) |
||
86 | ->page($page, $this->tenantService->getLimit()) |
||
87 | ->withCount([ |
||
88 | 'subscriptions' => function(Builder $query) use($pool) { |
||
89 | $query->where('pool_id', $pool->id); |
||
90 | }, |
||
91 | ]) |
||
92 | ->orderBy('name', 'asc') |
||
93 | ->get(); |
||
94 | } |
||
95 | |||
96 | /** |
||
97 | * Get the number of members. |
||
98 | * |
||
99 | * @param Pool $pool |
||
100 | * @param string $search |
||
101 | * @param bool $filter|null |
||
102 | * |
||
103 | * @return int |
||
104 | */ |
||
105 | public function getMemberCount(Pool $pool, string $search, ?bool $filter): int |
||
106 | { |
||
107 | return $this->getQuery($pool, $search, $filter)->count(); |
||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
![]() |
|||
108 | } |
||
109 | |||
110 | /** |
||
111 | * @param Pool $pool |
||
112 | * @param int $memberId |
||
113 | * |
||
114 | * @return void |
||
115 | */ |
||
116 | public function createSubscription(Pool $pool, int $memberId) |
||
117 | { |
||
118 | // When the remitments are planned, don't create a subscription |
||
119 | // if receivables already exist on the pool. |
||
120 | // if($pool->remit_planned && |
||
121 | // $pool->subscriptions()->whereHas('receivables')->count() > 0) |
||
122 | // { |
||
123 | // throw new MessageException(trans('tontine.subscription.errors.create')); |
||
124 | // } |
||
125 | |||
126 | $member = $pool->round->members()->find($memberId); |
||
127 | $subscription = new Subscription(); |
||
128 | $subscription->title = ''; |
||
129 | $subscription->pool()->associate($pool); |
||
130 | $subscription->member()->associate($member); |
||
131 | |||
132 | DB::transaction(function() use($subscription) { |
||
133 | $subscription->save(); |
||
134 | |||
135 | $this->poolSyncService->subscriptionCreated($subscription); |
||
136 | }); |
||
137 | } |
||
138 | |||
139 | /** |
||
140 | * @param Pool $pool |
||
141 | * @param int $memberId |
||
142 | * |
||
143 | * @return void |
||
144 | */ |
||
145 | public function deleteSubscription(Pool $pool, int $memberId) |
||
146 | { |
||
147 | // When the remitments are planned, don't delete a subscription |
||
148 | // if receivables already exist on the pool. |
||
149 | // if($pool->remit_planned && |
||
150 | // $pool->subscriptions()->whereHas('receivables')->count() > 0) |
||
151 | // { |
||
152 | // throw new MessageException(trans('tontine.subscription.errors.delete')); |
||
153 | // } |
||
154 | $subscriptions = $pool->subscriptions() |
||
155 | ->where('member_id', $memberId) |
||
156 | ->withCount('receivables') |
||
157 | ->get() |
||
158 | ->sortBy('receivables_count'); |
||
159 | if($subscriptions->count() === 0) |
||
160 | { |
||
161 | throw new MessageException(trans('tontine.subscription.errors.not_found')); |
||
162 | } |
||
163 | |||
164 | // Since the subscriptions are sorted by receivables count, those with no receivable |
||
165 | // will be deleted in priority. |
||
166 | $subscription = $subscriptions->first(); |
||
167 | DB::transaction(function() use($subscription) { |
||
168 | $this->poolSyncService->subscriptionDeleted($subscription); |
||
169 | |||
170 | $subscription->delete(); |
||
171 | }); |
||
172 | } |
||
173 | |||
174 | /** |
||
175 | * Get the number of subscriptions. |
||
176 | * |
||
177 | * @param Pool $pool |
||
178 | * |
||
179 | * @return int |
||
180 | */ |
||
181 | public function getSubscriptionCount(Pool $pool): int |
||
182 | { |
||
183 | return $pool->subscriptions()->count(); |
||
184 | } |
||
185 | |||
186 | /** |
||
187 | * @param Subscription $subscription |
||
188 | * @param Session $session |
||
189 | * |
||
190 | * @return void |
||
191 | */ |
||
192 | public function setPayableSession(Subscription $subscription, Session $session) |
||
193 | { |
||
194 | $subscription->payable->session()->associate($session); |
||
195 | $subscription->payable->save(); |
||
196 | } |
||
197 | |||
198 | /** |
||
199 | * @param Subscription $subscription |
||
200 | * |
||
201 | * @return void |
||
202 | */ |
||
203 | public function unsetPayableSession(Subscription $subscription) |
||
204 | { |
||
205 | if(($subscription->payable->session_id)) |
||
206 | { |
||
207 | $subscription->payable->session()->dissociate(); |
||
208 | $subscription->payable->save(); |
||
209 | } |
||
210 | } |
||
211 | |||
212 | /** |
||
213 | * @param Session $session |
||
214 | * @param Subscription|null $subscription |
||
215 | * |
||
216 | * @return bool |
||
217 | */ |
||
218 | private function canChangeBeneficiary(Session $session, ?Subscription $subscription): bool |
||
219 | { |
||
220 | // Can't change the beneficiary if the session is closed, |
||
221 | if($session->closed) |
||
0 ignored issues
–
show
|
|||
222 | { |
||
223 | return false; |
||
224 | } |
||
225 | // Or if the collected amount has already been remitted. |
||
226 | return $subscription === null || $subscription->payable === null || |
||
227 | $subscription->payable->remitment === null; |
||
228 | } |
||
229 | |||
230 | /** |
||
231 | * Set or unset the beneficiary of a given pool. |
||
232 | * |
||
233 | * @param Pool $pool |
||
234 | * @param int $sessionId |
||
235 | * @param int $currSubscriptionId |
||
236 | * @param int $nextSubscriptionId |
||
237 | * |
||
238 | * @return bool |
||
239 | */ |
||
240 | public function saveBeneficiary(Pool $pool, int $sessionId, int $currSubscriptionId, |
||
241 | int $nextSubscriptionId): bool |
||
242 | { |
||
243 | $session = $pool->sessions()->find($sessionId); |
||
244 | $currSubscription = null; |
||
245 | $nextSubscription = null; |
||
246 | if($currSubscriptionId > 0) |
||
247 | { |
||
248 | $currSubscription = $pool->subscriptions() |
||
249 | ->with('payable') |
||
250 | ->find($currSubscriptionId); |
||
251 | if(!$this->canChangeBeneficiary($session, $currSubscription)) |
||
252 | { |
||
253 | return false; |
||
254 | } |
||
255 | } |
||
256 | if($nextSubscriptionId > 0) |
||
257 | { |
||
258 | $nextSubscription = $pool->subscriptions() |
||
259 | ->with('payable') |
||
260 | ->find($nextSubscriptionId); |
||
261 | } |
||
262 | |||
263 | DB::transaction(function() use($session, $currSubscription, $nextSubscription) { |
||
264 | // If the beneficiary already has a session assigned, first remove it. |
||
265 | if($currSubscription !== null) |
||
266 | { |
||
267 | $this->unsetPayableSession($currSubscription); |
||
268 | } |
||
269 | // If there is a new session assigned to the beneficiary, then save it. |
||
270 | if($nextSubscription !== null) |
||
271 | { |
||
272 | $this->setPayableSession($nextSubscription, $session); |
||
273 | } |
||
274 | }); |
||
275 | |||
276 | return true; |
||
277 | } |
||
278 | } |
||
279 |