GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( 37b02e...ebbbe1 )
by James
08:59
created

app/Repositories/Budget/BudgetRepository.php (2 issues)

Labels
Severity
1
<?php
2
/**
3
 * BudgetRepository.php
4
 * Copyright (c) 2017 [email protected]
5
 *
6
 * This file is part of Firefly III.
7
 *
8
 * Firefly III is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * Firefly III is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
20
 */
21
declare(strict_types=1);
22
23
namespace FireflyIII\Repositories\Budget;
24
25
use Carbon\Carbon;
26
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
27
use FireflyIII\Models\AccountType;
28
use FireflyIII\Models\AvailableBudget;
29
use FireflyIII\Models\Budget;
30
use FireflyIII\Models\BudgetLimit;
31
use FireflyIII\Models\Transaction;
32
use FireflyIII\Models\TransactionCurrency;
33
use FireflyIII\Models\TransactionJournal;
34
use FireflyIII\Models\TransactionType;
35
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
36
use FireflyIII\User;
37
use Illuminate\Database\Eloquent\Builder;
38
use Illuminate\Support\Collection;
39
use Log;
40
use Navigation;
41
use stdClass;
42
43
/**
44
 * Class BudgetRepository.
45
 */
46
class BudgetRepository implements BudgetRepositoryInterface
47
{
48
    /** @var User */
49
    private $user;
50
51
    /**
52
     * A method that returns the amount of money budgeted per day for this budget,
53
     * on average.
54
     *
55
     * @param Budget $budget
56
     *
57
     * @return string
58
     */
59
    public function budgetedPerDay(Budget $budget): string
60
    {
61
        $total = '0';
62
        $count = 0;
63
        foreach ($budget->budgetlimits as $limit) {
64
            $diff   = strval($limit->start_date->diffInDays($limit->end_date));
65
            $amount = strval($limit->amount);
66
            $perDay = bcdiv($amount, $diff);
67
            $total  = bcadd($total, $perDay);
68
            $count++;
69
        }
70
        $avg = $total;
71
        if ($count > 0) {
72
            $avg = bcdiv($total, strval($count));
73
        }
74
75
        return $avg;
76
    }
77
78
    /**
79
     * @return bool
80
     *
81
     * @throws \Exception
82
     * @throws \Exception
83
     */
84
    public function cleanupBudgets(): bool
85
    {
86
        // delete limits with amount 0:
87
        BudgetLimit::where('amount', 0)->delete();
88
89
        // do the clean up by hand because Sqlite can be tricky with this.
90
        $budgetLimits = BudgetLimit::orderBy('created_at', 'DESC')->get(['id', 'budget_id', 'start_date', 'end_date']);
91
        $count        = [];
92
        /** @var BudgetLimit $budgetLimit */
93
        foreach ($budgetLimits as $budgetLimit) {
94
            $key = $budgetLimit->budget_id . '-' . $budgetLimit->start_date->format('Y-m-d') . $budgetLimit->end_date->format('Y-m-d');
95
            if (isset($count[$key])) {
96
                // delete it!
97
                BudgetLimit::find($budgetLimit->id)->delete();
98
            }
99
            $count[$key] = true;
100
        }
101
102
        return true;
103
    }
104
105
    /**
106
     * This method collects various info on budgets, used on the budget page and on the index.
107
     *
108
     * @param Collection $budgets
109
     * @param Carbon     $start
110
     * @param Carbon     $end
111
     *
112
     * @return array
113
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
114
     */
115
    public function collectBudgetInformation(Collection $budgets, Carbon $start, Carbon $end): array
116
    {
117
        // get account information
118
        /** @var AccountRepositoryInterface $accountRepository */
119
        $accountRepository = app(AccountRepositoryInterface::class);
120
        $accounts          = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
121
        $defaultCurrency   = app('amount')->getDefaultCurrency();
122
        $return            = [];
123
        /** @var Budget $budget */
124
        foreach ($budgets as $budget) {
125
            $budgetId          = $budget->id;
126
            $return[$budgetId] = [
127
                'spent'      => $this->spentInPeriod(new Collection([$budget]), $accounts, $start, $end),
128
                'budgeted'   => '0',
129
                'currentRep' => false,
130
            ];
131
            $budgetLimits      = $this->getBudgetLimits($budget, $start, $end);
132
            $otherLimits       = new Collection;
133
134
            // get all the budget limits relevant between start and end and examine them:
135
            /** @var BudgetLimit $limit */
136
            foreach ($budgetLimits as $limit) {
137
                if ($limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end)
138
                ) {
139
                    $return[$budgetId]['currentLimit'] = $limit;
140
                    $return[$budgetId]['budgeted']     = round($limit->amount, $defaultCurrency->decimal_places);
141
                    continue;
142
                }
143
                // otherwise it's just one of the many relevant repetitions:
144
                $otherLimits->push($limit);
145
            }
146
            $return[$budgetId]['otherLimits'] = $otherLimits;
147
        }
148
149
        return $return;
150
    }
