mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Better management and calculation of available budgets.
This commit is contained in:
parent
1c7988fad2
commit
28efc15820
@ -88,8 +88,8 @@ abstract class Controller extends BaseController
|
|||||||
if ($page < 1) {
|
if ($page < 1) {
|
||||||
$page = 1;
|
$page = 1;
|
||||||
}
|
}
|
||||||
if ($page > pow(2,16)) {
|
if ($page > pow(2, 16)) {
|
||||||
$page = pow(2,16);
|
$page = pow(2, 16);
|
||||||
}
|
}
|
||||||
$bag->set('page', $page);
|
$bag->set('page', $page);
|
||||||
|
|
||||||
|
46
app/Events/Model/BudgetLimit/Created.php
Normal file
46
app/Events/Model/BudgetLimit/Created.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/*
|
||||||
|
* Created.php
|
||||||
|
* Copyright (c) 2023 james@firefly-iii.org
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FireflyIII\Events\Model\BudgetLimit;
|
||||||
|
|
||||||
|
use FireflyIII\Events\Event;
|
||||||
|
use FireflyIII\Models\BudgetLimit;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Created
|
||||||
|
*/
|
||||||
|
class Created extends Event
|
||||||
|
{
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
public BudgetLimit $budgetLimit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param BudgetLimit $budgetLimit
|
||||||
|
*/
|
||||||
|
public function __construct(BudgetLimit $budgetLimit)
|
||||||
|
{
|
||||||
|
$this->budgetLimit = $budgetLimit;
|
||||||
|
}
|
||||||
|
}
|
46
app/Events/Model/BudgetLimit/Deleted.php
Normal file
46
app/Events/Model/BudgetLimit/Deleted.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/*
|
||||||
|
* Created.php
|
||||||
|
* Copyright (c) 2023 james@firefly-iii.org
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FireflyIII\Events\Model\BudgetLimit;
|
||||||
|
|
||||||
|
use FireflyIII\Events\Event;
|
||||||
|
use FireflyIII\Models\BudgetLimit;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Deleted
|
||||||
|
*/
|
||||||
|
class Deleted extends Event
|
||||||
|
{
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
public BudgetLimit $budgetLimit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param BudgetLimit $budgetLimit
|
||||||
|
*/
|
||||||
|
public function __construct(BudgetLimit $budgetLimit)
|
||||||
|
{
|
||||||
|
$this->budgetLimit = $budgetLimit;
|
||||||
|
}
|
||||||
|
}
|
46
app/Events/Model/BudgetLimit/Updated.php
Normal file
46
app/Events/Model/BudgetLimit/Updated.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/*
|
||||||
|
* Created.php
|
||||||
|
* Copyright (c) 2023 james@firefly-iii.org
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FireflyIII\Events\Model\BudgetLimit;
|
||||||
|
|
||||||
|
use FireflyIII\Events\Event;
|
||||||
|
use FireflyIII\Models\BudgetLimit;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Updated
|
||||||
|
*/
|
||||||
|
class Updated extends Event
|
||||||
|
{
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
public BudgetLimit $budgetLimit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param BudgetLimit $budgetLimit
|
||||||
|
*/
|
||||||
|
public function __construct(BudgetLimit $budgetLimit)
|
||||||
|
{
|
||||||
|
$this->budgetLimit = $budgetLimit;
|
||||||
|
}
|
||||||
|
}
|
221
app/Handlers/Events/Model/BudgetLimitHandler.php
Normal file
221
app/Handlers/Events/Model/BudgetLimitHandler.php
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
/*
|
||||||
|
* BudgetLimitHandler.php
|
||||||
|
* Copyright (c) 2023 james@firefly-iii.org
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FireflyIII\Handlers\Events\Model;
|
||||||
|
|
||||||
|
use FireflyIII\Events\Model\BudgetLimit\Created;
|
||||||
|
use FireflyIII\Events\Model\BudgetLimit\Deleted;
|
||||||
|
use FireflyIII\Events\Model\BudgetLimit\Updated;
|
||||||
|
use FireflyIII\Models\AvailableBudget;
|
||||||
|
use FireflyIII\Models\BudgetLimit;
|
||||||
|
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Spatie\Period\Boundaries;
|
||||||
|
use Spatie\Period\Period;
|
||||||
|
use Spatie\Period\Precision;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class BudgetLimitHandler
|
||||||
|
*/
|
||||||
|
class BudgetLimitHandler
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param Created $event
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function created(Created $event): void
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('BudgetLimitHandler::created(%s)', $event->budgetLimit->id));
|
||||||
|
$this->updateAvailableBudget($event->budgetLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Updated $event
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function updated(Updated $event): void
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('BudgetLimitHandler::updated(%s)', $event->budgetLimit->id));
|
||||||
|
$this->updateAvailableBudget($event->budgetLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Deleted $event
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function deleted(Deleted $event): void
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('BudgetLimitHandler::deleted(%s)', $event->budgetLimit->id));
|
||||||
|
$this->updateAvailableBudget($event->budgetLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param AvailableBudget $availableBudget
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function calculateAmount(AvailableBudget $availableBudget): void
|
||||||
|
{
|
||||||
|
$repository = app(BudgetLimitRepositoryInterface::class);
|
||||||
|
$repository->setUser($availableBudget->user);
|
||||||
|
$newAmount = '0';
|
||||||
|
$abPeriod = Period::make($availableBudget->start_date, $availableBudget->end_date, Precision::DAY());
|
||||||
|
Log::debug(
|
||||||
|
sprintf(
|
||||||
|
'Now at AB #%d, ("%s" to "%s")',
|
||||||
|
$availableBudget->id,
|
||||||
|
$availableBudget->start_date->format('Y-m-d'),
|
||||||
|
$availableBudget->end_date->format('Y-m-d')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
// have to recalc everything just in case.
|
||||||
|
$set = $repository->getAllBudgetLimitsByCurrency($availableBudget->transactionCurrency, $availableBudget->start_date, $availableBudget->end_date);
|
||||||
|
/** @var BudgetLimit $budgetLimit */
|
||||||
|
foreach ($set as $budgetLimit) {
|
||||||
|
Log::debug(
|
||||||
|
sprintf(
|
||||||
|
'Found interesting budget limit #%d ("%s" to "%s")',
|
||||||
|
$budgetLimit->id,
|
||||||
|
$budgetLimit->start_date->format('Y-m-d'),
|
||||||
|
$budgetLimit->end_date->format('Y-m-d')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
// overlap in days:
|
||||||
|
$limitPeriod = Period::make(
|
||||||
|
$budgetLimit->start_date,
|
||||||
|
$budgetLimit->end_date,
|
||||||
|
precision: Precision::DAY(),
|
||||||
|
boundaries: Boundaries::EXCLUDE_NONE()
|
||||||
|
);
|
||||||
|
// if both equal eachother, amount from this BL must be added to the AB
|
||||||
|
if ($limitPeriod->equals($abPeriod)) {
|
||||||
|
Log::debug('Limit period is the same as AB period.');
|
||||||
|
$newAmount = bcadd($newAmount, $budgetLimit->amount);
|
||||||
|
}
|
||||||
|
// if budget limit period inside AB period, can be added in full.
|
||||||
|
|
||||||
|
if (!$limitPeriod->equals($abPeriod) && $abPeriod->contains($limitPeriod)) {
|
||||||
|
Log::debug('Limit period falls inside of AB period.');
|
||||||
|
$newAmount = bcadd($newAmount, $budgetLimit->amount);
|
||||||
|
}
|
||||||
|
if (!$limitPeriod->equals($abPeriod) && $abPeriod->overlapsWith($limitPeriod)) {
|
||||||
|
Log::debug('Limit period overlaps AB period.');
|
||||||
|
$overlap = $abPeriod->overlap($limitPeriod);
|
||||||
|
if (null !== $overlap) {
|
||||||
|
$length = $overlap->length();
|
||||||
|
$daily = bcmul($this->getDailyAmount($budgetLimit), (string)$length);
|
||||||
|
Log::debug(sprintf('Length of overlap is %d days, so daily amount is %s', $length, $daily));
|
||||||
|
$newAmount = bcadd($newAmount, $daily);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log::debug(sprintf('Concluded new amount for this AB must be %s', $newAmount));
|
||||||
|
$availableBudget->amount = $newAmount;
|
||||||
|
$availableBudget->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param BudgetLimit $budgetLimit
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function getDailyAmount(BudgetLimit $budgetLimit): string
|
||||||
|
{
|
||||||
|
$limitPeriod = Period::make(
|
||||||
|
$budgetLimit->start_date,
|
||||||
|
$budgetLimit->end_date,
|
||||||
|
precision: Precision::DAY(),
|
||||||
|
boundaries: Boundaries::EXCLUDE_NONE()
|
||||||
|
);
|
||||||
|
$days = $limitPeriod->length();
|
||||||
|
$amount = bcdiv((string)$budgetLimit->amount, (string)$days, 12);
|
||||||
|
Log::debug(
|
||||||
|
sprintf('Total amount for budget limit #%d is %s. Nr. of days is %d. Amount per day is %s', $budgetLimit->id, $budgetLimit->amount, $days, $amount)
|
||||||
|
);
|
||||||
|
return $amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param BudgetLimit $budgetLimit
|
||||||
|
* @return void
|
||||||
|
* @throws \FireflyIII\Exceptions\FireflyException
|
||||||
|
* @throws \Psr\Container\ContainerExceptionInterface
|
||||||
|
* @throws \Psr\Container\NotFoundExceptionInterface
|
||||||
|
*/
|
||||||
|
private function updateAvailableBudget(BudgetLimit $budgetLimit): void
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('Now in updateAvailableBudget(#%d)', $budgetLimit->id));
|
||||||
|
|
||||||
|
// based on the view range of the user (month week quarter etc) the budget limit could
|
||||||
|
// either overlap multiple available budget periods or be contained in a single one.
|
||||||
|
// all have to be created or updated.
|
||||||
|
$viewRange = app('preferences')->get('viewRange', '1M')->data;
|
||||||
|
$start = app('navigation')->startOfPeriod($budgetLimit->start_date, $viewRange);
|
||||||
|
$end = app('navigation')->startOfPeriod($budgetLimit->end_date, $viewRange);
|
||||||
|
$end = app('navigation')->endOfPeriod($end, $viewRange);
|
||||||
|
$user = $budgetLimit->budget->user;
|
||||||
|
|
||||||
|
// limit period in total is:
|
||||||
|
$limitPeriod = Period::make($start, $end, precision: Precision::DAY(), boundaries: Boundaries::EXCLUDE_NONE());
|
||||||
|
|
||||||
|
// from the start until the end of the budget limit, need to loop!
|
||||||
|
$current = clone $start;
|
||||||
|
while ($current <= $end) {
|
||||||
|
$currentEnd = app('navigation')->endOfPeriod($current, $viewRange);
|
||||||
|
|
||||||
|
// create or find AB for this particular period, and set the amount accordingly.
|
||||||
|
/** @var AvailableBudget $availableBudget */
|
||||||
|
$availableBudget = $user->availableBudgets()->where('start_date', $current->format('Y-m-d'))->where(
|
||||||
|
'end_date',
|
||||||
|
$currentEnd->format('Y-m-d')
|
||||||
|
)->where('transaction_currency_id', $budgetLimit->transaction_currency_id)->first();
|
||||||
|
if (null !== $availableBudget) {
|
||||||
|
Log::debug('Found AB, will update.');
|
||||||
|
$this->calculateAmount($availableBudget);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// if not exists:
|
||||||
|
$currentPeriod = Period::make($current, $currentEnd, precision: Precision::DAY(), boundaries: Boundaries::EXCLUDE_NONE());
|
||||||
|
$daily = $this->getDailyAmount($budgetLimit);
|
||||||
|
$amount = bcmul($daily, (string)$currentPeriod->length(), 12);
|
||||||
|
|
||||||
|
// no need to calculate if period is equal.
|
||||||
|
if ($currentPeriod->equals($limitPeriod)) {
|
||||||
|
$amount = $budgetLimit->amount;
|
||||||
|
}
|
||||||
|
Log::debug(sprintf('Will create AB for period %s to %s', $current->format('Y-m-d'), $currentEnd->format('Y-m-d')));
|
||||||
|
$availableBudget = new AvailableBudget(
|
||||||
|
[
|
||||||
|
'user_id' => $budgetLimit->budget->user->id,
|
||||||
|
'transaction_currency_id' => $budgetLimit->transaction_currency_id,
|
||||||
|
'start_date' => $current,
|
||||||
|
'end_date' => $currentEnd,
|
||||||
|
'amount' => $amount,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$availableBudget->save();
|
||||||
|
|
||||||
|
// prep for next loop
|
||||||
|
$current = app('navigation')->addPeriod($current, $viewRange, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -89,12 +89,13 @@ class BudgetLimitController extends Controller
|
|||||||
$collection = $this->currencyRepos->get();
|
$collection = $this->currencyRepos->get();
|
||||||
$budgetLimits = $this->blRepository->getBudgetLimits($budget, $start, $end);
|
$budgetLimits = $this->blRepository->getBudgetLimits($budget, $start, $end);
|
||||||
|
|
||||||
// remove already budgeted currencies:
|
// remove already budgeted currencies with the same date range
|
||||||
$currencies = $collection->filter(
|
$currencies = $collection->filter(
|
||||||
static function (TransactionCurrency $currency) use ($budgetLimits) {
|
static function (TransactionCurrency $currency) use ($budgetLimits, $start, $end) {
|
||||||
/** @var AvailableBudget $budget */
|
/** @var BudgetLimit $limit */
|
||||||
foreach ($budgetLimits as $budget) {
|
foreach ($budgetLimits as $limit) {
|
||||||
if ($budget->transaction_currency_id === $currency->id) {
|
if ($limit->transaction_currency_id === $currency->id && $limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end)
|
||||||
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,9 +40,9 @@ use Illuminate\Contracts\View\Factory;
|
|||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use JsonException;
|
use JsonException;
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Psr\Container\ContainerExceptionInterface;
|
use Psr\Container\ContainerExceptionInterface;
|
||||||
use Psr\Container\NotFoundExceptionInterface;
|
use Psr\Container\NotFoundExceptionInterface;
|
||||||
|
|
||||||
@ -101,12 +101,23 @@ class IndexController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index(Request $request, Carbon $start = null, Carbon $end = null)
|
public function index(Request $request, Carbon $start = null, Carbon $end = null)
|
||||||
{
|
{
|
||||||
Log::debug('Start of IndexController::index()');
|
Log::debug(sprintf('Start of IndexController::index("%s", "%s")', $start?->format('Y-m-d'), $end?->format('Y-m-d')));
|
||||||
|
|
||||||
// collect some basic vars:
|
// collect some basic vars:
|
||||||
$range = app('navigation')->getViewRange(true);
|
$range = app('navigation')->getViewRange(true);
|
||||||
$start = $start ?? session('start', today(config('app.timezone'))->startOfMonth());
|
$isCustomRange = session('is_custom_range', false);
|
||||||
$end = $end ?? app('navigation')->endOfPeriod($start, $range);
|
if (false === $isCustomRange) {
|
||||||
|
$start = $start ?? session('start', today(config('app.timezone'))->startOfMonth());
|
||||||
|
$end = $end ?? app('navigation')->endOfPeriod($start, $range);
|
||||||
|
}
|
||||||
|
|
||||||
|
// overrule start and end if necessary:
|
||||||
|
if (true === $isCustomRange) {
|
||||||
|
$start = $start ?? session('start', today(config('app.timezone'))->startOfMonth());
|
||||||
|
$end = $end ?? session('end', today(config('app.timezone'))->endOfMonth());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||||
$currencies = $this->currencyRepository->get();
|
$currencies = $this->currencyRepository->get();
|
||||||
$budgeted = '0';
|
$budgeted = '0';
|
||||||
|
@ -24,6 +24,9 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Models;
|
namespace FireflyIII\Models;
|
||||||
|
|
||||||
use Eloquent;
|
use Eloquent;
|
||||||
|
use FireflyIII\Events\Model\BudgetLimit\Created;
|
||||||
|
use FireflyIII\Events\Model\BudgetLimit\Deleted;
|
||||||
|
use FireflyIII\Events\Model\BudgetLimit\Updated;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
@ -81,6 +84,12 @@ class BudgetLimit extends Model
|
|||||||
/** @var array Fields that can be filled */
|
/** @var array Fields that can be filled */
|
||||||
protected $fillable = ['budget_id', 'start_date', 'end_date', 'amount', 'transaction_currency_id'];
|
protected $fillable = ['budget_id', 'start_date', 'end_date', 'amount', 'transaction_currency_id'];
|
||||||
|
|
||||||
|
protected $dispatchesEvents = [
|
||||||
|
'created' => Created::class,
|
||||||
|
'updated' => Updated::class,
|
||||||
|
'deleted' => Deleted::class,
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Route binder. Converts the key in the URL to the specified object (or throw 404).
|
* Route binder. Converts the key in the URL to the specified object (or throw 404).
|
||||||
*
|
*
|
||||||
|
@ -29,6 +29,9 @@ use FireflyIII\Events\AdminRequestedTestMessage;
|
|||||||
use FireflyIII\Events\ChangedPiggyBankAmount;
|
use FireflyIII\Events\ChangedPiggyBankAmount;
|
||||||
use FireflyIII\Events\DestroyedTransactionGroup;
|
use FireflyIII\Events\DestroyedTransactionGroup;
|
||||||
use FireflyIII\Events\DetectedNewIPAddress;
|
use FireflyIII\Events\DetectedNewIPAddress;
|
||||||
|
use FireflyIII\Events\Model\BudgetLimit\Created;
|
||||||
|
use FireflyIII\Events\Model\BudgetLimit\Deleted;
|
||||||
|
use FireflyIII\Events\Model\BudgetLimit\Updated;
|
||||||
use FireflyIII\Events\NewVersionAvailable;
|
use FireflyIII\Events\NewVersionAvailable;
|
||||||
use FireflyIII\Events\RegisteredUser;
|
use FireflyIII\Events\RegisteredUser;
|
||||||
use FireflyIII\Events\RequestedNewPassword;
|
use FireflyIII\Events\RequestedNewPassword;
|
||||||
@ -42,6 +45,7 @@ use FireflyIII\Events\UpdatedAccount;
|
|||||||
use FireflyIII\Events\UpdatedTransactionGroup;
|
use FireflyIII\Events\UpdatedTransactionGroup;
|
||||||
use FireflyIII\Events\UserChangedEmail;
|
use FireflyIII\Events\UserChangedEmail;
|
||||||
use FireflyIII\Events\WarnUserAboutBill;
|
use FireflyIII\Events\WarnUserAboutBill;
|
||||||
|
use FireflyIII\Models\Budget;
|
||||||
use FireflyIII\Models\BudgetLimit;
|
use FireflyIII\Models\BudgetLimit;
|
||||||
use FireflyIII\Models\PiggyBank;
|
use FireflyIII\Models\PiggyBank;
|
||||||
use FireflyIII\Models\PiggyBankRepetition;
|
use FireflyIII\Models\PiggyBankRepetition;
|
||||||
@ -160,6 +164,17 @@ class EventServiceProvider extends ServiceProvider
|
|||||||
ChangedPiggyBankAmount::class => [
|
ChangedPiggyBankAmount::class => [
|
||||||
'FireflyIII\Handlers\Events\PiggyBankEventHandler@changePiggyAmount',
|
'FireflyIII\Handlers\Events\PiggyBankEventHandler@changePiggyAmount',
|
||||||
],
|
],
|
||||||
|
// budget related events: CRUD budget limit
|
||||||
|
Created::class => [
|
||||||
|
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@created',
|
||||||
|
],
|
||||||
|
Updated::class => [
|
||||||
|
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@updated',
|
||||||
|
],
|
||||||
|
Deleted::class => [
|
||||||
|
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@deleted',
|
||||||
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -169,7 +184,6 @@ class EventServiceProvider extends ServiceProvider
|
|||||||
{
|
{
|
||||||
parent::boot();
|
parent::boot();
|
||||||
$this->registerCreateEvents();
|
$this->registerCreateEvents();
|
||||||
$this->registerBudgetEvents();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -188,57 +202,4 @@ class EventServiceProvider extends ServiceProvider
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO needs a dedicated method.
|
|
||||||
*/
|
|
||||||
protected function registerBudgetEvents(): void
|
|
||||||
{
|
|
||||||
$func = static function (BudgetLimit $limit) {
|
|
||||||
Log::debug('Trigger budget limit event.');
|
|
||||||
// find available budget with same period and same currency or create it.
|
|
||||||
// then set it or add money:
|
|
||||||
$user = $limit->budget->user;
|
|
||||||
$availableBudget = $user
|
|
||||||
->availableBudgets()
|
|
||||||
->where('start_date', $limit->start_date->format('Y-m-d'))
|
|
||||||
->where('end_date', $limit->end_date->format('Y-m-d'))
|
|
||||||
->where('transaction_currency_id', $limit->transaction_currency_id)
|
|
||||||
->first();
|
|
||||||
// update!
|
|
||||||
if (null !== $availableBudget) {
|
|
||||||
$repository = app(BudgetLimitRepositoryInterface::class);
|
|
||||||
$repository->setUser($user);
|
|
||||||
$set = $repository->getAllBudgetLimitsByCurrency($limit->transactionCurrency, $limit->start_date, $limit->end_date);
|
|
||||||
$sum = (string)$set->sum('amount');
|
|
||||||
|
|
||||||
|
|
||||||
Log::debug(
|
|
||||||
sprintf(
|
|
||||||
'Because budget limit #%d had its amount changed to %s, available budget limit #%d will be updated.',
|
|
||||||
$limit->id,
|
|
||||||
$limit->amount,
|
|
||||||
$availableBudget->id
|
|
||||||
)
|
|
||||||
);
|
|
||||||
$availableBudget->amount = $sum;
|
|
||||||
$availableBudget->save();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Log::debug('Does not exist, create it.');
|
|
||||||
// create it.
|
|
||||||
$data = [
|
|
||||||
'amount' => $limit->amount,
|
|
||||||
'start' => $limit->start_date,
|
|
||||||
'end' => $limit->end_date,
|
|
||||||
'currency_id' => $limit->transaction_currency_id,
|
|
||||||
];
|
|
||||||
$repository = app(AvailableBudgetRepositoryInterface::class);
|
|
||||||
$repository->setUser($user);
|
|
||||||
$repository->store($data);
|
|
||||||
};
|
|
||||||
|
|
||||||
BudgetLimit::created($func);
|
|
||||||
BudgetLimit::updated($func);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -104,6 +104,7 @@
|
|||||||
"ramsey/uuid": "^4.7",
|
"ramsey/uuid": "^4.7",
|
||||||
"rcrowe/twigbridge": "^0.14",
|
"rcrowe/twigbridge": "^0.14",
|
||||||
"spatie/laravel-ignition": "^2",
|
"spatie/laravel-ignition": "^2",
|
||||||
|
"spatie/period": "^2.4",
|
||||||
"symfony/http-client": "^6.0",
|
"symfony/http-client": "^6.0",
|
||||||
"symfony/mailgun-mailer": "^6.0",
|
"symfony/mailgun-mailer": "^6.0",
|
||||||
"therobfonz/laravel-mandrill-driver": "^5.0"
|
"therobfonz/laravel-mandrill-driver": "^5.0"
|
||||||
|
56
composer.lock
generated
56
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "7578458e9e378acae56c08bbd053f38b",
|
"content-hash": "3d09d838fdf529c07df3563a3d96de5c",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "bacon/bacon-qr-code",
|
"name": "bacon/bacon-qr-code",
|
||||||
@ -5806,6 +5806,60 @@
|
|||||||
],
|
],
|
||||||
"time": "2023-04-12T09:26:00+00:00"
|
"time": "2023-04-12T09:26:00+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "spatie/period",
|
||||||
|
"version": "2.4.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/spatie/period.git",
|
||||||
|
"reference": "85fbbea7b24fdff0c924aeed5b109be93c025850"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/spatie/period/zipball/85fbbea7b24fdff0c924aeed5b109be93c025850",
|
||||||
|
"reference": "85fbbea7b24fdff0c924aeed5b109be93c025850",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"larapack/dd": "^1.1",
|
||||||
|
"nesbot/carbon": "^2.63",
|
||||||
|
"pestphp/pest": "^1.22",
|
||||||
|
"phpunit/phpunit": "^9.5",
|
||||||
|
"spatie/ray": "^1.31"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Spatie\\Period\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Brent Roose",
|
||||||
|
"email": "brent@spatie.be",
|
||||||
|
"homepage": "https://spatie.be",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Complex period comparisons",
|
||||||
|
"homepage": "https://github.com/spatie/period",
|
||||||
|
"keywords": [
|
||||||
|
"period",
|
||||||
|
"spatie"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/spatie/period/issues",
|
||||||
|
"source": "https://github.com/spatie/period/tree/2.4.0"
|
||||||
|
},
|
||||||
|
"time": "2023-02-20T14:31:09+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/console",
|
"name": "symfony/console",
|
||||||
"version": "v6.2.8",
|
"version": "v6.2.8",
|
||||||
|
@ -261,6 +261,7 @@
|
|||||||
{% if not budgetLimit.in_range %}
|
{% if not budgetLimit.in_range %}
|
||||||
<small class="text-muted">
|
<small class="text-muted">
|
||||||
{{ trans('firefly.budget_limit_not_in_range', {start: budgetLimit.start_date, end: budgetLimit.end_date}) }}
|
{{ trans('firefly.budget_limit_not_in_range', {start: budgetLimit.start_date, end: budgetLimit.end_date}) }}
|
||||||
|
|
||||||
</small><br>
|
</small><br>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="input-group bl_entry" data-budget-limit-id="{{ budgetLimit.id }}">
|
<div class="input-group bl_entry" data-budget-limit-id="{{ budgetLimit.id }}">
|
||||||
|
Loading…
Reference in New Issue
Block a user