diff --git a/app/Api/V1/Controllers/Search/TransactionController.php b/app/Api/V1/Controllers/Search/TransactionController.php
index 2a157532f9..f4fbe86782 100644
--- a/app/Api/V1/Controllers/Search/TransactionController.php
+++ b/app/Api/V1/Controllers/Search/TransactionController.php
@@ -71,6 +71,8 @@ class TransactionController extends Controller
$resource = new Collection($transactions, $transformer, 'transactions');
$resource->setPaginator(new IlluminatePaginatorAdapter($groups));
- return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
+ $array = $manager->createData($resource)->toArray();
+
+ return response()->json($array)->header('Content-Type', self::CONTENT_TYPE);
}
}
diff --git a/app/Api/V1/Controllers/System/CronController.php b/app/Api/V1/Controllers/System/CronController.php
index e944a80c4e..8faa85a521 100644
--- a/app/Api/V1/Controllers/System/CronController.php
+++ b/app/Api/V1/Controllers/System/CronController.php
@@ -54,6 +54,10 @@ class CronController extends Controller
$return = [];
$return['recurring_transactions'] = $this->runRecurring($config['force'], $config['date']);
$return['auto_budgets'] = $this->runAutoBudget($config['force'], $config['date']);
+ if (true === config('cer.enabled')) {
+ $return['exchange_rates'] = $this->exchangeRatesCronJob($config['force'], $config['date']);
+ }
+ $return['bill_warnings'] = $this->billWarningCronJob($config['force'], $config['date']);
return response()->json($return);
}
diff --git a/app/Api/V1/Requests/Models/Budget/StoreRequest.php b/app/Api/V1/Requests/Models/Budget/StoreRequest.php
index 4ab222f327..d53e144fe9 100644
--- a/app/Api/V1/Requests/Models/Budget/StoreRequest.php
+++ b/app/Api/V1/Requests/Models/Budget/StoreRequest.php
@@ -79,9 +79,9 @@ class StoreRequest extends FormRequest
'currency_code' => 'exists:transaction_currencies,code',
'notes' => 'nullable|between:1,65536',
// auto budget info
- 'auto_budget_type' => 'in:reset,rollover,none',
- 'auto_budget_amount' => 'numeric|min:0|max:1000000000|required_if:auto_budget_type,reset|required_if:auto_budget_type,rollover',
- 'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly|required_if:auto_budget_type,reset|required_if:auto_budget_type,rollover',
+ 'auto_budget_type' => 'in:reset,rollover,adjusted,none',
+ 'auto_budget_amount' => 'numeric|min:0|max:1000000000|required_if:auto_budget_type,reset|required_if:auto_budget_type,rollover|required_if:auto_budget_type,adjusted',
+ 'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly|required_if:auto_budget_type,reset|required_if:auto_budget_type,rollover|required_if:auto_budget_type,adjusted',
];
}
diff --git a/app/Api/V1/Requests/Models/Budget/UpdateRequest.php b/app/Api/V1/Requests/Models/Budget/UpdateRequest.php
index ea584488ee..f4de11b76b 100644
--- a/app/Api/V1/Requests/Models/Budget/UpdateRequest.php
+++ b/app/Api/V1/Requests/Models/Budget/UpdateRequest.php
@@ -67,6 +67,7 @@ class UpdateRequest extends FormRequest
'none' => 0,
'reset' => 1,
'rollover' => 2,
+ 'adjusted' => 3,
];
$allData['auto_budget_type'] = $types[$allData['auto_budget_type']] ?? 0;
}
@@ -88,7 +89,7 @@ class UpdateRequest extends FormRequest
'name' => sprintf('between:1,100|uniqueObjectForUser:budgets,name,%d', $budget->id),
'active' => [new IsBoolean()],
'notes' => 'nullable|between:1,65536',
- 'auto_budget_type' => 'in:reset,rollover,none',
+ 'auto_budget_type' => 'in:reset,rollover,adjusted,none',
'auto_budget_currency_id' => 'exists:transaction_currencies,id',
'auto_budget_currency_code' => 'exists:transaction_currencies,code',
'auto_budget_amount' => 'min:0|max:1000000000',
diff --git a/app/Console/Commands/Tools/Cron.php b/app/Console/Commands/Tools/Cron.php
index 83c9d1f222..0ff0874a63 100644
--- a/app/Console/Commands/Tools/Cron.php
+++ b/app/Console/Commands/Tools/Cron.php
@@ -77,7 +77,7 @@ class Cron extends Command
*/
if (true === config('cer.enabled')) {
try {
- $this->exchangeRatesCronJob($force, $date);
+ //$this->exchangeRatesCronJob($force, $date);
} catch (FireflyException $e) {
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
@@ -89,7 +89,7 @@ class Cron extends Command
* Fire recurring transaction cron job.
*/
try {
- $this->recurringCronJob($force, $date);
+ //$this->recurringCronJob($force, $date);
} catch (FireflyException $e) {
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
@@ -111,7 +111,7 @@ class Cron extends Command
* Fire bill warning cron job
*/
try {
- $this->billWarningCronJob($force, $date);
+ //$this->billWarningCronJob($force, $date);
} catch (FireflyException $e) {
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
diff --git a/app/Enums/AutoBudgetType.php b/app/Enums/AutoBudgetType.php
index a9d8719021..ac8341a212 100644
--- a/app/Enums/AutoBudgetType.php
+++ b/app/Enums/AutoBudgetType.php
@@ -31,4 +31,5 @@ enum AutoBudgetType: int
{
case AUTO_BUDGET_RESET = 1;
case AUTO_BUDGET_ROLLOVER = 2;
+ case AUTO_BUDGET_ADJUSTED = 3;
}
diff --git a/app/Http/Controllers/Budget/CreateController.php b/app/Http/Controllers/Budget/CreateController.php
index 0ae5c47199..8de40a769d 100644
--- a/app/Http/Controllers/Budget/CreateController.php
+++ b/app/Http/Controllers/Budget/CreateController.php
@@ -78,6 +78,7 @@ class CreateController extends Controller
0 => (string)trans('firefly.auto_budget_none'),
AutoBudget::AUTO_BUDGET_RESET => (string)trans('firefly.auto_budget_reset'),
AutoBudget::AUTO_BUDGET_ROLLOVER => (string)trans('firefly.auto_budget_rollover'),
+ AutoBudget::AUTO_BUDGET_ADJUSTED => (string)trans('firefly.auto_budget_adjusted'),
];
$autoBudgetPeriods = [
'daily' => (string)trans('firefly.auto_budget_period_daily'),
diff --git a/app/Http/Controllers/Budget/EditController.php b/app/Http/Controllers/Budget/EditController.php
index dd2847d82c..e35b1e30fc 100644
--- a/app/Http/Controllers/Budget/EditController.php
+++ b/app/Http/Controllers/Budget/EditController.php
@@ -82,6 +82,7 @@ class EditController extends Controller
0 => (string)trans('firefly.auto_budget_none'),
AutoBudget::AUTO_BUDGET_RESET => (string)trans('firefly.auto_budget_reset'),
AutoBudget::AUTO_BUDGET_ROLLOVER => (string)trans('firefly.auto_budget_rollover'),
+ AutoBudget::AUTO_BUDGET_ADJUSTED => (string)trans('firefly.auto_budget_adjusted'),
];
$autoBudgetPeriods = [
'daily' => (string)trans('firefly.auto_budget_period_daily'),
diff --git a/app/Http/Requests/BudgetFormStoreRequest.php b/app/Http/Requests/BudgetFormStoreRequest.php
index d0706a57ab..3e1817b93c 100644
--- a/app/Http/Requests/BudgetFormStoreRequest.php
+++ b/app/Http/Requests/BudgetFormStoreRequest.php
@@ -65,7 +65,7 @@ class BudgetFormStoreRequest extends FormRequest
return [
'name' => 'required|between:1,100|uniqueObjectForUser:budgets,name',
'active' => 'numeric|between:0,1',
- 'auto_budget_type' => 'numeric|integer|gte:0|lte:2',
+ 'auto_budget_type' => 'numeric|integer|gte:0|lte:3',
'auto_budget_currency_id' => 'exists:transaction_currencies,id',
'auto_budget_amount' => 'min:0|max:1000000000|required_if:auto_budget_type,1|required_if:auto_budget_type,2',
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
diff --git a/app/Jobs/CreateAutoBudgetLimits.php b/app/Jobs/CreateAutoBudgetLimits.php
index e308384e04..fba256ac57 100644
--- a/app/Jobs/CreateAutoBudgetLimits.php
+++ b/app/Jobs/CreateAutoBudgetLimits.php
@@ -81,6 +81,70 @@ class CreateAutoBudgetLimits implements ShouldQueue
}
}
+ /**
+ * @param AutoBudget $autoBudget
+ * @return void
+ */
+ private function createAdjustedLimit(AutoBudget $autoBudget): void
+ {
+ Log::debug(sprintf('Will now manage rollover for auto budget #%d', $autoBudget->id));
+ // current period:
+ $start = app('navigation')->startOfPeriod($this->date, $autoBudget->period);
+ $end = app('navigation')->endOfPeriod($start, $autoBudget->period);
+
+ // which means previous period:
+ $previousStart = app('navigation')->subtractPeriod($start, $autoBudget->period);
+ $previousEnd = app('navigation')->endOfPeriod($previousStart, $autoBudget->period);
+
+ Log::debug(
+ sprintf(
+ 'Current period is %s-%s, so previous period is %s-%s',
+ $start->format('Y-m-d'),
+ $end->format('Y-m-d'),
+ $previousStart->format('Y-m-d'),
+ $previousEnd->format('Y-m-d')
+ )
+ );
+
+ // has budget limit in previous period?
+ $budgetLimit = $this->findBudgetLimit($autoBudget->budget, $previousStart, $previousEnd);
+
+ if (null === $budgetLimit) {
+ Log::debug('No budget limit exists in previous period, so create one.');
+ // if not, create standard amount and we're done.
+ $this->createBudgetLimit($autoBudget, $start, $end);
+ return;
+ }
+ Log::debug('Budget limit exists for previous period.');
+
+ // if has one, calculate expenses and use that as a base.
+ $repository = app(OperationsRepositoryInterface::class);
+ $repository->setUser($autoBudget->budget->user);
+ $spent = $repository->sumExpenses($previousStart, $previousEnd, null, new Collection([$autoBudget->budget]), $autoBudget->transactionCurrency);
+ $currencyId = (int)$autoBudget->transaction_currency_id;
+ $spentAmount = $spent[$currencyId]['sum'] ?? '0';
+ Log::debug(sprintf('Spent in previous budget period (%s-%s) is %s', $previousStart->format('Y-m-d'), $previousEnd->format('Y-m-d'), $spentAmount));
+
+ // what you spent in previous period PLUS the amount for the current period,
+ // if that is more than zero, that's the amount that will be set.
+
+ $budgetAvailable = bcadd(bcadd($budgetLimit->amount, $autoBudget->amount), $spentAmount);
+ $totalAmount = $autoBudget->amount;
+ Log::debug(sprintf('Total amount available for current budget period is %s', $budgetAvailable));
+
+ if (-1 !== bccomp( $budgetAvailable, $totalAmount)) {
+ Log::info(sprintf('There is no overspending, no need to adjust. Budget limit amount will be %s.', $totalAmount));
+ // create budget limit:
+ $this->createBudgetLimit($autoBudget, $start, $end, $totalAmount);
+ }
+ if (1 !== bccomp($budgetAvailable, $totalAmount)) {
+ Log::info(sprintf('There was overspending, so the new amount will be %s.', $budgetAvailable));
+ // create budget limit:
+ $this->createBudgetLimit($autoBudget, $start, $end, $budgetAvailable);
+ }
+ Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id));
+ }
+
/**
* @param AutoBudget $autoBudget
*
@@ -148,6 +212,13 @@ class CreateAutoBudgetLimits implements ShouldQueue
return;
}
+ if (null === $budgetLimit && AutoBudget::AUTO_BUDGET_ADJUSTED === (int)$autoBudget->auto_budget_type) {
+ // budget limit exists already,
+ $this->createAdjustedLimit($autoBudget);
+ Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id));
+
+ return;
+ }
Log::debug(sprintf('Done with auto budget #%d', $autoBudget->id));
}
diff --git a/app/Models/AutoBudget.php b/app/Models/AutoBudget.php
index a05537bf99..7d1f646b5f 100644
--- a/app/Models/AutoBudget.php
+++ b/app/Models/AutoBudget.php
@@ -69,6 +69,7 @@ class AutoBudget extends Model
public const AUTO_BUDGET_RESET = 1;
public const AUTO_BUDGET_ROLLOVER = 2;
+ public const AUTO_BUDGET_ADJUSTED = 3;
protected $fillable = ['budget_id','amount','period'];
diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php
index 75213a621f..0257d08d01 100644
--- a/app/Repositories/Budget/BudgetRepository.php
+++ b/app/Repositories/Budget/BudgetRepository.php
@@ -829,6 +829,9 @@ class BudgetRepository implements BudgetRepositoryInterface
if ('rollover' === $type) {
$type = AutoBudget::AUTO_BUDGET_ROLLOVER;
}
+ if('adjusted' === $type) {
+ $type = AutoBudget::AUTO_BUDGET_ADJUSTED;
+ }
$repos = app(CurrencyRepositoryInterface::class);
$currency = null;
diff --git a/app/Transformers/BudgetTransformer.php b/app/Transformers/BudgetTransformer.php
index 2ea099c621..ca3daf4139 100644
--- a/app/Transformers/BudgetTransformer.php
+++ b/app/Transformers/BudgetTransformer.php
@@ -78,6 +78,7 @@ class BudgetTransformer extends AbstractTransformer
$types = [
AutoBudget::AUTO_BUDGET_RESET => 'reset',
AutoBudget::AUTO_BUDGET_ROLLOVER => 'rollover',
+ AutoBudget::AUTO_BUDGET_ADJUSTED => 'adjusted',
];
if (null !== $autoBudget) {
diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php
index 461cd5f510..920756f9fd 100644
--- a/resources/lang/en_US/firefly.php
+++ b/resources/lang/en_US/firefly.php
@@ -1656,6 +1656,7 @@ return [
'auto_budget_none' => 'No auto-budget',
'auto_budget_reset' => 'Set a fixed amount every period',
'auto_budget_rollover' => 'Add an amount every period',
+ 'auto_budget_adjusted' => 'Add an amount every period and correct for overspending',
'auto_budget_period_daily' => 'Daily',
'auto_budget_period_weekly' => 'Weekly',
'auto_budget_period_monthly' => 'Monthly',
@@ -1665,6 +1666,7 @@ return [
'auto_budget_help' => 'You can read more about this feature in the help. Click the top-right (?) icon.',
'auto_budget_reset_icon' => 'This budget will be set periodically',
'auto_budget_rollover_icon' => 'The budget amount will increase periodically',
+ 'auto_budget_adjusted_icon' => 'The budget amount will increase periodically and will correct for overspending',
'remove_budgeted_amount' => 'Remove budgeted amount in :currency',
// bills:
diff --git a/resources/views/budgets/index.twig b/resources/views/budgets/index.twig
index 0ebfacb9a8..4edf9d4843 100644
--- a/resources/views/budgets/index.twig
+++ b/resources/views/budgets/index.twig
@@ -239,6 +239,9 @@
{% if 2 == budget.auto_budget.auto_budget_type %}
{% endif %}
+ {% if 3 == budget.auto_budget.auto_budget_type %}
+
+ {% endif %}
{% endif %}
{% if budget.attachments.count() > 0 %}