151
152
    /**
153
     * @param Budget $budget
154
     *
155
     * @return bool
156
     *
157
     * @throws \Exception
158
     */
159
    public function destroy(Budget $budget): bool
160
    {
161
        $budget->delete();
162
163
        return true;
164
    }
165
166
    /**
167
     * Filters entries from the result set generated by getBudgetPeriodReport.
168
     *
169
     * @param Collection $set
170
     * @param int        $budgetId
171
     * @param array      $periods
172
     *
173
     * @return array
174
     */
175
    public function filterAmounts(Collection $set, int $budgetId, array $periods): array
176
    {
177
        $arr  = [];
178
        $keys = array_keys($periods);
179
        foreach ($keys as $period) {
180
            /** @var stdClass $object */
181
            $result = $set->filter(
182
                function (TransactionJournal $object) use ($budgetId, $period) {
183
                    $result = strval($object->period_marker) === strval($period) && $budgetId === intval($object->budget_id);
0 ignored issues
show
The property budget_id does not exist on FireflyIII\Models\TransactionJournal. Did you mean budgets?
Loading history...
The property period_marker does not seem to exist on FireflyIII\Models\TransactionJournal. 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...
184
185
                    return $result;
186
                }
187
            );
188
            $amount = '0';
189
            if (null !== $result->first()) {
190
                $amount = $result->first()->sum_of_period;
191
            }
192
193
            $arr[$period] = $amount;
194
        }
195
196
        return $arr;
197
    }
198
199
    /**
200
     * Find a budget.
201
     *
202
     * @param int $budgetId
203
     *
204
     * @return Budget
205
     */
206
    public function find(int $budgetId): Budget
207
    {
208
        $budget = $this->user->budgets()->find($budgetId);
209
        if (null === $budget) {
210
            $budget = new Budget;
211
        }
212
213
        return $budget;
214
    }
215
216
    /**
217
     * Find a budget.
218
     *
219
     * @param string $name
220
     *
221
     * @return Budget|null
222
     */
223
    public function findByName(string $name): ?Budget
224
    {
225
        $budgets = $this->user->budgets()->get(['budgets.*']);
226
        /** @var Budget $budget */
227
        foreach ($budgets as $budget) {
228
            if ($budget->name === $name) {
229
                return $budget;
230
            }
231
        }
232
233
        return null;
234
    }
235
236
    /**
237
     * Find a budget or return NULL
238
     *
239
     * @param int $budgetId
240
     *
241
     * @return Budget|null
242
     */
243
    public function findNull(int $budgetId): ?Budget
244
    {
245
        return $this->user->budgets()->find($budgetId);
246
    }
247
248
    /**
249
     * This method returns the oldest journal or transaction date known to this budget.
250
     * Will cache result.
251
     *
252
     * @param Budget $budget
253
     *
254
     * @return Carbon
255
     * @throws \InvalidArgumentException
256
     */
