mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-11-07 10:28:22 +00:00
Compare commits
101 Commits
develop-20
...
develop-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3253b2e569 | ||
|
|
11ac955303 | ||
|
|
caf9a31bc4 | ||
|
|
8c84ab5855 | ||
|
|
91a56a7396 | ||
|
|
f5806ea6de | ||
|
|
60b3692ac9 | ||
|
|
0bdb4f2e69 | ||
|
|
f89f50c2db | ||
|
|
394df46961 | ||
|
|
535e5e4f50 | ||
|
|
00ba2a46d2 | ||
|
|
a3ff26e3e4 | ||
|
|
54aeb4b4ef | ||
|
|
9eb3ad62dd | ||
|
|
4f0e978687 | ||
|
|
e1cf9f7a79 | ||
|
|
7ce055a22c | ||
|
|
7bd915930c | ||
|
|
75aa2d99fd | ||
|
|
f52bc0e242 | ||
|
|
55cf924794 | ||
|
|
df3e4a6554 | ||
|
|
7c4ada458e | ||
|
|
2a4a98dd10 | ||
|
|
a3bf845851 | ||
|
|
78e832cdba | ||
|
|
d47e4c4f24 | ||
|
|
056329291f | ||
|
|
977946064d | ||
|
|
1870345ddf | ||
|
|
6980717075 | ||
|
|
1fe0aebacb | ||
|
|
cbf7aef0c1 | ||
|
|
8c3e6c0189 | ||
|
|
cf7ee79c1c | ||
|
|
7e344e4332 | ||
|
|
0a55e9fb4e | ||
|
|
ed2e0e86dc | ||
|
|
9d1fb2cd6a | ||
|
|
57617b750f | ||
|
|
f6411fdc5a | ||
|
|
02e24fc919 | ||
|
|
dbc0210304 | ||
|
|
a709e224d4 | ||
|
|
83f3eddf44 | ||
|
|
7cfc4c2671 | ||
|
|
1c6055cb2d | ||
|
|
4f4576e458 | ||
|
|
84d3bcbb37 | ||
|
|
c91c87d646 | ||
|
|
e09b6034f7 | ||
|
|
ad67bb80f3 | ||
|
|
a88d0de34d | ||
|
|
ecf498cc81 | ||
|
|
569f553d26 | ||
|
|
7d45bc46b8 | ||
|
|
08553fcfb2 | ||
|
|
58c76bee94 | ||
|
|
3fa1b6dd27 | ||
|
|
63aa8adab7 | ||
|
|
70b8ea0acb | ||
|
|
d800a01e33 | ||
|
|
52f3ec7d3d | ||
|
|
d4978a09ee | ||
|
|
77095276e2 | ||
|
|
b77a8591dc | ||
|
|
132d7d9ff8 | ||
|
|
a981e2c5cb | ||
|
|
77b88b7758 | ||
|
|
b9894eea57 | ||
|
|
469319a240 | ||
|
|
5a55593e34 | ||
|
|
8979e5ad5a | ||
|
|
3ab65c27ac | ||
|
|
a3cac6fd0f | ||
|
|
1acb5d8681 | ||
|
|
f24cdc7897 | ||
|
|
a2b611253b | ||
|
|
252459c29b | ||
|
|
3582baf9f7 | ||
|
|
b03ecab035 | ||
|
|
547a4e9dbb | ||
|
|
62e33a51bd | ||
|
|
8b0ee7e20a | ||
|
|
0e0ec89b26 | ||
|
|
ec08485c2b | ||
|
|
d91d30c8f0 | ||
|
|
634a43c361 | ||
|
|
f2f86e1139 | ||
|
|
a1c870c962 | ||
|
|
98be3a1414 | ||
|
|
e3c3a0a84b | ||
|
|
b254074867 | ||
|
|
f21a5b3000 | ||
|
|
6029fe2e84 | ||
|
|
651e11ed1c | ||
|
|
5ba29cdacd | ||
|
|
f948cc95b4 | ||
|
|
11e721c6c9 | ||
|
|
2ddc012549 |
13
.ci/php-cs-fixer/composer.lock
generated
13
.ci/php-cs-fixer/composer.lock
generated
@@ -402,16 +402,16 @@
|
||||
},
|
||||
{
|
||||
"name": "friendsofphp/php-cs-fixer",
|
||||
"version": "v3.88.2",
|
||||
"version": "v3.89.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
|
||||
"reference": "a8d15584bafb0f0d9d938827840060fd4a3ebc99"
|
||||
"reference": "f34967da2866ace090a2b447de1f357356474573"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/a8d15584bafb0f0d9d938827840060fd4a3ebc99",
|
||||
"reference": "a8d15584bafb0f0d9d938827840060fd4a3ebc99",
|
||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/f34967da2866ace090a2b447de1f357356474573",
|
||||
"reference": "f34967da2866ace090a2b447de1f357356474573",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -426,7 +426,6 @@
|
||||
"php": "^7.4 || ^8.0",
|
||||
"react/child-process": "^0.6.6",
|
||||
"react/event-loop": "^1.5",
|
||||
"react/promise": "^3.3",
|
||||
"react/socket": "^1.16",
|
||||
"react/stream": "^1.4",
|
||||
"sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0",
|
||||
@@ -494,7 +493,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
|
||||
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.88.2"
|
||||
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.89.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -502,7 +501,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-09-27T00:24:15+00:00"
|
||||
"time": "2025-10-24T12:05:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/container",
|
||||
|
||||
@@ -4,6 +4,8 @@ Over time, many people have contributed to Firefly III. Their efforts are not al
|
||||
Please find below all the people who contributed to the Firefly III code. Their names are mentioned in the year of their first contribution.
|
||||
|
||||
## 2025
|
||||
- jreyesr
|
||||
- codearena-bot
|
||||
- Nicky De Maeyer
|
||||
- Denis Iskandarov
|
||||
- Lompi
|
||||
|
||||
@@ -84,7 +84,7 @@ class AccountController extends Controller
|
||||
$data = $request->getData();
|
||||
$types = $data['types'];
|
||||
$query = $data['query'];
|
||||
$date = $data['date'] ?? today(config('app.timezone'));
|
||||
$date = $data['date'];
|
||||
$return = [];
|
||||
$timer = Timer::getInstance();
|
||||
$timer->start(sprintf('AC accounts "%s"', $query));
|
||||
|
||||
@@ -67,6 +67,8 @@ abstract class Controller extends BaseController
|
||||
|
||||
protected bool $convertToPrimary = false;
|
||||
protected TransactionCurrency $primaryCurrency;
|
||||
|
||||
/** @deprecated use Request classes */
|
||||
protected ParameterBag $parameters;
|
||||
|
||||
/**
|
||||
@@ -98,7 +100,8 @@ abstract class Controller extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to grab all parameters from the URL.
|
||||
* @deprecated use Request classes
|
||||
* Method to grab all parameters from the URL
|
||||
*/
|
||||
private function getParameters(): ParameterBag
|
||||
{
|
||||
|
||||
@@ -25,6 +25,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Api\V1\Controllers\Models\Account;
|
||||
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\PaginationRequest;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
@@ -69,22 +70,25 @@ class ListController extends Controller
|
||||
);
|
||||
}
|
||||
|
||||
public function attachments(Account $account): JsonResponse
|
||||
public function attachments(Account $account, PaginationRequest $request): JsonResponse
|
||||
{
|
||||
$manager = $this->getManager();
|
||||
$pageSize = $this->parameters->get('limit');
|
||||
[
|
||||
'limit' => $limit,
|
||||
'offset' => $offset,
|
||||
'page' => $page,
|
||||
] = $request->attributes->all();
|
||||
$collection = $this->repository->getAttachments($account);
|
||||
|
||||
$count = $collection->count();
|
||||
$attachments = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
||||
$attachments = $collection->slice($offset, $limit);
|
||||
|
||||
// make paginator:
|
||||
$paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page'));
|
||||
$paginator = new LengthAwarePaginator($attachments, $count, $limit, $page);
|
||||
$paginator->setPath(route('api.v1.accounts.attachments', [$account->id]).$this->buildParams());
|
||||
|
||||
/** @var AttachmentTransformer $transformer */
|
||||
$transformer = app(AttachmentTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new FractalCollection($attachments, $transformer, 'attachments');
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
@@ -71,43 +71,45 @@ class ShowController extends Controller
|
||||
public function index(ShowRequest $request): JsonResponse
|
||||
{
|
||||
$manager = $this->getManager();
|
||||
$params = $request->getParameters();
|
||||
$this->parameters->set('type', $params['type']);
|
||||
|
||||
// types to get, page size:
|
||||
$types = $this->mapAccountTypes($params['type']);
|
||||
[
|
||||
'types' => $types,
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
'offset' => $offset,
|
||||
'sort' => $sort,
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
'date' => $date,
|
||||
]
|
||||
= $request->attributes->all();
|
||||
|
||||
// get list of accounts. Count it and split it.
|
||||
$this->repository->resetAccountOrder();
|
||||
$collection = $this->repository->getAccountsByType($types, $params['sort']);
|
||||
$collection = $this->repository->getAccountsByType($types, $sort);
|
||||
$count = $collection->count();
|
||||
|
||||
// continue sort:
|
||||
// TODO if the user sorts on DB dependent field there must be no slice before enrichment, only after.
|
||||
// TODO still need to figure out how to do this easily.
|
||||
$accounts = $collection->slice(($this->parameters->get('page') - 1) * $params['limit'], $params['limit']);
|
||||
|
||||
// #11007 go to the end of the previous day.
|
||||
$this->parameters->set('start', $this->parameters->get('start')?->subSecond());
|
||||
$accounts = $collection->slice($offset, $limit);
|
||||
|
||||
// enrich
|
||||
/** @var User $admin */
|
||||
$admin = auth()->user();
|
||||
$enrichment = new AccountEnrichment();
|
||||
$enrichment->setSort($params['sort']);
|
||||
$enrichment->setDate($this->parameters->get('date'));
|
||||
$enrichment->setStart($this->parameters->get('start'));
|
||||
$enrichment->setEnd($this->parameters->get('end'));
|
||||
$enrichment->setSort($sort);
|
||||
$enrichment->setDate($date);
|
||||
$enrichment->setStart($start);
|
||||
$enrichment->setEnd($end);
|
||||
$enrichment->setUser($admin);
|
||||
$accounts = $enrichment->enrich($accounts);
|
||||
|
||||
// make paginator:
|
||||
$paginator = new LengthAwarePaginator($accounts, $count, $params['limit'], $this->parameters->get('page'));
|
||||
$paginator = new LengthAwarePaginator($accounts, $count, $limit, $page);
|
||||
$paginator->setPath(route('api.v1.accounts.index').$this->buildParams());
|
||||
|
||||
/** @var AccountTransformer $transformer */
|
||||
$transformer = app(AccountTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new FractalCollection($accounts, $transformer, self::RESOURCE_KEY);
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
@@ -126,26 +128,25 @@ class ShowController extends Controller
|
||||
// get list of accounts. Count it and split it.
|
||||
$this->repository->resetAccountOrder();
|
||||
$account->refresh();
|
||||
$manager = $this->getManager();
|
||||
|
||||
// #11007 go to the end of the previous day.
|
||||
$this->parameters->set('start', $this->parameters->get('start')?->subSecond());
|
||||
$manager = $this->getManager();
|
||||
['start' => $start,
|
||||
'end' => $end,
|
||||
'date' => $date,] = $request->attributes->all();
|
||||
|
||||
// enrich
|
||||
/** @var User $admin */
|
||||
$admin = auth()->user();
|
||||
$enrichment = new AccountEnrichment();
|
||||
$enrichment->setDate($this->parameters->get('date'));
|
||||
$enrichment->setStart($this->parameters->get('start'));
|
||||
$enrichment->setEnd($this->parameters->get('end'));
|
||||
$admin = auth()->user();
|
||||
$enrichment = new AccountEnrichment();
|
||||
$enrichment->setDate($date);
|
||||
$enrichment->setStart($start);
|
||||
$enrichment->setEnd($end);
|
||||
$enrichment->setUser($admin);
|
||||
$account = $enrichment->enrichSingle($account);
|
||||
$account = $enrichment->enrichSingle($account);
|
||||
|
||||
|
||||
/** @var AccountTransformer $transformer */
|
||||
$transformer = app(AccountTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
$resource = new Item($account, $transformer, self::RESOURCE_KEY);
|
||||
$transformer = app(AccountTransformer::class);
|
||||
$resource = new Item($account, $transformer, self::RESOURCE_KEY);
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
|
||||
}
|
||||
|
||||
@@ -172,7 +172,6 @@ class ShowController extends Controller
|
||||
|
||||
/** @var BudgetLimitTransformer $transformer */
|
||||
$transformer = app(BudgetLimitTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new Item($budgetLimit, $transformer, 'budget_limits');
|
||||
|
||||
|
||||
@@ -102,6 +102,8 @@ class ListController extends Controller
|
||||
|
||||
// #11007 go to the end of the previous day.
|
||||
$this->parameters->set('start', $this->parameters->get('start')?->subSecond());
|
||||
// #11018 also end of the day.
|
||||
$this->parameters->set('end', $this->parameters->get('end')?->endOfDay());
|
||||
|
||||
// enrich
|
||||
/** @var User $admin */
|
||||
|
||||
@@ -25,7 +25,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Api\V1\Controllers\Models\UserGroup;
|
||||
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Data\DateRequest;
|
||||
use FireflyIII\Api\V1\Requests\PaginationRequest;
|
||||
use FireflyIII\Repositories\UserGroup\UserGroupRepositoryInterface;
|
||||
use FireflyIII\Transformers\UserGroupTransformer;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
@@ -52,17 +52,19 @@ class IndexController extends Controller
|
||||
);
|
||||
}
|
||||
|
||||
public function index(DateRequest $request): JsonResponse
|
||||
public function index(PaginationRequest $request): JsonResponse
|
||||
{
|
||||
$administrations = $this->repository->get();
|
||||
$pageSize = $this->parameters->get('limit');
|
||||
[
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
'offset' => $offset,
|
||||
] = $request->attributes->all();
|
||||
$count = $administrations->count();
|
||||
$administrations = $administrations->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
||||
$paginator = new LengthAwarePaginator($administrations, $count, $pageSize, $this->parameters->get('page'));
|
||||
$administrations = $administrations->slice($offset, $limit);
|
||||
$paginator = new LengthAwarePaginator($administrations, $count, $limit, $page);
|
||||
$transformer = new UserGroupTransformer();
|
||||
|
||||
$transformer->setParameters($this->parameters); // give params to transformer
|
||||
|
||||
return response()
|
||||
->json($this->jsonApiList(self::RESOURCE_KEY, $paginator, $transformer))
|
||||
->header('Content-Type', self::CONTENT_TYPE)
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace FireflyIII\Api\V1\Controllers\Summary;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Data\DateRequest;
|
||||
use FireflyIII\Api\V1\Requests\Summary\BasicRequest;
|
||||
use FireflyIII\Enums\AccountTypeEnum;
|
||||
use FireflyIII\Enums\TransactionTypeEnum;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
@@ -88,34 +88,25 @@ class BasicController extends Controller
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This endpoint is documented at:
|
||||
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/summary/getBasicSummary
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function basic(DateRequest $request): JsonResponse
|
||||
public function basic(BasicRequest $request): JsonResponse
|
||||
{
|
||||
// parameters for boxes:
|
||||
$dates = $request->getAll();
|
||||
$start = $dates['start'];
|
||||
$end = $dates['end'];
|
||||
$code = $request->get('currency_code');
|
||||
['start' => $start, 'end' => $end, 'code' => $code] = $request->attributes->all();
|
||||
// balance information:
|
||||
$balanceData = $this->getBalanceInformation($start, $end);
|
||||
$billData = $this->getSubscriptionInformation($start, $end);
|
||||
$spentData = $this->getLeftToSpendInfo($start, $end);
|
||||
$netWorthData = $this->getNetWorthInfo($end);
|
||||
$balanceData = $this->getBalanceInformation($start, $end);
|
||||
$billData = $this->getSubscriptionInformation($start, $end);
|
||||
$spentData = $this->getLeftToSpendInfo($start, $end);
|
||||
$netWorthData = $this->getNetWorthInfo($end);
|
||||
// $balanceData = [];
|
||||
// $billData = [];
|
||||
// $spentData = [];
|
||||
// $netWorthData = [];
|
||||
$total = array_merge($balanceData, $billData, $spentData, $netWorthData);
|
||||
$total = array_merge($balanceData, $billData, $spentData, $netWorthData);
|
||||
|
||||
// give new keys
|
||||
$return = [];
|
||||
$return = [];
|
||||
foreach ($total as $entry) {
|
||||
if (null === $code || ($code === $entry['currency_code'])) {
|
||||
if ('' === $code || ($code === $entry['currency_code'])) {
|
||||
$return[$entry['key']] = $entry;
|
||||
}
|
||||
}
|
||||
@@ -589,8 +580,6 @@ class BasicController extends Controller
|
||||
|
||||
private function getNetWorthInfo(Carbon $end): array
|
||||
{
|
||||
$end->endOfDay();
|
||||
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
Log::debug(sprintf('getNetWorthInfo up until "%s".', $end->format('Y-m-d H:i:s')));
|
||||
|
||||
97
app/Api/V1/Requests/AggregateFormRequest.php
Normal file
97
app/Api/V1/Requests/AggregateFormRequest.php
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025 https://github.com/ctrl-f5
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Validation\Validator;
|
||||
use RuntimeException;
|
||||
|
||||
abstract class AggregateFormRequest extends ApiRequest
|
||||
{
|
||||
/**
|
||||
* @var ApiRequest[]
|
||||
*/
|
||||
protected array $requests = [];
|
||||
|
||||
/** @return class-string[] */
|
||||
abstract protected function getRequests(): array;
|
||||
|
||||
public function initialize(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null): void
|
||||
{
|
||||
parent::initialize($query, $request, $attributes, $cookies, $files, $server, $content);
|
||||
|
||||
// instantiate all subrequests and share current requests' bags with them
|
||||
Log::debug('Initializing AggregateFormRequest.');
|
||||
foreach ($this->getRequests() as $config) {
|
||||
$requestClass = is_array($config) ? array_shift($config) : $config;
|
||||
|
||||
if (!is_a($requestClass, Request::class, true)) {
|
||||
throw new RuntimeException('getRequests() must return class-strings of subclasses of Request');
|
||||
}
|
||||
Log::debug(sprintf('Initializing subrequest %s', $requestClass));
|
||||
|
||||
$instance = $this->requests[] = new $requestClass();
|
||||
$instance->request = $this->request;
|
||||
$instance->query = $this->query;
|
||||
$instance->attributes = $this->attributes;
|
||||
$instance->cookies = $this->cookies;
|
||||
$instance->files = $this->files;
|
||||
$instance->server = $this->server;
|
||||
$instance->headers = $this->headers;
|
||||
|
||||
if ($instance instanceof ApiRequest) {
|
||||
$instance->handleConfig(is_array($config) ? $config : []);
|
||||
}
|
||||
}
|
||||
Log::debug('Done initializing AggregateFormRequest.');
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
// check all subrequests for rules and combine them
|
||||
return array_reduce(
|
||||
$this->requests,
|
||||
static fn (array $rules, FormRequest $request) => $rules
|
||||
+ (
|
||||
method_exists($request, 'rules')
|
||||
? $request->rules()
|
||||
: []
|
||||
),
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
// register all subrequests' validators
|
||||
foreach ($this->requests as $request) {
|
||||
if (method_exists($request, 'withValidator')) {
|
||||
Log::debug(sprintf('Process withValidator from class %s', get_class($request)));
|
||||
$request->withValidator($validator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
43
app/Api/V1/Requests/ApiRequest.php
Normal file
43
app/Api/V1/Requests/ApiRequest.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025 https://github.com/ctrl-f5
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Requests;
|
||||
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ApiRequest extends FormRequest
|
||||
{
|
||||
use ChecksLogin;
|
||||
use ConvertsDataTypes;
|
||||
|
||||
protected string $required = '';
|
||||
|
||||
public function handleConfig(array $config): void
|
||||
{
|
||||
if (in_array('required', $config, true)) {
|
||||
$this->required = 'required';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,10 +48,12 @@ class AutocompleteRequest extends FormRequest
|
||||
// remove 'initial balance' from allowed types. its internal
|
||||
$array = array_diff($array, [AccountTypeEnum::INITIAL_BALANCE->value, AccountTypeEnum::RECONCILIATION->value]);
|
||||
|
||||
$date = $this->getCarbonDate('date') ?? today(config('app.timezone'));
|
||||
|
||||
return [
|
||||
'types' => $array,
|
||||
'query' => $this->convertString('query'),
|
||||
'date' => $this->getCarbonDate('date'),
|
||||
'date' => $date->endOfDay(),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* DateRequest.php
|
||||
* Copyright (c) 2021 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/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Requests\Data;
|
||||
|
||||
use FireflyIII\Exceptions\ValidationException;
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
/**
|
||||
* Request class for end points that require date parameters.
|
||||
*
|
||||
* Class DateRequest
|
||||
*/
|
||||
class DateRequest extends FormRequest
|
||||
{
|
||||
use ChecksLogin;
|
||||
use ConvertsDataTypes;
|
||||
|
||||
/**
|
||||
* Get all data from the request.
|
||||
*/
|
||||
public function getAll(): array
|
||||
{
|
||||
$start = $this->getCarbonDate('start');
|
||||
$end = $this->getCarbonDate('end');
|
||||
if (null === $start) {
|
||||
$start = now()->startOfMonth();
|
||||
}
|
||||
if (null === $end) {
|
||||
$end = now()->endOfMonth();
|
||||
}
|
||||
// sanity check on dates:
|
||||
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
|
||||
|
||||
|
||||
$start->startOfDay();
|
||||
$end->endOfDay();
|
||||
if ($start->diffInYears($end, true) > 5) {
|
||||
throw new ValidationException('Date range out of range.');
|
||||
}
|
||||
|
||||
return [
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
'date' => $this->getCarbonDate('date'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'date' => 'date|after:1970-01-02|before:2038-01-17',
|
||||
'start' => 'date|after:1970-01-02|before:2038-01-17|before:end|required_with:end',
|
||||
'end' => 'date|after:1970-01-02|before:2038-01-17|after:start|required_with:start',
|
||||
];
|
||||
}
|
||||
}
|
||||
57
app/Api/V1/Requests/DateRangeRequest.php
Normal file
57
app/Api/V1/Requests/DateRangeRequest.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025 https://github.com/ctrl-f5
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Requests;
|
||||
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
class DateRangeRequest extends ApiRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'start' => sprintf('date|after:1970-01-02|before:2038-01-17|before:end|required_with:end|%s', $this->required),
|
||||
'end' => sprintf('date|after:1970-01-02|before:2038-01-17|after:start|required_with:start|%s', $this->required),
|
||||
];
|
||||
}
|
||||
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(
|
||||
function (Validator $validator): void {
|
||||
if ($validator->failed()) {
|
||||
// set null values
|
||||
$this->attributes->set('start', null);
|
||||
$this->attributes->set('end', null);
|
||||
|
||||
return;
|
||||
}
|
||||
$start = $this->getCarbonDate('start')?->startOfDay();
|
||||
$end = $this->getCarbonDate('end')?->endOfDay();
|
||||
|
||||
$this->attributes->set('start', $start);
|
||||
$this->attributes->set('end', $end);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
58
app/Api/V1/Requests/DateRequest.php
Normal file
58
app/Api/V1/Requests/DateRequest.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025 https://github.com/ctrl-f5
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Requests;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
class DateRequest extends ApiRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'date' => 'date|after:1970-01-02|before:2038-01-17|'.$this->required,
|
||||
];
|
||||
}
|
||||
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(
|
||||
function (Validator $validator): void {
|
||||
if ($validator->failed()) {
|
||||
return;
|
||||
}
|
||||
$date = $this->getCarbonDate('date')?->endOfDay();
|
||||
|
||||
// if we also have a range, date must be in that range
|
||||
$start = $this->attributes->get('start');
|
||||
$end = $this->attributes->get('end');
|
||||
if ($date instanceof Carbon && $start instanceof Carbon && $end instanceof Carbon && !$date->between($start, $end)) {
|
||||
$validator->errors()->add('date', (string)trans('validation.between_date'));
|
||||
}
|
||||
|
||||
$this->attributes->set('date', $date);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
57
app/Api/V1/Requests/Models/Account/AccountTypeApiRequest.php
Normal file
57
app/Api/V1/Requests/Models/Account/AccountTypeApiRequest.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* AccountTypeApiRequest.php
|
||||
* Copyright (c) 2025 https://github.com/ctrl-f5
|
||||
*
|
||||
* 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\Api\V1\Requests\Models\Account;
|
||||
|
||||
use FireflyIII\Api\V1\Requests\ApiRequest;
|
||||
use FireflyIII\Support\Http\Api\AccountFilter;
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
class AccountTypeApiRequest extends ApiRequest
|
||||
{
|
||||
use AccountFilter;
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'type' => sprintf('in:%s', implode(',', array_keys($this->types))),
|
||||
];
|
||||
}
|
||||
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(
|
||||
function (Validator $validator): void {
|
||||
if ($validator->failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$type = $this->convertString('type', 'all');
|
||||
$this->attributes->add([
|
||||
'type' => $type,
|
||||
'types' => $this->mapAccountTypes($type),
|
||||
]);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -23,77 +23,21 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Requests\Models\Account;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Api\V1\Requests\AggregateFormRequest;
|
||||
use FireflyIII\Api\V1\Requests\DateRangeRequest;
|
||||
use FireflyIII\Api\V1\Requests\DateRequest;
|
||||
use FireflyIII\Api\V1\Requests\PaginationRequest;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Rules\IsValidSortInstruction;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use FireflyIII\Support\Http\Api\AccountFilter;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
class ShowRequest extends FormRequest
|
||||
class ShowRequest extends AggregateFormRequest
|
||||
{
|
||||
use AccountFilter;
|
||||
use ConvertsDataTypes;
|
||||
|
||||
public function getParameters(): array
|
||||
protected function getRequests(): array
|
||||
{
|
||||
$limit = $this->convertInteger('limit');
|
||||
if (0 === $limit) {
|
||||
// get default for user:
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$limit = (int)Preferences::getForUser($user, 'listPageSize', 50)->data;
|
||||
}
|
||||
|
||||
$page = $this->convertInteger('page');
|
||||
$page = min(max(1, $page), 2 ** 16);
|
||||
|
||||
return [
|
||||
'type' => $this->convertString('type', 'all'),
|
||||
'limit' => $limit,
|
||||
'sort' => $this->convertSortParameters('sort', Account::class),
|
||||
'page' => $page,
|
||||
[PaginationRequest::class, 'sort_class' => Account::class],
|
||||
DateRangeRequest::class,
|
||||
DateRequest::class,
|
||||
AccountTypeApiRequest::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
$keys = implode(',', array_keys($this->types));
|
||||
|
||||
return [
|
||||
'date' => 'date',
|
||||
'start' => 'date|present_with:end|before_or_equal:end|before:2038-01-17|after:1970-01-02',
|
||||
'end' => 'date|present_with:start|after_or_equal:start|before:2038-01-17|after:1970-01-02',
|
||||
'sort' => ['nullable', new IsValidSortInstruction(Account::class)],
|
||||
'type' => sprintf('in:%s', $keys),
|
||||
'limit' => 'numeric|min:1|max:131337',
|
||||
'page' => 'numeric|min:1|max:131337',
|
||||
];
|
||||
}
|
||||
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(
|
||||
function (Validator $validator): void {
|
||||
if (count($validator->failed()) > 0) {
|
||||
return;
|
||||
}
|
||||
$data = $validator->getData();
|
||||
|
||||
|
||||
if (array_key_exists('date', $data) && array_key_exists('start', $data) && array_key_exists('end', $data)) {
|
||||
// assume valid dates, before we got here.
|
||||
$start = Carbon::parse($data['start'], config('app.timezone'))->startOfDay();
|
||||
$end = Carbon::parse($data['end'], config('app.timezone'))->endOfDay();
|
||||
$date = Carbon::parse($data['date'], config('app.timezone'));
|
||||
if (!$date->between($start, $end)) {
|
||||
$validator->errors()->add('date', (string)trans('validation.between_date'));
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* CurrencyCodeRequest.php
|
||||
* Copyright (c) 2025 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\Api\V1\Requests\Models\TransactionCurrency;
|
||||
|
||||
use FireflyIII\Api\V1\Requests\ApiRequest;
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
class CurrencyCodeRequest extends ApiRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'code' => sprintf('exists:transaction_currencies,code|%s', $this->required),
|
||||
];
|
||||
}
|
||||
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(
|
||||
function (Validator $validator): void {
|
||||
if (!$validator->valid()) {
|
||||
return;
|
||||
}
|
||||
$code = $this->convertString('code', '');
|
||||
$this->attributes->set('code', $code);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
84
app/Api/V1/Requests/PaginationRequest.php
Normal file
84
app/Api/V1/Requests/PaginationRequest.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025 https://github.com/ctrl-f5
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Requests;
|
||||
|
||||
use FireflyIII\Rules\IsValidSortInstruction;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Validation\Validator;
|
||||
use RuntimeException;
|
||||
|
||||
class PaginationRequest extends ApiRequest
|
||||
{
|
||||
private ?string $sortClass = null;
|
||||
|
||||
public function handleConfig(array $config): void
|
||||
{
|
||||
parent::handleConfig($config);
|
||||
|
||||
$this->sortClass = $config['sort_class'] ?? null;
|
||||
|
||||
if (!$this->sortClass) {
|
||||
throw new RuntimeException('PaginationRequest requires a sort_class config');
|
||||
}
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'sort' => ['nullable', new IsValidSortInstruction((string)$this->sortClass)],
|
||||
'limit' => 'numeric|min:1|max:131337',
|
||||
'page' => 'numeric|min:1|max:131337',
|
||||
];
|
||||
}
|
||||
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(
|
||||
function (Validator $validator): void {
|
||||
if ($validator->failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$limit = $this->convertInteger('limit');
|
||||
if (0 === $limit) {
|
||||
// get default for user:
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$limit = (int)Preferences::getForUser($user, 'listPageSize', 50)->data;
|
||||
}
|
||||
|
||||
$page = $this->convertInteger('page');
|
||||
$page = min(max(1, $page), 2 ** 16);
|
||||
$offset = ($page - 1) * $limit;
|
||||
$sort = $this->sortClass ? $this->convertSortParameters('sort', $this->sortClass) : $this->get('sort');
|
||||
|
||||
$this->attributes->set('limit', $limit);
|
||||
$this->attributes->set('sort', $sort);
|
||||
$this->attributes->set('page', $page);
|
||||
$this->attributes->set('offset', $offset);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
39
app/Api/V1/Requests/Summary/BasicRequest.php
Normal file
39
app/Api/V1/Requests/Summary/BasicRequest.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* BasicRequest.php
|
||||
* Copyright (c) 2025 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\Api\V1\Requests\Summary;
|
||||
|
||||
use FireflyIII\Api\V1\Requests\AggregateFormRequest;
|
||||
use FireflyIII\Api\V1\Requests\DateRangeRequest;
|
||||
use FireflyIII\Api\V1\Requests\Models\TransactionCurrency\CurrencyCodeRequest;
|
||||
|
||||
class BasicRequest extends AggregateFormRequest
|
||||
{
|
||||
protected function getRequests(): array
|
||||
{
|
||||
return [
|
||||
[DateRangeRequest::class, 'required'],
|
||||
CurrencyCodeRequest::class,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ class SetsLatestVersion extends Command
|
||||
|
||||
return 0;
|
||||
}
|
||||
FireflyConfig::set('ff3_version', config('firefly.version'));
|
||||
FireflyConfig::set('ff3_build_time', (int) config('firefly.build_time'));
|
||||
$this->friendlyInfo('Updated version.');
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -87,7 +87,7 @@ class UpgradesDatabase extends Command
|
||||
$this->call($command, $args);
|
||||
}
|
||||
// index will set FF3 version.
|
||||
FireflyConfig::set('ff3_version', (string) config('firefly.version'));
|
||||
FireflyConfig::set('ff3_build_time', (int) config('firefly.build_time'));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -84,8 +84,10 @@ class AttachmentFactory
|
||||
return $attachment;
|
||||
}
|
||||
|
||||
public function setUser(User $user): void
|
||||
public function setUser(User $user): static
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace FireflyIII\Factory;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Create piggy bank events.
|
||||
@@ -36,9 +37,9 @@ class PiggyBankEventFactory
|
||||
{
|
||||
public function create(TransactionJournal $journal, ?PiggyBank $piggyBank): void
|
||||
{
|
||||
app('log')->debug(sprintf('Now in PiggyBankEventCreate for a %s', $journal->transactionType->type));
|
||||
Log::debug(sprintf('Now in PiggyBankEventCreate for a %s', $journal->transactionType->type));
|
||||
if (!$piggyBank instanceof PiggyBank) {
|
||||
app('log')->debug('Piggy bank is null');
|
||||
Log::debug('Piggy bank is null');
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -49,7 +50,7 @@ class PiggyBankEventFactory
|
||||
|
||||
$amount = $piggyRepos->getExactAmount($piggyBank, $journal);
|
||||
if (0 === bccomp($amount, '0')) {
|
||||
app('log')->debug('Amount is zero, will not create event.');
|
||||
Log::debug('Amount is zero, will not create event.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -221,10 +221,26 @@ class TransactionJournalFactory
|
||||
];
|
||||
Log::debug('Source info:', $sourceInfo);
|
||||
Log::debug('Destination info:', $destInfo);
|
||||
$sourceAccount = $this->getAccount($type->type, 'source', $sourceInfo);
|
||||
$destinationAccount = $this->getAccount($type->type, 'destination', $destInfo, $sourceAccount);
|
||||
$destinationAccount = null;
|
||||
$sourceAccount = null;
|
||||
if (TransactionTypeEnum::DEPOSIT->value === $type->type) {
|
||||
Log::debug('Transaction type is deposit, start with destination first.');
|
||||
$destinationAccount = $this->getAccount($type->type, 'destination', $destInfo);
|
||||
$sourceAccount = $this->getAccount($type->type, 'source', $sourceInfo, $destinationAccount);
|
||||
}
|
||||
if (TransactionTypeEnum::DEPOSIT->value !== $type->type) {
|
||||
Log::debug('Transaction type is not deposit, start with source first.');
|
||||
$sourceAccount = $this->getAccount($type->type, 'source', $sourceInfo);
|
||||
$destinationAccount = $this->getAccount($type->type, 'destination', $destInfo, $sourceAccount);
|
||||
}
|
||||
|
||||
Log::debug('Done with getAccount(2x)');
|
||||
|
||||
// there is a safety catch here. If either account is NULL, they will be replaced with the cash account.
|
||||
if (null === $destinationAccount) {
|
||||
Log::warning('Destination account is NULL, will replace with cash account.');
|
||||
$destinationAccount = $this->accountRepository->getCashAccount();
|
||||
}
|
||||
|
||||
// this is the moment for a reconciliation sanity check (again).
|
||||
if (TransactionTypeEnum::RECONCILIATION->value === $type->type) {
|
||||
|
||||
@@ -137,9 +137,12 @@ class MonthReportGenerator implements ReportGeneratorInterface
|
||||
;
|
||||
$journals = $collector->getExtractedJournals();
|
||||
$journals = array_reverse($journals, true);
|
||||
// this call is correct.
|
||||
Log::debug(sprintf('getAuditReport: Call finalAccountBalance with date/time "%s"', $date->toIso8601String()));
|
||||
$dayBeforeBalance = Steam::finalAccountBalance($account, $date);
|
||||
|
||||
Log::debug(sprintf('getAuditReport: Call accountsBalancesOptimized with date/time "%s"', $date->toIso8601String()));
|
||||
// 2025-10-08 replace with accountsBalancesOptimized.
|
||||
// $dayBeforeBalance = Steam::finalAccountBalance($account, $date);
|
||||
$dayBeforeBalance = Steam::accountsBalancesOptimized(new Collection()->push($account), $date)[$account->id];
|
||||
|
||||
$startBalance = $dayBeforeBalance['balance'];
|
||||
$primaryCurrency = app('amount')->getPrimaryCurrencyByUserGroup($account->user->userGroup);
|
||||
$currency = $accountRepository->getAccountCurrency($account) ?? $primaryCurrency;
|
||||
@@ -176,12 +179,14 @@ class MonthReportGenerator implements ReportGeneratorInterface
|
||||
// call is correct.
|
||||
Log::debug(sprintf('getAuditReport end: Call finalAccountBalance with date/time "%s"', $this->end->toIso8601String()));
|
||||
|
||||
// 2025-10-08 replace with accountsBalancesOptimized:
|
||||
return [
|
||||
'journals' => $journals,
|
||||
'currency' => $currency,
|
||||
'exists' => 0 !== count($journals),
|
||||
'end' => $this->end->isoFormat((string) trans('config.month_and_day_moment_js', [], $locale)),
|
||||
'endBalance' => Steam::finalAccountBalance($account, $this->end)['balance'],
|
||||
// 'endBalance' => Steam::finalAccountBalance($account, $this->end)['balance'],
|
||||
'endBalance' => Steam::accountsBalancesOptimized(new Collection()->push($account), $this->end)[$account->id]['balance'],
|
||||
'dayBefore' => $date->isoFormat((string) trans('config.month_and_day_moment_js', [], $locale)),
|
||||
'dayBeforeBalance' => $dayBeforeBalance,
|
||||
];
|
||||
|
||||
@@ -27,6 +27,7 @@ use FireflyIII\Enums\WebhookTrigger;
|
||||
use FireflyIII\Events\RequestedSendWebhookMessages;
|
||||
use FireflyIII\Events\StoredTransactionGroup;
|
||||
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface;
|
||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||
@@ -105,10 +106,18 @@ class StoredGroupEventHandler
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($event->transactionGroup->transactionJournals as $journal) {
|
||||
/** @var null|Transaction $source */
|
||||
$source = $journal->transactions()->where('amount', '<', '0')->first();
|
||||
|
||||
/** @var null|Transaction $dest */
|
||||
$dest = $journal->transactions()->where('amount', '>', '0')->first();
|
||||
$repository->deleteStatisticsForModel($source->account, $journal->date);
|
||||
$repository->deleteStatisticsForModel($dest->account, $journal->date);
|
||||
|
||||
if (null !== $source) {
|
||||
$repository->deleteStatisticsForModel($source->account, $journal->date);
|
||||
}
|
||||
if (null !== $dest) {
|
||||
$repository->deleteStatisticsForModel($dest->account, $journal->date);
|
||||
}
|
||||
$categories = $journal->categories;
|
||||
$tags = $journal->tags;
|
||||
$budgets = $journal->budgets;
|
||||
|
||||
@@ -62,12 +62,13 @@ trait AccountCollection
|
||||
if (null === $account) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 2025-10-08 replace with accountsBalancesOptimized
|
||||
// the balance must be found BEFORE the transaction date.
|
||||
// so sub one second. This is not perfect, but works well enough.
|
||||
$date = clone $transaction['date'];
|
||||
$date->subSecond();
|
||||
Log::debug(sprintf('accountBalanceIs: Call finalAccountBalance with date/time "%s"', $date->toIso8601String()));
|
||||
$balance = Steam::finalAccountBalance($account, $date);
|
||||
// so inclusive = false
|
||||
Log::debug(sprintf('accountBalanceIs: Call accountsBalancesOptimized with date/time "%s"', $transaction['date']->toIso8601String()));
|
||||
$balance = Steam::accountsBalancesOptimized(new Collection()->push($account), $transaction['date'], convertToPrimary: null, inclusive: false)[$account->id];
|
||||
// $balance = Steam::finalAccountBalance($account, $date);
|
||||
$result = bccomp((string) $balance['balance'], $value);
|
||||
Log::debug(sprintf('"%s" vs "%s" is %d', $balance['balance'], $value, $result));
|
||||
|
||||
|
||||
@@ -25,8 +25,6 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Helpers\Report;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Deprecated;
|
||||
use FireflyIII\Enums\AccountTypeEnum;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\UserGroup;
|
||||
@@ -130,55 +128,4 @@ class NetWorth implements NetWorthInterface
|
||||
$this->accountRepository->setUserGroup($userGroup);
|
||||
|
||||
}
|
||||
|
||||
#[Deprecated]
|
||||
public function sumNetWorthByCurrency(Carbon $date): array
|
||||
{
|
||||
/**
|
||||
* Collect accounts
|
||||
*/
|
||||
$accounts = $this->getAccounts();
|
||||
$return = [];
|
||||
Log::debug(sprintf('SumNetWorth: accountsBalancesOptimized("%s")', $date->format('Y-m-d H:i:s')));
|
||||
$balances = Steam::accountsBalancesOptimized($accounts, $date);
|
||||
foreach ($accounts as $account) {
|
||||
$currency = $this->accountRepository->getAccountCurrency($account);
|
||||
$balance = $balances[$account->id]['balance'] ?? '0';
|
||||
|
||||
// always subtract virtual balance.
|
||||
$virtualBalance = $account->virtual_balance;
|
||||
if ('' !== $virtualBalance) {
|
||||
$balance = bcsub($balance, (string) $virtualBalance);
|
||||
}
|
||||
|
||||
$return[$currency->id] ??= [
|
||||
'id' => (string) $currency->id,
|
||||
'name' => $currency->name,
|
||||
'symbol' => $currency->symbol,
|
||||
'code' => $currency->code,
|
||||
'decimal_places' => $currency->decimal_places,
|
||||
'sum' => '0',
|
||||
];
|
||||
$return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], (string) $balance);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
private function getAccounts(): Collection
|
||||
{
|
||||
$accounts = $this->accountRepository->getAccountsByType(
|
||||
[AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value]
|
||||
);
|
||||
$filtered = new Collection();
|
||||
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
if (1 === (int) $this->accountRepository->getMetaValue($account, 'include_net_worth')) {
|
||||
$filtered->push($account);
|
||||
}
|
||||
}
|
||||
|
||||
return $filtered;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Helpers\Report;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Deprecated;
|
||||
use FireflyIII\Models\UserGroup;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
@@ -49,12 +48,4 @@ interface NetWorthInterface
|
||||
public function setUser(Authenticatable|User|null $user): void;
|
||||
|
||||
public function setUserGroup(UserGroup $userGroup): void;
|
||||
|
||||
/**
|
||||
* TODO move to repository
|
||||
*
|
||||
* Same as above but cleaner function with less dependencies.
|
||||
*/
|
||||
#[Deprecated]
|
||||
public function sumNetWorthByCurrency(Carbon $date): array;
|
||||
}
|
||||
|
||||
@@ -74,32 +74,30 @@ class IndexController extends Controller
|
||||
*/
|
||||
public function inactive(Request $request, string $objectType)
|
||||
{
|
||||
$inactivePage = true;
|
||||
$subTitle = (string) trans(sprintf('firefly.%s_accounts_inactive', $objectType));
|
||||
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType));
|
||||
$types = config(sprintf('firefly.accountTypesByIdentifier.%s', $objectType));
|
||||
$collection = $this->repository->getInactiveAccountsByType($types);
|
||||
$total = $collection->count();
|
||||
$page = 0 === (int) $request->get('page') ? 1 : (int) $request->get('page');
|
||||
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
|
||||
$accounts = $collection->slice(($page - 1) * $pageSize, $pageSize);
|
||||
$inactivePage = true;
|
||||
$subTitle = (string) trans(sprintf('firefly.%s_accounts_inactive', $objectType));
|
||||
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType));
|
||||
$types = config(sprintf('firefly.accountTypesByIdentifier.%s', $objectType));
|
||||
$collection = $this->repository->getInactiveAccountsByType($types);
|
||||
$total = $collection->count();
|
||||
$page = 0 === (int) $request->get('page') ? 1 : (int) $request->get('page');
|
||||
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
|
||||
$accounts = $collection->slice(($page - 1) * $pageSize, $pageSize);
|
||||
unset($collection);
|
||||
|
||||
/** @var Carbon $start */
|
||||
$start = clone session('start', today(config('app.timezone'))->startOfMonth());
|
||||
$start = clone session('start', today(config('app.timezone'))->startOfMonth());
|
||||
|
||||
/** @var Carbon $end */
|
||||
$end = clone session('end', today(config('app.timezone'))->endOfMonth());
|
||||
$end = clone session('end', today(config('app.timezone'))->endOfMonth());
|
||||
|
||||
// #10618 go to the end of the previous day.
|
||||
$start->subSecond();
|
||||
|
||||
$ids = $accounts->pluck('id')->toArray();
|
||||
Log::debug(sprintf('inactive start: accountsBalancesOptimized("%s")', $start->format('Y-m-d H:i:s')));
|
||||
Log::debug(sprintf('inactive end: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s')));
|
||||
$startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary);
|
||||
$endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary);
|
||||
$activities = Steam::getLastActivities($ids);
|
||||
$ids = $accounts->pluck('id')->toArray();
|
||||
Log::debug(sprintf('inactive start: accountsBalancesInRange("%s", "%s")', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
|
||||
[
|
||||
$startBalances,
|
||||
$endBalances,
|
||||
] = Steam::accountsBalancesInRange($accounts, $start, $end, $this->primaryCurrency, $this->convertToPrimary);
|
||||
$activities = Steam::getLastActivities($ids);
|
||||
|
||||
|
||||
$accounts->each(
|
||||
@@ -119,7 +117,7 @@ class IndexController extends Controller
|
||||
);
|
||||
|
||||
// make paginator:
|
||||
$accounts = new LengthAwarePaginator($accounts, $total, $pageSize, $page);
|
||||
$accounts = new LengthAwarePaginator($accounts, $total, $pageSize, $page);
|
||||
$accounts->setPath(route('accounts.inactive.index', [$objectType]));
|
||||
|
||||
return view('accounts.index', compact('objectType', 'inactivePage', 'subTitleIcon', 'subTitle', 'page', 'accounts'));
|
||||
@@ -169,14 +167,12 @@ class IndexController extends Controller
|
||||
/** @var Carbon $end */
|
||||
$end = clone session('end', today(config('app.timezone'))->endOfMonth());
|
||||
|
||||
// #10618 go to the end of the previous day.
|
||||
$start->subSecond();
|
||||
|
||||
$ids = $accounts->pluck('id')->toArray();
|
||||
Log::debug(sprintf('index start: accountsBalancesOptimized("%s")', $start->format('Y-m-d H:i:s')));
|
||||
Log::debug(sprintf('index end: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s')));
|
||||
$startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary);
|
||||
$endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary);
|
||||
Log::debug(sprintf('index: accountsBalancesInRange("%s", "%s")', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
|
||||
[
|
||||
$startBalances,
|
||||
$endBalances,
|
||||
] = Steam::accountsBalancesInRange($accounts, $start, $end, $this->primaryCurrency, $this->convertToPrimary);
|
||||
$activities = Steam::getLastActivities($ids);
|
||||
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ use FireflyIII\User;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Routing\Redirector;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\View\View;
|
||||
|
||||
@@ -113,13 +114,20 @@ class ReconcileController extends Controller
|
||||
$start->startOfDay();
|
||||
$end->endOfDay();
|
||||
|
||||
$startDate = clone $start;
|
||||
$startDate->subDay()->endOfDay(); // this is correct, subday endofday ends at 23:59:59
|
||||
// $startDate = clone $start;
|
||||
// $startDate->subDay()->endOfDay(); // this is correct, subday endofday ends at 23:59:59
|
||||
// both are validated and are correct.
|
||||
Log::debug(sprintf('reconcile: Call finalAccountBalance with date/time "%s"', $startDate->toIso8601String()));
|
||||
Log::debug(sprintf('reconcile2: Call finalAccountBalance with date/time "%s"', $end->toIso8601String()));
|
||||
$startBalance = Steam::bcround(Steam::finalAccountBalance($account, $startDate)['balance'], $currency->decimal_places);
|
||||
$endBalance = Steam::bcround(Steam::finalAccountBalance($account, $end)['balance'], $currency->decimal_places);
|
||||
// Log::debug(sprintf('reconcile: Call finalAccountBalance with date/time "%s"', $startDate->toIso8601String()));
|
||||
// Log::debug(sprintf('reconcile2: Call finalAccountBalance with date/time "%s"', $end->toIso8601String()));
|
||||
// $startBalance = Steam::bcround(Steam::finalAccountBalance($account, $startDate)['balance'], $currency->decimal_places);
|
||||
// $endBalance = Steam::bcround(Steam::finalAccountBalance($account, $end)['balance'], $currency->decimal_places);
|
||||
|
||||
// 2025-10-08 replace with accountsBalancesOptimized
|
||||
// no longer need to do subday->endofday on $start, set inclusive = false for the same effect.
|
||||
$startBalance = Steam::bcround(Steam::accountsBalancesOptimized(new Collection()->push($account), $start, convertToPrimary: null, inclusive: false)[$account->id]['balance'], $currency->decimal_places);
|
||||
$endBalance = Steam::bcround(Steam::accountsBalancesOptimized(new Collection()->push($account), $end)[$account->id]['balance'], $currency->decimal_places);
|
||||
|
||||
|
||||
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $account->accountType->type));
|
||||
$subTitle = (string) trans('firefly.reconcile_account', ['account' => $account->name]);
|
||||
|
||||
|
||||
@@ -169,8 +169,9 @@ class ShowController extends Controller
|
||||
$now = $end;
|
||||
}
|
||||
|
||||
Log::debug(sprintf('show: Call finalAccountBalance with date/time "%s"', $now->toIso8601String()));
|
||||
$balances = Steam::filterAccountBalance(Steam::finalAccountBalance($account, $now), $account, $this->convertToPrimary, $accountCurrency);
|
||||
// 2025-10-08 replace finalAccountBalance with accountsBalancesOptimized.
|
||||
$balances = Steam::accountsBalancesOptimized(new Collection()->push($account), $now)[$account->id];
|
||||
// $balances = Steam::filterAccountBalance(Steam::finalAccountBalance($account, $now), $account, $this->convertToPrimary, $accountCurrency);
|
||||
|
||||
return view(
|
||||
'accounts.show',
|
||||
@@ -237,8 +238,12 @@ class ShowController extends Controller
|
||||
$chartUrl = route('chart.account.period', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]);
|
||||
$showAll = true;
|
||||
// correct
|
||||
Log::debug(sprintf('showAll: Call finalAccountBalance with date/time "%s"', $end->toIso8601String()));
|
||||
$balances = Steam::filterAccountBalance(Steam::finalAccountBalance($account, $end), $account, $this->convertToPrimary, $accountCurrency);
|
||||
Log::debug(sprintf('showAll: Call accountsBalancesOptimized with date/time "%s"', $end->toIso8601String()));
|
||||
|
||||
// 2025-10-08 replace finalAccountBalance with accountsBalancesOptimized.
|
||||
// $balances = Steam::finalAccountBalance($account, $end);
|
||||
// $balances = Steam::filterAccountBalance($balances, $account, $this->convertToPrimary, $accountCurrency);
|
||||
$balances = Steam::accountsBalancesOptimized(new Collection()->push($account), $end)[$account->id];
|
||||
|
||||
return view(
|
||||
'accounts.show',
|
||||
|
||||
@@ -116,10 +116,11 @@ class AccountController extends Controller
|
||||
$accountNames = $this->extractNames($accounts);
|
||||
|
||||
// grab all balances
|
||||
Log::debug(sprintf('expenseAccounts: accountsBalancesOptimized("%s")', $start->format('Y-m-d H:i:s')));
|
||||
Log::debug(sprintf('expenseAccounts: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s')));
|
||||
$startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary);
|
||||
$endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary);
|
||||
Log::debug(sprintf('expenseAccounts: accountsBalancesInRange("%s", "%s")', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
|
||||
[
|
||||
$startBalances,
|
||||
$endBalances,
|
||||
] = Steam::accountsBalancesInRange($accounts, $start, $end, $this->primaryCurrency, $this->convertToPrimary);
|
||||
Log::debug('Done collecting balances');
|
||||
// loop the accounts, then check for balance and currency info.
|
||||
foreach ($accounts as $account) {
|
||||
@@ -521,9 +522,9 @@ class AccountController extends Controller
|
||||
$range = Steam::filterAccountBalances($range, $account, $this->convertToPrimary, $accountCurrency);
|
||||
Log::debug('Get and filter balance for entire range end');
|
||||
// temp, get end balance.
|
||||
Log::debug(sprintf('period: Call finalAccountBalance with date/time "%s"', $end->toIso8601String()));
|
||||
Steam::finalAccountBalance($account, $end);
|
||||
Log::debug('END temp get end balance done');
|
||||
// Log::debug(sprintf('period: Call finalAccountBalance with date/time "%s"', $end->toIso8601String()));
|
||||
// Steam::finalAccountBalance($account, $end);
|
||||
// Log::debug('END temp get end balance done');
|
||||
|
||||
$previous = array_values($range)[0];
|
||||
$accountCurrency ??= $this->primaryCurrency; // do this AFTER getting the balances.
|
||||
@@ -666,10 +667,11 @@ class AccountController extends Controller
|
||||
$accountNames = $this->extractNames($accounts);
|
||||
|
||||
// grab all balances
|
||||
Log::debug(sprintf('revAccounts: accountsBalancesOptimized("%s")', $start->format('Y-m-d H:i:s')));
|
||||
Log::debug(sprintf('revAccounts: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s')));
|
||||
$startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary);
|
||||
$endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary);
|
||||
Log::debug(sprintf('revAccounts: accountsBalancesInRange("%s", "%s")', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
|
||||
[
|
||||
$startBalances,
|
||||
$endBalances,
|
||||
] = Steam::accountsBalancesInRange($accounts, $start, $end, $this->primaryCurrency, $this->convertToPrimary);
|
||||
|
||||
|
||||
// loop the accounts, then check for balance and currency info.
|
||||
|
||||
@@ -77,6 +77,7 @@ abstract class Controller extends BaseController
|
||||
View::share('DEMO_USERNAME', config('firefly.demo_username'));
|
||||
View::share('DEMO_PASSWORD', config('firefly.demo_password'));
|
||||
View::share('FF_VERSION', config('firefly.version'));
|
||||
View::share('FF_BUILD_TIME', config('firefly.build_time'));
|
||||
|
||||
// is webhooks enabled?
|
||||
View::share('featuringWebhooks', true === config('firefly.feature_flags.webhooks') && true === config('firefly.allow_webhooks'));
|
||||
@@ -131,6 +132,7 @@ abstract class Controller extends BaseController
|
||||
$this->primaryCurrency = null;
|
||||
// get shown-intro-preference:
|
||||
if (auth()->check()) {
|
||||
View::share('anonymous', Steam::anonymous());
|
||||
$this->primaryCurrency = Amount::getPrimaryCurrency();
|
||||
$language = Steam::getLanguage();
|
||||
$locale = Steam::getLocale();
|
||||
|
||||
@@ -30,6 +30,7 @@ use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\Support\Http\Controllers\GetConfigurationData;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
@@ -114,6 +115,7 @@ class JavascriptController extends Controller
|
||||
'currencyCode' => $currency->code,
|
||||
'currencySymbol' => $currency->symbol,
|
||||
'accountingLocaleInfo' => $accounting,
|
||||
'anonymous' => var_export(Steam::anonymous(), true),
|
||||
'language' => $lang,
|
||||
'dateRangeTitle' => $dateRange['title'],
|
||||
'locale' => $locale,
|
||||
|
||||
@@ -38,6 +38,7 @@ use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\Support\Http\Controllers\DateCalculation;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class BoxController.
|
||||
@@ -165,7 +166,7 @@ class BoxController extends Controller
|
||||
$allAccounts = $accountRepository->getActiveAccountsByType(
|
||||
[AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value]
|
||||
);
|
||||
app('log')->debug(sprintf('Found %d accounts.', $allAccounts->count()));
|
||||
Log::debug(sprintf('Found %d accounts.', $allAccounts->count()));
|
||||
|
||||
// filter list on preference of being included.
|
||||
$filtered = $allAccounts->filter(
|
||||
@@ -173,7 +174,7 @@ class BoxController extends Controller
|
||||
$includeNetWorth = $accountRepository->getMetaValue($account, 'include_net_worth');
|
||||
$result = null === $includeNetWorth || '1' === $includeNetWorth;
|
||||
if (false === $result) {
|
||||
app('log')->debug(sprintf('Will not include "%s" in net worth charts.', $account->name));
|
||||
Log::debug(sprintf('Will not include "%s" in net worth charts.', $account->name));
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
@@ -81,6 +81,12 @@ class ReconcileController extends Controller
|
||||
if (!$start instanceof Carbon && !$end instanceof Carbon) {
|
||||
throw new FireflyException('Invalid dates submitted.');
|
||||
}
|
||||
if (!is_numeric($startBalance)) {
|
||||
$startBalance = '0';
|
||||
}
|
||||
if (!is_numeric($endBalance)) {
|
||||
$endBalance = '0';
|
||||
}
|
||||
if ($end->lt($start)) {
|
||||
[$start, $end] = [$end, $start];
|
||||
}
|
||||
@@ -197,10 +203,16 @@ class ReconcileController extends Controller
|
||||
|
||||
$currency = $this->accountRepos->getAccountCurrency($account) ?? $this->primaryCurrency;
|
||||
// correct
|
||||
Log::debug(sprintf('transactions: Call finalAccountBalance with date/time "%s"', $startDate->toIso8601String()));
|
||||
Log::debug(sprintf('transactions2: Call finalAccountBalance with date/time "%s"', $end->toIso8601String()));
|
||||
$startBalance = Steam::bcround(Steam::finalAccountBalance($account, $startDate)['balance'], $currency->decimal_places);
|
||||
$endBalance = Steam::bcround(Steam::finalAccountBalance($account, $end)['balance'], $currency->decimal_places);
|
||||
Log::debug(sprintf('transactions: Call accountsBalancesOptimized with date/time "%s"', $startDate->toIso8601String()));
|
||||
Log::debug(sprintf('transactions2: Call accountsBalancesOptimized with date/time "%s"', $end->toIso8601String()));
|
||||
|
||||
// 2025-10-08 replace finalAccountBalance with accountsBalancesOptimized
|
||||
// $startBalance = Steam::bcround(Steam::finalAccountBalance($account, $startDate)['balance'], $currency->decimal_places);
|
||||
// $endBalance = Steam::bcround(Steam::finalAccountBalance($account, $end)['balance'], $currency->decimal_places);
|
||||
|
||||
$startBalance = Steam::accountsBalancesOptimized(new Collection()->push($account), $startDate)[$account->id];
|
||||
$endBalance = Steam::accountsBalancesOptimized(new Collection()->push($account), $end)[$account->id];
|
||||
|
||||
|
||||
// get the transactions
|
||||
$selectionStart = clone $start;
|
||||
|
||||
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Rule;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Http\Requests\SelectTransactionsRequest;
|
||||
@@ -56,7 +57,7 @@ class SelectController extends Controller
|
||||
|
||||
$this->middleware(
|
||||
static function ($request, $next) {
|
||||
app('view')->share('title', (string) trans('firefly.rules'));
|
||||
app('view')->share('title', (string)trans('firefly.rules'));
|
||||
app('view')->share('mainTitleIcon', 'fa-random');
|
||||
|
||||
return $next($request);
|
||||
@@ -73,11 +74,20 @@ class SelectController extends Controller
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$accounts = implode(',', $request->get('accounts'));
|
||||
|
||||
// create new rule engine:
|
||||
$newRuleEngine = app(RuleEngineInterface::class);
|
||||
$newRuleEngine->setUser($user);
|
||||
|
||||
// add date operators.
|
||||
if (null !== $request->get('start')) {
|
||||
$startDate = new Carbon($request->get('start'));
|
||||
$newRuleEngine->addOperator(['type' => 'date_after', 'value' => $startDate->format('Y-m-d')]);
|
||||
}
|
||||
if (null !== $request->get('end')) {
|
||||
$endDate = new Carbon($request->get('end'));
|
||||
$newRuleEngine->addOperator(['type' => 'date_before', 'value' => $endDate->format('Y-m-d')]);
|
||||
}
|
||||
|
||||
// add extra operators:
|
||||
$newRuleEngine->addOperator(['type' => 'account_id', 'value' => $accounts]);
|
||||
|
||||
@@ -102,7 +112,7 @@ class SelectController extends Controller
|
||||
return redirect(route('rules.index'));
|
||||
}
|
||||
// does the user have shared accounts?
|
||||
$subTitle = (string) trans('firefly.apply_rule_selection', ['title' => $rule->title]);
|
||||
$subTitle = (string)trans('firefly.apply_rule_selection', ['title' => $rule->title]);
|
||||
|
||||
return view('rules.rule.select-transactions', compact('rule', 'subTitle'));
|
||||
}
|
||||
@@ -127,7 +137,7 @@ class SelectController extends Controller
|
||||
|
||||
// warn if nothing.
|
||||
if (0 === count($textTriggers)) {
|
||||
return response()->json(['html' => '', 'warning' => (string) trans('firefly.warning_no_valid_triggers')]);
|
||||
return response()->json(['html' => '', 'warning' => (string)trans('firefly.warning_no_valid_triggers')]);
|
||||
}
|
||||
|
||||
foreach ($textTriggers as $textTrigger) {
|
||||
@@ -160,7 +170,7 @@ class SelectController extends Controller
|
||||
// Warn the user if only a subset of transactions is returned
|
||||
$warning = '';
|
||||
if (0 === count($collection)) {
|
||||
$warning = (string) trans('firefly.warning_no_matching_transactions');
|
||||
$warning = (string)trans('firefly.warning_no_matching_transactions');
|
||||
}
|
||||
|
||||
// Return json response
|
||||
@@ -190,7 +200,7 @@ class SelectController extends Controller
|
||||
$triggers = $rule->ruleTriggers;
|
||||
|
||||
if (0 === count($triggers)) {
|
||||
return response()->json(['html' => '', 'warning' => (string) trans('firefly.warning_no_valid_triggers')]);
|
||||
return response()->json(['html' => '', 'warning' => (string)trans('firefly.warning_no_valid_triggers')]);
|
||||
}
|
||||
// create new rule engine:
|
||||
$newRuleEngine = app(RuleEngineInterface::class);
|
||||
@@ -202,7 +212,7 @@ class SelectController extends Controller
|
||||
|
||||
$warning = '';
|
||||
if (0 === count($collection)) {
|
||||
$warning = (string) trans('firefly.warning_no_matching_transactions');
|
||||
$warning = (string)trans('firefly.warning_no_matching_transactions');
|
||||
}
|
||||
|
||||
// Return json response
|
||||
|
||||
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\RuleGroup;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Http\Requests\SelectTransactionsRequest;
|
||||
@@ -73,6 +74,16 @@ class ExecutionController extends Controller
|
||||
$newRuleEngine = app(RuleEngineInterface::class);
|
||||
$newRuleEngine->setUser($user);
|
||||
|
||||
// add date operators.
|
||||
if (null !== $request->get('start')) {
|
||||
$startDate = new Carbon($request->get('start'));
|
||||
$newRuleEngine->addOperator(['type' => 'date_after', 'value' => $startDate->format('Y-m-d')]);
|
||||
}
|
||||
if (null !== $request->get('end')) {
|
||||
$endDate = new Carbon($request->get('end'));
|
||||
$newRuleEngine->addOperator(['type' => 'date_before', 'value' => $endDate->format('Y-m-d')]);
|
||||
}
|
||||
|
||||
// add extra operators:
|
||||
$newRuleEngine->addOperator(['type' => 'account_id', 'value' => $accounts]);
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\View\View;
|
||||
use Laravel\Passport\Passport;
|
||||
use phpseclib3\Crypt\RSA;
|
||||
@@ -81,8 +82,10 @@ class InstallController extends Controller
|
||||
public function index()
|
||||
{
|
||||
app('view')->share('FF_VERSION', config('firefly.version'));
|
||||
|
||||
// index will set FF3 version.
|
||||
FireflyConfig::set('ff3_version', (string) config('firefly.version'));
|
||||
FireflyConfig::set('ff3_build_time', (int) config('firefly.build_time'));
|
||||
|
||||
return view('install.index');
|
||||
}
|
||||
@@ -98,18 +101,18 @@ class InstallController extends Controller
|
||||
'errorMessage' => null,
|
||||
];
|
||||
|
||||
app('log')->debug(sprintf('Will now run commands. Request index is %d', $requestIndex));
|
||||
Log::debug(sprintf('Will now run commands. Request index is %d', $requestIndex));
|
||||
$indexes = array_keys($this->upgradeCommands);
|
||||
if (array_key_exists($requestIndex, $indexes)) {
|
||||
$command = $indexes[$requestIndex];
|
||||
$parameters = $this->upgradeCommands[$command];
|
||||
app('log')->debug(sprintf('Will now execute command "%s" with parameters', $command), $parameters);
|
||||
Log::debug(sprintf('Will now execute command "%s" with parameters', $command), $parameters);
|
||||
|
||||
try {
|
||||
$result = $this->executeCommand($command, $parameters);
|
||||
} catch (FireflyException $e) {
|
||||
app('log')->error($e->getMessage());
|
||||
app('log')->error($e->getTraceAsString());
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
if (str_contains($e->getMessage(), 'open_basedir restriction in effect')) {
|
||||
$this->lastError = self::BASEDIR_ERROR;
|
||||
}
|
||||
@@ -134,7 +137,7 @@ class InstallController extends Controller
|
||||
*/
|
||||
private function executeCommand(string $command, array $args): bool
|
||||
{
|
||||
app('log')->debug(sprintf('Will now call command %s with args.', $command), $args);
|
||||
Log::debug(sprintf('Will now call command %s with args.', $command), $args);
|
||||
|
||||
try {
|
||||
if ('generate-keys' === $command) {
|
||||
@@ -142,7 +145,7 @@ class InstallController extends Controller
|
||||
}
|
||||
if ('generate-keys' !== $command) {
|
||||
Artisan::call($command, $args);
|
||||
app('log')->debug(Artisan::output());
|
||||
Log::debug(Artisan::output());
|
||||
}
|
||||
} catch (Exception $e) { // intentional generic exception
|
||||
throw new FireflyException($e->getMessage(), 0, $e);
|
||||
|
||||
@@ -37,6 +37,7 @@ use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Services\Internal\Update\JournalUpdateService;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\Support\Http\Controllers\ModelInformation;
|
||||
use FireflyIII\Transformers\TransactionGroupTransformer;
|
||||
@@ -45,6 +46,7 @@ use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Redirector;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\View\View;
|
||||
|
||||
@@ -228,11 +230,13 @@ class ConvertController extends Controller
|
||||
foreach ($accountList as $account) {
|
||||
$date = today()->endOfDay();
|
||||
Log::debug(sprintf('getLiabilities: Call finalAccountBalance with date/time "%s"', $date->toIso8601String()));
|
||||
$balance = Steam::finalAccountBalance($account, $date)['balance'];
|
||||
// 2025-10-08 replace finalAccountBalance with accountsBalancesOptimized.
|
||||
// $balance = Steam::finalAccountBalance($account, $date)['balance'];
|
||||
$balance = Steam::accountsBalancesOptimized(new Collection()->push($account), $date)[$account->id]['balance'] ?? '0';
|
||||
$currency = $this->accountRepository->getAccountCurrency($account) ?? $this->primaryCurrency;
|
||||
$role = 'l_'.$account->accountType->type;
|
||||
$key = (string) trans('firefly.opt_group_'.$role);
|
||||
$grouped[$key][$account->id] = $account->name.' ('.app('amount')->formatAnything($currency, $balance, false).')';
|
||||
$role = sprintf('l_%s', $account->accountType->type);
|
||||
$key = (string) trans(sprintf('firefly.opt_group_%s', $role));
|
||||
$grouped[$key][$account->id] = sprintf('%s (%s)', $account->name, Amount::formatAnything($currency, $balance, false));
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
@@ -252,15 +256,18 @@ class ConvertController extends Controller
|
||||
foreach ($accountList as $account) {
|
||||
$date = today()->endOfDay();
|
||||
Log::debug(sprintf('getAssetAccounts: Call finalAccountBalance with date/time "%s"', $date->toIso8601String()));
|
||||
$balance = Steam::finalAccountBalance($account, $date)['balance'];
|
||||
// 2025-10-08 replace finalAccountBalance with accountsBalancesOptimized.
|
||||
// $balance = Steam::finalAccountBalance($account, $date)['balance'];
|
||||
$balance = Steam::accountsBalancesOptimized(new Collection()->push($account), $date)[$account->id]['balance'] ?? '0';
|
||||
|
||||
$currency = $this->accountRepository->getAccountCurrency($account) ?? $this->primaryCurrency;
|
||||
$role = (string) $this->accountRepository->getMetaValue($account, 'account_role');
|
||||
if ('' === $role) {
|
||||
$role = 'no_account_type';
|
||||
}
|
||||
|
||||
$key = (string) trans('firefly.opt_group_'.$role);
|
||||
$grouped[$key][$account->id] = $account->name.' ('.app('amount')->formatAnything($currency, $balance, false).')';
|
||||
$key = (string) trans(sprintf('firefly.opt_group_%s', $role));
|
||||
$grouped[$key][$account->id] = sprintf('%s (%s)', $account->name, Amount::formatAnything($currency, $balance, false));
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
|
||||
@@ -50,10 +50,11 @@ class AccountTasker implements AccountTaskerInterface, UserGroupInterface
|
||||
$yesterday = clone $start;
|
||||
$yesterday->subDay()->endOfDay(); // exactly up until $start but NOT including.
|
||||
$end->endOfDay(); // needs to be end of day to be correct.
|
||||
Log::debug(sprintf('getAccountReport: accountsBalancesOptimized("%s")', $yesterday->format('Y-m-d H:i:s')));
|
||||
Log::debug(sprintf('getAccountReport: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s')));
|
||||
$startSet = Steam::accountsBalancesOptimized($accounts, $yesterday);
|
||||
$endSet = Steam::accountsBalancesOptimized($accounts, $end);
|
||||
Log::debug(sprintf('getAccountReport: accountsBalancesInRange("%s", "%s")', $yesterday->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
|
||||
[
|
||||
$startSet,
|
||||
$endSet,
|
||||
] = Steam::accountsBalancesInRange($accounts, $yesterday, $end);
|
||||
Log::debug('Start of accountreport');
|
||||
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
|
||||
@@ -375,7 +375,9 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface, UserGroupInte
|
||||
{
|
||||
Log::debug(sprintf('leftOnAccount("%s","%s","%s")', $piggyBank->name, $account->name, $date->format('Y-m-d H:i:s')));
|
||||
Log::debug(sprintf('leftOnAccount: Call finalAccountBalance with date/time "%s"', $date->toIso8601String()));
|
||||
$balance = Steam::finalAccountBalance($account, $date)['balance'];
|
||||
// 2025-10-08 replace finalAccountBalance with accountsBalancesOptimized.
|
||||
$balance = Steam::accountsBalancesOptimized(new Collection()->push($account), $date)[$account->id]['balance'];
|
||||
// $balance = Steam::finalAccountBalance($account, $date)['balance'];
|
||||
|
||||
Log::debug(sprintf('Balance is: %s', $balance));
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface, UserGroupInte
|
||||
/** @var Rule $entry */
|
||||
foreach ($set as $entry) {
|
||||
if ($entry->order !== $count) {
|
||||
app('log')->debug(sprintf('Rule #%d was on spot %d but must be on spot %d', $entry->id, $entry->order, $count));
|
||||
Log::debug(sprintf('Rule #%d was on spot %d but must be on spot %d', $entry->id, $entry->order, $count));
|
||||
$entry->order = $count;
|
||||
$entry->save();
|
||||
}
|
||||
@@ -167,7 +167,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface, UserGroupInte
|
||||
if ($action->order !== $index) {
|
||||
$action->order = $index;
|
||||
$action->save();
|
||||
app('log')->debug(sprintf('Rule action #%d was on spot %d but must be on spot %d', $action->id, $action->order, $index));
|
||||
Log::debug(sprintf('Rule action #%d was on spot %d but must be on spot %d', $action->id, $action->order, $index));
|
||||
}
|
||||
++$index;
|
||||
}
|
||||
@@ -189,7 +189,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface, UserGroupInte
|
||||
if ($order !== $index) {
|
||||
$trigger->order = $index;
|
||||
$trigger->save();
|
||||
app('log')->debug(sprintf('Rule trigger #%d was on spot %d but must be on spot %d', $trigger->id, $order, $index));
|
||||
Log::debug(sprintf('Rule trigger #%d was on spot %d but must be on spot %d', $trigger->id, $order, $index));
|
||||
}
|
||||
++$index;
|
||||
}
|
||||
@@ -235,6 +235,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface, UserGroupInte
|
||||
public function getActiveStoreRules(RuleGroup $group): Collection
|
||||
{
|
||||
return $group->rules()
|
||||
->orderBy('rules.order', 'ASC')
|
||||
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
|
||||
->where('rule_triggers.trigger_type', 'user_action')
|
||||
->where('rule_triggers.trigger_value', 'store-journal')
|
||||
@@ -262,6 +263,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface, UserGroupInte
|
||||
[ // @phpstan-ignore-line
|
||||
'rules' => static function (HasMany $query): void {
|
||||
$query->orderBy('order', 'ASC');
|
||||
$query->where('rules.active', true);
|
||||
},
|
||||
'rules.ruleTriggers' => static function (HasMany $query): void {
|
||||
$query->orderBy('order', 'ASC');
|
||||
@@ -275,23 +277,23 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface, UserGroupInte
|
||||
if (null === $filter) {
|
||||
return $groups;
|
||||
}
|
||||
app('log')->debug(sprintf('Will filter getRuleGroupsWithRules on "%s".', $filter));
|
||||
Log::debug(sprintf('Will filter getRuleGroupsWithRules on "%s".', $filter));
|
||||
|
||||
return $groups->map(
|
||||
static function (RuleGroup $group) use ($filter) { // @phpstan-ignore-line
|
||||
app('log')->debug(sprintf('Now filtering group #%d', $group->id));
|
||||
Log::debug(sprintf('Now filtering group #%d', $group->id));
|
||||
// filter the rules in the rule group:
|
||||
$group->rules = $group->rules->filter(
|
||||
static function (Rule $rule) use ($filter) {
|
||||
app('log')->debug(sprintf('Now filtering rule #%d', $rule->id));
|
||||
Log::debug(sprintf('Now filtering rule #%d', $rule->id));
|
||||
foreach ($rule->ruleTriggers as $trigger) {
|
||||
if ('user_action' === $trigger->trigger_type && $filter === $trigger->trigger_value) {
|
||||
app('log')->debug(sprintf('Rule #%d triggers on %s, include it.', $rule->id, $filter));
|
||||
Log::debug(sprintf('Rule #%d triggers on %s, include it.', $rule->id, $filter));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
app('log')->debug(sprintf('Rule #%d does not trigger on %s, do not include it.', $rule->id, $filter));
|
||||
Log::debug(sprintf('Rule #%d does not trigger on %s, do not include it.', $rule->id, $filter));
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -318,6 +320,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface, UserGroupInte
|
||||
[ // @phpstan-ignore-line
|
||||
'rules' => static function (HasMany $query): void {
|
||||
$query->orderBy('order', 'ASC');
|
||||
$query->where('rules.active', true);
|
||||
},
|
||||
'rules.ruleTriggers' => static function (HasMany $query): void {
|
||||
$query->orderBy('order', 'ASC');
|
||||
@@ -331,23 +334,23 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface, UserGroupInte
|
||||
if (null === $filter) {
|
||||
return $groups;
|
||||
}
|
||||
app('log')->debug(sprintf('Will filter getRuleGroupsWithRules on "%s".', $filter));
|
||||
Log::debug(sprintf('Will filter getRuleGroupsWithRules on "%s".', $filter));
|
||||
|
||||
return $groups->map(
|
||||
static function (RuleGroup $group) use ($filter) { // @phpstan-ignore-line
|
||||
app('log')->debug(sprintf('Now filtering group #%d', $group->id));
|
||||
Log::debug(sprintf('Now filtering group #%d', $group->id));
|
||||
// filter the rules in the rule group:
|
||||
$group->rules = $group->rules->filter(
|
||||
static function (Rule $rule) use ($filter) {
|
||||
app('log')->debug(sprintf('Now filtering rule #%d', $rule->id));
|
||||
Log::debug(sprintf('Now filtering rule #%d', $rule->id));
|
||||
foreach ($rule->ruleTriggers as $trigger) {
|
||||
if ('user_action' === $trigger->trigger_type && $filter === $trigger->trigger_value) {
|
||||
app('log')->debug(sprintf('Rule #%d triggers on %s, include it.', $rule->id, $filter));
|
||||
Log::debug(sprintf('Rule #%d triggers on %s, include it.', $rule->id, $filter));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
app('log')->debug(sprintf('Rule #%d does not trigger on %s, do not include it.', $rule->id, $filter));
|
||||
Log::debug(sprintf('Rule #%d does not trigger on %s, do not include it.', $rule->id, $filter));
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -414,7 +417,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface, UserGroupInte
|
||||
->decrement('order')
|
||||
;
|
||||
$ruleGroup->order = $newOrder;
|
||||
app('log')->debug(sprintf('Order of group #%d ("%s") is now %d', $ruleGroup->id, $ruleGroup->title, $newOrder));
|
||||
Log::debug(sprintf('Order of group #%d ("%s") is now %d', $ruleGroup->id, $ruleGroup->title, $newOrder));
|
||||
$ruleGroup->save();
|
||||
|
||||
return;
|
||||
@@ -425,7 +428,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface, UserGroupInte
|
||||
->increment('order')
|
||||
;
|
||||
$ruleGroup->order = $newOrder;
|
||||
app('log')->debug(sprintf('Order of group #%d ("%s") is now %d', $ruleGroup->id, $ruleGroup->title, $newOrder));
|
||||
Log::debug(sprintf('Order of group #%d ("%s") is now %d', $ruleGroup->id, $ruleGroup->title, $newOrder));
|
||||
$ruleGroup->save();
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,11 @@ class IsValidSortInstruction implements ValidationRule
|
||||
|
||||
return;
|
||||
}
|
||||
if ('' === $value) {
|
||||
// don't validate.
|
||||
|
||||
return;
|
||||
}
|
||||
$validParameters = config(sprintf('firefly.allowed_sort_parameters.%s', $shortClass));
|
||||
if (!is_array($validParameters)) {
|
||||
$fail('validation.no_sort_instructions')->translate(['object' => $shortClass]);
|
||||
|
||||
@@ -88,6 +88,7 @@ trait JournalServiceTrait
|
||||
|
||||
// the account that Firefly III creates must be "creatable", aka select the one we can create from the list just in case
|
||||
$creatableType = $this->getCreatableType($expectedTypes[$transactionType]);
|
||||
Log::debug(sprintf('Creatable type is "%s"', $creatableType), $expectedTypes[$transactionType]);
|
||||
|
||||
// if the result is NULL but the ID is set, an account could exist of the wrong type.
|
||||
// that data can be used to create a new account of the right type.
|
||||
@@ -227,9 +228,11 @@ trait JournalServiceTrait
|
||||
}
|
||||
|
||||
// find by preferred type.
|
||||
Log::debug('Find by preferred type.');
|
||||
$result = $this->accountRepository->findByName($data['name'], [$types[0]]);
|
||||
|
||||
// or any expected type.
|
||||
Log::debug('Find by any expected type.');
|
||||
$result ??= $this->accountRepository->findByName($data['name'], $types);
|
||||
|
||||
if (null !== $result) {
|
||||
|
||||
@@ -163,6 +163,7 @@ class Amount
|
||||
*/
|
||||
public function formatFlat(string $symbol, int $decimalPlaces, string $amount, ?bool $coloured = null): string
|
||||
{
|
||||
$amount = Steam::anonymous() ? '0' : $amount;
|
||||
$locale = Steam::getLocale();
|
||||
$rounded = Steam::bcround($amount, $decimalPlaces);
|
||||
$coloured ??= true;
|
||||
|
||||
@@ -49,14 +49,14 @@ class RemoteUserGuard implements Guard
|
||||
{
|
||||
/** @var null|Request $request */
|
||||
$request = $app->get('request');
|
||||
Log::debug(sprintf('Created RemoteUserGuard for %s "%s"', $request?->getMethod(), $request?->getRequestUri()));
|
||||
// Log::debug(sprintf('Created RemoteUserGuard for %s "%s"', $request?->getMethod(), $request?->getRequestUri()));
|
||||
$this->application = $app;
|
||||
$this->user = null;
|
||||
}
|
||||
|
||||
public function authenticate(): void
|
||||
{
|
||||
Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
// Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
if ($this->user instanceof User) {
|
||||
Log::debug(sprintf('%s is found: #%d, "%s".', $this->user::class, $this->user->id, $this->user->email));
|
||||
|
||||
@@ -104,21 +104,21 @@ class RemoteUserGuard implements Guard
|
||||
|
||||
public function check(): bool
|
||||
{
|
||||
Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
// Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
|
||||
return $this->user() instanceof User;
|
||||
}
|
||||
|
||||
public function guest(): bool
|
||||
{
|
||||
Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
// Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
|
||||
return !$this->check();
|
||||
}
|
||||
|
||||
public function hasUser(): bool
|
||||
{
|
||||
Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
// Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
|
||||
throw new FireflyException('Did not implement RemoteUserGuard::hasUser()');
|
||||
}
|
||||
@@ -128,14 +128,14 @@ class RemoteUserGuard implements Guard
|
||||
*/
|
||||
public function id(): int|string|null
|
||||
{
|
||||
Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
// Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
|
||||
return $this->user?->id;
|
||||
}
|
||||
|
||||
public function setUser(Authenticatable|User|null $user): void // @phpstan-ignore-line
|
||||
{
|
||||
Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
// Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
if ($user instanceof User) {
|
||||
$this->user = $user;
|
||||
|
||||
@@ -146,7 +146,7 @@ class RemoteUserGuard implements Guard
|
||||
|
||||
public function user(): ?User
|
||||
{
|
||||
Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
// Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
$user = $this->user;
|
||||
if (!$user instanceof User) {
|
||||
Log::debug('User is NULL');
|
||||
@@ -164,14 +164,14 @@ class RemoteUserGuard implements Guard
|
||||
*/
|
||||
public function validate(array $credentials = []): bool
|
||||
{
|
||||
Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
// Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
|
||||
throw new FireflyException('Did not implement RemoteUserGuard::validate()');
|
||||
}
|
||||
|
||||
public function viaRemember(): bool
|
||||
{
|
||||
Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
// Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -634,10 +634,10 @@ trait PeriodOverview
|
||||
$currencySymbol = $journal['currency_symbol'];
|
||||
$currencyDecimalPlaces = $journal['currency_decimal_places'];
|
||||
$foreignCurrencyId = $journal['foreign_currency_id'];
|
||||
$amount = $journal['amount'] ?? '0';
|
||||
$amount = (string) ($journal['amount'] ?? '0');
|
||||
|
||||
if ($this->convertToPrimary && $currencyId !== $this->primaryCurrency->id && $foreignCurrencyId !== $this->primaryCurrency->id) {
|
||||
$amount = $journal['pc_amount'] ?? '0';
|
||||
$amount = (string) ($journal['pc_amount'] ?? '0');
|
||||
$currencyId = $this->primaryCurrency->id;
|
||||
$currencyCode = $this->primaryCurrency->code;
|
||||
$currencyName = $this->primaryCurrency->name;
|
||||
@@ -650,7 +650,7 @@ trait PeriodOverview
|
||||
$currencyName = $journal['foreign_currency_name'];
|
||||
$currencySymbol = $journal['foreign_currency_symbol'];
|
||||
$currencyDecimalPlaces = $journal['foreign_currency_decimal_places'];
|
||||
$amount = $journal['foreign_amount'] ?? '0';
|
||||
$amount = (string) ($journal['foreign_amount'] ?? '0');
|
||||
}
|
||||
$return[$currencyId] ??= [
|
||||
'amount' => '0',
|
||||
|
||||
@@ -289,8 +289,10 @@ class AccountEnrichment implements EnrichmentInterface
|
||||
{
|
||||
$this->balances = Steam::accountsBalancesOptimized($this->collection, $this->getDate(), $this->primaryCurrency, $this->convertToPrimary);
|
||||
if ($this->start instanceof Carbon && $this->end instanceof Carbon) {
|
||||
$this->startBalances = Steam::accountsBalancesOptimized($this->collection, $this->start, $this->primaryCurrency, $this->convertToPrimary);
|
||||
$this->endBalances = Steam::accountsBalancesOptimized($this->collection, $this->end, $this->primaryCurrency, $this->convertToPrimary);
|
||||
[
|
||||
$this->startBalances,
|
||||
$this->endBalances,
|
||||
] = Steam::accountsBalancesInRange($this->collection, $this->start, $this->end, $this->primaryCurrency, $this->convertToPrimary);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -154,6 +154,9 @@ class BudgetLimitEnrichment implements EnrichmentInterface
|
||||
$this->start = $this->collection->min('start_date') ?? Carbon::now()->startOfMonth();
|
||||
$this->end = $this->collection->max('end_date') ?? Carbon::now()->endOfMonth();
|
||||
|
||||
// #11096 make sure that the max end date is also at the end of the day,
|
||||
$this->end->endOfDay();
|
||||
|
||||
/** @var BudgetLimit $limit */
|
||||
foreach ($this->collection as $limit) {
|
||||
$id = (int)$limit->id;
|
||||
|
||||
@@ -420,6 +420,7 @@ class Navigation
|
||||
'week' => (string)trans('config.week_in_year_js'),
|
||||
'weekly' => (string)trans('config.week_in_year_js'),
|
||||
'1M' => (string)trans('config.month_js'),
|
||||
'MTD' => (string)trans('config.month_js'),
|
||||
'month' => (string)trans('config.month_js'),
|
||||
'monthly' => (string)trans('config.month_js'),
|
||||
'1Y' => (string)trans('config.year_js'),
|
||||
@@ -427,6 +428,11 @@ class Navigation
|
||||
'year' => (string)trans('config.year_js'),
|
||||
'yearly' => (string)trans('config.year_js'),
|
||||
'6M' => (string)trans('config.half_year_js'),
|
||||
'last7' => (string)trans('config.specific_day_js'),
|
||||
'last30' => (string)trans('config.month_js'),
|
||||
'last90' => (string)trans('config.month_js'),
|
||||
'last365' => (string)trans('config.year_js'),
|
||||
'QTD' => (string)trans('config.month_js'),
|
||||
];
|
||||
|
||||
if (array_key_exists($repeatFrequency, $formatMap)) {
|
||||
|
||||
@@ -402,21 +402,21 @@ trait ConvertsDataTypes
|
||||
*/
|
||||
protected function getCarbonDate(string $field): ?Carbon
|
||||
{
|
||||
$result = null;
|
||||
$data = (string)$this->get($field);
|
||||
Log::debug(sprintf('Date string is "%s"', $data));
|
||||
|
||||
Log::debug(sprintf('Date string is "%s"', (string)$this->get($field)));
|
||||
if ('' === $data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$result = '' !== (string)$this->get($field) ? new Carbon((string)$this->get($field), config('app.timezone')) : null;
|
||||
return new Carbon($data, config('app.timezone'));
|
||||
} catch (InvalidFormatException) {
|
||||
// @ignoreException
|
||||
Log::debug(sprintf('Exception when parsing date "%s".', $this->get($field)));
|
||||
}
|
||||
if (!$result instanceof Carbon) {
|
||||
Log::debug(sprintf('Exception when parsing date "%s".', $this->get($field)));
|
||||
Log::debug(sprintf('Exception when parsing date "%s".', $data));
|
||||
}
|
||||
|
||||
return $result;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,9 +49,9 @@ use function Safe\preg_replace;
|
||||
*/
|
||||
class Steam
|
||||
{
|
||||
public function accountsBalancesOptimized(Collection $accounts, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null): array
|
||||
public function accountsBalancesOptimized(Collection $accounts, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null, bool $inclusive = true): array
|
||||
{
|
||||
Log::debug(sprintf('accountsBalancesOptimized: Called for %d account(s) with date/time "%s"', $accounts->count(), $date->toIso8601String()));
|
||||
Log::debug(sprintf('accountsBalancesOptimized: Called for %d account(s) with date/time "%s" (inclusive: %s)', $accounts->count(), $date->toIso8601String(), var_export($inclusive, true)));
|
||||
$result = [];
|
||||
$convertToPrimary ??= Amount::convertToPrimary();
|
||||
$primary ??= Amount::getPrimaryCurrency();
|
||||
@@ -61,14 +61,15 @@ class Steam
|
||||
$arrayOfSums = Transaction::whereIn('account_id', $accounts->pluck('id')->toArray())
|
||||
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id')
|
||||
->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s'))
|
||||
->where('transaction_journals.date', $inclusive ? '<=' : '<', $date->format('Y-m-d H:i:s'))
|
||||
->groupBy(['transactions.account_id', 'transaction_currencies.code'])
|
||||
->get(['transactions.account_id', 'transaction_currencies.code', DB::raw('SUM(transactions.amount) as sum_of_amount')])->toArray()
|
||||
;
|
||||
|
||||
Log::debug('Array of sums: ', $arrayOfSums);
|
||||
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
// this array is PER account, so we wait a bit before we change code here.
|
||||
$return = [
|
||||
'pc_balance' => '0',
|
||||
'balance' => '0', // this key is overwritten right away, but I must remember it is always created.
|
||||
@@ -76,19 +77,19 @@ class Steam
|
||||
$currency = $currencies[$account->id];
|
||||
|
||||
// second array
|
||||
$accountSum = array_filter($arrayOfSums, fn ($entry) => $entry['account_id'] === $account->id);
|
||||
if (0 === count($accountSum)) {
|
||||
$accountSums = array_filter($arrayOfSums, fn ($entry) => $entry['account_id'] === $account->id);
|
||||
if (0 === count($accountSums)) {
|
||||
$result[$account->id] = $return;
|
||||
|
||||
continue;
|
||||
}
|
||||
$accountSum = array_values($accountSum)[0];
|
||||
$sumOfAmount = (string)$accountSum['sum_of_amount'];
|
||||
$sumOfAmount = $this->floatalize('' === $sumOfAmount ? '0' : $sumOfAmount);
|
||||
$sumsByCode = [
|
||||
$accountSum['code'] => $sumOfAmount,
|
||||
];
|
||||
|
||||
$sumsByCode = [];
|
||||
foreach ($accountSums as $accountSum) {
|
||||
// $accountSum = array_values($accountSum)[0];
|
||||
$sumOfAmount = (string)$accountSum['sum_of_amount'];
|
||||
$sumOfAmount = $this->floatalize('' === $sumOfAmount ? '0' : $sumOfAmount);
|
||||
$sumsByCode[$accountSum['code']] = $sumOfAmount;
|
||||
}
|
||||
// Log::debug('All balances are (joined)', $others);
|
||||
// if there is no request to convert, take this as "balance" and "pc_balance".
|
||||
$return['balance'] = $sumsByCode[$currency->code] ?? '0';
|
||||
@@ -96,6 +97,7 @@ class Steam
|
||||
unset($return['pc_balance']);
|
||||
// Log::debug(sprintf('Set balance to %s, unset pc_balance', $return['balance']));
|
||||
}
|
||||
|
||||
// if there is a request to convert, convert to "pc_balance" and use "balance" for whichever amount is in the primary currency.
|
||||
if ($convertToPrimary) {
|
||||
$return['pc_balance'] = $this->convertAllBalances($sumsByCode, $primary, $date);
|
||||
@@ -119,12 +121,23 @@ class Steam
|
||||
}
|
||||
$final = array_merge($return, $sumsByCode);
|
||||
$result[$account->id] = $final;
|
||||
// Log::debug('Final balance is', $final);
|
||||
Log::debug(sprintf('Final balance for account #%d is', $account->id), $final);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls accountsBalancesOptimized for the given accounts and makes sure that inclusive is set to false, so it properly gets the balance of a range.
|
||||
*/
|
||||
public function accountsBalancesInRange(Collection $accounts, Carbon $start, Carbon $end, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null): array
|
||||
{
|
||||
return [
|
||||
$this->accountsBalancesOptimized($accounts, $start, $primary, $convertToPrimary, inclusive: false),
|
||||
$this->accountsBalancesOptimized($accounts, $end, $primary, $convertToPrimary),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* https://stackoverflow.com/questions/1642614/how-to-ceil-floor-and-round-bcmath-numbers
|
||||
*/
|
||||
@@ -276,7 +289,9 @@ class Steam
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns smaller than or equal to, so be careful with END OF DAY.
|
||||
* @deprecated
|
||||
* By default this method returns "smaller than or equal to", so be careful with END OF DAY.
|
||||
* If you need end of day balance, use "inclusive = false".
|
||||
*
|
||||
* Returns the balance of an account at exact moment given. Array with at least one value.
|
||||
* Always returns:
|
||||
@@ -288,18 +303,17 @@ class Steam
|
||||
* --> "pc_balance": balance in the user's primary currency, with all amounts converted to the primary currency.
|
||||
* "EUR": balance in EUR (or whatever currencies the account has balance in)
|
||||
*/
|
||||
public function finalAccountBalance(Account $account, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null): array
|
||||
public function finalAccountBalance(Account $account, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null, bool $inclusive = true): array
|
||||
{
|
||||
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($account->id);
|
||||
$cache->addProperty($date);
|
||||
if ($cache->has()) {
|
||||
Log::debug(sprintf('CACHED finalAccountBalance(#%d, %s)', $account->id, $date->format('Y-m-d H:i:s')));
|
||||
|
||||
Log::debug(sprintf('CACHED finalAccountBalance(#%d, %s, inclusive:%s)', $account->id, $date->format('Y-m-d H:i:s'), var_export($inclusive, true)));
|
||||
// return $cache->get();
|
||||
}
|
||||
// Log::debug(sprintf('finalAccountBalance(#%d, %s)', $account->id, $date->format('Y-m-d H:i:s')));
|
||||
Log::debug(sprintf('finalAccountBalance(#%d, %s)', $account->id, $date->format('Y-m-d H:i:s')));
|
||||
if (null === $convertToPrimary) {
|
||||
$convertToPrimary = Amount::convertToPrimary($account->user);
|
||||
}
|
||||
@@ -312,7 +326,6 @@ class Steam
|
||||
$accountCurrency = $account->meta['currency'];
|
||||
}
|
||||
if (!$currencyPresent) {
|
||||
|
||||
$accountCurrency = $this->getAccountCurrency($account);
|
||||
}
|
||||
$hasCurrency = null !== $accountCurrency;
|
||||
@@ -325,11 +338,11 @@ class Steam
|
||||
$array = $account->transactions()
|
||||
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id')
|
||||
->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s'))
|
||||
->where('transaction_journals.date', $inclusive ? '<=' : '<', $date->format('Y-m-d H:i:s'))
|
||||
->get(['transaction_currencies.code', 'transactions.amount'])->toArray()
|
||||
;
|
||||
$others = $this->groupAndSumTransactions($array, 'code', 'amount');
|
||||
// Log::debug('All balances are (joined)', $others);
|
||||
Log::debug('All balances are (joined)', $others);
|
||||
// if there is no request to convert, take this as "balance" and "pc_balance".
|
||||
$return['balance'] = $others[$currency->code] ?? '0';
|
||||
if (!$convertToPrimary) {
|
||||
@@ -338,7 +351,7 @@ class Steam
|
||||
}
|
||||
// if there is a request to convert, convert to "pc_balance" and use "balance" for whichever amount is in the primary currency.
|
||||
if ($convertToPrimary) {
|
||||
$return['pc_balance'] = $this->convertAllBalances($others, $primary, $date); // todo sum all and convert.
|
||||
$return['pc_balance'] = $this->convertAllBalances($others, $primary, $date);
|
||||
// Log::debug(sprintf('Set pc_balance to %s', $return['pc_balance']));
|
||||
}
|
||||
|
||||
@@ -358,18 +371,21 @@ class Steam
|
||||
// Log::debug(sprintf('Virtual balance makes the (primary currency) total %s', $return['balance']));
|
||||
}
|
||||
$final = array_merge($return, $others);
|
||||
// Log::debug('Final balance is', $final);
|
||||
Log::debug('Final balance is', $final);
|
||||
$cache->store($final);
|
||||
|
||||
return $final;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the balance for the given account in the range, with daily precision.
|
||||
*/
|
||||
public function finalAccountBalanceInRange(Account $account, Carbon $start, Carbon $end, bool $convertToPrimary): array
|
||||
{
|
||||
// expand period.
|
||||
$start->startOfDay();
|
||||
$end->endOfDay();
|
||||
Log::debug(sprintf('finalAccountBalanceInRange(#%d, %s, %s)', $account->id, $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
|
||||
Log::debug(sprintf('called finalAccountBalanceInRange(#%d, %s, %s)', $account->id, $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
|
||||
|
||||
// set up cache
|
||||
$cache = new CacheProperties();
|
||||
@@ -379,28 +395,21 @@ class Steam
|
||||
$cache->addProperty($convertToPrimary);
|
||||
$cache->addProperty($end);
|
||||
if ($cache->has()) {
|
||||
return $cache->get();
|
||||
Log::debug('Return cached finalAccountBalanceInRange');
|
||||
// return $cache->get();
|
||||
}
|
||||
|
||||
$balances = [];
|
||||
$formatted = $start->format('Y-m-d');
|
||||
/*
|
||||
* To make sure the start balance is correct, we need to get the balance at the exact end of the previous day.
|
||||
* Since we just did "startOfDay" we can do subDay()->endOfDay() to get the correct moment.
|
||||
* THAT will be the start balance.
|
||||
*/
|
||||
$request = clone $start;
|
||||
$request->subDay()->endOfDay();
|
||||
Log::debug('Get first balance to start.');
|
||||
Log::debug(sprintf('finalAccountBalanceInRange: Call finalAccountBalance with date/time "%s"', $request->toIso8601String()));
|
||||
$startBalance = $this->finalAccountBalance($account, $request);
|
||||
// 2025-10-08 replaced finalAccountBalance with accountsBalancesOptimized:
|
||||
$primaryCurrency = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup);
|
||||
$startBalance = $this->accountsBalancesOptimized(new Collection()->push($account), $start, $primaryCurrency, $convertToPrimary, false)[$account->id];
|
||||
$accountCurrency = $this->getAccountCurrency($account);
|
||||
$hasCurrency = $accountCurrency instanceof TransactionCurrency;
|
||||
$currency = $accountCurrency ?? $primaryCurrency;
|
||||
Log::debug(sprintf('Currency is %s', $currency->code));
|
||||
|
||||
|
||||
// set start balances:
|
||||
$startBalance[$currency->code] ??= '0';
|
||||
if ($hasCurrency) {
|
||||
@@ -542,7 +551,7 @@ class Steam
|
||||
try {
|
||||
$hostName = gethostbyaddr($ipAddress);
|
||||
} catch (Exception $e) {
|
||||
app('log')->error($e->getMessage());
|
||||
Log::error($e->getMessage());
|
||||
$hostName = $ipAddress;
|
||||
}
|
||||
|
||||
@@ -618,6 +627,19 @@ class Steam
|
||||
return $locale;
|
||||
}
|
||||
|
||||
public function anonymous(): bool // get preference
|
||||
{
|
||||
$singleton = PreferencesSingleton::getInstance();
|
||||
$cached = $singleton->getPreference('anonymous');
|
||||
if (null !== $cached) {
|
||||
return $cached;
|
||||
}
|
||||
$anonymous = app('preferences')->get('anonymous', config('firefly.default_preferences.anonymous', false))->data;
|
||||
$singleton->setPreference('anonymous', $anonymous);
|
||||
|
||||
return $anonymous;
|
||||
}
|
||||
|
||||
public function getLocaleArray(string $locale): array
|
||||
{
|
||||
return [
|
||||
@@ -757,7 +779,7 @@ class Steam
|
||||
$current = $converter->convert($currency, $primary, $date, $amount);
|
||||
Log::debug(sprintf('Convert %s %s to %s %s', $currency->code, $amount, $primary->code, $current));
|
||||
}
|
||||
$total = bcadd((string) $current, $total);
|
||||
$total = bcadd((string)$current, $total);
|
||||
}
|
||||
|
||||
return $total;
|
||||
|
||||
@@ -67,32 +67,16 @@ trait IsOldVersion
|
||||
protected function isOldVersionInstalled(): bool
|
||||
{
|
||||
// version compare thing.
|
||||
$configVersion = (string)config('firefly.version');
|
||||
$dbVersion = (string)FireflyConfig::getFresh('ff3_version', '1.0')->data;
|
||||
$compare = 0;
|
||||
// compare develop to develop
|
||||
if (str_starts_with($configVersion, 'develop') && str_starts_with($dbVersion, 'develop')) {
|
||||
$compare = $this->compareDevelopVersions($configVersion, $dbVersion);
|
||||
}
|
||||
// user has develop installed, goes to normal version.
|
||||
if (!str_starts_with($configVersion, 'develop') && str_starts_with($dbVersion, 'develop')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// user has normal, goes to develop version.
|
||||
if (str_starts_with($configVersion, 'develop') && !str_starts_with($dbVersion, 'develop')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// compare normal with normal.
|
||||
if (!str_starts_with($configVersion, 'develop') && !str_starts_with($dbVersion, 'develop')) {
|
||||
$compare = version_compare($configVersion, $dbVersion);
|
||||
}
|
||||
if (-1 === $compare) {
|
||||
Log::warning(sprintf('The current configured Firefly III version (%s) is older than the required version (%s). Redirect to migrate routine.', $dbVersion, $configVersion));
|
||||
$configBuildTime = (int)config('firefly.build_time');
|
||||
$dbBuildTime = (int)FireflyConfig::getFresh('ff3_build_time', 123)->data;
|
||||
$configTime = Carbon::createFromTimestamp($configBuildTime, config('app.timezone'));
|
||||
$dbTime = Carbon::createFromTimestamp($dbBuildTime, config('app.timezone'));
|
||||
if ($dbBuildTime < $configBuildTime) {
|
||||
Log::warning(sprintf('Your database was last managed by an older version of Firefly III (I see %s, I expect %s). Redirect to migrate routine.', $dbTime->format('Y-m-d H:i:s'), $configTime->format('Y-m-d H:i:s')));
|
||||
|
||||
return true;
|
||||
}
|
||||
Log::debug(sprintf('Your database is up to date (I see %s, I expect %s).', $dbTime->format('Y-m-d H:i:s'), $configTime->format('Y-m-d H:i:s')));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Support\System;
|
||||
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use Illuminate\Contracts\Encryption\DecryptException;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
@@ -63,10 +64,10 @@ class OAuthKeys
|
||||
$privateKey = '';
|
||||
$publicKey = '';
|
||||
// better check if keys are in the database:
|
||||
if (app('fireflyconfig')->has(self::PRIVATE_KEY) && app('fireflyconfig')->has(self::PUBLIC_KEY)) {
|
||||
if (FireflyConfig::has(self::PRIVATE_KEY) && FireflyConfig::has(self::PUBLIC_KEY)) {
|
||||
try {
|
||||
$privateKey = (string)app('fireflyconfig')->get(self::PRIVATE_KEY)?->data;
|
||||
$publicKey = (string)app('fireflyconfig')->get(self::PUBLIC_KEY)?->data;
|
||||
$privateKey = (string)FireflyConfig::get(self::PRIVATE_KEY)?->data;
|
||||
$publicKey = (string)FireflyConfig::get(self::PUBLIC_KEY)?->data;
|
||||
} catch (ContainerExceptionInterface|FireflyException|NotFoundExceptionInterface $e) {
|
||||
app('log')->error(sprintf('Could not validate keysInDatabase(): %s', $e->getMessage()));
|
||||
app('log')->error($e->getTraceAsString());
|
||||
@@ -87,8 +88,8 @@ class OAuthKeys
|
||||
*/
|
||||
public static function restoreKeysFromDB(): bool
|
||||
{
|
||||
$privateKey = (string)app('fireflyconfig')->get(self::PRIVATE_KEY)?->data;
|
||||
$publicKey = (string)app('fireflyconfig')->get(self::PUBLIC_KEY)?->data;
|
||||
$privateKey = (string)FireflyConfig::get(self::PRIVATE_KEY)?->data;
|
||||
$publicKey = (string)FireflyConfig::get(self::PUBLIC_KEY)?->data;
|
||||
|
||||
try {
|
||||
$privateContent = Crypt::decrypt($privateKey);
|
||||
@@ -98,8 +99,8 @@ class OAuthKeys
|
||||
app('log')->error($e->getMessage());
|
||||
|
||||
// delete config vars from DB:
|
||||
app('fireflyconfig')->delete(self::PRIVATE_KEY);
|
||||
app('fireflyconfig')->delete(self::PUBLIC_KEY);
|
||||
FireflyConfig::delete(self::PRIVATE_KEY);
|
||||
FireflyConfig::delete(self::PUBLIC_KEY);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -115,8 +116,8 @@ class OAuthKeys
|
||||
{
|
||||
$private = storage_path('oauth-private.key');
|
||||
$public = storage_path('oauth-public.key');
|
||||
app('fireflyconfig')->set(self::PRIVATE_KEY, Crypt::encrypt(file_get_contents($private)));
|
||||
app('fireflyconfig')->set(self::PUBLIC_KEY, Crypt::encrypt(file_get_contents($public)));
|
||||
FireflyConfig::set(self::PRIVATE_KEY, Crypt::encrypt(file_get_contents($private)));
|
||||
FireflyConfig::set(self::PUBLIC_KEY, Crypt::encrypt(file_get_contents($public)));
|
||||
}
|
||||
|
||||
public static function verifyKeysRoutine(): void
|
||||
|
||||
@@ -30,6 +30,7 @@ use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\Support\Search\OperatorQuerySearch;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use League\CommonMark\GithubFlavoredMarkdownConverter;
|
||||
@@ -155,9 +156,12 @@ class General extends AbstractExtension
|
||||
}
|
||||
|
||||
/** @var Carbon $date */
|
||||
$date = session('end', today(config('app.timezone'))->endOfMonth());
|
||||
$date = now();
|
||||
Log::debug(sprintf('twig balance: Call finalAccountBalance with date/time "%s"', $date->toIso8601String()));
|
||||
$info = Steam::finalAccountBalance($account, $date);
|
||||
|
||||
// 2025-10-08 replace finalAccountBalance with accountsBalancesOptimized.
|
||||
$info = Steam::accountsBalancesOptimized(new Collection()->push($account), $date)[$account->id];
|
||||
// $info = Steam::finalAccountBalance($account, $date);
|
||||
$currency = Steam::getAccountCurrency($account);
|
||||
$primary = Amount::getPrimaryCurrency();
|
||||
$convertToPrimary = Amount::convertToPrimary();
|
||||
|
||||
@@ -59,9 +59,7 @@ class UpdatePiggyBank implements ActionInterface
|
||||
|
||||
$piggyBank = $this->findPiggyBank($user, $actionValue);
|
||||
if (!$piggyBank instanceof PiggyBank) {
|
||||
Log::info(
|
||||
sprintf('No piggy bank named "%s", cant execute action #%d of rule #%d', $actionValue, $this->action->id, $this->action->rule_id)
|
||||
);
|
||||
Log::info(sprintf('No piggy bank named "%s", cant execute action #%d of rule #%d', $actionValue, $this->action->id, $this->action->rule_id));
|
||||
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_piggy', ['name' => $actionValue])));
|
||||
|
||||
return false;
|
||||
|
||||
@@ -506,9 +506,23 @@ class SearchRuleEngine implements RuleEngineInterface
|
||||
*/
|
||||
private function fireGroup(RuleGroup $group): void
|
||||
{
|
||||
Log::debug(sprintf('Going to fire group #%d with %d rule(s)', $group->id, $group->rules->count()));
|
||||
|
||||
$rules = $group->rules()->orderBy('order', 'ASC')->get();
|
||||
$rules = [];
|
||||
if ($group->relationLoaded('rules')) {
|
||||
Log::debug('Group rules have been pre-loaded, do not reload them.');
|
||||
$rules = $group->rules;
|
||||
}
|
||||
if (!$group->relationLoaded('rules')) {
|
||||
Log::debug('Group rules have NOT been pre-loaded, load them NOW.');
|
||||
$rules = $group->rules()
|
||||
->orderBy('rules.order', 'ASC')
|
||||
// ->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
|
||||
// ->where('rule_triggers.trigger_type', 'user_action')
|
||||
// ->where('rule_triggers.trigger_value', 'store-journal')
|
||||
->where('rules.active', true)
|
||||
->get(['rules.*'])
|
||||
;
|
||||
}
|
||||
Log::debug(sprintf('Going to fire group #%d with %d rule(s)', $group->id, $rules->count()));
|
||||
|
||||
/** @var Rule $rule */
|
||||
foreach ($rules as $rule) {
|
||||
|
||||
@@ -34,11 +34,17 @@ abstract class AbstractTransformer extends TransformerAbstract
|
||||
{
|
||||
protected ParameterBag $parameters;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
final public function getParameters(): ParameterBag
|
||||
{
|
||||
return $this->parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
final public function setParameters(ParameterBag $parameters): void
|
||||
{
|
||||
$this->parameters = $parameters;
|
||||
|
||||
@@ -30,7 +30,6 @@ use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use Symfony\Component\HttpFoundation\ParameterBag;
|
||||
|
||||
/**
|
||||
* Class AccountTransformer
|
||||
@@ -46,7 +45,6 @@ class AccountTransformer extends AbstractTransformer
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->parameters = new ParameterBag();
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->convertToPrimary = Amount::convertToPrimary();
|
||||
$this->primary = Amount::getPrimaryCurrency();
|
||||
|
||||
@@ -12,6 +12,7 @@ Everything from v6.4.1, plus:
|
||||
|
||||
- [Issue 11015](https://github.com/firefly-iii/firefly-iii/issues/11015) (Call to a member function subSecond() on null when viewing accounts) reported by @sirgio145
|
||||
- [Issue 11016](https://github.com/firefly-iii/firefly-iii/issues/11016) (Undefined array key 1 when viewing subscriptions) reported by @anuneo
|
||||
- [Issue 11018](https://github.com/firefly-iii/firefly-iii/issues/11018) (/v1/accounts balance_difference last day is not accounted for) reported by @ctrl-f5
|
||||
|
||||
## 6.4.1 - 2025-10-07
|
||||
|
||||
|
||||
188
composer.lock
generated
188
composer.lock
generated
@@ -1878,16 +1878,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v12.32.5",
|
||||
"version": "v12.35.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "77b2740391cd2a825ba59d6fada45e9b8b9bcc5a"
|
||||
"reference": "d6d6e3cb68238e2fb25b440f222442adef5a8a15"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/77b2740391cd2a825ba59d6fada45e9b8b9bcc5a",
|
||||
"reference": "77b2740391cd2a825ba59d6fada45e9b8b9bcc5a",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/d6d6e3cb68238e2fb25b440f222442adef5a8a15",
|
||||
"reference": "d6d6e3cb68238e2fb25b440f222442adef5a8a15",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1999,7 +1999,7 @@
|
||||
"league/flysystem-sftp-v3": "^3.25.1",
|
||||
"mockery/mockery": "^1.6.10",
|
||||
"opis/json-schema": "^2.4.1",
|
||||
"orchestra/testbench-core": "^10.6.5",
|
||||
"orchestra/testbench-core": "^10.7.0",
|
||||
"pda/pheanstalk": "^5.0.6|^7.0.0",
|
||||
"php-http/discovery": "^1.15",
|
||||
"phpstan/phpstan": "^2.0",
|
||||
@@ -2093,7 +2093,7 @@
|
||||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2025-09-30T17:39:22+00:00"
|
||||
"time": "2025-10-23T15:25:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/passport",
|
||||
@@ -2296,16 +2296,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/serializable-closure",
|
||||
"version": "v2.0.5",
|
||||
"version": "v2.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/serializable-closure.git",
|
||||
"reference": "3832547db6e0e2f8bb03d4093857b378c66eceed"
|
||||
"reference": "038ce42edee619599a1debb7e81d7b3759492819"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/3832547db6e0e2f8bb03d4093857b378c66eceed",
|
||||
"reference": "3832547db6e0e2f8bb03d4093857b378c66eceed",
|
||||
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/038ce42edee619599a1debb7e81d7b3759492819",
|
||||
"reference": "038ce42edee619599a1debb7e81d7b3759492819",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2353,7 +2353,7 @@
|
||||
"issues": "https://github.com/laravel/serializable-closure/issues",
|
||||
"source": "https://github.com/laravel/serializable-closure"
|
||||
},
|
||||
"time": "2025-09-22T17:29:40+00:00"
|
||||
"time": "2025-10-09T13:42:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/slack-notification-channel",
|
||||
@@ -2485,34 +2485,34 @@
|
||||
},
|
||||
{
|
||||
"name": "lcobucci/clock",
|
||||
"version": "3.3.1",
|
||||
"version": "3.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/lcobucci/clock.git",
|
||||
"reference": "db3713a61addfffd615b79bf0bc22f0ccc61b86b"
|
||||
"reference": "f91d84f65cb3e974988bbe872b5da8ca132a155f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/lcobucci/clock/zipball/db3713a61addfffd615b79bf0bc22f0ccc61b86b",
|
||||
"reference": "db3713a61addfffd615b79bf0bc22f0ccc61b86b",
|
||||
"url": "https://api.github.com/repos/lcobucci/clock/zipball/f91d84f65cb3e974988bbe872b5da8ca132a155f",
|
||||
"reference": "f91d84f65cb3e974988bbe872b5da8ca132a155f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "~8.2.0 || ~8.3.0 || ~8.4.0",
|
||||
"php": "~8.3.0 || ~8.4.0",
|
||||
"psr/clock": "^1.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/clock-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"infection/infection": "^0.29",
|
||||
"infection/infection": "^0.31",
|
||||
"lcobucci/coding-standard": "^11.1.0",
|
||||
"phpstan/extension-installer": "^1.3.1",
|
||||
"phpstan/phpstan": "^1.10.25",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.1.3",
|
||||
"phpstan/phpstan-phpunit": "^1.3.13",
|
||||
"phpstan/phpstan-strict-rules": "^1.5.1",
|
||||
"phpunit/phpunit": "^11.3.6"
|
||||
"phpstan/phpstan": "^2.0.0",
|
||||
"phpstan/phpstan-deprecation-rules": "^2.0.0",
|
||||
"phpstan/phpstan-phpunit": "^2.0.0",
|
||||
"phpstan/phpstan-strict-rules": "^2.0.0",
|
||||
"phpunit/phpunit": "^12.0.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@@ -2533,7 +2533,7 @@
|
||||
"description": "Yet another clock abstraction",
|
||||
"support": {
|
||||
"issues": "https://github.com/lcobucci/clock/issues",
|
||||
"source": "https://github.com/lcobucci/clock/tree/3.3.1"
|
||||
"source": "https://github.com/lcobucci/clock/tree/3.4.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2545,26 +2545,26 @@
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-24T20:45:14+00:00"
|
||||
"time": "2025-10-08T18:00:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "lcobucci/jwt",
|
||||
"version": "5.5.0",
|
||||
"version": "5.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/lcobucci/jwt.git",
|
||||
"reference": "a835af59b030d3f2967725697cf88300f579088e"
|
||||
"reference": "bb3e9f21e4196e8afc41def81ef649c164bca25e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/a835af59b030d3f2967725697cf88300f579088e",
|
||||
"reference": "a835af59b030d3f2967725697cf88300f579088e",
|
||||
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/bb3e9f21e4196e8afc41def81ef649c164bca25e",
|
||||
"reference": "bb3e9f21e4196e8afc41def81ef649c164bca25e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-openssl": "*",
|
||||
"ext-sodium": "*",
|
||||
"php": "~8.2.0 || ~8.3.0 || ~8.4.0",
|
||||
"php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
|
||||
"psr/clock": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
@@ -2606,7 +2606,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/lcobucci/jwt/issues",
|
||||
"source": "https://github.com/lcobucci/jwt/tree/5.5.0"
|
||||
"source": "https://github.com/lcobucci/jwt/tree/5.6.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2618,7 +2618,7 @@
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2025-01-26T21:29:45+00:00"
|
||||
"time": "2025-10-17T11:30:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/commonmark",
|
||||
@@ -2811,16 +2811,16 @@
|
||||
},
|
||||
{
|
||||
"name": "league/csv",
|
||||
"version": "9.26.0",
|
||||
"version": "9.27.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/csv.git",
|
||||
"reference": "7fce732754d043f3938899e5183e2d0f3d31b571"
|
||||
"reference": "26de738b8fccf785397d05ee2fc07b6cd8749797"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/csv/zipball/7fce732754d043f3938899e5183e2d0f3d31b571",
|
||||
"reference": "7fce732754d043f3938899e5183e2d0f3d31b571",
|
||||
"url": "https://api.github.com/repos/thephpleague/csv/zipball/26de738b8fccf785397d05ee2fc07b6cd8749797",
|
||||
"reference": "26de738b8fccf785397d05ee2fc07b6cd8749797",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2898,7 +2898,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-10-01T11:24:54+00:00"
|
||||
"time": "2025-10-25T08:35:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/event",
|
||||
@@ -2956,16 +2956,16 @@
|
||||
},
|
||||
{
|
||||
"name": "league/flysystem",
|
||||
"version": "3.30.0",
|
||||
"version": "3.30.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/flysystem.git",
|
||||
"reference": "2203e3151755d874bb2943649dae1eb8533ac93e"
|
||||
"reference": "c139fd65c1f796b926f4aec0df37f6caa959a8da"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/2203e3151755d874bb2943649dae1eb8533ac93e",
|
||||
"reference": "2203e3151755d874bb2943649dae1eb8533ac93e",
|
||||
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/c139fd65c1f796b926f4aec0df37f6caa959a8da",
|
||||
"reference": "c139fd65c1f796b926f4aec0df37f6caa959a8da",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3033,9 +3033,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/thephpleague/flysystem/issues",
|
||||
"source": "https://github.com/thephpleague/flysystem/tree/3.30.0"
|
||||
"source": "https://github.com/thephpleague/flysystem/tree/3.30.1"
|
||||
},
|
||||
"time": "2025-06-25T13:29:59+00:00"
|
||||
"time": "2025-10-20T15:35:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/flysystem-local",
|
||||
@@ -4069,31 +4069,31 @@
|
||||
},
|
||||
{
|
||||
"name": "nunomaduro/termwind",
|
||||
"version": "v2.3.1",
|
||||
"version": "v2.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nunomaduro/termwind.git",
|
||||
"reference": "dfa08f390e509967a15c22493dc0bac5733d9123"
|
||||
"reference": "eb61920a53057a7debd718a5b89c2178032b52c0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nunomaduro/termwind/zipball/dfa08f390e509967a15c22493dc0bac5733d9123",
|
||||
"reference": "dfa08f390e509967a15c22493dc0bac5733d9123",
|
||||
"url": "https://api.github.com/repos/nunomaduro/termwind/zipball/eb61920a53057a7debd718a5b89c2178032b52c0",
|
||||
"reference": "eb61920a53057a7debd718a5b89c2178032b52c0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"php": "^8.2",
|
||||
"symfony/console": "^7.2.6"
|
||||
"symfony/console": "^7.3.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"illuminate/console": "^11.44.7",
|
||||
"laravel/pint": "^1.22.0",
|
||||
"illuminate/console": "^11.46.1",
|
||||
"laravel/pint": "^1.25.1",
|
||||
"mockery/mockery": "^1.6.12",
|
||||
"pestphp/pest": "^2.36.0 || ^3.8.2",
|
||||
"phpstan/phpstan": "^1.12.25",
|
||||
"pestphp/pest": "^2.36.0 || ^3.8.4",
|
||||
"phpstan/phpstan": "^1.12.32",
|
||||
"phpstan/phpstan-strict-rules": "^1.6.2",
|
||||
"symfony/var-dumper": "^7.2.6",
|
||||
"symfony/var-dumper": "^7.3.4",
|
||||
"thecodingmachine/phpstan-strict-rules": "^1.0.0"
|
||||
},
|
||||
"type": "library",
|
||||
@@ -4136,7 +4136,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nunomaduro/termwind/issues",
|
||||
"source": "https://github.com/nunomaduro/termwind/tree/v2.3.1"
|
||||
"source": "https://github.com/nunomaduro/termwind/tree/v2.3.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4152,7 +4152,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-05-08T08:14:37+00:00"
|
||||
"time": "2025-10-18T11:10:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nyholm/psr7",
|
||||
@@ -10052,28 +10052,28 @@
|
||||
},
|
||||
{
|
||||
"name": "webmozart/assert",
|
||||
"version": "1.11.0",
|
||||
"version": "1.12.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/webmozarts/assert.git",
|
||||
"reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991"
|
||||
"reference": "541057574806f942c94662b817a50f63f7345360"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991",
|
||||
"reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991",
|
||||
"url": "https://api.github.com/repos/webmozarts/assert/zipball/541057574806f942c94662b817a50f63f7345360",
|
||||
"reference": "541057574806f942c94662b817a50f63f7345360",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-ctype": "*",
|
||||
"ext-date": "*",
|
||||
"ext-filter": "*",
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"phpstan/phpstan": "<0.12.20",
|
||||
"vimeo/psalm": "<4.6.1 || 4.6.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.5.13"
|
||||
"suggest": {
|
||||
"ext-intl": "",
|
||||
"ext-simplexml": "",
|
||||
"ext-spl": ""
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
@@ -10104,9 +10104,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/webmozarts/assert/issues",
|
||||
"source": "https://github.com/webmozarts/assert/tree/1.11.0"
|
||||
"source": "https://github.com/webmozarts/assert/tree/1.12.0"
|
||||
},
|
||||
"time": "2022-06-03T18:03:27+00:00"
|
||||
"time": "2025-10-20T12:43:39+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
@@ -10549,16 +10549,16 @@
|
||||
},
|
||||
{
|
||||
"name": "driftingly/rector-laravel",
|
||||
"version": "2.0.7",
|
||||
"version": "2.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/driftingly/rector-laravel.git",
|
||||
"reference": "625dc02cee08d47ecf0ac86de2f02a55026cf34e"
|
||||
"reference": "abc336cbf06f53d90ab74cecfd319379fc55d408"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/driftingly/rector-laravel/zipball/625dc02cee08d47ecf0ac86de2f02a55026cf34e",
|
||||
"reference": "625dc02cee08d47ecf0ac86de2f02a55026cf34e",
|
||||
"url": "https://api.github.com/repos/driftingly/rector-laravel/zipball/abc336cbf06f53d90ab74cecfd319379fc55d408",
|
||||
"reference": "abc336cbf06f53d90ab74cecfd319379fc55d408",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -10578,9 +10578,9 @@
|
||||
"description": "Rector upgrades rules for Laravel Framework",
|
||||
"support": {
|
||||
"issues": "https://github.com/driftingly/rector-laravel/issues",
|
||||
"source": "https://github.com/driftingly/rector-laravel/tree/2.0.7"
|
||||
"source": "https://github.com/driftingly/rector-laravel/tree/2.1.1"
|
||||
},
|
||||
"time": "2025-08-19T20:49:47+00:00"
|
||||
"time": "2025-10-23T13:53:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "fakerphp/faker",
|
||||
@@ -11036,16 +11036,16 @@
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v5.6.1",
|
||||
"version": "v5.6.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2"
|
||||
"reference": "3a454ca033b9e06b63282ce19562e892747449bb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2",
|
||||
"reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb",
|
||||
"reference": "3a454ca033b9e06b63282ce19562e892747449bb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -11088,9 +11088,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1"
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2"
|
||||
},
|
||||
"time": "2025-08-13T20:13:15+00:00"
|
||||
"time": "2025-10-21T19:32:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
@@ -11333,11 +11333,11 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "2.1.30",
|
||||
"version": "2.1.31",
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/a4a7f159927983dd4f7c8020ed227d80b7f39d7d",
|
||||
"reference": "a4a7f159927983dd4f7c8020ed227d80b7f39d7d",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/ead89849d879fe203ce9292c6ef5e7e76f867b96",
|
||||
"reference": "ead89849d879fe203ce9292c6ef5e7e76f867b96",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -11382,7 +11382,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-10-02T16:07:52+00:00"
|
||||
"time": "2025-10-10T14:14:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-deprecation-rules",
|
||||
@@ -11815,16 +11815,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "12.4.0",
|
||||
"version": "12.4.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "f62aab5794e36ccd26860db2d1bbf89ac19028d9"
|
||||
"reference": "fc5413a2e6d240d2f6d9317bdf7f0a24e73de194"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f62aab5794e36ccd26860db2d1bbf89ac19028d9",
|
||||
"reference": "f62aab5794e36ccd26860db2d1bbf89ac19028d9",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fc5413a2e6d240d2f6d9317bdf7f0a24e73de194",
|
||||
"reference": "fc5413a2e6d240d2f6d9317bdf7f0a24e73de194",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -11892,7 +11892,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.4.0"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.4.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -11916,20 +11916,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-10-03T04:28:03+00:00"
|
||||
"time": "2025-10-09T14:08:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "rector/rector",
|
||||
"version": "2.2.1",
|
||||
"version": "2.2.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/rectorphp/rector.git",
|
||||
"reference": "e1aaf3061e9ae9342ed0824865e3a3360defddeb"
|
||||
"reference": "fb9418af7777dfb1c87a536dc58398b5b07c74b9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/rectorphp/rector/zipball/e1aaf3061e9ae9342ed0824865e3a3360defddeb",
|
||||
"reference": "e1aaf3061e9ae9342ed0824865e3a3360defddeb",
|
||||
"url": "https://api.github.com/repos/rectorphp/rector/zipball/fb9418af7777dfb1c87a536dc58398b5b07c74b9",
|
||||
"reference": "fb9418af7777dfb1c87a536dc58398b5b07c74b9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -11968,7 +11968,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/rectorphp/rector/issues",
|
||||
"source": "https://github.com/rectorphp/rector/tree/2.2.1"
|
||||
"source": "https://github.com/rectorphp/rector/tree/2.2.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -11976,7 +11976,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-10-06T21:25:14+00:00"
|
||||
"time": "2025-10-23T11:22:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
|
||||
@@ -78,8 +78,8 @@ return [
|
||||
'running_balance_column' => env('USE_RUNNING_BALANCE', false),
|
||||
// see cer.php for exchange rates feature flag.
|
||||
],
|
||||
'version' => 'develop/2025-10-07',
|
||||
'build_time' => 1759823774,
|
||||
'version' => 'develop/2025-10-25',
|
||||
'build_time' => 1761395572,
|
||||
'api_version' => '2.1.0', // field is no longer used.
|
||||
'db_version' => 28, // field is no longer used.
|
||||
|
||||
@@ -182,6 +182,7 @@ return [
|
||||
'darkMode' => 'browser',
|
||||
'list_length' => 10, // to be removed if v1 is cancelled.
|
||||
'default_preferences' => [
|
||||
'anonymous' => false,
|
||||
'frontpageAccounts' => [],
|
||||
'listPageSize' => 50,
|
||||
'currencyPreference' => 'EUR',
|
||||
@@ -225,6 +226,8 @@ return [
|
||||
// plain files
|
||||
'text/plain',
|
||||
'text/html',
|
||||
'text/xml',
|
||||
'application/xml',
|
||||
|
||||
// images
|
||||
'image/jpeg',
|
||||
|
||||
24
database/factories/AccountFactory.php
Normal file
24
database/factories/AccountFactory.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use FireflyIII\Enums\AccountTypeEnum;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class AccountFactory extends Factory
|
||||
{
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => $this->faker->name(),
|
||||
'active' => true,
|
||||
];
|
||||
}
|
||||
|
||||
public function withType(AccountTypeEnum $type): static
|
||||
{
|
||||
return $this->for(AccountType::where('type', $type->value)->first());
|
||||
}
|
||||
}
|
||||
@@ -11,34 +11,36 @@ return new class extends Migration
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('period_statistics', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->timestamps();
|
||||
if (!Schema::hasTable('period_statistics')) {
|
||||
Schema::create('period_statistics', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->timestamps();
|
||||
|
||||
// reference to user group id.
|
||||
$table->bigInteger('user_group_id', false, true);
|
||||
// reference to user group id.
|
||||
$table->bigInteger('user_group_id', false, true);
|
||||
|
||||
$table->integer('primary_statable_id', false, true)->nullable();
|
||||
$table->string('primary_statable_type', 255)->nullable();
|
||||
$table->integer('primary_statable_id', false, true)->nullable();
|
||||
$table->string('primary_statable_type', 255)->nullable();
|
||||
|
||||
$table->integer('secondary_statable_id', false, true)->nullable();
|
||||
$table->string('secondary_statable_type', 255)->nullable();
|
||||
$table->integer('secondary_statable_id', false, true)->nullable();
|
||||
$table->string('secondary_statable_type', 255)->nullable();
|
||||
|
||||
$table->integer('tertiary_statable_id', false, true)->nullable();
|
||||
$table->string('tertiary_statable_type', 255)->nullable();
|
||||
$table->integer('tertiary_statable_id', false, true)->nullable();
|
||||
$table->string('tertiary_statable_type', 255)->nullable();
|
||||
|
||||
$table->integer('transaction_currency_id', false, true);
|
||||
$table->foreign('transaction_currency_id')->references('id')->on('transaction_currencies')->onDelete('cascade');
|
||||
$table->integer('transaction_currency_id', false, true);
|
||||
$table->foreign('transaction_currency_id')->references('id')->on('transaction_currencies')->onDelete('cascade');
|
||||
|
||||
$table->dateTime('start')->nullable();
|
||||
$table->string('start_tz', 50)->nullable();
|
||||
$table->dateTime('end')->nullable();
|
||||
$table->string('end_tz', 50)->nullable();
|
||||
$table->string('type',255);
|
||||
$table->integer('count', false, true)->default(0);
|
||||
$table->decimal('amount', 32, 12);
|
||||
$table->foreign('user_group_id')->references('id')->on('user_groups')->onDelete('cascade');
|
||||
});
|
||||
$table->dateTime('start')->nullable();
|
||||
$table->string('start_tz', 50)->nullable();
|
||||
$table->dateTime('end')->nullable();
|
||||
$table->string('end_tz', 50)->nullable();
|
||||
$table->string('type', 255);
|
||||
$table->integer('count', false, true)->default(0);
|
||||
$table->decimal('amount', 32, 12);
|
||||
$table->foreign('user_group_id')->references('id')->on('user_groups')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -53,6 +53,10 @@ class TransactionCurrencySeeder extends Seeder
|
||||
$currencies[] = ['code' => 'BRL', 'name' => 'Brazilian real', 'symbol' => 'R$', 'decimal_places' => 2];
|
||||
$currencies[] = ['code' => 'CAD', 'name' => 'Canadian dollar', 'symbol' => 'C$', 'decimal_places' => 2];
|
||||
$currencies[] = ['code' => 'MXN', 'name' => 'Mexican peso', 'symbol' => 'MX$', 'decimal_places' => 2];
|
||||
$currencies[] = ['code' => 'PEN', 'name' => 'Peruvian Sol', 'symbol' => 'S/', 'decimal_places' => 2];
|
||||
$currencies[] = ['code' => 'ARS', 'name' => 'Argentinian Peso', 'symbol' => '$', 'decimal_places' => 2];
|
||||
$currencies[] = ['code' => 'COP', 'name' => 'Colombian Peso', 'symbol' => '$', 'decimal_places' => 2];
|
||||
$currencies[] = ['code' => 'CLP', 'name' => 'Chilean Peso', 'symbol' => '$', 'decimal_places' => 2];
|
||||
|
||||
// oceanian currencies
|
||||
$currencies[] = ['code' => 'IDR', 'name' => 'Indonesian rupiah', 'symbol' => 'Rp', 'decimal_places' => 2];
|
||||
|
||||
752
package-lock.json
generated
752
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -42,13 +42,11 @@ function formatLabel(str, maxwidth) {
|
||||
if (concat.length > maxwidth) {
|
||||
sections.push(temp);
|
||||
temp = "";
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (index === (words.length - 1)) {
|
||||
sections.push(concat);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
temp = concat;
|
||||
return;
|
||||
}
|
||||
@@ -62,8 +60,7 @@ function formatLabel(str, maxwidth) {
|
||||
|
||||
if (item.length < maxwidth) {
|
||||
temp = item;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
sections.push(item);
|
||||
}
|
||||
|
||||
@@ -98,9 +95,11 @@ var defaultChartOptions = {
|
||||
ticks: {
|
||||
callback: function (tickValue) {
|
||||
"use strict";
|
||||
if (anonymous) {
|
||||
return accounting.formatMoney(0);
|
||||
}
|
||||
// use first symbol or null:
|
||||
return accounting.formatMoney(tickValue);
|
||||
|
||||
},
|
||||
beginAtZero: true
|
||||
}
|
||||
@@ -112,8 +111,11 @@ var defaultChartOptions = {
|
||||
callbacks: {
|
||||
label: function (tooltipItem, data) {
|
||||
"use strict";
|
||||
return data.datasets[tooltipItem.datasetIndex].label + ': ' +
|
||||
accounting.formatMoney(tooltipItem.yLabel, data.datasets[tooltipItem.datasetIndex].currency_symbol);
|
||||
var string = accounting.formatMoney(tooltipItem.yLabel, data.datasets[tooltipItem.datasetIndex].currency_symbol);
|
||||
if (anonymous) {
|
||||
string = accounting.formatMoney(0);
|
||||
}
|
||||
return data.datasets[tooltipItem.datasetIndex].label + ': ' + string;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,7 +127,11 @@ var pieOptionsWithCurrency = {
|
||||
label: function (tooltipItem, data) {
|
||||
"use strict";
|
||||
var value = data.datasets[0].data[tooltipItem.index];
|
||||
return data.labels[tooltipItem.index] + ': ' + accounting.formatMoney(value, data.datasets[tooltipItem.datasetIndex].currency_symbol[tooltipItem.index]);
|
||||
var string = accounting.formatMoney(value, data.datasets[tooltipItem.datasetIndex].currency_symbol[tooltipItem.index]);
|
||||
if (anonymous) {
|
||||
string = accounting.formatMoney(0);
|
||||
}
|
||||
return data.labels[tooltipItem.index] + ': ' + string;
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -139,7 +145,11 @@ var defaultPieOptions = {
|
||||
label: function (tooltipItem, data) {
|
||||
"use strict";
|
||||
var value = data.datasets[0].data[tooltipItem.index];
|
||||
return data.labels[tooltipItem.index] + ': ' + accounting.formatMoney(value);
|
||||
var string = accounting.formatMoney(value);
|
||||
if (anonymous) {
|
||||
string = accounting.formatMoney(0);
|
||||
}
|
||||
return data.labels[tooltipItem.index] + ': ' + string;
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -153,7 +163,11 @@ var neutralDefaultPieOptions = {
|
||||
label: function (tooltipItem, data) {
|
||||
"use strict";
|
||||
var value = data.datasets[0].data[tooltipItem.index];
|
||||
return data.labels[tooltipItem.index] + ': ' + accounting.formatMoney(value, '¤');
|
||||
var string = accounting.formatMoney(value, '¤');
|
||||
if(anonymous) {
|
||||
string = accounting.formatMoney(0);
|
||||
}
|
||||
return data.labels[tooltipItem.index] + ': ' + string;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -47,7 +47,6 @@ function parseToLocalDates() {
|
||||
$(function () {
|
||||
"use strict";
|
||||
|
||||
|
||||
configAccounting(currencySymbol);
|
||||
|
||||
// on submit of logout button:
|
||||
|
||||
@@ -17,13 +17,42 @@
|
||||
* 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/>.
|
||||
*/
|
||||
/** global: token, helpPageTitle */
|
||||
/** global: token, helpPageTitle, anonymous */
|
||||
$(function () {
|
||||
"use strict";
|
||||
$('#help').click(showHelp);
|
||||
$('#anonymous').click(changeAnonymity)
|
||||
|
||||
});
|
||||
|
||||
function submitAnonymity(value) {
|
||||
$.ajax({
|
||||
url: 'api/v1/preferences/anonymous',
|
||||
data: JSON.stringify({data: value}),
|
||||
type: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content'),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function changeAnonymity(e) {
|
||||
if (anonymous) {
|
||||
console.log('Will DISABLE.');
|
||||
submitAnonymity(false);
|
||||
alert(anonymous_warning_off_txt);
|
||||
window.location.reload(true);
|
||||
}
|
||||
if (!anonymous) {
|
||||
console.log('Will ENABLE.');
|
||||
submitAnonymity(true);
|
||||
alert(anonymous_warning_on_txt);
|
||||
window.location.reload(true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function showHelp(e) {
|
||||
"use strict";
|
||||
var target = $(e.target);
|
||||
|
||||
@@ -235,6 +235,8 @@ return [
|
||||
'advanced_options_explain' => 'Some pages in Firefly III have advanced options hidden behind this button. This page doesn\'t have anything fancy here, but do check out the others!',
|
||||
'here_be_dragons' => 'Hic sunt dracones',
|
||||
'bad_date_transaction' => 'Firefly III has detected you have transactions from before the year 1970. Please correct these transactions at your earliest convenience.',
|
||||
'anonymous_warning_on' => 'For your privacy, all amounts are now displayed as "zero". Warning: text input boxes may still show the original amounts!',
|
||||
'anonymous_warning_off' => 'Amounts will be visible again. Please be mindful of your surroundings.',
|
||||
|
||||
// Webhooks
|
||||
'webhooks' => 'Webhooks',
|
||||
|
||||
@@ -90,15 +90,15 @@
|
||||
var iAmOwed = '{{ 'i_am_owed_amount'|_|escape('js') }}';
|
||||
var iOwe = '{{ 'i_owe_amount'|_|escape('js') }}';
|
||||
</script>
|
||||
<script type="text/javascript" src="v1/js/lib/modernizr-custom.js?v={{ FF_VERSION }}"
|
||||
<script type="text/javascript" src="v1/js/lib/modernizr-custom.js?v={{ FF_BUILD_TIME }}"
|
||||
nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/jquery-ui.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/accounts/create.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/jquery-ui.min.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/accounts/create.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
<link href="v1/css/jquery-ui/jquery-ui.structure.min.css?v={{ FF_VERSION }}" type="text/css" rel="stylesheet"
|
||||
<link href="v1/css/jquery-ui/jquery-ui.structure.min.css?v={{ FF_BUILD_TIME }}" type="text/css" rel="stylesheet"
|
||||
media="all" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/css/jquery-ui/jquery-ui.theme.min.css?v={{ FF_VERSION }}" type="text/css" rel="stylesheet"
|
||||
<link href="v1/css/jquery-ui/jquery-ui.theme.min.css?v={{ FF_BUILD_TIME }}" type="text/css" rel="stylesheet"
|
||||
media="all" nonce="{{ JS_NONCE }}">
|
||||
{% endblock %}
|
||||
|
||||
@@ -122,15 +122,15 @@
|
||||
var iAmOwed = '{{ 'i_am_owed_amount'|_|escape('js') }}';
|
||||
var iOwe = '{{ 'i_owe_amount'|_|escape('js') }}';
|
||||
</script>
|
||||
<script type="text/javascript" src="v1/js/lib/modernizr-custom.js?v={{ FF_VERSION }}"
|
||||
<script type="text/javascript" src="v1/js/lib/modernizr-custom.js?v={{ FF_BUILD_TIME }}"
|
||||
nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/jquery-ui.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/accounts/edit.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/jquery-ui.min.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/accounts/edit.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
<link href="v1/css/jquery-ui/jquery-ui.structure.min.css?v={{ FF_VERSION }}" type="text/css" rel="stylesheet"
|
||||
<link href="v1/css/jquery-ui/jquery-ui.structure.min.css?v={{ FF_BUILD_TIME }}" type="text/css" rel="stylesheet"
|
||||
media="all" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/css/jquery-ui/jquery-ui.theme.min.css?v={{ FF_VERSION }}" type="text/css" rel="stylesheet"
|
||||
<link href="v1/css/jquery-ui/jquery-ui.theme.min.css?v={{ FF_BUILD_TIME }}" type="text/css" rel="stylesheet"
|
||||
media="all" nonce="{{ JS_NONCE }}">
|
||||
{% endblock %}
|
||||
|
||||
@@ -90,6 +90,6 @@
|
||||
<script type="text/javascript" nonce="{{ JS_NONCE }}">
|
||||
var objectType = '{{ objectType|escape }}';
|
||||
</script>
|
||||
<script src="v1/js/lib/jquery-ui.min.js?v={{ FF_VERSION }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" nonce="{{ JS_NONCE }}" src="v1/js/ff/accounts/index.js?v={{ FF_VERSION }}"></script>
|
||||
<script src="v1/js/lib/jquery-ui.min.js?v={{ FF_BUILD_TIME }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" nonce="{{ JS_NONCE }}" src="v1/js/ff/accounts/index.js?v={{ FF_BUILD_TIME }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -133,5 +133,5 @@
|
||||
var indexUrl = '{{ indexUrl }}';
|
||||
var selectRangeAndBalance = '{{ 'select_range_and_balance'|_|escape('js') }}';
|
||||
</script>
|
||||
<script src="v1/js/ff/accounts/reconcile.js?v={{ FF_VERSION }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/ff/accounts/reconcile.js?v={{ FF_BUILD_TIME }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -219,25 +219,25 @@
|
||||
|
||||
</script>
|
||||
{% if location %}
|
||||
<script src="v1/lib/leaflet/leaflet.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/lib/leaflet/leaflet.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endif %}
|
||||
<script type="text/javascript" src="v1/js/lib/Chart.bundle.min.js?v={{ FF_VERSION }}"
|
||||
<script type="text/javascript" src="v1/js/lib/Chart.bundle.min.js?v={{ FF_BUILD_TIME }}"
|
||||
nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/chartjs-plugin-annotation.min.js?v={{ FF_VERSION }}"
|
||||
<script type="text/javascript" src="v1/js/lib/chartjs-plugin-annotation.min.js?v={{ FF_BUILD_TIME }}"
|
||||
nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/charts.defaults.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/charts.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/charts.defaults.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/charts.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
|
||||
<script src="v1/js/lib/jquery-ui.min.js?v={{ FF_VERSION }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/lib/jquery.color-2.1.2.min.js?v={{ FF_VERSION }}" type="text/javascript"
|
||||
<script src="v1/js/lib/jquery-ui.min.js?v={{ FF_BUILD_TIME }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/lib/jquery.color-2.1.2.min.js?v={{ FF_BUILD_TIME }}" type="text/javascript"
|
||||
nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/ff/accounts/show.js?v={{ FF_VERSION }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/ff/accounts/show.js?v={{ FF_BUILD_TIME }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
{# required for groups.twig #}
|
||||
<script type="text/javascript" src="v1/js/ff/list/groups.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/list/groups.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
{% if location %}
|
||||
<link rel="stylesheet" href="v1/lib/leaflet/leaflet.css?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}">
|
||||
<link rel="stylesheet" href="v1/lib/leaflet/leaflet.css?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}">
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -68,10 +68,10 @@
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script type="text/javascript" src="v1/js/lib/bootstrap-sortable.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/bootstrap-sortable.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
<link rel="stylesheet" href="v1/css/bootstrap-sortable.css?v={{ FF_VERSION }}" type="text/css" media="all" nonce="{{ JS_NONCE }}">
|
||||
<link rel="stylesheet" href="v1/css/bootstrap-sortable.css?v={{ FF_BUILD_TIME }}" type="text/css" media="all" nonce="{{ JS_NONCE }}">
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -68,17 +68,17 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
<link href="v1/css/bootstrap-tagsinput.css?v={{ FF_VERSION }}" type="text/css" rel="stylesheet" media="all" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/css/jquery-ui/jquery-ui.structure.min.css?v={{ FF_VERSION }}" type="text/css" rel="stylesheet" media="all" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/css/jquery-ui/jquery-ui.theme.min.css?v={{ FF_VERSION }}" type="text/css" rel="stylesheet" media="all" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/css/bootstrap-tagsinput.css?v={{ FF_BUILD_TIME }}" type="text/css" rel="stylesheet" media="all" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/css/jquery-ui/jquery-ui.structure.min.css?v={{ FF_BUILD_TIME }}" type="text/css" rel="stylesheet" media="all" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/css/jquery-ui/jquery-ui.theme.min.css?v={{ FF_BUILD_TIME }}" type="text/css" rel="stylesheet" media="all" nonce="{{ JS_NONCE }}">
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script type="text/javascript" src="v1/js/lib/bootstrap-tagsinput.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/modernizr-custom.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/jquery-ui.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/bills/create.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/bootstrap-tagsinput.min.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/modernizr-custom.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/jquery-ui.min.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/bills/create.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
|
||||
{# auto complete for object groups #}
|
||||
<script type="text/javascript" src="v1/js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/object-groups/create-edit.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/object-groups/create-edit.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -74,24 +74,24 @@
|
||||
|
||||
{% endblock %}
|
||||
{% block styles %}
|
||||
<link href="v1/css/bootstrap-tagsinput.css?v={{ FF_VERSION }}" type="text/css" rel="stylesheet" media="all"
|
||||
<link href="v1/css/bootstrap-tagsinput.css?v={{ FF_BUILD_TIME }}" type="text/css" rel="stylesheet" media="all"
|
||||
nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/css/jquery-ui/jquery-ui.structure.min.css?v={{ FF_VERSION }}" type="text/css" rel="stylesheet"
|
||||
<link href="v1/css/jquery-ui/jquery-ui.structure.min.css?v={{ FF_BUILD_TIME }}" type="text/css" rel="stylesheet"
|
||||
media="all" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/css/jquery-ui/jquery-ui.theme.min.css?v={{ FF_VERSION }}" type="text/css" rel="stylesheet"
|
||||
<link href="v1/css/jquery-ui/jquery-ui.theme.min.css?v={{ FF_BUILD_TIME }}" type="text/css" rel="stylesheet"
|
||||
media="all" nonce="{{ JS_NONCE }}">
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script type="text/javascript" src="v1/js/lib/bootstrap-tagsinput.min.js?v={{ FF_VERSION }}"
|
||||
<script type="text/javascript" src="v1/js/lib/bootstrap-tagsinput.min.js?v={{ FF_BUILD_TIME }}"
|
||||
nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/modernizr-custom.js?v={{ FF_VERSION }}"
|
||||
<script type="text/javascript" src="v1/js/lib/modernizr-custom.js?v={{ FF_BUILD_TIME }}"
|
||||
nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/jquery-ui.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/accounts/edit.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/jquery-ui.min.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/accounts/edit.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
|
||||
{# auto complete for object groups #}
|
||||
<script type="text/javascript" src="v1/js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_VERSION }}"
|
||||
<script type="text/javascript" src="v1/js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_BUILD_TIME }}"
|
||||
nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/object-groups/create-edit.js?v={{ FF_VERSION }}"
|
||||
<script type="text/javascript" src="v1/js/ff/object-groups/create-edit.js?v={{ FF_BUILD_TIME }}"
|
||||
nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -41,6 +41,6 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="v1/js/lib/jquery-ui.min.js?v={{ FF_VERSION }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/bills/index.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/lib/jquery-ui.min.js?v={{ FF_BUILD_TIME }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/bills/index.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -188,10 +188,10 @@
|
||||
var billCurrencySymbol = "{{ convertToPrimary ? primaryCurrency.symbol : object.data.currency.symbol }}";
|
||||
var billUrl = '{{ route('chart.bill.single', [object.data.id]) }}';
|
||||
</script>
|
||||
<script type="text/javascript" src="v1/js/lib/Chart.bundle.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/charts.defaults.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/charts.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/bills/show.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/Chart.bundle.min.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/charts.defaults.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/charts.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/bills/show.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{# required for groups.twig #}
|
||||
<script type="text/javascript" src="v1/js/ff/list/groups.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/list/groups.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -60,5 +60,5 @@
|
||||
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script type="text/javascript" src="v1/js/ff/budgets/create.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/budgets/create.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -61,5 +61,5 @@
|
||||
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script type="text/javascript" src="v1/js/ff/budgets/edit.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/budgets/edit.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -267,9 +267,14 @@
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon">{{ primaryCurrency.symbol }}</div>
|
||||
<input type="hidden" name="balance_currency_id" value="{{ primaryCurrency.id }}"/>
|
||||
{% if not anonymous %}
|
||||
<input class="form-control budget_amount" data-original="0" data-id="{{ budget.id }}"
|
||||
data-currency="{{ primaryCurrency.id }}" data-limit="0" value="0" autocomplete="off" min="0" name="amount"
|
||||
type="number">
|
||||
{% endif %}
|
||||
{% if anonymous %}
|
||||
---
|
||||
{% endif %}
|
||||
</div>
|
||||
<span class="text-danger budget_warning" data-id="{{ budget.id }}" data-budgetLimit="{{ budgetLimit.id }}"
|
||||
style="display:none;"></span>
|
||||
@@ -283,10 +288,15 @@
|
||||
{% endif %}
|
||||
<div class="input-group bl_entry" data-budget-limit-id="{{ budgetLimit.id }}">
|
||||
<div class="input-group-addon">{{ budgetLimit.currency_symbol }}</div>
|
||||
{% if not anonymous %}
|
||||
<input class="form-control budget_amount" data-original="{{ budgetLimit.amount }}"
|
||||
data-id="{{ budget.id }}" data-limit="{{ budgetLimit.id }}" value="{{ budgetLimit.amount }}"
|
||||
autocomplete="off"
|
||||
min="0" name="amount" type="number">
|
||||
{% endif %}
|
||||
{% if anonymous %}
|
||||
---
|
||||
{% endif %}
|
||||
<div class="input-group-btn">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true"
|
||||
aria-expanded="false"><span class="caret"></span></button>
|
||||
@@ -455,11 +465,11 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
<link href="v1/css/bootstrap-sortable.css?v={{ FF_VERSION }}" type="text/css" rel="stylesheet" media="all" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/css/bootstrap-sortable.css?v={{ FF_BUILD_TIME }}" type="text/css" rel="stylesheet" media="all" nonce="{{ JS_NONCE }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="v1/js/lib/jquery-ui.min.js?v={{ FF_VERSION }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/lib/jquery-ui.min.js?v={{ FF_BUILD_TIME }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" nonce="{{ JS_NONCE }}">
|
||||
|
||||
// index route.
|
||||
@@ -478,6 +488,6 @@
|
||||
var periodStart = "{{ start.format('Y-m-d') }}";
|
||||
var periodEnd = "{{ end.format('Y-m-d') }}";
|
||||
</script>
|
||||
<script type="text/javascript" src="v1/js/lib/bootstrap-sortable.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/budgets/index.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/bootstrap-sortable.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/budgets/index.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -60,5 +60,5 @@
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
{# required for groups.twig #}
|
||||
<script type="text/javascript" src="v1/js/ff/list/groups.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/list/groups.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -230,10 +230,10 @@
|
||||
{% endif %}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="v1/js/lib/Chart.bundle.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/charts.defaults.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/charts.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/budgets/show.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/Chart.bundle.min.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/charts.defaults.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/charts.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/budgets/show.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{# required for groups.twig #}
|
||||
<script type="text/javascript" src="v1/js/ff/list/groups.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/list/groups.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -53,5 +53,5 @@
|
||||
</form>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script type="text/javascript" src="v1/js/ff/budgets/create.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/budgets/create.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -59,5 +59,5 @@
|
||||
</form>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script type="text/javascript" src="v1/js/ff/categories/edit.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/categories/edit.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -40,10 +40,10 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
<link href="v1/css/bootstrap-sortable.css?v={{ FF_VERSION }}" type="text/css" rel="stylesheet" media="all" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/css/bootstrap-sortable.css?v={{ FF_BUILD_TIME }}" type="text/css" rel="stylesheet" media="all" nonce="{{ JS_NONCE }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script type="text/javascript" src="v1/js/lib/bootstrap-sortable.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/categories/index.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/bootstrap-sortable.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/categories/index.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -60,5 +60,5 @@
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
{# required for groups.twig #}
|
||||
<script type="text/javascript" src="v1/js/ff/list/groups.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/list/groups.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -113,10 +113,10 @@
|
||||
var everything = '{{ route('chart.category.all', [category.id]) }}';
|
||||
var specific = '{{ route('chart.category.specific', [category.id, start.format('Ymd')]) }}';
|
||||
</script>
|
||||
<script type="text/javascript" src="v1/js/lib/Chart.bundle.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/charts.defaults.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/charts.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/categories/show.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/Chart.bundle.min.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/charts.defaults.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/charts.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/categories/show.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{# required for groups.twig #}
|
||||
<script type="text/javascript" src="v1/js/ff/list/groups.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/list/groups.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -98,5 +98,5 @@
|
||||
var redirectUrl = "{{ route('currencies.index') }}";
|
||||
var updateCurrencyUrl = "{{ route('api.v1.currencies.update', ['']) }}";
|
||||
</script>
|
||||
<script type="text/javascript" src="v1/js/ff/currencies/index.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/currencies/index.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -211,14 +211,14 @@
|
||||
{% endif %}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="v1/js/lib/Chart.bundle.min.js?v={{ FF_VERSION }}"
|
||||
<script type="text/javascript" src="v1/js/lib/Chart.bundle.min.js?v={{ FF_BUILD_TIME }}"
|
||||
nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/chartjs-plugin-annotation.min.js?v={{ FF_VERSION }}"
|
||||
<script type="text/javascript" src="v1/js/lib/chartjs-plugin-annotation.min.js?v={{ FF_BUILD_TIME }}"
|
||||
nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/charts.defaults.js?v={{ FF_VERSION }}"
|
||||
<script type="text/javascript" src="v1/js/ff/charts.defaults.js?v={{ FF_BUILD_TIME }}"
|
||||
nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/charts.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/index.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/charts.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/index.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endblock %}
|
||||
{% block styles %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -26,6 +26,7 @@ ranges: ranges
|
||||
};
|
||||
|
||||
var uid = "{{ uid }}";
|
||||
var anonymous = {{ anonymous }};
|
||||
var language = "{{ language|escape }}";
|
||||
var locale = "{{ locale|escape }}";
|
||||
var currencyCode = '{{ currencyCode|escape('js') }}';
|
||||
@@ -51,6 +52,9 @@ var acc_config_new = {format: accountingConfig};
|
||||
var helpPageTitle = "{{ trans('firefly.help_for_this_page')|escape('js') }}";
|
||||
var helpPageBody = "{{ trans('firefly.help_for_this_page_body')|escape('js') }}";
|
||||
|
||||
var anonymous_warning_on_txt = "{{ trans('firefly.anonymous_warning_on')|escape('js') }}";
|
||||
var anonymous_warning_off_txt= "{{ trans('firefly.anonymous_warning_off')|escape('js') }}";
|
||||
|
||||
var edit_selected_txt = "{{ trans('firefly.mass_edit')|escape('js') }}";
|
||||
var edit_bulk_selected_txt = "{{ trans('firefly.bulk_edit')|escape('js') }}";
|
||||
var delete_selected_txt = "{{ trans('firefly.mass_delete')|escape('js') }}";
|
||||
|
||||
@@ -29,14 +29,14 @@
|
||||
</title>
|
||||
<meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'>
|
||||
{# CSS things #}
|
||||
<link href="v1/lib/bs/css/bootstrap.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css"
|
||||
<link href="v1/lib/bs/css/bootstrap.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css"
|
||||
nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/lib/fa/css/font-awesome.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css"
|
||||
<link href="v1/lib/fa/css/font-awesome.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css"
|
||||
nonce="{{ JS_NONCE }}">
|
||||
|
||||
|
||||
{# the theme #}
|
||||
<link href="v1/lib/adminlte/css/AdminLTE.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css"
|
||||
<link href="v1/lib/adminlte/css/AdminLTE.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css"
|
||||
nonce="{{ JS_NONCE }}">
|
||||
{% if 'browser' == darkMode %}
|
||||
<script nonce="{{ JS_NONCE }}">
|
||||
@@ -46,43 +46,43 @@
|
||||
document.documentElement.style.display = 'none';
|
||||
document.head.insertAdjacentHTML(
|
||||
'beforeend',
|
||||
'<link rel="stylesheet" href="v1/lib/adminlte/css/skins/skin-light.min.css?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}" onload="document.documentElement.style.display = \'\'">',
|
||||
'<link rel="stylesheet" href="v1/lib/adminlte/css/skins/skin-light.min.css?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}" onload="document.documentElement.style.display = \'\'">',
|
||||
);
|
||||
}
|
||||
</script>
|
||||
<link href="v1/css/daterangepicker-default.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css"
|
||||
<link href="v1/css/daterangepicker-default.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css"
|
||||
nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/lib/adminlte/css/skins/skin-dark.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css"
|
||||
<link href="v1/lib/adminlte/css/skins/skin-dark.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css"
|
||||
nonce="{{ JS_NONCE }}" media="(prefers-color-scheme: dark)">
|
||||
<link href="v1/lib/adminlte/css/skins/skin-light.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css"
|
||||
<link href="v1/lib/adminlte/css/skins/skin-light.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css"
|
||||
nonce="{{ JS_NONCE }}" media="(prefers-color-scheme: light)">
|
||||
{% endif %}
|
||||
{% if 'dark' == darkMode %}
|
||||
<link href="v1/css/daterangepicker-dark.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css"
|
||||
<link href="v1/css/daterangepicker-dark.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css"
|
||||
nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/lib/adminlte/css/skins/skin-dark.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css"
|
||||
<link href="v1/lib/adminlte/css/skins/skin-dark.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css"
|
||||
nonce="{{ JS_NONCE }}">
|
||||
{% endif %}
|
||||
{% if 'light' == darkMode %}
|
||||
<link href="v1/css/daterangepicker-light.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css"
|
||||
<link href="v1/css/daterangepicker-light.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css"
|
||||
nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/lib/adminlte/css/skins/skin-light.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css"
|
||||
<link href="v1/lib/adminlte/css/skins/skin-light.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css"
|
||||
nonce="{{ JS_NONCE }}">
|
||||
{% endif %}
|
||||
{# Firefly III customisations #}
|
||||
<link href="v1/css/firefly.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/css/firefly.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
|
||||
{# Extra CSS for the demo: #}
|
||||
{% if not shownDemo %}
|
||||
<link href="v1/lib/intro/introjs.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css"
|
||||
<link href="v1/lib/intro/introjs.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css"
|
||||
nonce="{{ JS_NONCE }}">
|
||||
{% endif %}
|
||||
|
||||
{# Any local custom CSS. #}
|
||||
{% block styles %}{% endblock %}
|
||||
<!--[if lt IE 9]>
|
||||
<script src="v1/js/lib/html5shiv.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/lib/respond.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/lib/html5shiv.min.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/lib/respond.min.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<![endif]-->
|
||||
|
||||
{# this entry is in the header so it's loaded early #}
|
||||
@@ -111,7 +111,16 @@
|
||||
</a>
|
||||
<div class="navbar-custom-menu">
|
||||
<ul class="nav navbar-nav">
|
||||
|
||||
<li class="hidden-sm hidden-xs">
|
||||
<a href="#" id="anonymous">
|
||||
{% if anonymous %}
|
||||
<span class="text-danger fa fa-eye-slash"></span>
|
||||
{% endif %}
|
||||
{% if not anonymous %}
|
||||
<span class="text-success fa fa-eye"></span>
|
||||
{% endif %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="hidden-sm hidden-xs">
|
||||
<a href="#" id="help" data-route="{{ original_route_name }}"
|
||||
data-extra="{{ objectType|default("") }}">
|
||||
@@ -219,8 +228,8 @@
|
||||
{% block definitions %}{% endblock %}
|
||||
|
||||
{# Moment JS #}
|
||||
<script src="v1/js/lib/moment.min.js?v={{ FF_VERSION }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/lib/moment/{{ language|replace({'-':'_'}) }}.js?v={{ FF_VERSION }}" type="text/javascript"
|
||||
<script src="v1/js/lib/moment.min.js?v={{ FF_BUILD_TIME }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/lib/moment/{{ language|replace({'-':'_'}) }}.js?v={{ FF_BUILD_TIME }}" type="text/javascript"
|
||||
nonce="{{ JS_NONCE }}"></script>
|
||||
|
||||
{# All kinds of variables. #}
|
||||
@@ -229,32 +238,32 @@
|
||||
type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
|
||||
{# Base script: jquery and bootstrap #}
|
||||
<script src="v1/js/app.js?v={{ FF_VERSION }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/app.js?v={{ FF_BUILD_TIME }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
|
||||
{# Vue scripts and components. Only loaded when needed #}
|
||||
{% if VUE_SCRIPT_NAME %}
|
||||
<script src="v1/js/app_vue.js?v={{ FF_VERSION }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/{{ VUE_SCRIPT_NAME }}.js?v={{ FF_VERSION }}" type="text/javascript"
|
||||
<script src="v1/js/app_vue.js?v={{ FF_BUILD_TIME }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/{{ VUE_SCRIPT_NAME }}.js?v={{ FF_BUILD_TIME }}" type="text/javascript"
|
||||
nonce="{{ JS_NONCE }}"></script>
|
||||
{% endif %}
|
||||
|
||||
{# date range picker, current template, etc. #}
|
||||
<script src="v1/js/lib/daterangepicker.js?v={{ FF_VERSION }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/lib/adminlte/js/adminlte.min.js?v={{ FF_VERSION }}" type="text/javascript"
|
||||
<script src="v1/js/lib/daterangepicker.js?v={{ FF_BUILD_TIME }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/lib/adminlte/js/adminlte.min.js?v={{ FF_BUILD_TIME }}" type="text/javascript"
|
||||
nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/accounting.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/lib/accounting.min.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
|
||||
{# Firefly III code #}
|
||||
<script type="text/javascript" src="v1/js/ff/firefly.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/help.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/firefly.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/help.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% if not shownDemo %}
|
||||
<script type="text/javascript" nonce="{{ JS_NONCE }}">
|
||||
var routeForTour = "{{ current_route_name }}";
|
||||
var routeStepsUrl = "{{ route('json.intro', [current_route_name, objectType|default("")]) }}";
|
||||
var routeForFinishedTour = "{{ route('json.intro.finished', [current_route_name, objectType|default("")]) }}";
|
||||
</script>
|
||||
<script type="text/javascript" src="v1/lib/intro/intro.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/intro/intro.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/lib/intro/intro.min.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/intro/intro.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
{% endif %}
|
||||
|
||||
{% block scripts %}{% endblock %}
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
{# CSS things #}
|
||||
|
||||
{# libraries #}
|
||||
<link href="v1/lib/bs/css/bootstrap.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/lib/fa/css/font-awesome.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/lib/bs/css/bootstrap.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/lib/fa/css/font-awesome.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
|
||||
{# the theme #}
|
||||
<link href="v1/lib/adminlte/css/AdminLTE.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/lib/adminlte/css/AdminLTE.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
{% if 'browser' == darkMode %}
|
||||
<script nonce="{{ JS_NONCE }}">
|
||||
// If `prefers-color-scheme` is not supported, fall back to light mode.
|
||||
@@ -27,26 +27,26 @@
|
||||
document.documentElement.style.display = 'none';
|
||||
document.head.insertAdjacentHTML(
|
||||
'beforeend',
|
||||
'<link rel="stylesheet" href="v1/lib/adminlte/css/skins/skin-light.min.css?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}" onload="document.documentElement.style.display = \'\'">',
|
||||
'<link rel="stylesheet" href="v1/lib/adminlte/css/skins/skin-light.min.css?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}" onload="document.documentElement.style.display = \'\'">',
|
||||
);
|
||||
}
|
||||
</script>
|
||||
<link href="v1/lib/adminlte/css/skins/skin-dark.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}" media="(prefers-color-scheme: dark)">
|
||||
<link href="v1/lib/adminlte/css/skins/skin-light.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}" media="(prefers-color-scheme: light)">
|
||||
<link href="v1/lib/adminlte/css/skins/skin-dark.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}" media="(prefers-color-scheme: dark)">
|
||||
<link href="v1/lib/adminlte/css/skins/skin-light.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}" media="(prefers-color-scheme: light)">
|
||||
{% endif %}
|
||||
{% if 'dark' == darkMode %}
|
||||
<link href="v1/lib/adminlte/css/skins/skin-dark.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/lib/adminlte/css/skins/skin-dark.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
{% endif %}
|
||||
{% if 'light' == darkMode %}
|
||||
<link href="v1/lib/adminlte/css/skins/skin-light.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/lib/adminlte/css/skins/skin-light.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
{% endif %}
|
||||
|
||||
{# Firefly III customisations #}
|
||||
<link href="v1/css/firefly.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/css/firefly.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
<script src="v1/js/lib/html5shiv.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/lib/respond.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/lib/html5shiv.min.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/lib/respond.min.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<![endif]-->
|
||||
|
||||
{# favicons #}
|
||||
@@ -61,7 +61,7 @@
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
<script src="v1/js/app.js?v={{ FF_VERSION }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/app.js?v={{ FF_BUILD_TIME }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
|
||||
{% if config('firefly.tracker_site_id') != '' and config('firefly.tracker_url') != '' %}
|
||||
<!-- This tracker tag is only here because this instance of Firefly III was purposefully configured to include it -->
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
{# CSS things #}
|
||||
|
||||
{# libraries #}
|
||||
<link href="v1/lib/bs/css/bootstrap.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/lib/fa/css/font-awesome.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/lib/bs/css/bootstrap.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/lib/fa/css/font-awesome.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
|
||||
{# the theme #}
|
||||
<link href="v1/lib/adminlte/css/AdminLTE.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/lib/adminlte/css/AdminLTE.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
{% if 'browser' == darkMode %}
|
||||
<script nonce="{{ JS_NONCE }}">
|
||||
// If `prefers-color-scheme` is not supported, fall back to light mode.
|
||||
@@ -37,26 +37,26 @@
|
||||
document.documentElement.style.display = 'none';
|
||||
document.head.insertAdjacentHTML(
|
||||
'beforeend',
|
||||
'<link rel="stylesheet" href="v1/lib/adminlte/css/skins/skin-light.min.css?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}" onload="document.documentElement.style.display = \'\'">',
|
||||
'<link rel="stylesheet" href="v1/lib/adminlte/css/skins/skin-light.min.css?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}" onload="document.documentElement.style.display = \'\'">',
|
||||
);
|
||||
}
|
||||
</script>
|
||||
<link href="v1/lib/adminlte/css/skins/skin-dark.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}" media="(prefers-color-scheme: dark)">
|
||||
<link href="v1/lib/adminlte/css/skins/skin-light.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}" media="(prefers-color-scheme: light)">
|
||||
<link href="v1/lib/adminlte/css/skins/skin-dark.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}" media="(prefers-color-scheme: dark)">
|
||||
<link href="v1/lib/adminlte/css/skins/skin-light.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}" media="(prefers-color-scheme: light)">
|
||||
{% endif %}
|
||||
{% if 'dark' == darkMode %}
|
||||
<link href="v1/lib/adminlte/css/skins/skin-dark.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/lib/adminlte/css/skins/skin-dark.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
{% endif %}
|
||||
{% if 'light' == darkMode %}
|
||||
<link href="v1/lib/adminlte/css/skins/skin-light.min.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/lib/adminlte/css/skins/skin-light.min.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
{% endif %}
|
||||
|
||||
{# Firefly III customisations #}
|
||||
<link href="v1/css/firefly.css?v={{ FF_VERSION }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
<link href="v1/css/firefly.css?v={{ FF_BUILD_TIME }}" rel="stylesheet" type="text/css" nonce="{{ JS_NONCE }}">
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
<script src="v1/js/lib/html5shiv.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/lib/respond.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/lib/html5shiv.min.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/lib/respond.min.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<![endif]-->
|
||||
|
||||
{# favicons #}
|
||||
@@ -75,8 +75,8 @@
|
||||
Developed by James Cole, the source code is licensed under the <a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL-3.0-or-later</a>.
|
||||
</small>
|
||||
</div>
|
||||
<script src="v1/js/app.js?v={{ FF_VERSION }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/guest.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||
<script src="v1/js/app.js?v={{ FF_BUILD_TIME }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||
<script type="text/javascript" src="v1/js/ff/guest.js?v={{ FF_BUILD_TIME }}" nonce="{{ JS_NONCE }}"></script>
|
||||
|
||||
{% if config('firefly.tracker_site_id') != '' and config('firefly.tracker_url') != '' %}
|
||||
<!-- This tracker tag is only here because this instance of Firefly III was purposefully configured to include it -->
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user