mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-12-26 05:01:22 +00:00
Compare commits
35 Commits
develop-20
...
develop-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f94fdc4979 | ||
|
|
a0a0e28447 | ||
|
|
f6f7783b94 | ||
|
|
d233cc1de8 | ||
|
|
37671499c8 | ||
|
|
c83b79998d | ||
|
|
ed842c2b42 | ||
|
|
8c5f114339 | ||
|
|
8b2f1d0b4f | ||
|
|
591a1b3050 | ||
|
|
42ec3fe02b | ||
|
|
370a398b5e | ||
|
|
554d89b6e9 | ||
|
|
cb049f5dda | ||
|
|
0728668d41 | ||
|
|
dfc187874e | ||
|
|
225588f3e7 | ||
|
|
06cc6c29aa | ||
|
|
b2d4469908 | ||
|
|
c398383905 | ||
|
|
7af9dce33b | ||
|
|
038790a5d6 | ||
|
|
fb3295bde1 | ||
|
|
43a4fd2ecb | ||
|
|
899c72d068 | ||
|
|
d118c0d886 | ||
|
|
6d4004d1ed | ||
|
|
ae60cd5b28 | ||
|
|
ab31a72199 | ||
|
|
2c1b9534f3 | ||
|
|
7028cb1546 | ||
|
|
dc1ecf6a42 | ||
|
|
3a27f9d02c | ||
|
|
4b27ab38f8 | ||
|
|
df5756dc86 |
24
.ci/php-cs-fixer/composer.lock
generated
24
.ci/php-cs-fixer/composer.lock
generated
@@ -1259,16 +1259,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v7.1.6",
|
||||
"version": "v7.1.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57"
|
||||
"reference": "3284aafcac338b6e86fd955ee4d794cbe434151a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/bb5192af6edc797cbab5c8e8ecfea2fe5f421e57",
|
||||
"reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/3284aafcac338b6e86fd955ee4d794cbe434151a",
|
||||
"reference": "3284aafcac338b6e86fd955ee4d794cbe434151a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1332,7 +1332,7 @@
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v7.1.6"
|
||||
"source": "https://github.com/symfony/console/tree/v7.1.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1348,7 +1348,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-10-09T08:46:59+00:00"
|
||||
"time": "2024-11-05T15:34:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
@@ -2246,16 +2246,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v7.1.6",
|
||||
"version": "v7.1.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e"
|
||||
"reference": "9b8a40b7289767aa7117e957573c2a535efe6585"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e",
|
||||
"reference": "6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/9b8a40b7289767aa7117e957573c2a535efe6585",
|
||||
"reference": "9b8a40b7289767aa7117e957573c2a535efe6585",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2287,7 +2287,7 @@
|
||||
"description": "Executes commands in sub-processes",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/process/tree/v7.1.6"
|
||||
"source": "https://github.com/symfony/process/tree/v7.1.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2303,7 +2303,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-25T14:20:29+00:00"
|
||||
"time": "2024-11-06T09:25:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
|
||||
2
.github/label-actions.yml
vendored
2
.github/label-actions.yml
vendored
@@ -4,6 +4,7 @@
|
||||
feature:
|
||||
issues:
|
||||
# Post a comment, `{issue-author}` is an optional placeholder
|
||||
unlabel: feature
|
||||
comment: |
|
||||
Hi there!
|
||||
|
||||
@@ -32,6 +33,7 @@ epic:
|
||||
Thank you for your contributions.
|
||||
|
||||
enhancement:
|
||||
unlabel: enhancement
|
||||
issues:
|
||||
# Post a comment, `{issue-author}` is an optional placeholder
|
||||
comment: |
|
||||
|
||||
@@ -52,7 +52,7 @@ class AboutController extends Controller
|
||||
$data
|
||||
= [
|
||||
'version' => config('firefly.version'),
|
||||
'api_version' => config('firefly.api_version'),
|
||||
'api_version' => config('firefly.version'),
|
||||
'php_version' => $phpVersion,
|
||||
'os' => $phpOs,
|
||||
'driver' => $currentDriver,
|
||||
|
||||
@@ -46,6 +46,8 @@ class DateRequest extends FormRequest
|
||||
{
|
||||
$start = $this->getCarbonDate('start');
|
||||
$end = $this->getCarbonDate('end');
|
||||
$start->startOfDay();
|
||||
$end->endOfDay();
|
||||
if ($start->diffInYears($end, true) > 5) {
|
||||
throw new FireflyException('Date range out of range.');
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ class StoreRequest extends FormRequest
|
||||
// all custom fields:
|
||||
'internal_reference' => $this->clearString((string)$object['internal_reference']),
|
||||
'external_id' => $this->clearString((string)$object['external_id']),
|
||||
'original_source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')),
|
||||
'original_source' => sprintf('ff3-v%s', config('firefly.version')),
|
||||
'recurrence_id' => $this->integerFromValue($object['recurrence_id']),
|
||||
'bunq_payment_id' => $this->clearString((string)$object['bunq_payment_id']),
|
||||
'external_url' => $this->clearString((string)$object['external_url']),
|
||||
|
||||
@@ -44,7 +44,7 @@ class DateRequest extends FormRequest
|
||||
public function getAll(): array
|
||||
{
|
||||
return [
|
||||
'start' => $this->getCarbonDate('start'),
|
||||
'start' => $this->getCarbonDate('start')->startOfDay(),
|
||||
'end' => $this->getCarbonDate('end')->endOfDay(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ class StoreRequest extends FormRequest
|
||||
// all custom fields:
|
||||
'internal_reference' => $this->clearString((string)$object['internal_reference']),
|
||||
'external_id' => $this->clearString((string)$object['external_id']),
|
||||
'original_source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')),
|
||||
'original_source' => sprintf('ff3-v%s', config('firefly.version')),
|
||||
'recurrence_id' => $this->integerFromValue($object['recurrence_id']),
|
||||
'bunq_payment_id' => $this->clearString((string)$object['bunq_payment_id']),
|
||||
'external_url' => $this->clearString((string)$object['external_url']),
|
||||
|
||||
117
app/Console/Commands/Integrity/AddTimezonesToDates.php
Normal file
117
app/Console/Commands/Integrity/AddTimezonesToDates.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* AddTimezonesToDates.php
|
||||
* Copyright (c) 2024 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\Console\Commands\Integrity;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\AccountBalance;
|
||||
use FireflyIII\Models\AvailableBudget;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\CurrencyExchangeRate;
|
||||
use FireflyIII\Models\InvitedUser;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Models\PiggyBankEvent;
|
||||
use FireflyIII\Models\PiggyBankRepetition;
|
||||
use FireflyIII\Models\Recurrence;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class AddTimezonesToDates extends Command
|
||||
{
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'firefly-iii:add-timezones-to-dates';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Make sure all dates have a timezone.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$models = [
|
||||
AccountBalance::class => ['date'], // done
|
||||
AvailableBudget::class => ['start_date', 'end_date'], // done
|
||||
Bill::class => ['date', 'end_date', 'extension_date'], // done
|
||||
BudgetLimit::class => ['start_date', 'end_date'], // done
|
||||
CurrencyExchangeRate::class => ['date'], // done
|
||||
InvitedUser::class => ['expires'],
|
||||
PiggyBankEvent::class => ['date'],
|
||||
PiggyBankRepetition::class => ['startdate', 'targetdate'],
|
||||
PiggyBank::class => ['startdate', 'targetdate'], // done
|
||||
Recurrence::class => ['first_date', 'repeat_until', 'latest_date'],
|
||||
Tag::class => ['date'],
|
||||
TransactionJournal::class => ['date'],
|
||||
];
|
||||
foreach ($models as $model => $fields) {
|
||||
$this->addTimezoneToModel($model, $fields);
|
||||
}
|
||||
}
|
||||
|
||||
private function addTimezoneToModel(string $model, array $fields): void
|
||||
{
|
||||
foreach ($fields as $field) {
|
||||
$this->addTimezoneToModelField($model, $field);
|
||||
}
|
||||
}
|
||||
|
||||
private function addTimezoneToModelField(string $model, string $field): void
|
||||
{
|
||||
$shortModel = str_replace('FireflyIII\Models\\', '', $model);
|
||||
$timezoneField = sprintf('%s_tz', $field);
|
||||
$items = new Collection();
|
||||
|
||||
try {
|
||||
$items = $model::whereNull($timezoneField)->get();
|
||||
} catch (QueryException $e) {
|
||||
$this->friendlyError(sprintf('Cannot add timezone information to field "%s" of model "%s". Field does not exist.', $field, $shortModel));
|
||||
Log::error($e->getMessage());
|
||||
}
|
||||
if (0 === $items->count()) {
|
||||
$this->friendlyPositive(sprintf('Timezone information is present in field "%s" of model "%s".', $field, $shortModel));
|
||||
|
||||
return;
|
||||
}
|
||||
$this->friendlyInfo(sprintf('Adding timezone information to field "%s" of model "%s".', $field, $shortModel));
|
||||
|
||||
foreach ($items as $item) {
|
||||
$item->{$timezoneField} = config('app.timezone');
|
||||
$item->saveQuietly();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ class ReportIntegrity extends Command
|
||||
return 1;
|
||||
}
|
||||
$commands = [
|
||||
'firefly-iii:add-timezones-to-dates',
|
||||
'firefly-iii:create-group-memberships',
|
||||
'firefly-iii:report-empty-objects',
|
||||
'firefly-iii:report-sum',
|
||||
|
||||
@@ -46,10 +46,15 @@ class Cron extends Command
|
||||
protected $signature = 'firefly-iii:cron
|
||||
{--F|force : Force the cron job(s) to execute.}
|
||||
{--date= : Set the date in YYYY-MM-DD to make Firefly III think that\'s the current date.}
|
||||
{--download-cer : Download exchange rates. Other tasks will be skipped unless also requested.}
|
||||
{--create-recurring : Create recurring transactions. Other tasks will be skipped unless also requested.}
|
||||
{--create-auto-budgets : Create auto budgets. Other tasks will be skipped unless also requested.}
|
||||
{--send-bill-warnings : Send bill warnings. Other tasks will be skipped unless also requested.}
|
||||
';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$doAll = !$this->option('download-cer') && !$this->option('create-recurring') && !$this->option('create-auto-budgets') && !$this->option('send-bill-warnings');
|
||||
$date = null;
|
||||
|
||||
try {
|
||||
@@ -60,7 +65,7 @@ class Cron extends Command
|
||||
$force = (bool)$this->option('force'); // @phpstan-ignore-line
|
||||
|
||||
// Fire exchange rates cron job.
|
||||
if (true === config('cer.download_enabled')) {
|
||||
if (true === config('cer.download_enabled') && ($doAll || $this->option('download-cer'))) {
|
||||
try {
|
||||
$this->exchangeRatesCronJob($force, $date);
|
||||
} catch (FireflyException $e) {
|
||||
@@ -71,30 +76,36 @@ class Cron extends Command
|
||||
}
|
||||
|
||||
// Fire recurring transaction cron job.
|
||||
try {
|
||||
$this->recurringCronJob($force, $date);
|
||||
} catch (FireflyException $e) {
|
||||
app('log')->error($e->getMessage());
|
||||
app('log')->error($e->getTraceAsString());
|
||||
$this->friendlyError($e->getMessage());
|
||||
if ($doAll || $this->option('create-recurring')) {
|
||||
try {
|
||||
$this->recurringCronJob($force, $date);
|
||||
} catch (FireflyException $e) {
|
||||
app('log')->error($e->getMessage());
|
||||
app('log')->error($e->getTraceAsString());
|
||||
$this->friendlyError($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Fire auto-budget cron job:
|
||||
try {
|
||||
$this->autoBudgetCronJob($force, $date);
|
||||
} catch (FireflyException $e) {
|
||||
app('log')->error($e->getMessage());
|
||||
app('log')->error($e->getTraceAsString());
|
||||
$this->friendlyError($e->getMessage());
|
||||
if ($doAll || $this->option('create-auto-budgets')) {
|
||||
try {
|
||||
$this->autoBudgetCronJob($force, $date);
|
||||
} catch (FireflyException $e) {
|
||||
app('log')->error($e->getMessage());
|
||||
app('log')->error($e->getTraceAsString());
|
||||
$this->friendlyError($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Fire bill warning cron job
|
||||
try {
|
||||
$this->billWarningCronJob($force, $date);
|
||||
} catch (FireflyException $e) {
|
||||
app('log')->error($e->getMessage());
|
||||
app('log')->error($e->getTraceAsString());
|
||||
$this->friendlyError($e->getMessage());
|
||||
if ($doAll || $this->option('send-bill-warnings')) {
|
||||
try {
|
||||
$this->billWarningCronJob($force, $date);
|
||||
} catch (FireflyException $e) {
|
||||
app('log')->error($e->getMessage());
|
||||
app('log')->error($e->getTraceAsString());
|
||||
$this->friendlyError($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$this->friendlyInfo('More feedback on the cron jobs can be found in the log files.');
|
||||
|
||||
@@ -67,6 +67,7 @@ class UpgradeDatabase extends Command
|
||||
'firefly-iii:restore-oauth-keys',
|
||||
'firefly-iii:correct-account-balance',
|
||||
// also just in case, some integrity commands:
|
||||
'firefly-iii:add-timezones-to-dates',
|
||||
'firefly-iii:create-group-memberships',
|
||||
'firefly-iii:upgrade-group-information',
|
||||
'firefly-iii:upgrade-currency-preferences',
|
||||
|
||||
@@ -58,20 +58,23 @@ class BillFactory
|
||||
/** @var Bill $bill */
|
||||
$bill = Bill::create(
|
||||
[
|
||||
'name' => $data['name'],
|
||||
'match' => 'MIGRATED_TO_RULES',
|
||||
'amount_min' => $data['amount_min'],
|
||||
'user_id' => $this->user->id,
|
||||
'user_group_id' => $this->user->user_group_id,
|
||||
'transaction_currency_id' => $currency->id,
|
||||
'amount_max' => $data['amount_max'],
|
||||
'date' => $data['date'],
|
||||
'end_date' => $data['end_date'] ?? null,
|
||||
'extension_date' => $data['extension_date'] ?? null,
|
||||
'repeat_freq' => $data['repeat_freq'],
|
||||
'skip' => $skip,
|
||||
'automatch' => true,
|
||||
'active' => $active,
|
||||
'name' => $data['name'],
|
||||
'match' => 'MIGRATED_TO_RULES',
|
||||
'amount_min' => $data['amount_min'],
|
||||
'user_id' => $this->user->id,
|
||||
'user_group_id' => $this->user->user_group_id,
|
||||
'transaction_currency_id' => $currency->id,
|
||||
'amount_max' => $data['amount_max'],
|
||||
'date' => $data['date'],
|
||||
'date_tz' => $data['date']->format('e'),
|
||||
'end_date' => $data['end_date'] ?? null,
|
||||
'end_date_tz' => $data['end_date']?->format('e'),
|
||||
'extension_date' => $data['extension_date'] ?? null,
|
||||
'extension_date_tz' => $data['extension_date']?->format('e'),
|
||||
'repeat_freq' => $data['repeat_freq'],
|
||||
'skip' => $skip,
|
||||
'automatch' => true,
|
||||
'active' => $active,
|
||||
]
|
||||
);
|
||||
} catch (QueryException $e) {
|
||||
|
||||
@@ -225,7 +225,8 @@ class TransactionJournalFactory
|
||||
'bill_id' => $billId,
|
||||
'transaction_currency_id' => $currency->id,
|
||||
'description' => substr($description, 0, 1000),
|
||||
'date' => $carbon->format('Y-m-d H:i:s'),
|
||||
'date' => $carbon,
|
||||
'date_tz' => $carbon->format('e'),
|
||||
'order' => $order,
|
||||
'tag_count' => 0,
|
||||
'completed' => 0,
|
||||
|
||||
@@ -131,12 +131,14 @@ class BudgetLimitHandler
|
||||
app('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,
|
||||
'user_group_id' => $budgetLimit->budget->user->user_group_id,
|
||||
'transaction_currency_id' => $budgetLimit->transaction_currency_id,
|
||||
'start_date' => $current,
|
||||
'end_date' => $currentEnd,
|
||||
'amount' => $amount,
|
||||
'user_id' => $budgetLimit->budget->user->id,
|
||||
'user_group_id' => $budgetLimit->budget->user->user_group_id,
|
||||
'transaction_currency_id' => $budgetLimit->transaction_currency_id,
|
||||
'start_date' => $current,
|
||||
'start_date_tz' => $current->format('e'),
|
||||
'end_date' => $currentEnd,
|
||||
'end_date_tz' => $currentEnd->format('e'),
|
||||
'amount' => $amount,
|
||||
]
|
||||
);
|
||||
$availableBudget->save();
|
||||
|
||||
@@ -58,6 +58,7 @@ class PiggyBankEventHandler
|
||||
'piggy_bank_id' => $event->piggyBank->id,
|
||||
'transaction_journal_id' => $journal?->id,
|
||||
'date' => $date->format('Y-m-d'),
|
||||
'date_tz' => $date->format('e'),
|
||||
'amount' => $event->amount,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -37,7 +37,9 @@ class PiggyBankObserver
|
||||
$repetition = new PiggyBankRepetition();
|
||||
$repetition->piggyBank()->associate($piggyBank);
|
||||
$repetition->startdate = $piggyBank->startdate;
|
||||
$repetition->startdate_tz = $piggyBank->startdate->format('e');
|
||||
$repetition->targetdate = $piggyBank->targetdate;
|
||||
$repetition->targetdate_tz = $piggyBank->targetdate->format('e');
|
||||
$repetition->currentamount = '0';
|
||||
$repetition->save();
|
||||
}
|
||||
|
||||
@@ -117,6 +117,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
'transaction_journals.transaction_type_id',
|
||||
'transaction_journals.description',
|
||||
'transaction_journals.date',
|
||||
'transaction_journals.date_tz',
|
||||
'transaction_journals.order',
|
||||
|
||||
// types
|
||||
|
||||
@@ -151,6 +151,7 @@ class CategoryController extends Controller
|
||||
*/
|
||||
private function reportPeriodChart(Collection $accounts, Carbon $start, Carbon $end, ?Category $category): array
|
||||
{
|
||||
|
||||
$income = [];
|
||||
$expenses = [];
|
||||
$categoryId = 0;
|
||||
@@ -169,8 +170,8 @@ class CategoryController extends Controller
|
||||
$categoryId = $category->id;
|
||||
// this gives us all currencies
|
||||
$collection = new Collection([$category]);
|
||||
$expenses = $opsRepository->listExpenses($start, $end, null, $collection);
|
||||
$income = $opsRepository->listIncome($start, $end, null, $collection);
|
||||
$expenses = $opsRepository->listExpenses($start, $end, $accounts, $collection);
|
||||
$income = $opsRepository->listIncome($start, $end, $accounts, $collection);
|
||||
}
|
||||
$currencies = array_unique(array_merge(array_keys($income), array_keys($expenses)));
|
||||
$periods = app('navigation')->listOfPeriods($start, $end);
|
||||
|
||||
@@ -122,6 +122,7 @@ class CategoryReportController extends Controller
|
||||
|
||||
public function categoryIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): JsonResponse
|
||||
{
|
||||
|
||||
$result = [];
|
||||
$earned = $this->opsRepository->listIncome($start, $end, $accounts, $categories);
|
||||
|
||||
|
||||
@@ -65,6 +65,16 @@ class FrontpageController extends Controller
|
||||
$info[] = $entry;
|
||||
}
|
||||
}
|
||||
|
||||
// sort by current percentage (lowest at the top)
|
||||
uasort(
|
||||
$info,
|
||||
static function (array $a, array $b) {
|
||||
return $a['percentage'] <=> $b['percentage'];
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
$html = '';
|
||||
if (0 !== count($info)) {
|
||||
try {
|
||||
|
||||
@@ -77,17 +77,18 @@ class ShowController extends Controller
|
||||
*/
|
||||
public function show(Recurrence $recurrence)
|
||||
{
|
||||
$repos = app(AttachmentRepositoryInterface::class);
|
||||
$repos = app(AttachmentRepositoryInterface::class);
|
||||
|
||||
/** @var RecurrenceTransformer $transformer */
|
||||
$transformer = app(RecurrenceTransformer::class);
|
||||
$transformer = app(RecurrenceTransformer::class);
|
||||
$transformer->setParameters(new ParameterBag());
|
||||
|
||||
$array = $transformer->transform($recurrence);
|
||||
$array = $transformer->transform($recurrence);
|
||||
|
||||
$groups = $this->recurring->getTransactions($recurrence);
|
||||
$today = today(config('app.timezone'));
|
||||
$array['repeat_until'] = null !== $array['repeat_until'] ? new Carbon($array['repeat_until']) : null;
|
||||
$groups = $this->recurring->getTransactions($recurrence);
|
||||
$today = today(config('app.timezone'));
|
||||
$array['repeat_until'] = null !== $array['repeat_until'] ? new Carbon($array['repeat_until']) : null;
|
||||
$array['journal_count'] = $this->recurring->getJournalCount($recurrence);
|
||||
|
||||
// transform dates back to Carbon objects and expand information
|
||||
foreach ($array['repetitions'] as $index => $repetition) {
|
||||
@@ -103,9 +104,9 @@ class ShowController extends Controller
|
||||
}
|
||||
|
||||
// add attachments to the recurrence object.
|
||||
$attachments = $recurrence->attachments()->get();
|
||||
$array['attachments'] = [];
|
||||
$attachmentTransformer = app(AttachmentTransformer::class);
|
||||
$attachments = $recurrence->attachments()->get();
|
||||
$array['attachments'] = [];
|
||||
$attachmentTransformer = app(AttachmentTransformer::class);
|
||||
|
||||
/** @var Attachment $attachment */
|
||||
foreach ($attachments as $attachment) {
|
||||
@@ -114,7 +115,16 @@ class ShowController extends Controller
|
||||
$array['attachments'][] = $item;
|
||||
}
|
||||
|
||||
$subTitle = (string)trans('firefly.overview_for_recurrence', ['title' => $recurrence->title]);
|
||||
if (null !== $array['nr_of_repetitions']) {
|
||||
$left = $array['nr_of_repetitions'] - $array['journal_count'];
|
||||
$left = max(0, $left);
|
||||
// limit each repetition to X occurrences:
|
||||
foreach ($array['repetitions'] as $index => $repetition) {
|
||||
$array['repetitions'][$index]['occurrences'] = array_slice($repetition['occurrences'], 0, $left);
|
||||
}
|
||||
}
|
||||
|
||||
$subTitle = (string)trans('firefly.overview_for_recurrence', ['title' => $recurrence->title]);
|
||||
|
||||
return view('recurring.show', compact('recurrence', 'subTitle', 'array', 'groups', 'today'));
|
||||
}
|
||||
|
||||
@@ -40,24 +40,24 @@ class TriggerController extends Controller
|
||||
{
|
||||
public function trigger(Recurrence $recurrence, TriggerRecurrenceRequest $request): RedirectResponse
|
||||
{
|
||||
$all = $request->getAll();
|
||||
$date = $all['date'];
|
||||
$all = $request->getAll();
|
||||
$date = $all['date'];
|
||||
|
||||
// grab the date from the last time the recurrence fired:
|
||||
$backupDate = $recurrence->latest_date;
|
||||
$backupDate = $recurrence->latest_date;
|
||||
|
||||
// fire the recurring cron job on the given date, then post-date the created transaction.
|
||||
app('log')->info(sprintf('Trigger: will now fire recurring cron job task for date "%s".', $date->format('Y-m-d H:i:s')));
|
||||
|
||||
/** @var CreateRecurringTransactions $job */
|
||||
$job = app(CreateRecurringTransactions::class);
|
||||
$job = app(CreateRecurringTransactions::class);
|
||||
$job->setRecurrences(new Collection([$recurrence]));
|
||||
$job->setDate($date);
|
||||
$job->setForce(false);
|
||||
$job->handle();
|
||||
app('log')->debug('Done with recurrence.');
|
||||
|
||||
$groups = $job->getGroups();
|
||||
$groups = $job->getGroups();
|
||||
|
||||
/** @var TransactionGroup $group */
|
||||
foreach ($groups as $group) {
|
||||
@@ -68,7 +68,8 @@ class TriggerController extends Controller
|
||||
$journal->save();
|
||||
}
|
||||
}
|
||||
$recurrence->latest_date = $backupDate;
|
||||
$recurrence->latest_date = $backupDate;
|
||||
$recurrence->latest_date_tz = $backupDate?->format('e');
|
||||
$recurrence->save();
|
||||
app('preferences')->mark();
|
||||
|
||||
|
||||
@@ -177,10 +177,11 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
// has repeated X times.
|
||||
$journalCount = $this->repository->getJournalCount($recurrence);
|
||||
if (0 !== $recurrence->repetitions && $journalCount >= $recurrence->repetitions && false === $this->force) {
|
||||
app('log')->info(sprintf('Recurrence #%d has run %d times, so will run no longer.', $recurrence->id, $recurrence->repetitions));
|
||||
app('log')->info(sprintf('Recurrence #%d has run %d times, so will run no longer.', $recurrence->id, $journalCount));
|
||||
|
||||
return false;
|
||||
}
|
||||
app('log')->debug(sprintf('Recurrence #%d has run %d times, max is %d times.', $recurrence->id, $journalCount, $recurrence->repetitions));
|
||||
|
||||
// is no longer running
|
||||
if ($this->repeatUntilHasPassed($recurrence)) {
|
||||
@@ -202,8 +203,8 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
sprintf(
|
||||
'Recurrence #%d is set to run on %s, and today\'s date is %s. Skipped.',
|
||||
$recurrence->id,
|
||||
$recurrence->first_date->format('Y-m-d'),
|
||||
$this->date->format('Y-m-d')
|
||||
$recurrence->first_date->format('Y-m-d H:i:s'),
|
||||
$this->date->format('Y-m-d H:i:s')
|
||||
)
|
||||
);
|
||||
|
||||
@@ -244,9 +245,10 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
private function hasNotStartedYet(Recurrence $recurrence): bool
|
||||
{
|
||||
$startDate = $this->getStartDate($recurrence);
|
||||
app('log')->debug(sprintf('Start date is %s', $startDate->format('Y-m-d')));
|
||||
app('log')->debug(sprintf('Start date is %s', $startDate->format('Y-m-d H:i:s')));
|
||||
app('log')->debug(sprintf('Ask date is %s', $this->date->format('Y-m-d H:i:s')));
|
||||
|
||||
return $startDate->gt($this->date);
|
||||
return $startDate->gte($this->date);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -341,7 +343,7 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
app('log')->debug(sprintf('%s IS today (%s)', $date->format('Y-m-d'), $this->date->format('Y-m-d')));
|
||||
|
||||
// count created journals on THIS day.
|
||||
$journalCount = $this->repository->getJournalCount($recurrence, $date, $date);
|
||||
$journalCount = $this->repository->getJournalCount($recurrence, $date, $date);
|
||||
if ($journalCount > 0 && false === $this->force) {
|
||||
app('log')->info(sprintf('Already created %d journal(s) for date %s', $journalCount, $date->format('Y-m-d')));
|
||||
|
||||
@@ -359,11 +361,11 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
}
|
||||
|
||||
// create transaction array and send to factory.
|
||||
$groupTitle = null;
|
||||
$count = $recurrence->recurrenceTransactions->count();
|
||||
$groupTitle = null;
|
||||
$count = $recurrence->recurrenceTransactions->count();
|
||||
// #8844, if there is one recurrence transaction, use the first title as the title.
|
||||
// #9305, if there is one recurrence transaction, group title must be NULL.
|
||||
$groupTitle = null;
|
||||
$groupTitle = null;
|
||||
|
||||
// #8844, if there are more, use the recurrence transaction itself.
|
||||
if ($count > 1) {
|
||||
@@ -376,14 +378,14 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
return null;
|
||||
}
|
||||
|
||||
$array = [
|
||||
$array = [
|
||||
'user' => $recurrence->user_id,
|
||||
'group_title' => $groupTitle,
|
||||
'transactions' => $this->getTransactionData($recurrence, $repetition, $date),
|
||||
];
|
||||
|
||||
/** @var TransactionGroup $group */
|
||||
$group = $this->groupRepository->store($array);
|
||||
$group = $this->groupRepository->store($array);
|
||||
++$this->created;
|
||||
app('log')->info(sprintf('Created new transaction group #%d', $group->id));
|
||||
|
||||
@@ -392,7 +394,8 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
$this->groups->push($group);
|
||||
|
||||
// update recurring thing:
|
||||
$recurrence->latest_date = $date;
|
||||
$recurrence->latest_date = $date;
|
||||
$recurrence->latest_date_tz = $date?->format('e');
|
||||
$recurrence->save();
|
||||
|
||||
return $group;
|
||||
|
||||
@@ -14,7 +14,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
class AccountBalance extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
protected $fillable = ['account_id', 'title', 'transaction_currency_id', 'balance'];
|
||||
protected $fillable = ['account_id', 'title', 'transaction_currency_id', 'balance', 'date', 'date_tz'];
|
||||
|
||||
public function account(): BelongsTo
|
||||
{
|
||||
|
||||
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use FireflyIII\Enums\AccountTypeEnum;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
@@ -34,19 +35,46 @@ class AccountType extends Model
|
||||
{
|
||||
use ReturnsIntegerIdTrait;
|
||||
|
||||
/** @deprecated */
|
||||
public const string ASSET = 'Asset account';
|
||||
|
||||
/** @deprecated */
|
||||
public const string BENEFICIARY = 'Beneficiary account';
|
||||
|
||||
/** @deprecated */
|
||||
public const string CASH = 'Cash account';
|
||||
|
||||
/** @deprecated */
|
||||
public const string CREDITCARD = 'Credit card';
|
||||
|
||||
/** @deprecated */
|
||||
public const string DEBT = 'Debt';
|
||||
|
||||
/** @deprecated */
|
||||
public const string DEFAULT = 'Default account';
|
||||
|
||||
/** @deprecated */
|
||||
public const string EXPENSE = 'Expense account';
|
||||
|
||||
/** @deprecated */
|
||||
public const string IMPORT = 'Import account';
|
||||
|
||||
/** @deprecated */
|
||||
public const string INITIAL_BALANCE = 'Initial balance account';
|
||||
|
||||
/** @deprecated */
|
||||
public const string LIABILITY_CREDIT = 'Liability credit account';
|
||||
|
||||
/** @deprecated */
|
||||
public const string LOAN = 'Loan';
|
||||
|
||||
/** @deprecated */
|
||||
public const string MORTGAGE = 'Mortgage';
|
||||
|
||||
/** @deprecated */
|
||||
public const string RECONCILIATION = 'Reconciliation account';
|
||||
|
||||
/** @deprecated */
|
||||
public const string REVENUE = 'Revenue account';
|
||||
|
||||
protected $casts
|
||||
@@ -61,4 +89,11 @@ class AccountType extends Model
|
||||
{
|
||||
return $this->hasMany(Account::class);
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
// 'type' => AccountTypeEnum::class,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use FireflyIII\Enums\AutoBudgetType;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
@@ -53,6 +54,13 @@ class AutoBudget extends Model
|
||||
return $this->belongsTo(TransactionCurrency::class);
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
// 'auto_budget_type' => AutoBudgetType::class,
|
||||
];
|
||||
}
|
||||
|
||||
protected function amount(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
|
||||
@@ -51,7 +51,7 @@ class AvailableBudget extends Model
|
||||
'transaction_currency_id' => 'int',
|
||||
];
|
||||
|
||||
protected $fillable = ['user_id', 'user_group_id', 'transaction_currency_id', 'amount', 'start_date', 'end_date'];
|
||||
protected $fillable = ['user_id', 'user_group_id', 'transaction_currency_id', 'amount', 'start_date', 'end_date', 'start_date_tz', 'end_date_tz'];
|
||||
|
||||
/**
|
||||
* Route binder. Converts the key in the URL to the specified object (or throw 404).
|
||||
|
||||
@@ -68,6 +68,7 @@ class Bill extends Model
|
||||
'user_group_id',
|
||||
'amount_max',
|
||||
'date',
|
||||
'date_tz',
|
||||
'repeat_freq',
|
||||
'skip',
|
||||
'automatch',
|
||||
@@ -75,6 +76,8 @@ class Bill extends Model
|
||||
'transaction_currency_id',
|
||||
'end_date',
|
||||
'extension_date',
|
||||
'end_date_tz',
|
||||
'extension_date_tz',
|
||||
];
|
||||
|
||||
protected $hidden = ['amount_min_encrypted', 'amount_max_encrypted', 'name_encrypted', 'match_encrypted'];
|
||||
|
||||
@@ -54,7 +54,7 @@ class BudgetLimit extends Model
|
||||
'deleted' => Deleted::class,
|
||||
];
|
||||
|
||||
protected $fillable = ['budget_id', 'start_date', 'end_date', 'amount', 'transaction_currency_id'];
|
||||
protected $fillable = ['budget_id', 'start_date', 'end_date', 'start_date_tz', 'end_date_tz', 'amount', 'transaction_currency_id'];
|
||||
|
||||
/**
|
||||
* Route binder. Converts the key in the URL to the specified object (or throw 404).
|
||||
|
||||
@@ -49,7 +49,7 @@ class CurrencyExchangeRate extends Model
|
||||
'to_currency_id' => 'int',
|
||||
'date' => 'datetime',
|
||||
];
|
||||
protected $fillable = ['user_id', 'from_currency_id', 'to_currency_id', 'date', 'rate'];
|
||||
protected $fillable = ['user_id', 'from_currency_id', 'to_currency_id', 'date', 'date_tz', 'rate'];
|
||||
|
||||
public function fromCurrency(): BelongsTo
|
||||
{
|
||||
|
||||
@@ -44,7 +44,7 @@ class InvitedUser extends Model
|
||||
'expires' => 'datetime',
|
||||
'redeemed' => 'boolean',
|
||||
];
|
||||
protected $fillable = ['user_id', 'email', 'invite_code', 'expires', 'redeemed'];
|
||||
protected $fillable = ['user_id', 'email', 'invite_code', 'expires', 'expires_tz', 'redeemed'];
|
||||
|
||||
/**
|
||||
* Route binder. Converts the key in the URL to the specified object (or throw 404).
|
||||
|
||||
@@ -53,7 +53,7 @@ class PiggyBank extends Model
|
||||
'encrypted' => 'boolean',
|
||||
];
|
||||
|
||||
protected $fillable = ['name', 'account_id', 'order', 'targetamount', 'startdate', 'targetdate', 'active'];
|
||||
protected $fillable = ['name', 'account_id', 'order', 'targetamount', 'startdate', 'startdate_tz', 'targetdate', 'targetdate_tz', 'active'];
|
||||
|
||||
protected $hidden = ['targetamount_encrypted', 'encrypted'];
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ class PiggyBankEvent extends Model
|
||||
'date' => 'date',
|
||||
];
|
||||
|
||||
protected $fillable = ['piggy_bank_id', 'transaction_journal_id', 'date', 'amount'];
|
||||
protected $fillable = ['piggy_bank_id', 'transaction_journal_id', 'date', 'date_tz', 'amount'];
|
||||
|
||||
protected $hidden = ['amount_encrypted'];
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class PiggyBankRepetition extends Model
|
||||
'targetdate' => 'date',
|
||||
];
|
||||
|
||||
protected $fillable = ['piggy_bank_id', 'startdate', 'targetdate', 'currentamount'];
|
||||
protected $fillable = ['piggy_bank_id', 'startdate', 'startdate_tz', 'targetdate', 'targetdate_tz', 'currentamount'];
|
||||
|
||||
public function piggyBank(): BelongsTo
|
||||
{
|
||||
|
||||
@@ -60,7 +60,7 @@ class Recurrence extends Model
|
||||
];
|
||||
|
||||
protected $fillable
|
||||
= ['user_id', 'transaction_type_id', 'title', 'description', 'first_date', 'repeat_until', 'latest_date', 'repetitions', 'apply_rules', 'active'];
|
||||
= ['user_id', 'transaction_type_id', 'title', 'description', 'first_date', 'first_date_tz', 'repeat_until', 'repeat_until_tz', 'latest_date', 'latest_date_tz', 'repetitions', 'apply_rules', 'active'];
|
||||
|
||||
/** @var string The table to store the data in */
|
||||
protected $table = 'recurrences';
|
||||
|
||||
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use FireflyIII\Enums\RecurrenceRepetitionWeekend;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
@@ -58,6 +59,13 @@ class RecurrenceRepetition extends Model
|
||||
/** @var string The table to store the data in */
|
||||
protected $table = 'recurrences_repetitions';
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
// 'weekend' => RecurrenceRepetitionWeekend::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function recurrence(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Recurrence::class);
|
||||
|
||||
@@ -53,7 +53,7 @@ class Tag extends Model
|
||||
'longitude' => 'float',
|
||||
];
|
||||
|
||||
protected $fillable = ['user_id', 'user_group_id', 'tag', 'date', 'description', 'tagMode'];
|
||||
protected $fillable = ['user_id', 'user_group_id', 'tag', 'date', 'date_tz', 'description', 'tagMode'];
|
||||
|
||||
protected $hidden = ['zoomLevel', 'latitude', 'longitude'];
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
@@ -76,6 +77,7 @@ class TransactionJournal extends Model
|
||||
'completed',
|
||||
'order',
|
||||
'date',
|
||||
'date_tz',
|
||||
];
|
||||
|
||||
protected $hidden = ['encrypted'];
|
||||
@@ -88,7 +90,7 @@ class TransactionJournal extends Model
|
||||
public static function routeBinder(string $value): self
|
||||
{
|
||||
if (auth()->check()) {
|
||||
$journalId = (int)$value;
|
||||
$journalId = (int) $value;
|
||||
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
@@ -167,12 +169,16 @@ class TransactionJournal extends Model
|
||||
|
||||
public function scopeAfter(EloquentBuilder $query, Carbon $date): EloquentBuilder
|
||||
{
|
||||
return $query->where('transaction_journals.date', '>=', $date->format('Y-m-d 00:00:00'));
|
||||
Log::debug(sprintf('scopeAfter("%s")', $date->format('Y-m-d H:i:s')));
|
||||
|
||||
return $query->where('transaction_journals.date', '>=', $date->format('Y-m-d H:i:s'));
|
||||
}
|
||||
|
||||
public function scopeBefore(EloquentBuilder $query, Carbon $date): EloquentBuilder
|
||||
{
|
||||
return $query->where('transaction_journals.date', '<=', $date->format('Y-m-d 00:00:00'));
|
||||
Log::debug(sprintf('scopeBefore("%s")', $date->format('Y-m-d H:i:s')));
|
||||
|
||||
return $query->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s'));
|
||||
}
|
||||
|
||||
public function scopeTransactionTypes(EloquentBuilder $query, array $types): void
|
||||
@@ -238,14 +244,14 @@ class TransactionJournal extends Model
|
||||
protected function order(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: static fn ($value) => (int)$value,
|
||||
get: static fn ($value) => (int) $value,
|
||||
);
|
||||
}
|
||||
|
||||
protected function transactionTypeId(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: static fn ($value) => (int)$value,
|
||||
get: static fn ($value) => (int) $value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use FireflyIII\Enums\TransactionTypeEnum;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
@@ -53,6 +54,13 @@ class TransactionType extends Model
|
||||
];
|
||||
protected $fillable = ['type'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
// 'type' => TransactionTypeEnum::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Route binder. Converts the key in the URL to the specified object (or throw 404).
|
||||
*
|
||||
|
||||
@@ -53,6 +53,15 @@ class Webhook extends Model
|
||||
];
|
||||
protected $fillable = ['active', 'trigger', 'response', 'delivery', 'user_id', 'user_group_id', 'url', 'title', 'secret'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
// 'delivery' => WebhookDelivery::class,
|
||||
// 'response' => WebhookResponse::class,
|
||||
// 'trigger' => WebhookTrigger::class,
|
||||
];
|
||||
}
|
||||
|
||||
public static function getDeliveries(): array
|
||||
{
|
||||
$array = [];
|
||||
@@ -130,7 +139,7 @@ class Webhook extends Model
|
||||
public static function routeBinder(string $value): self
|
||||
{
|
||||
if (auth()->check()) {
|
||||
$webhookId = (int)$value;
|
||||
$webhookId = (int) $value;
|
||||
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
@@ -306,6 +306,8 @@ class BillRepository implements BillRepositoryInterface
|
||||
{
|
||||
// app('log')->debug('Now in getPaidDatesInRange()');
|
||||
|
||||
Log::debug(sprintf('Search for linked journals between %s and %s', $start->toW3cString(), $end->toW3cString()));
|
||||
|
||||
return $bill->transactionJournals()
|
||||
->before($end)->after($start)->get(
|
||||
[
|
||||
@@ -435,15 +437,17 @@ class BillRepository implements BillRepositoryInterface
|
||||
}
|
||||
// find the most recent date for this bill NOT in the future. Cache this date:
|
||||
$start = clone $bill->date;
|
||||
$start->startOfDay();
|
||||
app('log')->debug('nextExpectedMatch: Start is '.$start->format('Y-m-d'));
|
||||
|
||||
while ($start < $date) {
|
||||
app('log')->debug(sprintf('$start (%s) < $date (%s)', $start->format('Y-m-d'), $date->format('Y-m-d')));
|
||||
app('log')->debug(sprintf('$start (%s) < $date (%s)', $start->format('Y-m-d H:i:s'), $date->format('Y-m-d H:i:s')));
|
||||
$start = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip);
|
||||
app('log')->debug('Start is now '.$start->format('Y-m-d'));
|
||||
app('log')->debug('Start is now '.$start->format('Y-m-d H:i:s'));
|
||||
}
|
||||
|
||||
$end = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip);
|
||||
$end->endOfDay();
|
||||
|
||||
// see if the bill was paid in this period.
|
||||
$journalCount = $bill->transactionJournals()->before($end)->after($start)->count();
|
||||
|
||||
@@ -198,11 +198,13 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
|
||||
->where('end_date', $end->format('Y-m-d'))->first()
|
||||
;
|
||||
if (null === $availableBudget) {
|
||||
$availableBudget = new AvailableBudget();
|
||||
$availableBudget = new AvailableBudget();
|
||||
$availableBudget->user()->associate($this->user);
|
||||
$availableBudget->transactionCurrency()->associate($currency);
|
||||
$availableBudget->start_date = $start->startOfDay()->format('Y-m-d'); // @phpstan-ignore-line
|
||||
$availableBudget->end_date = $end->endOfDay()->format('Y-m-d'); // @phpstan-ignore-line
|
||||
$availableBudget->start_date = $start->startOfDay()->format('Y-m-d'); // @phpstan-ignore-line
|
||||
$availableBudget->start_date_tz = $start->format('e');
|
||||
$availableBudget->end_date = $end->endOfDay()->format('Y-m-d'); // @phpstan-ignore-line
|
||||
$availableBudget->end_date_tz = $end->format('e');
|
||||
}
|
||||
$availableBudget->amount = $amount;
|
||||
$availableBudget->save();
|
||||
@@ -235,7 +237,9 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
|
||||
'transaction_currency_id' => $data['currency_id'],
|
||||
'amount' => $data['amount'],
|
||||
'start_date' => $start->format('Y-m-d'),
|
||||
'start_date_tz' => $start->format('e'),
|
||||
'end_date' => $end->format('Y-m-d'),
|
||||
'end_date_tz' => $end->format('e'),
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -255,8 +259,9 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
|
||||
if (array_key_exists('start', $data)) {
|
||||
$start = $data['start'];
|
||||
if ($start instanceof Carbon) {
|
||||
$start = $data['start']->startOfDay();
|
||||
$availableBudget->start_date = $start->format('Y-m-d');
|
||||
$start = $data['start']->startOfDay();
|
||||
$availableBudget->start_date = $start->format('Y-m-d');
|
||||
$availableBudget->start_date_tz = $start->format('e');
|
||||
$availableBudget->save();
|
||||
}
|
||||
}
|
||||
@@ -264,8 +269,9 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
|
||||
if (array_key_exists('end', $data)) {
|
||||
$end = $data['end'];
|
||||
if ($end instanceof Carbon) {
|
||||
$end = $data['end']->endOfDay();
|
||||
$availableBudget->end_date = $end->format('Y-m-d');
|
||||
$end = $data['end']->endOfDay();
|
||||
$availableBudget->end_date = $end->format('Y-m-d');
|
||||
$availableBudget->end_date_tz = $end->format('e');
|
||||
$availableBudget->save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,7 +277,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
|
||||
$currency->save();
|
||||
|
||||
// find the budget:
|
||||
$budget = $this->user->budgets()->find((int)$data['budget_id']);
|
||||
$budget = $this->user->budgets()->find((int) $data['budget_id']);
|
||||
if (null === $budget) {
|
||||
throw new FireflyException('200004: Budget does not exist.');
|
||||
}
|
||||
@@ -323,8 +323,15 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
|
||||
{
|
||||
$budgetLimit->amount = array_key_exists('amount', $data) ? $data['amount'] : $budgetLimit->amount;
|
||||
$budgetLimit->budget_id = array_key_exists('budget_id', $data) ? $data['budget_id'] : $budgetLimit->budget_id;
|
||||
$budgetLimit->start_date = array_key_exists('start', $data) ? $data['start']->format('Y-m-d 00:00:00') : $budgetLimit->start_date;
|
||||
$budgetLimit->end_date = array_key_exists('end', $data) ? $data['end']->format('Y-m-d 23:59:59') : $budgetLimit->end_date;
|
||||
|
||||
if (array_key_exists('start', $data)) {
|
||||
$budgetLimit->start_date = $data['start']->startOfDay();
|
||||
$budgetLimit->start_date_tz = $data['start']->format('e');
|
||||
}
|
||||
if (array_key_exists('end', $data)) {
|
||||
$budgetLimit->end_date = $data['end']->endOfDay();
|
||||
$budgetLimit->end_date_tz = $data['end']->format('e');
|
||||
}
|
||||
|
||||
// if no currency has been provided, use the user's default currency:
|
||||
$currency = null;
|
||||
@@ -351,7 +358,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
|
||||
public function updateLimitAmount(Budget $budget, Carbon $start, Carbon $end, string $amount): ?BudgetLimit
|
||||
{
|
||||
// count the limits:
|
||||
$limits = $budget->budgetlimits()
|
||||
$limits = $budget->budgetlimits()
|
||||
->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00'))
|
||||
->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00'))
|
||||
->count('budget_limits.*')
|
||||
@@ -360,7 +367,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
|
||||
|
||||
// there might be a budget limit for these dates:
|
||||
/** @var null|BudgetLimit $limit */
|
||||
$limit = $budget->budgetlimits()
|
||||
$limit = $budget->budgetlimits()
|
||||
->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00'))
|
||||
->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00'))
|
||||
->first(['budget_limits.*'])
|
||||
@@ -395,11 +402,13 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
|
||||
}
|
||||
app('log')->debug('No existing budget limit, create a new one');
|
||||
// or create one and return it.
|
||||
$limit = new BudgetLimit();
|
||||
$limit = new BudgetLimit();
|
||||
$limit->budget()->associate($budget);
|
||||
$limit->start_date = $start->startOfDay();
|
||||
$limit->end_date = $end->startOfDay();
|
||||
$limit->amount = $amount;
|
||||
$limit->start_date = $start->startOfDay();
|
||||
$limit->start_date_tz = $start->format('e');
|
||||
$limit->end_date = $end->startOfDay();
|
||||
$limit->end_date_tz = $end->format('e');
|
||||
$limit->amount = $amount;
|
||||
$limit->save();
|
||||
app('log')->debug(sprintf('Created new budget limit with ID #%d and amount %s', $limit->id, $amount));
|
||||
|
||||
|
||||
@@ -89,11 +89,12 @@ class CurrencyRepository implements CurrencyRepositoryInterface
|
||||
{
|
||||
return CurrencyExchangeRate::create(
|
||||
[
|
||||
'user_id' => $this->user->id,
|
||||
'from_currency_id' => $fromCurrency->id,
|
||||
'to_currency_id' => $toCurrency->id,
|
||||
'date' => $date,
|
||||
'rate' => $rate,
|
||||
'user_id' => $this->user->id,
|
||||
'from_currency_id' => $fromCurrency->id,
|
||||
'to_currency_id' => $toCurrency->id,
|
||||
'date' => $date,
|
||||
'date_tz' => $date->format('e'),
|
||||
'rate' => $rate,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -178,12 +178,12 @@ trait ModifiesPiggyBanks
|
||||
*/
|
||||
public function store(array $data): PiggyBank
|
||||
{
|
||||
$order = $this->getMaxOrder() + 1;
|
||||
$order = $this->getMaxOrder() + 1;
|
||||
if (array_key_exists('order', $data)) {
|
||||
$order = $data['order'];
|
||||
}
|
||||
$data['order'] = 31337; // very high when creating.
|
||||
$piggyData = $data;
|
||||
$data['order'] = 31337; // very high when creating.
|
||||
$piggyData = $data;
|
||||
// unset fields just in case.
|
||||
unset($piggyData['object_group_title'], $piggyData['object_group_id'], $piggyData['notes'], $piggyData['current_amount']);
|
||||
|
||||
@@ -192,6 +192,9 @@ trait ModifiesPiggyBanks
|
||||
$piggyData['targetamount'] = '0';
|
||||
}
|
||||
|
||||
$piggyData['startdate_tz'] = $piggyData['startdate']?->format('e');
|
||||
$piggyData['targetdate_tz'] = $piggyData['targetdate']?->format('e');
|
||||
|
||||
try {
|
||||
/** @var PiggyBank $piggyBank */
|
||||
$piggyBank = PiggyBank::create($piggyData);
|
||||
@@ -208,13 +211,13 @@ trait ModifiesPiggyBanks
|
||||
$this->updateNote($piggyBank, $data['notes']);
|
||||
|
||||
// repetition is auto created.
|
||||
$repetition = $this->getRepetition($piggyBank);
|
||||
$repetition = $this->getRepetition($piggyBank);
|
||||
if (null !== $repetition && array_key_exists('current_amount', $data) && '' !== $data['current_amount']) {
|
||||
$repetition->currentamount = $data['current_amount'];
|
||||
$repetition->save();
|
||||
}
|
||||
|
||||
$objectGroupTitle = $data['object_group_title'] ?? '';
|
||||
$objectGroupTitle = $data['object_group_title'] ?? '';
|
||||
if ('' !== $objectGroupTitle) {
|
||||
$objectGroup = $this->findOrCreateObjectGroup($objectGroupTitle);
|
||||
if (null !== $objectGroup) {
|
||||
@@ -223,7 +226,7 @@ trait ModifiesPiggyBanks
|
||||
}
|
||||
}
|
||||
// try also with ID
|
||||
$objectGroupId = (int)($data['object_group_id'] ?? 0);
|
||||
$objectGroupId = (int)($data['object_group_id'] ?? 0);
|
||||
if (0 !== $objectGroupId) {
|
||||
$objectGroup = $this->findObjectGroupById($objectGroupId);
|
||||
if (null !== $objectGroup) {
|
||||
@@ -373,10 +376,12 @@ trait ModifiesPiggyBanks
|
||||
$piggyBank->targetamount = '0';
|
||||
}
|
||||
if (array_key_exists('targetdate', $data) && '' !== $data['targetdate']) {
|
||||
$piggyBank->targetdate = $data['targetdate'];
|
||||
$piggyBank->targetdate = $data['targetdate'];
|
||||
$piggyBank->targetdate_tz = $data['targetdate']->format('e');
|
||||
}
|
||||
if (array_key_exists('startdate', $data)) {
|
||||
$piggyBank->startdate = $data['startdate'];
|
||||
$piggyBank->startdate = $data['startdate'];
|
||||
$piggyBank->startdate_tz = $data['targetdate']->format('e');
|
||||
}
|
||||
$piggyBank->save();
|
||||
|
||||
|
||||
@@ -67,13 +67,13 @@ class RecurringRepository implements RecurringRepositoryInterface
|
||||
$set
|
||||
= TransactionJournalMeta::where(static function (Builder $q1) use ($recurrence): void {
|
||||
$q1->where('name', 'recurrence_id');
|
||||
$q1->where('data', json_encode((string)$recurrence->id));
|
||||
$q1->where('data', json_encode((string) $recurrence->id));
|
||||
})->get(['journal_meta.transaction_journal_id']);
|
||||
|
||||
// there are X journals made for this recurrence. Any of them meant for today?
|
||||
foreach ($set as $journalMeta) {
|
||||
$count = TransactionJournalMeta::where(static function (Builder $q2) use ($date): void {
|
||||
$string = (string)$date;
|
||||
$string = (string) $date;
|
||||
app('log')->debug(sprintf('Search for date: %s', json_encode($string)));
|
||||
$q2->where('name', 'recurrence_date');
|
||||
$q2->where('data', json_encode($string));
|
||||
@@ -141,7 +141,7 @@ class RecurringRepository implements RecurringRepositoryInterface
|
||||
/** @var RecurrenceTransactionMeta $meta */
|
||||
foreach ($recTransaction->recurrenceTransactionMeta as $meta) {
|
||||
if ('bill_id' === $meta->name) {
|
||||
$return = (int)$meta->value;
|
||||
$return = (int) $meta->value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ class RecurringRepository implements RecurringRepositoryInterface
|
||||
/** @var RecurrenceTransactionMeta $meta */
|
||||
foreach ($recTransaction->recurrenceTransactionMeta as $meta) {
|
||||
if ('budget_id' === $meta->name) {
|
||||
$return = (int)$meta->value;
|
||||
$return = (int) $meta->value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ class RecurringRepository implements RecurringRepositoryInterface
|
||||
/** @var RecurrenceTransactionMeta $meta */
|
||||
foreach ($recTransaction->recurrenceTransactionMeta as $meta) {
|
||||
if ('category_id' === $meta->name) {
|
||||
$return = (int)$meta->value;
|
||||
$return = (int) $meta->value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ class RecurringRepository implements RecurringRepositoryInterface
|
||||
/** @var RecurrenceTransactionMeta $meta */
|
||||
foreach ($recTransaction->recurrenceTransactionMeta as $meta) {
|
||||
if ('category_name' === $meta->name) {
|
||||
$return = (string)$meta->value;
|
||||
$return = (string) $meta->value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +204,7 @@ class RecurringRepository implements RecurringRepositoryInterface
|
||||
*/
|
||||
public function getJournalCount(Recurrence $recurrence, ?Carbon $start = null, ?Carbon $end = null): int
|
||||
{
|
||||
Log::debug(sprintf('Now in getJournalCount(#%d, "%s", "%s")', $recurrence->id, $start?->format('Y-m-d H:i:s'), $end?->format('Y-m-d H:i:s')));
|
||||
$query = TransactionJournal::leftJoin('journal_meta', 'journal_meta.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transaction_journals.user_id', $recurrence->user_id)
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
@@ -216,8 +217,10 @@ class RecurringRepository implements RecurringRepositoryInterface
|
||||
if (null !== $end) {
|
||||
$query->where('transaction_journals.date', '<=', $end->format('Y-m-d 00:00:00'));
|
||||
}
|
||||
$count = $query->count('transaction_journals.id');
|
||||
Log::debug(sprintf('Count is %d', $count));
|
||||
|
||||
return $query->count('transaction_journals.id');
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,7 +231,7 @@ class RecurringRepository implements RecurringRepositoryInterface
|
||||
return TransactionJournalMeta::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id')
|
||||
->where('transaction_journals.user_id', $this->user->id)
|
||||
->where('journal_meta.name', '=', 'recurrence_id')
|
||||
->where('journal_meta.data', '=', json_encode((string)$recurrence->id))
|
||||
->where('journal_meta.data', '=', json_encode((string) $recurrence->id))
|
||||
->get(['journal_meta.transaction_journal_id'])->pluck('transaction_journal_id')->toArray()
|
||||
;
|
||||
}
|
||||
@@ -241,7 +244,7 @@ class RecurringRepository implements RecurringRepositoryInterface
|
||||
/** @var null|Note $note */
|
||||
$note = $recurrence->notes()->first();
|
||||
|
||||
return (string)$note?->text;
|
||||
return (string) $note?->text;
|
||||
}
|
||||
|
||||
public function getPiggyBank(RecurrenceTransaction $transaction): ?int
|
||||
@@ -251,7 +254,7 @@ class RecurringRepository implements RecurringRepositoryInterface
|
||||
/** @var RecurrenceTransactionMeta $metaEntry */
|
||||
foreach ($meta as $metaEntry) {
|
||||
if ('piggy_bank_id' === $metaEntry->name) {
|
||||
return (int)$metaEntry->value;
|
||||
return (int) $metaEntry->value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,12 +284,12 @@ class RecurringRepository implements RecurringRepositoryInterface
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->where('transaction_journals.user_id', $this->user->id)
|
||||
->where('name', 'recurrence_id')
|
||||
->where('data', json_encode((string)$recurrence->id))
|
||||
->where('data', json_encode((string) $recurrence->id))
|
||||
->get()->pluck('transaction_journal_id')->toArray()
|
||||
;
|
||||
$search = [];
|
||||
foreach ($journalMeta as $journalId) {
|
||||
$search[] = (int)$journalId;
|
||||
$search[] = (int) $journalId;
|
||||
}
|
||||
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
@@ -314,13 +317,13 @@ class RecurringRepository implements RecurringRepositoryInterface
|
||||
->whereNull('transaction_journals.deleted_at')
|
||||
->where('transaction_journals.user_id', $this->user->id)
|
||||
->where('name', 'recurrence_id')
|
||||
->where('data', json_encode((string)$recurrence->id))
|
||||
->where('data', json_encode((string) $recurrence->id))
|
||||
->get()->pluck('transaction_journal_id')->toArray()
|
||||
;
|
||||
$search = [];
|
||||
|
||||
foreach ($journalMeta as $journalId) {
|
||||
$search[] = (int)$journalId;
|
||||
$search[] = (int) $journalId;
|
||||
}
|
||||
if (0 === count($search)) {
|
||||
return new Collection();
|
||||
@@ -442,28 +445,28 @@ class RecurringRepository implements RecurringRepositoryInterface
|
||||
if (is_array($language)) {
|
||||
$language = 'en_US';
|
||||
}
|
||||
$language = (string)$language;
|
||||
$language = (string) $language;
|
||||
if ('daily' === $repetition->repetition_type) {
|
||||
return (string)trans('firefly.recurring_daily', [], $language);
|
||||
return (string) trans('firefly.recurring_daily', [], $language);
|
||||
}
|
||||
if ('weekly' === $repetition->repetition_type) {
|
||||
$dayOfWeek = trans(sprintf('config.dow_%s', $repetition->repetition_moment), [], $language);
|
||||
if ($repetition->repetition_skip > 0) {
|
||||
return (string)trans('firefly.recurring_weekly_skip', ['weekday' => $dayOfWeek, 'skip' => $repetition->repetition_skip + 1], $language);
|
||||
return (string) trans('firefly.recurring_weekly_skip', ['weekday' => $dayOfWeek, 'skip' => $repetition->repetition_skip + 1], $language);
|
||||
}
|
||||
|
||||
return (string)trans('firefly.recurring_weekly', ['weekday' => $dayOfWeek], $language);
|
||||
return (string) trans('firefly.recurring_weekly', ['weekday' => $dayOfWeek], $language);
|
||||
}
|
||||
if ('monthly' === $repetition->repetition_type) {
|
||||
if ($repetition->repetition_skip > 0) {
|
||||
return (string)trans(
|
||||
return (string) trans(
|
||||
'firefly.recurring_monthly_skip',
|
||||
['dayOfMonth' => $repetition->repetition_moment, 'skip' => $repetition->repetition_skip + 1],
|
||||
$language
|
||||
);
|
||||
}
|
||||
|
||||
return (string)trans(
|
||||
return (string) trans(
|
||||
'firefly.recurring_monthly',
|
||||
['dayOfMonth' => $repetition->repetition_moment, 'skip' => $repetition->repetition_skip - 1],
|
||||
$language
|
||||
@@ -474,7 +477,7 @@ class RecurringRepository implements RecurringRepositoryInterface
|
||||
// first part is number of week, second is weekday.
|
||||
$dayOfWeek = trans(sprintf('config.dow_%s', $parts[1]), [], $language);
|
||||
|
||||
return (string)trans('firefly.recurring_ndom', ['weekday' => $dayOfWeek, 'dayOfMonth' => $parts[0]], $language);
|
||||
return (string) trans('firefly.recurring_ndom', ['weekday' => $dayOfWeek, 'dayOfMonth' => $parts[0]], $language);
|
||||
}
|
||||
if ('yearly' === $repetition->repetition_type) {
|
||||
$today = today(config('app.timezone'))->endOfYear();
|
||||
@@ -482,11 +485,11 @@ class RecurringRepository implements RecurringRepositoryInterface
|
||||
if (null === $repDate) {
|
||||
$repDate = clone $today;
|
||||
}
|
||||
$diffInYears = (int)$today->diffInYears($repDate, true);
|
||||
$diffInYears = (int) $today->diffInYears($repDate, true);
|
||||
$repDate->addYears($diffInYears); // technically not necessary.
|
||||
$string = $repDate->isoFormat((string)trans('config.month_and_day_no_year_js'));
|
||||
$string = $repDate->isoFormat((string) trans('config.month_and_day_no_year_js'));
|
||||
|
||||
return (string)trans('firefly.recurring_yearly', ['date' => $string], $language);
|
||||
return (string) trans('firefly.recurring_yearly', ['date' => $string], $language);
|
||||
}
|
||||
|
||||
return '';
|
||||
@@ -520,16 +523,16 @@ class RecurringRepository implements RecurringRepositoryInterface
|
||||
public function totalTransactions(Recurrence $recurrence, RecurrenceRepetition $repetition): int
|
||||
{
|
||||
// if repeat = null just return 0.
|
||||
if (null === $recurrence->repeat_until && 0 === (int)$recurrence->repetitions) {
|
||||
if (null === $recurrence->repeat_until && 0 === (int) $recurrence->repetitions) {
|
||||
return 0;
|
||||
}
|
||||
// expect X transactions then stop. Return that number
|
||||
if (null === $recurrence->repeat_until && 0 !== (int)$recurrence->repetitions) {
|
||||
return (int)$recurrence->repetitions;
|
||||
if (null === $recurrence->repeat_until && 0 !== (int) $recurrence->repetitions) {
|
||||
return (int) $recurrence->repetitions;
|
||||
}
|
||||
|
||||
// need to calculate, this depends on the repetition:
|
||||
if (null !== $recurrence->repeat_until && 0 === (int)$recurrence->repetitions) {
|
||||
if (null !== $recurrence->repeat_until && 0 === (int) $recurrence->repetitions) {
|
||||
$occurrences = $this->getOccurrencesInRange($repetition, $recurrence->first_date ?? today(), $recurrence->repeat_until);
|
||||
|
||||
return count($occurrences);
|
||||
|
||||
@@ -276,6 +276,7 @@ class UserRepository implements UserRepositoryInterface
|
||||
$invitee->email = $email;
|
||||
$invitee->redeemed = false;
|
||||
$invitee->expires = $now;
|
||||
$invitee->expires_tz = $now->format('e');
|
||||
$invitee->save();
|
||||
|
||||
return $invitee;
|
||||
|
||||
@@ -105,10 +105,12 @@ class IsUniqueAccount implements ValidationRule, DataAwareRule
|
||||
/**
|
||||
* TODO duplicate from old validation class.
|
||||
*/
|
||||
private function validateAccountAnonymously(): void
|
||||
private function validateAccountAnonymously(): bool
|
||||
{
|
||||
if (!array_key_exists('user_id', $this->data)) {
|
||||
$this->fail('No user ID provided.');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var User $user */
|
||||
|
||||
@@ -40,6 +40,7 @@ use FireflyIII\Models\RecurrenceTransaction;
|
||||
use FireflyIII\Models\RecurrenceTransactionMeta;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Validation\AccountValidator;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Trait RecurringTransactionTrait
|
||||
@@ -212,10 +213,14 @@ trait RecurringTransactionTrait
|
||||
|
||||
private function setBudget(RecurrenceTransaction $transaction, int $budgetId): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
$budgetFactory = app(BudgetFactory::class);
|
||||
$budgetFactory->setUser($transaction->recurrence->user);
|
||||
$budget = $budgetFactory->find($budgetId, null);
|
||||
if (null === $budget) {
|
||||
// remove budget from recurring transaction:
|
||||
$transaction->recurrenceTransactionMeta()->where('name', 'budget_id')->delete();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -235,6 +240,9 @@ trait RecurringTransactionTrait
|
||||
$billFactory->setUser($transaction->recurrence->user);
|
||||
$bill = $billFactory->find($billId, null);
|
||||
if (null === $bill) {
|
||||
// remove bill from recurring transaction:
|
||||
$transaction->recurrenceTransactionMeta()->where('name', 'bill_id')->delete();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -145,7 +145,8 @@ class BillUpdateService
|
||||
$bill->amount_max = $data['amount_max'];
|
||||
}
|
||||
if (array_key_exists('date', $data) && '' !== (string)$data['date']) {
|
||||
$bill->date = $data['date'];
|
||||
$bill->date = $data['date'];
|
||||
$bill->date_tz = $data['date']->format('e');
|
||||
}
|
||||
if (array_key_exists('repeat_freq', $data) && '' !== (string)$data['repeat_freq']) {
|
||||
$bill->repeat_freq = $data['repeat_freq'];
|
||||
@@ -157,10 +158,12 @@ class BillUpdateService
|
||||
$bill->active = $data['active'];
|
||||
}
|
||||
if (array_key_exists('end_date', $data)) {
|
||||
$bill->end_date = $data['end_date'];
|
||||
$bill->end_date = $data['end_date'];
|
||||
$bill->end_date_tz = $data['end_date']->format('e');
|
||||
}
|
||||
if (array_key_exists('extension_date', $data)) {
|
||||
$bill->extension_date = $data['extension_date'];
|
||||
$bill->extension_date = $data['extension_date'];
|
||||
$bill->extension_date_tz = $data['extension_date']->format('e');
|
||||
}
|
||||
|
||||
$bill->match = 'EMPTY';
|
||||
|
||||
@@ -490,6 +490,7 @@ class JournalUpdateService
|
||||
}
|
||||
// do some parsing.
|
||||
app('log')->debug(sprintf('Create date value from string "%s".', $value));
|
||||
$this->transactionJournal->date_tz = $value->format('e');
|
||||
}
|
||||
event(
|
||||
new TriggeredAuditLog(
|
||||
@@ -610,6 +611,13 @@ class JournalUpdateService
|
||||
'data' => $value,
|
||||
];
|
||||
$factory->updateOrCreate($set);
|
||||
// also set date with timezone.
|
||||
$set = [
|
||||
'journal' => $this->transactionJournal,
|
||||
'name' => sprintf('%s_tz', $field),
|
||||
'data' => $value->format('e'),
|
||||
];
|
||||
$factory->updateOrCreate($set);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,11 +64,13 @@ class RecurrenceUpdateService
|
||||
$recurrence->description = $info['description'];
|
||||
}
|
||||
if (array_key_exists('first_date', $info)) {
|
||||
$recurrence->first_date = $info['first_date'];
|
||||
$recurrence->first_date = $info['first_date'];
|
||||
$recurrence->first_date_tz = $info['first_date']?->format('e');
|
||||
}
|
||||
if (array_key_exists('repeat_until', $info)) {
|
||||
$recurrence->repeat_until = $info['repeat_until'];
|
||||
$recurrence->repetitions = 0;
|
||||
$recurrence->repeat_until = $info['repeat_until'];
|
||||
$recurrence->repeat_until_tz = $info['repeat_until']?->format('e');
|
||||
$recurrence->repetitions = 0;
|
||||
}
|
||||
if (array_key_exists('nr_of_repetitions', $info)) {
|
||||
if (0 !== (int)$info['nr_of_repetitions']) {
|
||||
|
||||
@@ -39,7 +39,6 @@ class EitherConfigKey
|
||||
|
||||
// firefly iii settings
|
||||
'firefly.version',
|
||||
'firefly.api_version',
|
||||
'firefly.default_location',
|
||||
'firefly.account_to_transaction',
|
||||
'firefly.allowed_opposing_types',
|
||||
|
||||
@@ -77,9 +77,7 @@ class RecurringCronjob extends AbstractCronjob
|
||||
{
|
||||
app('log')->info(sprintf('Will now fire recurring cron job task for date "%s".', $this->date->format('Y-m-d H:i:s')));
|
||||
|
||||
/** @var CreateRecurringTransactions $job */
|
||||
$job = app(CreateRecurringTransactions::class);
|
||||
$job->setDate($this->date);
|
||||
$job = new CreateRecurringTransactions($this->date);
|
||||
$job->setForce($this->force);
|
||||
$job->handle();
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Support\Http\Api;
|
||||
|
||||
use FireflyIII\Enums\AccountTypeEnum;
|
||||
use FireflyIII\Models\AccountType;
|
||||
|
||||
/**
|
||||
@@ -32,7 +33,7 @@ trait AccountFilter
|
||||
{
|
||||
protected array $types = [
|
||||
'all' => [
|
||||
AccountType::DEFAULT,
|
||||
AccountTypeEnum::DEFAULT->value,
|
||||
AccountType::CASH,
|
||||
AccountType::ASSET,
|
||||
AccountType::EXPENSE,
|
||||
|
||||
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Support\Http\Controllers;
|
||||
|
||||
use FireflyIII\Enums\AccountTypeEnum;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Bill;
|
||||
@@ -84,9 +85,9 @@ trait ModelInformation
|
||||
/** @var AccountType $mortgage */
|
||||
$mortgage = $repository->getAccountTypeByType(AccountType::MORTGAGE);
|
||||
$liabilityTypes = [
|
||||
$debt->id => (string)trans(sprintf('firefly.account_type_%s', AccountType::DEBT)),
|
||||
$loan->id => (string)trans(sprintf('firefly.account_type_%s', AccountType::LOAN)),
|
||||
$mortgage->id => (string)trans(sprintf('firefly.account_type_%s', AccountType::MORTGAGE)),
|
||||
$debt->id => (string)trans(sprintf('firefly.account_type_%s', AccountTypeEnum::DEBT->value)),
|
||||
$loan->id => (string)trans(sprintf('firefly.account_type_%s', AccountTypeEnum::LOAN->value)),
|
||||
$mortgage->id => (string)trans(sprintf('firefly.account_type_%s', AccountTypeEnum::MORTGAGE->value)),
|
||||
];
|
||||
asort($liabilityTypes);
|
||||
|
||||
|
||||
@@ -201,148 +201,6 @@ class AccountBalanceCalculator
|
||||
return $entry;
|
||||
}
|
||||
|
||||
// private function recalculateLatest(?Account $account): void
|
||||
// {
|
||||
// $query = Transaction::groupBy(['transactions.account_id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id']);
|
||||
//
|
||||
// if (null !== $account) {
|
||||
// $query->where('transactions.account_id', $account->id);
|
||||
// }
|
||||
// $result = $query->get(['transactions.account_id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id', \DB::raw('SUM(transactions.amount) as sum_amount'), \DB::raw('SUM(transactions.foreign_amount) as sum_foreign_amount')]);
|
||||
//
|
||||
// // reset account balances:
|
||||
// $this->resetAccountBalancesByAccount('balance', $account);
|
||||
//
|
||||
// /** @var \stdClass $row */
|
||||
// foreach ($result as $row) {
|
||||
// $account = (int) $row->account_id;
|
||||
// $transactionCurrency = (int) $row->transaction_currency_id;
|
||||
// $foreignCurrency = (int) $row->foreign_currency_id;
|
||||
// $sumAmount = (string) $row->sum_amount;
|
||||
// $sumForeignAmount = (string) $row->sum_foreign_amount;
|
||||
// $sumAmount = '' === $sumAmount ? '0' : $sumAmount;
|
||||
// $sumForeignAmount = '' === $sumForeignAmount ? '0' : $sumForeignAmount;
|
||||
//
|
||||
// // at this point SQLite may return scientific notation because why not. Terrible.
|
||||
// $sumAmount = app('steam')->floatalize($sumAmount);
|
||||
// $sumForeignAmount = app('steam')->floatalize($sumForeignAmount);
|
||||
//
|
||||
// // first create for normal currency:
|
||||
// $entry = $this->getAccountBalanceByAccount($account, $transactionCurrency);
|
||||
//
|
||||
// try {
|
||||
// $entry->balance = bcadd((string) $entry->balance, $sumAmount);
|
||||
// } catch (\ValueError $e) {
|
||||
// $message = sprintf('[a] Could not add "%s" to "%s": %s', $entry->balance, $sumAmount, $e->getMessage());
|
||||
// Log::error($message);
|
||||
//
|
||||
// throw new FireflyException($message, 0, $e);
|
||||
// }
|
||||
// $entry->save();
|
||||
//
|
||||
// // then do foreign amount, if present:
|
||||
// if ($foreignCurrency > 0) {
|
||||
// $entry = $this->getAccountBalanceByAccount($account, $foreignCurrency);
|
||||
//
|
||||
// try {
|
||||
// $entry->balance = bcadd((string) $entry->balance, $sumForeignAmount);
|
||||
// } catch (\ValueError $e) {
|
||||
// $message = sprintf('[b] Could not add "%s" to "%s": %s', $entry->balance, $sumForeignAmount, $e->getMessage());
|
||||
// Log::error($message);
|
||||
//
|
||||
// throw new FireflyException($message, 0, $e);
|
||||
// }
|
||||
// $entry->save();
|
||||
// }
|
||||
// }
|
||||
// Log::debug(sprintf('Recalculated %d account balance(s)', $result->count()));
|
||||
// }
|
||||
|
||||
// private function resetAccountBalancesByAccount(string $title, ?Account $account): void
|
||||
// {
|
||||
// if (null === $account) {
|
||||
// $count = AccountBalance::whereNotNull('updated_at')->where('title', $title)->update(['balance' => '0']);
|
||||
// Log::debug(sprintf('Set %d account balance(s) to zero.', $count));
|
||||
//
|
||||
// return;
|
||||
// }
|
||||
// $count = AccountBalance::where('account_id', $account->id)->where('title', $title)->update(['balance' => '0']);
|
||||
// Log::debug(sprintf('Set %d account balance(s) of account #%d to zero.', $count, $account->id));
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Als je alles opnieuw doet, verzamel je alle transactions en het bedrag en zet je dat neer als "balance after
|
||||
// * journal". Dat betekent, netjes op volgorde van datum en doorrekenen.
|
||||
// *
|
||||
// * Zodra je een transaction journal verplaatst (datum) moet je dat journal en alle latere journals opnieuw doen.
|
||||
// * Maar dan moet je van de account wel een beginnetje hebben, namelijk de balance tot en met dat moment.
|
||||
// *
|
||||
// * 1. Dus dan search je eerst naar die SUM, som alle transactions eerder dan (niet inclusief) de journal.
|
||||
// * 2. En vanaf daar pak je alle journals op of na de journal (dus ook de journal zelf) en begin je door te tellen.
|
||||
// * 3. Elke voorbij gaande journal entry "balance_after_journal" geef je een update of voeg je toe.
|
||||
// */
|
||||
// private function recalculateJournals(?Account $account, ?TransactionJournal $transactionJournal): void
|
||||
// {
|
||||
// $query = Transaction::groupBy(['transactions.account_id', 'transaction_journals.id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id']);
|
||||
// $query->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id');
|
||||
// $query->orderBy('transaction_journals.date', 'asc');
|
||||
// $amounts = [];
|
||||
// if (null !== $account) {
|
||||
// $query->where('transactions.account_id', $account->id);
|
||||
// }
|
||||
// if (null !== $account && null !== $transactionJournal) {
|
||||
// $query->where('transaction_journals.date', '>=', $transactionJournal->date);
|
||||
// $amounts = $this->getStartAmounts($account, $transactionJournal);
|
||||
// }
|
||||
// $result = $query->get(['transactions.account_id', 'transaction_journals.id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id', \DB::raw('SUM(transactions.amount) as sum_amount'), \DB::raw('SUM(transactions.foreign_amount) as sum_foreign_amount')]);
|
||||
//
|
||||
// /** @var \stdClass $row */
|
||||
// foreach ($result as $row) {
|
||||
// $account = (int) $row->account_id;
|
||||
// $transactionCurrency = (int) $row->transaction_currency_id;
|
||||
// $foreignCurrency = (int) $row->foreign_currency_id;
|
||||
// $sumAmount = (string) $row->sum_amount;
|
||||
// $sumForeignAmount = (string) $row->sum_foreign_amount;
|
||||
// $journalId = (int) $row->id;
|
||||
//
|
||||
// // check for empty strings
|
||||
// $sumAmount = '' === $sumAmount ? '0' : $sumAmount;
|
||||
// $sumForeignAmount = '' === $sumForeignAmount ? '0' : $sumForeignAmount;
|
||||
//
|
||||
// // new amounts:
|
||||
// $amounts[$account][$transactionCurrency] = bcadd($amounts[$account][$transactionCurrency] ?? '0', $sumAmount);
|
||||
// $amounts[$account][$foreignCurrency] = bcadd($amounts[$account][$foreignCurrency] ?? '0', $sumForeignAmount);
|
||||
//
|
||||
// // first create for normal currency:
|
||||
// $entry = self::getAccountBalanceByJournal('balance_after_journal', $account, $journalId, $transactionCurrency);
|
||||
// $entry->balance = $amounts[$account][$transactionCurrency];
|
||||
// $entry->save();
|
||||
//
|
||||
// // then do foreign amount, if present:
|
||||
// if ($foreignCurrency > 0) {
|
||||
// $entry = self::getAccountBalanceByJournal('balance_after_journal', $account, $journalId, $foreignCurrency);
|
||||
// $entry->balance = $amounts[$account][$foreignCurrency];
|
||||
// $entry->save();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // select transactions.account_id, transaction_journals.id, transactions.transaction_currency_id, transactions.foreign_currency_id, sum(transactions.amount), sum(transactions.foreign_amount)
|
||||
// //
|
||||
// // from transactions
|
||||
// //
|
||||
// // left join transaction_journals ON transaction_journals.id = transactions.transaction_journal_id
|
||||
// //
|
||||
// // group by account_id, transaction_journals.id, transaction_currency_id, foreign_currency_id
|
||||
// // order by transaction_journals.date desc
|
||||
// }
|
||||
|
||||
// private function getStartAmounts(Account $account, TransactionJournal $journal): array
|
||||
// {
|
||||
// exit('here we are 1');
|
||||
//
|
||||
// return [];
|
||||
// }
|
||||
|
||||
private function storeAccountBalances(array $balances): void
|
||||
{
|
||||
/**
|
||||
@@ -372,9 +230,18 @@ class AccountBalanceCalculator
|
||||
}
|
||||
|
||||
/** @var AccountBalance $object */
|
||||
$object = $account->accountBalances()->firstOrCreate(['title' => 'running_balance', 'balance' => '0', 'transaction_currency_id' => $currencyId, 'date' => $balance[1]]);
|
||||
$object = $account->accountBalances()->firstOrCreate(
|
||||
[
|
||||
'title' => 'running_balance',
|
||||
'balance' => '0',
|
||||
'transaction_currency_id' => $currencyId,
|
||||
'date' => $balance[1],
|
||||
'date_tz' => $balance[1]->format('e'),
|
||||
]
|
||||
);
|
||||
$object->balance = $balance[0];
|
||||
$object->date = $balance[1];
|
||||
$object->date_tz = $balance[1]->format('e');
|
||||
$object->save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ class UpdatePiggybank implements ActionInterface
|
||||
|
||||
if ($source->account_id === $piggyBank->account_id) {
|
||||
app('log')->debug('Piggy bank account is linked to source, so remove amount from piggy bank.');
|
||||
$this->removeAmount($piggyBank, $journalObj, $destination->amount);
|
||||
$this->removeAmount($piggyBank, $journal, $journalObj, $destination->amount);
|
||||
|
||||
event(
|
||||
new TriggeredAuditLog(
|
||||
@@ -102,7 +102,7 @@ class UpdatePiggybank implements ActionInterface
|
||||
}
|
||||
if ($destination->account_id === $piggyBank->account_id) {
|
||||
app('log')->debug('Piggy bank account is linked to source, so add amount to piggy bank.');
|
||||
$this->addAmount($piggyBank, $journalObj, $destination->amount);
|
||||
$this->addAmount($piggyBank, $journal, $journalObj, $destination->amount);
|
||||
|
||||
event(
|
||||
new TriggeredAuditLog(
|
||||
@@ -111,10 +111,11 @@ class UpdatePiggybank implements ActionInterface
|
||||
'add_to_piggy',
|
||||
null,
|
||||
[
|
||||
'currency_symbol' => $journalObj->transactionCurrency->symbol,
|
||||
'decimal_places' => $journalObj->transactionCurrency->decimal_places,
|
||||
'amount' => $destination->amount,
|
||||
'piggy' => $piggyBank->name,
|
||||
'currency_symbol' => $journalObj->transactionCurrency->symbol,
|
||||
'decimal_places' => $journalObj->transactionCurrency->decimal_places,
|
||||
'amount' => $destination->amount,
|
||||
'piggy' => $piggyBank->name,
|
||||
'piggy_id' => $piggyBank->id,
|
||||
]
|
||||
)
|
||||
);
|
||||
@@ -138,7 +139,7 @@ class UpdatePiggybank implements ActionInterface
|
||||
return $user->piggyBanks()->where('piggy_banks.name', $name)->first();
|
||||
}
|
||||
|
||||
private function removeAmount(PiggyBank $piggyBank, TransactionJournal $journal, string $amount): void
|
||||
private function removeAmount(PiggyBank $piggyBank, array $array, TransactionJournal $journal, string $amount): void
|
||||
{
|
||||
$repository = app(PiggyBankRepositoryInterface::class);
|
||||
$repository->setUser($journal->user);
|
||||
@@ -154,6 +155,7 @@ class UpdatePiggybank implements ActionInterface
|
||||
// if amount is zero, stop.
|
||||
if (0 === bccomp('0', $amount)) {
|
||||
app('log')->warning('Amount left is zero, stop.');
|
||||
event(new RuleActionFailedOnArray($this->action, $array, trans('rules.cannot_remove_zero_piggy', ['name' => $piggyBank->name])));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -161,6 +163,7 @@ class UpdatePiggybank implements ActionInterface
|
||||
// make sure we can remove amount:
|
||||
if (false === $repository->canRemoveAmount($piggyBank, $amount)) {
|
||||
app('log')->warning(sprintf('Cannot remove %s from piggy bank.', $amount));
|
||||
event(new RuleActionFailedOnArray($this->action, $array, trans('rules.cannot_remove_from_piggy', ['amount' => $amount, 'name' => $piggyBank->name])));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -169,7 +172,7 @@ class UpdatePiggybank implements ActionInterface
|
||||
$repository->removeAmount($piggyBank, $amount, $journal);
|
||||
}
|
||||
|
||||
private function addAmount(PiggyBank $piggyBank, TransactionJournal $journal, string $amount): void
|
||||
private function addAmount(PiggyBank $piggyBank, array $array, TransactionJournal $journal, string $amount): void
|
||||
{
|
||||
$repository = app(PiggyBankRepositoryInterface::class);
|
||||
$repository->setUser($journal->user);
|
||||
@@ -190,6 +193,7 @@ class UpdatePiggybank implements ActionInterface
|
||||
// if amount is zero, stop.
|
||||
if (0 === bccomp('0', $amount)) {
|
||||
app('log')->warning('Amount left is zero, stop.');
|
||||
event(new RuleActionFailedOnArray($this->action, $array, trans('rules.cannot_add_zero_piggy', ['name' => $piggyBank->name])));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -197,6 +201,7 @@ class UpdatePiggybank implements ActionInterface
|
||||
// make sure we can add amount:
|
||||
if (false === $repository->canAddAmount($piggyBank, $amount)) {
|
||||
app('log')->warning(sprintf('Cannot add %s to piggy bank.', $amount));
|
||||
event(new RuleActionFailedOnArray($this->action, $array, trans('rules.cannot_add_to_piggy', ['amount' => $amount, 'name' => $piggyBank->name])));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -194,11 +194,19 @@ class BillTransformer extends AbstractTransformer
|
||||
$searchStart = clone $start;
|
||||
$start->subDay();
|
||||
|
||||
app('log')->debug(sprintf('Parameters are start: %s end: %s', $start->format('Y-m-d'), $this->parameters->get('end')->format('Y-m-d')));
|
||||
/** @var Carbon $end */
|
||||
$end = clone $this->parameters->get('end');
|
||||
$searchEnd = clone $end;
|
||||
|
||||
// move the search dates to the start of the day.
|
||||
$searchStart->startOfDay();
|
||||
$searchEnd->endOfDay();
|
||||
|
||||
app('log')->debug(sprintf('Parameters are start: %s end: %s', $start->format('Y-m-d'), $end->format('Y-m-d')));
|
||||
app('log')->debug(sprintf('Search parameters are: start: %s', $searchStart->format('Y-m-d')));
|
||||
|
||||
// Get from database when bill was paid.
|
||||
$set = $this->repository->getPaidDatesInRange($bill, $searchStart, $this->parameters->get('end'));
|
||||
$set = $this->repository->getPaidDatesInRange($bill, $searchStart, $searchEnd);
|
||||
app('log')->debug(sprintf('Count %d entries in getPaidDatesInRange()', $set->count()));
|
||||
|
||||
// Grab from array the most recent payment. If none exist, fall back to the start date and pretend *that* was the last paid date.
|
||||
|
||||
31
changelog.md
31
changelog.md
@@ -3,6 +3,37 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## 6.1.22 - 2024-11-07
|
||||
|
||||
### Added
|
||||
|
||||
- [Discussion 8092](https://github.com/orgs/firefly-iii/discussions/8092) (Fresh Install - Register -> 403 Error - Forbidden) started by @pheonix-devapps
|
||||
- [Issue 9183](https://github.com/firefly-iii/firefly-iii/issues/9183) (2FA security improvements) reported by @JC5
|
||||
|
||||
### Fixed
|
||||
|
||||
- [Issue 9282](https://github.com/firefly-iii/firefly-iii/issues/9282) (Default report - no transactions for no budget) reported by @rymrg
|
||||
- [Issue 9147](https://github.com/firefly-iii/firefly-iii/issues/9147) (Store/Update Bill API end_date and extension_date cant be null in request) reported by @jkano
|
||||
- [Issue 9175](https://github.com/firefly-iii/firefly-iii/issues/9175) ("Attempt to read property "type" on null" when mass editing transactions) reported by @Still34
|
||||
- [Issue 9225](https://github.com/firefly-iii/firefly-iii/issues/9225) (Liability amount due calculated incorrectly on liabilities list when the liability is settled with a transfer to another liability) reported by @uumas
|
||||
- [Issue 9236](https://github.com/firefly-iii/firefly-iii/issues/9236) (Autocomplete not working for rrules having the bill as a trigger) reported by @pvieira84
|
||||
- [Issue 9305](https://github.com/firefly-iii/firefly-iii/issues/9305) (Recurring transactions get group title on overview page) reported by @zeitwidrig
|
||||
- [Issue 9106](https://github.com/firefly-iii/firefly-iii/issues/9106) (Inactive accounts are inaccessible when no active accounts of that type exist) reported by @codemicro
|
||||
- [Discussion 9324](https://github.com/orgs/firefly-iii/discussions/9324) (Consistent behavior accross DB Engines) started by @stackcoder
|
||||
- [Issue 9360](https://github.com/firefly-iii/firefly-iii/issues/9360) (Date incorrectly shown) reported by @enboig
|
||||
- [Issue 9303](https://github.com/firefly-iii/firefly-iii/issues/9303) (Rules > Rule > Action) reported by @EricVanCaenenberghe
|
||||
- [Issue 9416](https://github.com/firefly-iii/firefly-iii/issues/9416) (Linking Transaction to Bill doesn't mark as paid for 31st) reported by @harrhunt
|
||||
- [Issue 9389](https://github.com/firefly-iii/firefly-iii/issues/9389) (Budget and Bill field on Recurring transactions not updating) reported by @HHUBSS
|
||||
- [Discussion 9234](https://github.com/orgs/firefly-iii/discussions/9234) (Unsupported cipher or incorrect key length ( first run )) started by @spectroman
|
||||
- [Issue 9294](https://github.com/firefly-iii/firefly-iii/issues/9294) (Repetition counts ignored for recurring transactions) reported by @Syncena
|
||||
- [Issue 9427](https://github.com/firefly-iii/firefly-iii/issues/9427) (The standard financial report does not show all transactions for the income categories) reported by @Neroxeles
|
||||
|
||||
### API
|
||||
|
||||
- API version is no longer distinguished from Firefly III version. API jumps from v2.1.0 to v6.1.22
|
||||
- API v2 is cleaned up and misses a few previously available endpoints. They will be added in the future.
|
||||
- [Discussion 9271](https://github.com/orgs/firefly-iii/discussions/9271) (/v2/chart/balance/balance ignoring the `period` parameter) started by @victorbalssa
|
||||
|
||||
## 6.1.21 - 2024-09-30
|
||||
|
||||
### Added
|
||||
|
||||
242
composer.lock
generated
242
composer.lock
generated
@@ -62,16 +62,16 @@
|
||||
},
|
||||
{
|
||||
"name": "beberlei/assert",
|
||||
"version": "v3.3.2",
|
||||
"version": "v3.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/beberlei/assert.git",
|
||||
"reference": "cb70015c04be1baee6f5f5c953703347c0ac1655"
|
||||
"reference": "b5fd8eacd8915a1b627b8bfc027803f1939734dd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/beberlei/assert/zipball/cb70015c04be1baee6f5f5c953703347c0ac1655",
|
||||
"reference": "cb70015c04be1baee6f5f5c953703347c0ac1655",
|
||||
"url": "https://api.github.com/repos/beberlei/assert/zipball/b5fd8eacd8915a1b627b8bfc027803f1939734dd",
|
||||
"reference": "b5fd8eacd8915a1b627b8bfc027803f1939734dd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -79,7 +79,7 @@
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-simplexml": "*",
|
||||
"php": "^7.0 || ^8.0"
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "*",
|
||||
@@ -123,9 +123,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/beberlei/assert/issues",
|
||||
"source": "https://github.com/beberlei/assert/tree/v3.3.2"
|
||||
"source": "https://github.com/beberlei/assert/tree/v3.3.3"
|
||||
},
|
||||
"time": "2021-12-16T21:41:27+00:00"
|
||||
"time": "2024-07-15T13:18:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "brick/math",
|
||||
@@ -2000,16 +2000,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel-json-api/eloquent",
|
||||
"version": "v4.3.0",
|
||||
"version": "v4.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel-json-api/eloquent.git",
|
||||
"reference": "05644f31eb086e4d640f15eac7d03752ffcde99f"
|
||||
"reference": "98899c6d3ebaf801fb35ff4d1b7b9447fcede23b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel-json-api/eloquent/zipball/05644f31eb086e4d640f15eac7d03752ffcde99f",
|
||||
"reference": "05644f31eb086e4d640f15eac7d03752ffcde99f",
|
||||
"url": "https://api.github.com/repos/laravel-json-api/eloquent/zipball/98899c6d3ebaf801fb35ff4d1b7b9447fcede23b",
|
||||
"reference": "98899c6d3ebaf801fb35ff4d1b7b9447fcede23b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2058,9 +2058,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel-json-api/eloquent/issues",
|
||||
"source": "https://github.com/laravel-json-api/eloquent/tree/v4.3.0"
|
||||
"source": "https://github.com/laravel-json-api/eloquent/tree/v4.3.1"
|
||||
},
|
||||
"time": "2024-10-13T12:53:07+00:00"
|
||||
"time": "2024-10-31T18:03:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel-json-api/encoder-neomerx",
|
||||
@@ -2547,16 +2547,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v11.29.0",
|
||||
"version": "v11.30.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "425054512c362835ba9c0307561973c8eeac7385"
|
||||
"reference": "dff716442d9c229d716be82ccc9a7de52eb97193"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/425054512c362835ba9c0307561973c8eeac7385",
|
||||
"reference": "425054512c362835ba9c0307561973c8eeac7385",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/dff716442d9c229d716be82ccc9a7de52eb97193",
|
||||
"reference": "dff716442d9c229d716be82ccc9a7de52eb97193",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2752,7 +2752,7 @@
|
||||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2024-10-22T14:13:31+00:00"
|
||||
"time": "2024-10-30T15:00:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/passport",
|
||||
@@ -3016,16 +3016,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/slack-notification-channel",
|
||||
"version": "v3.3.2",
|
||||
"version": "v3.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/slack-notification-channel.git",
|
||||
"reference": "5e04e4a0834b84650a207a76b7a67de8156d0b04"
|
||||
"reference": "8ffbb9f0578956cc192bffc8d75f5b07beb35aa3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/slack-notification-channel/zipball/5e04e4a0834b84650a207a76b7a67de8156d0b04",
|
||||
"reference": "5e04e4a0834b84650a207a76b7a67de8156d0b04",
|
||||
"url": "https://api.github.com/repos/laravel/slack-notification-channel/zipball/8ffbb9f0578956cc192bffc8d75f5b07beb35aa3",
|
||||
"reference": "8ffbb9f0578956cc192bffc8d75f5b07beb35aa3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3075,9 +3075,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/slack-notification-channel/issues",
|
||||
"source": "https://github.com/laravel/slack-notification-channel/tree/v3.3.2"
|
||||
"source": "https://github.com/laravel/slack-notification-channel/tree/v3.4.0"
|
||||
},
|
||||
"time": "2024-09-04T06:56:09+00:00"
|
||||
"time": "2024-10-24T15:06:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/ui",
|
||||
@@ -3208,16 +3208,16 @@
|
||||
},
|
||||
{
|
||||
"name": "lcobucci/jwt",
|
||||
"version": "5.4.0",
|
||||
"version": "5.4.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/lcobucci/jwt.git",
|
||||
"reference": "aac4fd512681fd5cb4b77d2105ab7ec700c72051"
|
||||
"reference": "848815d2287abd5d3c285482f8e1f501b289a2e7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/aac4fd512681fd5cb4b77d2105ab7ec700c72051",
|
||||
"reference": "aac4fd512681fd5cb4b77d2105ab7ec700c72051",
|
||||
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/848815d2287abd5d3c285482f8e1f501b289a2e7",
|
||||
"reference": "848815d2287abd5d3c285482f8e1f501b289a2e7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3265,7 +3265,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/lcobucci/jwt/issues",
|
||||
"source": "https://github.com/lcobucci/jwt/tree/5.4.0"
|
||||
"source": "https://github.com/lcobucci/jwt/tree/5.4.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -3277,7 +3277,7 @@
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2024-10-08T22:06:45+00:00"
|
||||
"time": "2024-11-06T06:16:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/commonmark",
|
||||
@@ -4367,20 +4367,20 @@
|
||||
},
|
||||
{
|
||||
"name": "nesbot/carbon",
|
||||
"version": "3.8.0",
|
||||
"version": "3.8.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/briannesbitt/Carbon.git",
|
||||
"reference": "bbd3eef89af8ba66a3aa7952b5439168fbcc529f"
|
||||
"reference": "10ac0aa86b8062219ce21e8189123d611ca3ecd9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/bbd3eef89af8ba66a3aa7952b5439168fbcc529f",
|
||||
"reference": "bbd3eef89af8ba66a3aa7952b5439168fbcc529f",
|
||||
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/10ac0aa86b8062219ce21e8189123d611ca3ecd9",
|
||||
"reference": "10ac0aa86b8062219ce21e8189123d611ca3ecd9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"carbonphp/carbon-doctrine-types": "*",
|
||||
"carbonphp/carbon-doctrine-types": "<100.0",
|
||||
"ext-json": "*",
|
||||
"php": "^8.1",
|
||||
"psr/clock": "^1.0",
|
||||
@@ -4469,7 +4469,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-08-19T06:22:39+00:00"
|
||||
"time": "2024-11-03T16:02:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nette/schema",
|
||||
@@ -7085,16 +7085,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/cache",
|
||||
"version": "v7.1.6",
|
||||
"version": "v7.1.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/cache.git",
|
||||
"reference": "567ef6de47fdcba56eb6c0b344b857d1fce1cce0"
|
||||
"reference": "23b61c9592ee72233c31625f0ae805dd1571e928"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/cache/zipball/567ef6de47fdcba56eb6c0b344b857d1fce1cce0",
|
||||
"reference": "567ef6de47fdcba56eb6c0b344b857d1fce1cce0",
|
||||
"url": "https://api.github.com/repos/symfony/cache/zipball/23b61c9592ee72233c31625f0ae805dd1571e928",
|
||||
"reference": "23b61c9592ee72233c31625f0ae805dd1571e928",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -7162,7 +7162,7 @@
|
||||
"psr6"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/cache/tree/v7.1.6"
|
||||
"source": "https://github.com/symfony/cache/tree/v7.1.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -7178,7 +7178,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-10-25T15:39:55+00:00"
|
||||
"time": "2024-11-05T15:34:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/cache-contracts",
|
||||
@@ -7332,16 +7332,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v7.1.6",
|
||||
"version": "v7.1.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57"
|
||||
"reference": "3284aafcac338b6e86fd955ee4d794cbe434151a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/bb5192af6edc797cbab5c8e8ecfea2fe5f421e57",
|
||||
"reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/3284aafcac338b6e86fd955ee4d794cbe434151a",
|
||||
"reference": "3284aafcac338b6e86fd955ee4d794cbe434151a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -7405,7 +7405,7 @@
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v7.1.6"
|
||||
"source": "https://github.com/symfony/console/tree/v7.1.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -7421,7 +7421,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-10-09T08:46:59+00:00"
|
||||
"time": "2024-11-05T15:34:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/css-selector",
|
||||
@@ -7557,16 +7557,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/error-handler",
|
||||
"version": "v7.1.6",
|
||||
"version": "v7.1.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/error-handler.git",
|
||||
"reference": "d60117093c2a9fe667baa8fedf84e8a09b9c592f"
|
||||
"reference": "010e44661f4c6babaf8c4862fe68c24a53903342"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/error-handler/zipball/d60117093c2a9fe667baa8fedf84e8a09b9c592f",
|
||||
"reference": "d60117093c2a9fe667baa8fedf84e8a09b9c592f",
|
||||
"url": "https://api.github.com/repos/symfony/error-handler/zipball/010e44661f4c6babaf8c4862fe68c24a53903342",
|
||||
"reference": "010e44661f4c6babaf8c4862fe68c24a53903342",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -7612,7 +7612,7 @@
|
||||
"description": "Provides tools to manage errors and ease debugging PHP code",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/error-handler/tree/v7.1.6"
|
||||
"source": "https://github.com/symfony/error-handler/tree/v7.1.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -7628,7 +7628,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-25T14:20:29+00:00"
|
||||
"time": "2024-11-05T15:34:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
@@ -7916,16 +7916,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-client",
|
||||
"version": "v7.1.6",
|
||||
"version": "v7.1.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-client.git",
|
||||
"reference": "274e2f6886b43a36f8bd5dfeb67215f7ebf9e291"
|
||||
"reference": "90ab2a4992dcf5d1f19a9b8737eba36a7c305fd0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-client/zipball/274e2f6886b43a36f8bd5dfeb67215f7ebf9e291",
|
||||
"reference": "274e2f6886b43a36f8bd5dfeb67215f7ebf9e291",
|
||||
"url": "https://api.github.com/repos/symfony/http-client/zipball/90ab2a4992dcf5d1f19a9b8737eba36a7c305fd0",
|
||||
"reference": "90ab2a4992dcf5d1f19a9b8737eba36a7c305fd0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -7990,7 +7990,7 @@
|
||||
"http"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-client/tree/v7.1.6"
|
||||
"source": "https://github.com/symfony/http-client/tree/v7.1.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -8006,7 +8006,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-10-22T09:40:50+00:00"
|
||||
"time": "2024-11-05T16:45:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-client-contracts",
|
||||
@@ -8088,16 +8088,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-foundation",
|
||||
"version": "v7.1.6",
|
||||
"version": "v7.1.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-foundation.git",
|
||||
"reference": "3d7bbf071b25f802f7d55524d408bed414ea71e2"
|
||||
"reference": "5183b61657807099d98f3367bcccb850238b17a9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/3d7bbf071b25f802f7d55524d408bed414ea71e2",
|
||||
"reference": "3d7bbf071b25f802f7d55524d408bed414ea71e2",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/5183b61657807099d98f3367bcccb850238b17a9",
|
||||
"reference": "5183b61657807099d98f3367bcccb850238b17a9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -8145,7 +8145,7 @@
|
||||
"description": "Defines an object-oriented layer for the HTTP specification",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-foundation/tree/v7.1.6"
|
||||
"source": "https://github.com/symfony/http-foundation/tree/v7.1.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -8161,20 +8161,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-10-11T19:23:14+00:00"
|
||||
"time": "2024-11-06T09:02:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-kernel",
|
||||
"version": "v7.1.6",
|
||||
"version": "v7.1.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-kernel.git",
|
||||
"reference": "5d8315899cd76b2e7e29179bf5fea103e41bdf03"
|
||||
"reference": "7f137cda31fd41e422edcdc01915f2c095b84399"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/5d8315899cd76b2e7e29179bf5fea103e41bdf03",
|
||||
"reference": "5d8315899cd76b2e7e29179bf5fea103e41bdf03",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/7f137cda31fd41e422edcdc01915f2c095b84399",
|
||||
"reference": "7f137cda31fd41e422edcdc01915f2c095b84399",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -8259,7 +8259,7 @@
|
||||
"description": "Provides a structured process for converting a Request into a Response",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-kernel/tree/v7.1.6"
|
||||
"source": "https://github.com/symfony/http-kernel/tree/v7.1.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -8275,7 +8275,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-10-27T13:54:21+00:00"
|
||||
"time": "2024-11-06T09:54:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/mailer",
|
||||
@@ -9291,16 +9291,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v7.1.6",
|
||||
"version": "v7.1.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e"
|
||||
"reference": "9b8a40b7289767aa7117e957573c2a535efe6585"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e",
|
||||
"reference": "6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/9b8a40b7289767aa7117e957573c2a535efe6585",
|
||||
"reference": "9b8a40b7289767aa7117e957573c2a535efe6585",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -9332,7 +9332,7 @@
|
||||
"description": "Executes commands in sub-processes",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/process/tree/v7.1.6"
|
||||
"source": "https://github.com/symfony/process/tree/v7.1.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -9348,7 +9348,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-25T14:20:29+00:00"
|
||||
"time": "2024-11-06T09:25:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/psr-http-message-bridge",
|
||||
@@ -9932,16 +9932,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/var-dumper",
|
||||
"version": "v7.1.6",
|
||||
"version": "v7.1.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/var-dumper.git",
|
||||
"reference": "cb5bd55a6b8c2c1c7fb68b0aeae0e257948a720c"
|
||||
"reference": "f6ea51f669760cacd7464bf7eaa0be87b8072db1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/cb5bd55a6b8c2c1c7fb68b0aeae0e257948a720c",
|
||||
"reference": "cb5bd55a6b8c2c1c7fb68b0aeae0e257948a720c",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/f6ea51f669760cacd7464bf7eaa0be87b8072db1",
|
||||
"reference": "f6ea51f669760cacd7464bf7eaa0be87b8072db1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -9995,7 +9995,7 @@
|
||||
"dump"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/var-dumper/tree/v7.1.6"
|
||||
"source": "https://github.com/symfony/var-dumper/tree/v7.1.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -10011,7 +10011,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-25T14:20:29+00:00"
|
||||
"time": "2024-11-05T15:34:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/var-exporter",
|
||||
@@ -10144,16 +10144,16 @@
|
||||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
"version": "v3.14.0",
|
||||
"version": "v3.14.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/Twig.git",
|
||||
"reference": "126b2c97818dbff0cdf3fbfc881aedb3d40aae72"
|
||||
"reference": "f405356d20fb43603bcadc8b09bfb676cb04a379"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/126b2c97818dbff0cdf3fbfc881aedb3d40aae72",
|
||||
"reference": "126b2c97818dbff0cdf3fbfc881aedb3d40aae72",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/f405356d20fb43603bcadc8b09bfb676cb04a379",
|
||||
"reference": "f405356d20fb43603bcadc8b09bfb676cb04a379",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -10207,7 +10207,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/twigphp/Twig/issues",
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.14.0"
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.14.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -10219,7 +10219,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-09T17:55:12+00:00"
|
||||
"time": "2024-11-06T18:17:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "vlucas/phpdotenv",
|
||||
@@ -10525,16 +10525,16 @@
|
||||
},
|
||||
{
|
||||
"name": "barryvdh/laravel-ide-helper",
|
||||
"version": "v3.2.0",
|
||||
"version": "v3.2.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/barryvdh/laravel-ide-helper.git",
|
||||
"reference": "7956ccb4943f8532d008c17a1094b34bb6394d56"
|
||||
"reference": "07e3bd8796f3d1414801a03d3783f9d3ec9efc08"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/7956ccb4943f8532d008c17a1094b34bb6394d56",
|
||||
"reference": "7956ccb4943f8532d008c17a1094b34bb6394d56",
|
||||
"url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/07e3bd8796f3d1414801a03d3783f9d3ec9efc08",
|
||||
"reference": "07e3bd8796f3d1414801a03d3783f9d3ec9efc08",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -10603,7 +10603,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/barryvdh/laravel-ide-helper/issues",
|
||||
"source": "https://github.com/barryvdh/laravel-ide-helper/tree/v3.2.0"
|
||||
"source": "https://github.com/barryvdh/laravel-ide-helper/tree/v3.2.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -10615,7 +10615,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-10-17T16:43:13+00:00"
|
||||
"time": "2024-10-29T14:00:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "barryvdh/reflection-docblock",
|
||||
@@ -11114,16 +11114,16 @@
|
||||
},
|
||||
{
|
||||
"name": "larastan/larastan",
|
||||
"version": "v2.9.9",
|
||||
"version": "v2.9.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/larastan/larastan.git",
|
||||
"reference": "148faa138f0d8acb7d85f4a55693d3e13b6048d2"
|
||||
"reference": "9e7233d88f4f10796fadb8a2d85c5f2b55277c76"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/larastan/larastan/zipball/148faa138f0d8acb7d85f4a55693d3e13b6048d2",
|
||||
"reference": "148faa138f0d8acb7d85f4a55693d3e13b6048d2",
|
||||
"url": "https://api.github.com/repos/larastan/larastan/zipball/9e7233d88f4f10796fadb8a2d85c5f2b55277c76",
|
||||
"reference": "9e7233d88f4f10796fadb8a2d85c5f2b55277c76",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -11193,7 +11193,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/larastan/larastan/issues",
|
||||
"source": "https://github.com/larastan/larastan/tree/v2.9.9"
|
||||
"source": "https://github.com/larastan/larastan/tree/v2.9.10"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -11213,7 +11213,7 @@
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2024-10-15T19:41:22+00:00"
|
||||
"time": "2024-10-19T23:04:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel-json-api/testing",
|
||||
@@ -11282,16 +11282,16 @@
|
||||
},
|
||||
{
|
||||
"name": "maximebf/debugbar",
|
||||
"version": "v1.23.2",
|
||||
"version": "v1.23.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/maximebf/php-debugbar.git",
|
||||
"reference": "689720d724c771ac4add859056744b7b3f2406da"
|
||||
"reference": "687400043d77943ef95e8417cb44e1673ee57844"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/689720d724c771ac4add859056744b7b3f2406da",
|
||||
"reference": "689720d724c771ac4add859056744b7b3f2406da",
|
||||
"url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/687400043d77943ef95e8417cb44e1673ee57844",
|
||||
"reference": "687400043d77943ef95e8417cb44e1673ee57844",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -11344,9 +11344,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/maximebf/php-debugbar/issues",
|
||||
"source": "https://github.com/maximebf/php-debugbar/tree/v1.23.2"
|
||||
"source": "https://github.com/maximebf/php-debugbar/tree/v1.23.3"
|
||||
},
|
||||
"time": "2024-09-16T11:23:09+00:00"
|
||||
"time": "2024-10-29T12:24:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mockery/mockery",
|
||||
@@ -11722,23 +11722,23 @@
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/type-resolver",
|
||||
"version": "1.8.2",
|
||||
"version": "1.9.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/TypeResolver.git",
|
||||
"reference": "153ae662783729388a584b4361f2545e4d841e3c"
|
||||
"reference": "1fb5ba8d045f5dd984ebded5b1cc66f29459422d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c",
|
||||
"reference": "153ae662783729388a584b4361f2545e4d841e3c",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/1fb5ba8d045f5dd984ebded5b1cc66f29459422d",
|
||||
"reference": "1fb5ba8d045f5dd984ebded5b1cc66f29459422d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/deprecations": "^1.0",
|
||||
"php": "^7.3 || ^8.0",
|
||||
"phpdocumentor/reflection-common": "^2.0",
|
||||
"phpstan/phpdoc-parser": "^1.13"
|
||||
"phpstan/phpdoc-parser": "^1.18"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-tokenizer": "*",
|
||||
@@ -11774,9 +11774,9 @@
|
||||
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
|
||||
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2"
|
||||
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.9.0"
|
||||
},
|
||||
"time": "2024-02-23T11:10:43+00:00"
|
||||
"time": "2024-11-03T20:11:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpmyadmin/sql-parser",
|
||||
@@ -12437,16 +12437,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "10.5.37",
|
||||
"version": "10.5.38",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "c7cffa0efa2b70c22366523e6d804c9419eb2400"
|
||||
"reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c7cffa0efa2b70c22366523e6d804c9419eb2400",
|
||||
"reference": "c7cffa0efa2b70c22366523e6d804c9419eb2400",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a86773b9e887a67bc53efa9da9ad6e3f2498c132",
|
||||
"reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -12518,7 +12518,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.37"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.38"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -12534,7 +12534,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-10-19T13:03:41+00:00"
|
||||
"time": "2024-10-28T13:06:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
|
||||
@@ -110,9 +110,9 @@ return [
|
||||
'running_balance_column' => env('USE_RUNNING_BALANCE', false),
|
||||
// see cer.php for exchange rates feature flag.
|
||||
],
|
||||
'version' => 'develop/2024-10-28',
|
||||
'api_version' => '2.1.0',
|
||||
'db_version' => 24,
|
||||
'version' => 'develop/2024-11-06',
|
||||
'api_version' => '2.1.0', // field is no longer used.
|
||||
'db_version' => 25,
|
||||
|
||||
// generic settings
|
||||
'maxUploadSize' => 1073741824, // 1 GB
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
|
||||
return new class () extends Migration {
|
||||
private array $tables;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->tables = [
|
||||
'account_balances' => ['date'], // done
|
||||
'available_budgets' => ['start_date', 'end_date'], // done
|
||||
'bills' => ['date', 'end_date', 'extension_date'], // done
|
||||
'budget_limits' => ['start_date', 'end_date'], // done
|
||||
'currency_exchange_rates' => ['date'], // done
|
||||
'invited_users' => ['expires'],
|
||||
'piggy_bank_events' => ['date'],
|
||||
'piggy_bank_repetitions' => ['startdate', 'targetdate'],
|
||||
'piggy_banks' => ['startdate', 'targetdate'], // done
|
||||
'recurrences' => ['first_date', 'repeat_until', 'latest_date'],
|
||||
'tags' => ['date'],
|
||||
'transaction_journals' => ['date'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
* TODO journal_meta, all date fields?
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
foreach ($this->tables as $table => $columns) {
|
||||
foreach ($columns as $column) {
|
||||
$newColumn = sprintf('%s_tz', $column);
|
||||
if (Schema::hasColumn($table, $column) && !Schema::hasColumn($table, $newColumn)) {
|
||||
try {
|
||||
Schema::table(
|
||||
$table,
|
||||
static function (Blueprint $table) use ($column, $newColumn): void {
|
||||
$table->string($newColumn, 50)->nullable()->after($column);
|
||||
}
|
||||
);
|
||||
} catch (QueryException $e) {
|
||||
app('log')->error(sprintf('Could not add column "%s" to table "%s" query: %s', $newColumn, $table, $e->getMessage()));
|
||||
app('log')->error('If the column or index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void {}
|
||||
};
|
||||
657
package-lock.json
generated
657
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -755,7 +755,7 @@ return [
|
||||
'instructions_rule_from_journal' => 'Create a rule based on one of your transactions. Complement or submit the form below.',
|
||||
'rule_is_strict' => 'strict rule',
|
||||
'rule_is_not_strict' => 'non-strict rule',
|
||||
'rule_help_stop_processing' => 'When you check this box, later rules in this group will not be executed.',
|
||||
'rule_help_stop_processing' => 'When you check this box, later rules in this group will not be executed if this particular rule is executed.',
|
||||
'rule_help_strict' => 'In strict rules ALL triggers must fire for the action(s) to be executed. In non-strict rules, ANY trigger is enough for the action(s) to be executed.',
|
||||
'rule_help_active' => 'Inactive rules will never fire.',
|
||||
'stored_new_rule' => 'Stored new rule with title ":title"',
|
||||
@@ -2627,6 +2627,7 @@ return [
|
||||
'no_bills_create_default' => 'Create a bill',
|
||||
|
||||
// recurring transactions
|
||||
'recurrence_max_count' => 'This recurring transactions will be created at most :max time(s), and has been created :count time(s) already.',
|
||||
'create_right_now' => 'Create right now',
|
||||
'no_new_transaction_in_recurrence' => 'No new transaction was created. Perhaps it was already fired for this date?',
|
||||
'recurrences' => 'Recurring transactions',
|
||||
|
||||
@@ -71,4 +71,8 @@ return [
|
||||
'cannot_find_category' => 'Firefly III can\'t find category ":name"',
|
||||
'cannot_set_budget' => 'Firefly III can\'t set budget ":name" to a transaction of type ":type"',
|
||||
'journal_invalid_amount' => 'Firefly III can\'t set amount ":amount" because it is not a valid number.',
|
||||
'cannot_remove_zero_piggy' => 'Cannot remove zero amount from piggy bank ":name"',
|
||||
'cannot_remove_from_piggy' => 'Cannot remove ":amount" from piggy bank ":name"',
|
||||
'cannot_add_zero_piggy' => 'Cannot add zero amount to piggy bank ":name"',
|
||||
'cannot_add_to_piggy' => 'Cannot add ":amount" to piggy bank ":name"',
|
||||
];
|
||||
|
||||
@@ -93,10 +93,10 @@
|
||||
<code>{{ logEntry.after }}</code>
|
||||
{% endif %}
|
||||
{% if 'add_to_piggy' == logEntry.action %}
|
||||
{{ trans('firefly.ale_action_log_add', {amount: formatAmountBySymbol(logEntry.after.amount, logEntry.after.currency_symbol, logEntry.after.decimal_places, true), name: logEntry.after.name})|raw }}
|
||||
{{ trans('firefly.ale_action_log_add', {amount: formatAmountBySymbol(logEntry.after.amount, logEntry.after.currency_symbol, logEntry.after.decimal_places, true), name: logEntry.after.piggy})|raw }}
|
||||
{% endif %}
|
||||
{% if 'remove_from_piggy' == logEntry.action %}
|
||||
{{ trans('firefly.ale_action_log_remove', {amount: formatAmountBySymbol(logEntry.after.amount, logEntry.after.currency_symbol, logEntry.after.decimal_places, true), name: logEntry.after.name})|raw }}
|
||||
{{ trans('firefly.ale_action_log_remove', {amount: formatAmountBySymbol(logEntry.after.amount, logEntry.after.currency_symbol, logEntry.after.decimal_places, true), name: logEntry.after.piggy})|raw }}
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
|
||||
@@ -22,6 +22,17 @@
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<h4>{{ 'transaction_journal_meta'|_ }}</h4>
|
||||
{% if array.nr_of_repetitions > 0 %}
|
||||
<p>
|
||||
{% if array.journal_count >= array.nr_of_repetitions %}
|
||||
<span class="text-danger">{{ trans('firefly.recurrence_max_count', {count: array.journal_count, max: array.nr_of_repetitions}) }}</span>
|
||||
{% endif %}
|
||||
{% if array.journal_count < array.nr_of_repetitions %}
|
||||
{{ trans('firefly.recurrence_max_count', {count: array.journal_count, max: array.nr_of_repetitions}) }}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<p>{{ 'description'|_ }}: <em>{{ array.description }}</em></p>
|
||||
|
||||
{% if array.active == false %}
|
||||
|
||||
Reference in New Issue
Block a user