257
    public function firstUseDate(Budget $budget): Carbon
258
    {
259
        $oldest  = Carbon::create()->startOfYear();
260
        $journal = $budget->transactionJournals()->orderBy('date', 'ASC')->first();
261
        if (null !== $journal) {
262
            $oldest = $journal->date < $oldest ? $journal->date : $oldest;
263
        }
264
265
        $transaction = $budget
266
            ->transactions()
267
            ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.id')
268
            ->orderBy('transaction_journals.date', 'ASC')->first(['transactions.*', 'transaction_journals.date']);
269
        if (null !== $transaction) {
270
            $carbon = new Carbon($transaction->date);
271
            $oldest = $carbon < $oldest ? $carbon : $oldest;
272
        }
273
274
        return $oldest;
275
    }
276
277
    /**
278
     * @return Collection
279
     */
280
    public function getActiveBudgets(): Collection
281
    {
282
        /** @var Collection $set */
283
        $set = $this->user->budgets()->where('active', 1)->get();
284
285
        $set = $set->sortBy(
286
            function (Budget $budget) {
287
                return strtolower($budget->name);
288
            }
289
        );
290
291
        return $set;
292
    }
293
294
    /**
295
     * @param Carbon $start
296
     * @param Carbon $end
297
     *
298
     * @return Collection
299
     */
300
    public function getAllBudgetLimits(Carbon $start, Carbon $end): Collection
301
    {
302
        $set = BudgetLimit::leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id')
303
                          ->with(['budget'])
304
                          ->where('budgets.user_id', $this->user->id)
305
                          ->where(
306
                              function (Builder $q5) use ($start, $end) {
307
                                  $q5->where(
308
                                      function (Builder $q1) use ($start, $end) {
309
                                          $q1->where(
310
                                              function (Builder $q2) use ($start, $end) {
311
                                                  $q2->where('budget_limits.end_date', '>=', $start->format('Y-m-d 00:00:00'));
312
                                                  $q2->where('budget_limits.end_date', '<=', $end->format('Y-m-d 00:00:00'));
313
                                              }
314
                                          )
315
                                             ->orWhere(
316
                                                 function (Builder $q3) use ($start, $end) {
317
                                                     $q3->where('budget_limits.start_date', '>=', $start->format('Y-m-d 00:00:00'));
318
                                                     $q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d 00:00:00'));
319
                                                 }
320
                                             );
321
                                      }
322
                                  )
323
                                     ->orWhere(
324
                                         function (Builder $q4) use ($start, $end) {
325
                                             // or start is before start AND end is after end.
326
                                             $q4->where('budget_limits.start_date', '<=', $start->format('Y-m-d 00:00:00'));
327
                                             $q4->where('budget_limits.end_date', '>=', $end->format('Y-m-d 00:00:00'));
328
                                         }
329
                                     );
330
                              }
331
                          )->get(['budget_limits.*']);
332
333
        return $set;
334
    }
335
336
    /**
337
     * @param TransactionCurrency $currency
338
     * @param Carbon              $start
339
     * @param Carbon              $end
340
     *
341
     * @return string
342
     */
343
    public function getAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end): string
344
    {
345
        $amount          = '0';
346
        $availableBudget = $this->user->availableBudgets()
347
                                      ->where('transaction_currency_id', $currency->id)
348
                                      ->where('start_date', $start->format('Y-m-d 00:00:00'))
349
                                      ->where('end_date', $end->format('Y-m-d 00:00:00'))->first();
350
        if (null !== $availableBudget) {
351
            $amount = strval($availableBudget->amount);
352
        }
353
354
        return $amount;
355
    }
356
357
    /**
358
     * @param Budget $budget
359
     * @param Carbon $start
360
     * @param Carbon $end
361
     *
362
     * @return Collection
363
     */
