Completed
Push — master ( 505db6...72d5e4 )
by Arthur
05:13
created

PaymentRepository::getPaginated()   B

Complexity

Conditions 6
Paths 32

Size

Total Lines 25
Code Lines 13

Duplication

Lines 6
Ratio 24 %
Metric Value
dl 6
loc 25
rs 8.439
cc 6
eloc 13
nc 32
nop 1
1
<?php namespace BB\Repo;
2
3
use BB\Entities\Payment;
4
use BB\Events\MemberBalanceChanged;
5
use BB\Exceptions\NotImplementedException;
6
use BB\Exceptions\PaymentException;
7
use Carbon\Carbon;
8
9
class PaymentRepository extends DBRepository
10
{
11
12
    /**
13
     * @var Payment
14
     */
15
    protected $model;
16
17
    public static $SUBSCRIPTION = 'subscription';
18
    public static $INDUCTION = 'induction';
19
20
    private $reason = null;
21
    private $source = null;
22
23
    /**
24
     * @param Payment $model
25
     */
26
    public function __construct(Payment $model)
27
    {
28
        $this->model = $model;
29
        $this->perPage = 10;
30
    }
31
32
33
    public function getPaginated(array $params)
34
    {
35
        $model = $this->model;
36
37 View Code Duplication
        if ($this->hasDateFilter()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
38
            $model = $model->where('created_at', '>=', $this->startDate)->where('created_at', '<=', $this->endDate);
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
39
        }
40
41
        if ($this->hasMemberFilter()) {
42
            $model = $model->where('user_id', $this->memberId);
43
        }
44
45
        if ($this->hasReasonFilter()) {
46
            $model = $model->where('reason', $this->reason);
47
        }
48
49
        if ($this->hasSourceFilter()) {
50
            $model = $model->where('source', $this->source);
51
        }
52
53 View Code Duplication
        if ($this->isSortable($params)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
54
            return $model->orderBy($params['sortBy'], $params['direction'])->paginate($this->perPage);
55
        }
56
        return $model->paginate($this->perPage);
57
    }
58
59
60
    public function getTotalAmount()
61
    {
62
        $model = $this->model;
63
64 View Code Duplication
        if ($this->hasDateFilter()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
65
            $model = $model->where('created_at', '>=', $this->startDate)->where('created_at', '<=', $this->endDate);
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
66
        }
67
68
        if ($this->hasMemberFilter()) {
69
            $model = $model->where('user_id', $this->memberId);
70
        }
71
72
        if ($this->hasReasonFilter()) {
73
            $model = $model->where('reason', $this->reason);
74
        }
75
76
        if ($this->hasSourceFilter()) {
77
            $model = $model->where('source', $this->source);
78
        }
79
80
        return $model->get()->sum('amount');
81
    }
82
83
84
    /**
85
     * Record a payment against a user record
86
     *
87
     * @param string   $reason What was the reason. subscription, induction, etc...
88
     * @param int      $userId The users ID
89
     * @param string   $source gocardless, paypal
90
     * @param string   $sourceId A reference for the source
91
     * @param double   $amount Amount received before a fee
92
     * @param string   $status paid, pending, cancelled, refunded
93
     * @param double   $fee The fee charged by the payment provider
94
     * @param string   $ref
95
     * @param Carbon $paidDate
96
     * @return int The ID of the payment record
97
     */
98
    public function recordPayment($reason, $userId, $source, $sourceId, $amount, $status = 'paid', $fee = 0.0, $ref = '', Carbon $paidDate = null)
99
    {
100
        if ($paidDate == null) {
101
            $paidDate = new Carbon();
102
        }
103
        //If we have an existing similer record dont create another, except for when there is no source id
104
        $existingRecord = $this->model->where('source', $source)->where('source_id', $sourceId)->where('user_id', $userId)->first();
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
105
        if ( ! $existingRecord || empty($sourceId)) {
106
            $record                   = new $this->model;
107
            $record->user_id          = $userId;
108
            $record->reason           = $reason;
109
            $record->source           = $source;
110
            $record->source_id        = $sourceId;
111
            $record->amount           = $amount;
112
            $record->amount_minus_fee = ($amount - $fee);
113
            $record->fee              = $fee;
114
            $record->status           = $status;
115
            $record->reference        = $ref;
116
            if ($status == 'paid') {
117
                $record->paid_at = $paidDate;
118
            }
119
            $record->save();
120
        } else {
121
            $record = $existingRecord;
122
        }
123
124
        //Emit an event so that things like the balance updater can run
125
        \Event::fire('payment.create', array($userId, $reason, $ref, $record->id, $status));
126
127
        return $record->id;
128
    }
129
130
    /**
131
     * Record a subscription payment
132
     *
133
     * @param int    $userId The users ID
134
     * @param string $source gocardless, paypal
135
     * @param string $sourceId A reference for the source
136
     * @param double $amount Amount received before a fee
137
     * @param string $status paid, pending, cancelled, refunded
138
     * @param double $fee The fee charged by the payment provider
139
     * @param string|null   $ref
140
     * @param Carbon $paidDate
141
     * @return int  The ID of the payment record
142
     */
143
    public function recordSubscriptionPayment($userId, $source, $sourceId, $amount, $status = 'paid', $fee = 0.0, $ref = '', Carbon $paidDate = null)
144
    {
145
        return $this->recordPayment('subscription', $userId, $source, $sourceId, $amount, $status, $fee, $ref, $paidDate);
146
    }
147
148
    /**
149
     * An existing payment has been set to paid
150
     *
151
     * @param $paymentId
152
     * @param Carbon $paidDate
153
     */
154
    public function markPaymentPaid($paymentId, $paidDate)
155
    {
156
        $payment = $this->getById($paymentId);
157
        $payment->status = 'paid';
158
        $payment->paid_at = $paidDate;
159
        $payment->save();
160
161
        \Event::fire('payment.paid', array($payment->user_id, $paymentId, $payment->reason, $payment->reference, $paidDate));
162
    }
163
164
    /**
165
     * Record a payment failure or cancellation
166
     *
167
     * @param int    $paymentId
168
     * @param string $status
169
     */
170
    public function recordPaymentFailure($paymentId, $status = 'failed')
171
    {
172
        $this->update($paymentId, ['status' => $status]);
173
174
        $payment = $this->getById($paymentId);
175
176
        \Event::fire('payment.cancelled', array($paymentId, $payment->user_id, $payment->reason, $payment->reference, $status));
177
    }
178
179
    /**
180
     * Assign an unassigned payment to a user
181
     *
182
     * @param int $paymentId
183
     * @param int $userId
184
     *
185
     * @throws PaymentException
186
     */
187
    public function assignPaymentToUser($paymentId, $userId)
188
    {
189
        $payment = $this->getById($paymentId);
190
191
        if (!empty($payment->user_id)) {
192
            throw new PaymentException('Payment already assigned to user');
193
        }
194
195
        $this->update($paymentId, ['user_id' => $userId]);
196
    }
197
198
    public function refundPaymentToBalance($paymentId)
199
    {
200
        $payment = $this->getById($paymentId);
201
202
        if ($payment->reason !== 'donation') {
203
            throw new NotImplementedException('This hasn\'t been built yet');
204
        }
205
206
        $this->update($paymentId, ['reason' => 'balance']);
207
208
        event(new MemberBalanceChanged($payment->user_id));
209
    }
210
211
212
    /**
213
     * Fetch the users latest payment of a particular type
214
     * @param integer $userId
215
     * @param string  $reason
216
     * @return mixed
217
     */
218 View Code Duplication
    public function latestUserPayment($userId, $reason = 'subscription')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
219
    {
220
        return $this->model->where('user_id', $userId)
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
221
            ->whereRaw('reason = ? and (status = ? or status = ? or status = ?)', [$reason, 'paid', 'pending', 'withdrawn'])
222
            ->orderBy('created_at', 'desc')
223
            ->first();
224
    }
225
226
227
    /**
228
     * Get all user payments of a specific reason
229
     * @param $userId
230
     * @param string $reason
231
     * @return mixed
232
     */
233 View Code Duplication
    public function getUserPaymentsByReason($userId, $reason)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
234
    {
235
        return $this->model->where('user_id', $userId)
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
236
            ->whereRaw('reason = ? and (status = ? or status = ? or status = ?)', [$reason, 'paid', 'pending', 'withdrawn'])
237
            ->orderBy('created_at', 'desc')
238
            ->get();
239
    }
240
241
242
    /**
243
     * @param string $source
244
     */
245 View Code Duplication
    public function getUserPaymentsBySource($userId, $source)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
246
    {
247
        return $this->model->where('user_id', $userId)
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
248
            ->whereRaw('source = ? and (status = ? or status = ? or status = ?)', [$source, 'paid', 'pending', 'withdrawn'])
249
            ->orderBy('created_at', 'desc')
250
            ->get();
251
    }
252
253
    /**
254
     * Get all payments with a specific reference
255
     * @param string $reference
256
     * @return \Illuminate\Database\Eloquent\Collection
257
     */
258
    public function getPaymentsByReference($reference)
259
    {
260
        return $this->model->where('reference', $reference)->get();
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
261
    }
262
263
    /**
264
     * @param string $referencePrefix
265
     * @return \Illuminate\Database\Eloquent\Collection
266
     */
267
    public function getEquipmentFeePayments($referencePrefix)
268
    {
269
        return $this->model->where('reason', 'equipment-fee')->get()->filter(function($payment) use($referencePrefix) {
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
270
            return strpos($payment->reference, ':' . $referencePrefix) !== false;
271
        });
272
    }
273
274
275
    /**
276
     * Return a paginated list of balance affecting payment for a user
277
     * @param $userId
278
     * @return \Illuminate\Database\Eloquent\Collection
279
     */
280 View Code Duplication
    public function getBalancePaymentsPaginated($userId)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
281
    {
282
        return $this->model->where('user_id', $userId)
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
283
            ->whereRaw('(source = ? or reason = ?) and (status = ? or status = ? or status = ?)', ['balance', 'balance', 'paid', 'pending', 'withdrawn'])
284
            ->orderBy('created_at', 'desc')
285
            ->simplePaginate($this->perPage);
286
    }
287
288
289
    /**
290
     * Return a collection of payments specifically for storage boxes
291
     * @param integer $userId
292
     * @return mixed
293
     */
294 View Code Duplication
    public function getStorageBoxPayments($userId)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
295
    {
296
        return $this->model->where('user_id', $userId)
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
297
            ->whereRaw('reason = ? and (status = ? or status = ? or status = ?)', ['storage-box', 'paid', 'pending', 'withdrawn'])
298
            ->orderBy('created_at', 'desc')
299
            ->get();
300
    }
301
302
    public function dateFilter($startDate, $endDate)
303
    {
304
        $this->startDate = $startDate;
305
        $this->endDate = $endDate;
306
    }
307
308
    private function hasDateFilter()
309
    {
310
        return ($this->startDate && $this->endDate);
311
    }
312
313
    /**
314
     * Delete a record
315
     * @param $recordId
316
     * @return bool|null
317
     * @throws \Exception
318
     */
319
    public function delete($recordId)
320
    {
321
        $payment = $this->getById($recordId);
322
323
        $state = $payment->delete();
324
325
        //Fire an event, allows the balance to get updated
326
        \Event::fire('payment.delete', array($payment->user_id, $payment->source, $payment->reason, $payment->id));
327
328
        return $state;
329
    }
330
331
    public function canDelete($recordId)
0 ignored issues
show
Unused Code introduced by
The parameter $recordId is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
332
    {
333
        throw new NotImplementedException();
334
    }
335
336
    /**
337
     * Used for the getPaginated and getTotalAmount method
338
     * @param $reasonFilter
339
     */
340
    public function reasonFilter($reasonFilter)
341
    {
342
        $this->reason = $reasonFilter;
343
    }
344
345
    private function hasReasonFilter()
346
    {
347
        return ! is_null($this->reason);
348
    }
349
350
    /**
351
     * Used for the getPaginated and getTotalAmount method
352
     * @param string $sourceFilter
353
     */
354
    public function sourceFilter($sourceFilter)
355
    {
356
        $this->source = $sourceFilter;
357
    }
358
359
360
    private function hasSourceFilter()
361
    {
362
        return ! is_null($this->source);
363
    }
364
365
    /**
366
     * Used for the getPaginated and getTotalAmount method
367
     */
368
    public function resetFilters()
369
    {
370
        $this->source = null;
371
        $this->reason = null;
372
        $this->memberId = null;
373
        $this->startDate = null;
374
        $this->endDate = null;
375
    }
376
377
    /**
378
     * Fetch a payment record using the id provided by the payment provider
379
     *
380
     * @param $sourceId
381
     * @return Payment
382
     */
383
    public function getPaymentBySourceId($sourceId)
384
    {
385
        return $this->model->where('source_id', $sourceId)->first();
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
386
    }
387
388
    /**
389
     * Record a balance payment transfer between two users
390
     * 
391
     * @param integer $sourceUserId
392
     * @param integer $targetUserId
393
     * @param double $amount
394
     */
395
    public function recordBalanceTransfer($sourceUserId, $targetUserId, $amount)
396
    {
397
        $paymentId = $this->recordPayment('transfer', $sourceUserId, 'balance', '', $amount, 'paid', 0, $targetUserId);
398
        $this->recordPayment('balance', $targetUserId, 'transfer', $paymentId, $amount, 'paid', 0, $sourceUserId);
399
400
        //Both of these events aren't needed adn the balance payment fires its own
401
        // but for the sake of neatness they are here
402
        event(new MemberBalanceChanged($sourceUserId));
403
        event(new MemberBalanceChanged($targetUserId));
404
    }
405
}