364
    public function getBudgetLimits(Budget $budget, Carbon $start, Carbon $end): Collection
365
    {
366
        $set = $budget->budgetlimits()
367
                      ->where(
368
                          function (Builder $q5) use ($start, $end) {
369
                              $q5->where(
370
                                  function (Builder $q1) use ($start, $end) {
371
                                      // budget limit ends within period
372
                                      $q1->where(
373
                                          function (Builder $q2) use ($start, $end) {
374
                                              $q2->where('budget_limits.end_date', '>=', $start->format('Y-m-d 00:00:00'));
375
                                              $q2->where('budget_limits.end_date', '<=', $end->format('Y-m-d 00:00:00'));
376
                                          }
377
                                      )
378
                                          // budget limit start within period
379
                                         ->orWhere(
380
                                              function (Builder $q3) use ($start, $end) {
381
                                                  $q3->where('budget_limits.start_date', '>=', $start->format('Y-m-d 00:00:00'));
382
                                                  $q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d 00:00:00'));
383
                                              }
384
                                          );
385
                                  }
386
                              )
387
                                 ->orWhere(
388
                                     function (Builder $q4) use ($start, $end) {
389
                                         // or start is before start AND end is after end.
390
                                         $q4->where('budget_limits.start_date', '<=', $start->format('Y-m-d 00:00:00'));
391
                                         $q4->where('budget_limits.end_date', '>=', $end->format('Y-m-d 00:00:00'));
392
                                     }
393
                                 );
394
                          }
395
                      )->orderBy('budget_limits.start_date', 'DESC')->get(['budget_limits.*']);
396
397
        return $set;
398
    }
399
400
    /**
401
     * This method is being used to generate the budget overview in the year/multi-year report. Its used
402
     * in both the year/multi-year budget overview AND in the accompanying chart.
403
     *
404
     * @param Collection $budgets
405
     * @param Collection $accounts
406
     * @param Carbon     $start
407
     * @param Carbon     $end
408
     *
409
     * @return array
410
     */
411
    public function getBudgetPeriodReport(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): array
412
    {
413
        $carbonFormat = Navigation::preferredCarbonFormat($start, $end);
414
        $data         = [];
415
        // prep data array:
416
        /** @var Budget $budget */
417
        foreach ($budgets as $budget) {
418
            $data[$budget->id] = [
419
                'name'    => $budget->name,
420
                'sum'     => '0',
421
                'entries' => [],
422
            ];
423
        }
424
425
        // get all transactions:
426
        /** @var JournalCollectorInterface $collector */
427
        $collector = app(JournalCollectorInterface::class);
428
        $collector->setAccounts($accounts)->setRange($start, $end);
429
        $collector->setBudgets($budgets);
430
        $transactions = $collector->getJournals();
431
432
        // loop transactions:
433
        /** @var Transaction $transaction */
434
        foreach ($transactions as $transaction) {
435
            $budgetId                          = max(intval($transaction->transaction_journal_budget_id), intval($transaction->transaction_budget_id));
436
            $date                              = $transaction->date->format($carbonFormat);
437
            $data[$budgetId]['entries'][$date] = bcadd($data[$budgetId]['entries'][$date] ?? '0', $transaction->transaction_amount);
438
        }
439
440
        return $data;
441
    }
442
443
    /**
444
     * @return Collection
445
     */
446
    public function getBudgets(): Collection
447
    {
448
        /** @var Collection $set */
449
        $set = $this->user->budgets()->get();
450
451
        $set = $set->sortBy(
452
            function (Budget $budget) {
453
                return strtolower($budget->name);
454
            }
455
        );
456
457
        return $set;
458
    }
459
460
    /**
461
     * @return Collection
462
     */
463
    public function getInactiveBudgets(): Collection
464
    {
465
        /** @var Collection $set */
466
        $set = $this->user->budgets()->where('active', 0)->get();
467
468
        $set = $set->sortBy(
469
            function (Budget $budget) {
470
                return strtolower($budget->name);
471
            }
472
        );
473
474
        return $set;
475
    }
476
477
    /**
478
     * @param Collection $accounts
479
     * @param Carbon     $start
480
     * @param Carbon     $end
481
     *
482
     * @return array
483
     */
484
    public function getNoBudgetPeriodReport(Collection $accounts, Carbon $start, Carbon $end): array
485
    {
486
        $carbonFormat = Navigation::preferredCarbonFormat($start, $end);
487
        /** @var JournalCollectorInterface $collector */
488
        $collector = app(JournalCollectorInterface::class);
489
        $collector->setAccounts($accounts)->setRange($start, $end);
490
        $collector->setTypes([TransactionType::WITHDRAWAL]);
491
        $collector->withoutBudget();
492
        $transactions = $collector->getJournals();
493
        $result       = [
494
            'entries' => [],
495
            'name'    => strval(trans('firefly.no_budget')),
496
            'sum'     => '0',
497
        ];
498
499
        foreach ($transactions as $transaction) {
500
            $date = $transaction->date->format($carbonFormat);
501
502
            if (!isset($result['entries'][$date])) {
503
                $result['entries'][$date] = '0';
504
            }
505
            $result['entries'][$date] = bcadd($result['entries'][$date], $transaction->transaction_amount);
506
        }
507
508
        return $result;
509
    }
510
511
    /**
512
     * @param TransactionCurrency $currency
513
     * @param Carbon              $start
514
     * @param Carbon              $end
515
     * @param string              $amount
516
     *
517
     * @return bool
518
     */
519
    public function setAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end, string $amount): bool
520
    {
521
        $availableBudget = $this->user->availableBudgets()
522
                                      ->where('transaction_currency_id', $currency->id)
523
                                      ->where('start_date', $start->format('Y-m-d 00:00:00'))
524
                                      ->where('end_date', $end->format('Y-m-d 00:00:00'))->first();
525
        if (null === $availableBudget) {
526
            $availableBudget = new AvailableBudget;
527
            $availableBudget->user()->associate($this->user);
528
            $availableBudget->transactionCurrency()->associate($currency);
529
            $availableBudget->start_date = $start->format('Y-m-d 00:00:00');
530
            $availableBudget->end_date   = $end->format('Y-m-d 00:00:00');
531
        }
532
        $availableBudget->amount = $amount;
533
        $availableBudget->save();
534
535
        return true;
536
    }
537
538
    /**
539
     * @param User $user
540
     */
541
    public function setUser(User $user)
542
    {
543
        $this->user = $user;
544
    }
545
546
    /**
547
     * @param Collection $budgets
548
     * @param Collection $accounts
549
     * @param Carbon     $start
550
     * @param Carbon     $end
551
     *
552
     * @return string
553
     */
554
    public function spentInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): string
555
    {
556
        /** @var JournalCollectorInterface $collector */
557
        $collector = app(JournalCollectorInterface::class);
558
        $collector->setUser($this->user);
559
        $collector->setRange($start, $end)->setBudgets($budgets)->withBudgetInformation();
560
561
        if ($accounts->count() > 0) {
562
            $collector->setAccounts($accounts);
563
        }
564
        if (0 === $accounts->count()) {
565
            $collector->setAllAssetAccounts();
566
        }
567
568
        $set = $collector->getJournals();
569
        $sum = strval($set->sum('transaction_amount'));
570
571
        return $sum;
572
    }
573
574
    /**
575
     * @param Collection $accounts
576
     * @param Carbon     $start
577
     * @param Carbon     $end
578
     *
579
     * @return string
580
     */
581
    public function spentInPeriodWoBudget(Collection $accounts, Carbon $start, Carbon $end): string
582
    {
583
        /** @var JournalCollectorInterface $collector */
584
        $collector = app(JournalCollectorInterface::class);
585
        $collector->setUser($this->user);
586
        $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->withoutBudget();
587
588
        if ($accounts->count() > 0) {
589
            $collector->setAccounts($accounts);
590
        }
591
        if (0 === $accounts->count()) {
592
            $collector->setAllAssetAccounts();
593
        }
594
595
        $set = $collector->getJournals();
596
        $set = $set->filter(
597
            function (Transaction $transaction) {
598
                if (bccomp($transaction->transaction_amount, '0') === -1) {
599
                    return $transaction;
600
                }
601
602
                return null;
603
            }
604
        );
605
606
        $sum = strval($set->sum('transaction_amount'));
607
608
        return $sum;
609
    }
610
611
    /**
612
     * @param array $data
613
     *
614
     * @return Budget
615
     */
616
    public function store(array $data): Budget
617
    {
618
        $newBudget = new Budget(
619
            [
620
                'user_id' => $this->user->id,
621
                'name'    => $data['name'],
622
            ]
623
        );
624
        $newBudget->save();
625
626
        return $newBudget;
627
    }
628
629
    /**
630
     * @param Budget $budget
631
     * @param array  $data
632
     *
633
     * @return Budget
634
     */
635
    public function update(Budget $budget, array $data): Budget
636
    {
637
        // update the account:
638
        $budget->name   = $data['name'];
639
        $budget->active = $data['active'];
640
        $budget->save();
641
642
        return $budget;
643
    }
644
645
    /**
646
     * @param Budget $budget
647
     * @param Carbon $start
648
     * @param Carbon $end
649
     * @param string $amount
650
     *
651
     * @return BudgetLimit
652
     *
653
     * @throws \Exception
654
     */
655
    public function updateLimitAmount(Budget $budget, Carbon $start, Carbon $end, string $amount): BudgetLimit
656
    {
657
        $this->cleanupBudgets();
658
        // count the limits:
659
        $limits = $budget->budgetlimits()
660
                         ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00'))
661
                         ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00'))
662
                         ->get(['budget_limits.*'])->count();
663
        Log::debug(sprintf('Found %d budget limits.', $limits));
664
        // there might be a budget limit for these dates:
665
        /** @var BudgetLimit $limit */
666
        $limit = $budget->budgetlimits()
667
                        ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00'))
668
                        ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00'))
669
                        ->first(['budget_limits.*']);
670
671
        // if more than 1 limit found, delete the others:
672
        if ($limits > 1 && null !== $limit) {
673
            Log::debug(sprintf('Found more than 1, delete all except #%d', $limit->id));
674
            $budget->budgetlimits()
675
                   ->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00'))
676
                   ->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00'))
677
                   ->where('budget_limits.id', '!=', $limit->id)->delete();
678
        }
679
680
        // delete if amount is zero.
681
        // Returns 0 if the two operands are equal,
682
        // 1 if the left_operand is larger than the right_operand, -1 otherwise.
683
        if (null !== $limit && bccomp($amount, '0') <= 0) {
684
            Log::debug(sprintf('%s is zero, delete budget limit #%d', $amount, $limit->id));
685
            $limit->delete();
686
687
            return new BudgetLimit;
688
        }
689
        // update if exists:
690
        if (null !== $limit) {
691
            Log::debug(sprintf('Existing budget limit is #%d, update this to amount %s', $limit->id, $amount));
692
            $limit->amount = $amount;
693
            $limit->save();
694
695
            return $limit;
696
        }
697
        Log::debug('No existing budget limit, create a new one');
698
        // or create one and return it.
699
        $limit = new BudgetLimit;
700
        $limit->budget()->associate($budget);
701
        $limit->start_date = $start->format('Y-m-d 00:00:00');
702
        $limit->end_date   = $end->format('Y-m-d 00:00:00');
703
        $limit->amount     = $amount;
704
        $limit->save();
705
        Log::debug(sprintf('Created new budget limit with ID #%d and amount %s', $limit->id, $amount));
706
707
        return $limit;
708
    }
709
}
710