diff --git a/app/Api/V1/Controllers/Controller.php b/app/Api/V1/Controllers/Controller.php index 196cca9290..1752ef0648 100644 --- a/app/Api/V1/Controllers/Controller.php +++ b/app/Api/V1/Controllers/Controller.php @@ -108,12 +108,7 @@ abstract class Controller extends BaseController { $bag = new ParameterBag(); $page = (int)request()->get('page'); - if ($page < 1) { - $page = 1; - } - if ($page > 2 ** 16) { - $page = 2 ** 16; - } + $page = min(max(1, $page), 2 ** 16); $bag->set('page', $page); // some date fields: @@ -131,19 +126,15 @@ abstract class Controller extends BaseController $obj = null; if (null !== $date) { try { - $obj = Carbon::parse((string)$date); + $obj = Carbon::parse((string)$date, config('app.timezone')); } catch (InvalidFormatException $e) { // don't care - Log::warning( - sprintf( - 'Ignored invalid date "%s" in API controller parameter check: %s', - substr((string)$date, 0, 20), - $e->getMessage() - ) - ); + Log::warning(sprintf('Ignored invalid date "%s" in API controller parameter check: %s', substr((string)$date, 0, 20), $e->getMessage())); } } - $bag->set($field, $obj); + if($obj instanceof Carbon){ + $bag->set($field, $obj); + } } // integer fields: diff --git a/app/Api/V1/Controllers/Models/Account/ShowController.php b/app/Api/V1/Controllers/Models/Account/ShowController.php index 5cdb73d10d..cc992588b4 100644 --- a/app/Api/V1/Controllers/Models/Account/ShowController.php +++ b/app/Api/V1/Controllers/Models/Account/ShowController.php @@ -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\Models\Account\ShowRequest; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Account; use FireflyIII\Repositories\Account\AccountRepositoryInterface; @@ -33,7 +34,6 @@ use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment; use FireflyIII\Transformers\AccountTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; -use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; @@ -71,41 +71,40 @@ class ShowController extends Controller * * @throws FireflyException */ - public function index(Request $request): JsonResponse + public function index(ShowRequest $request): JsonResponse { - $manager = $this->getManager(); - $type = $request->get('type') ?? 'all'; - $this->parameters->set('type', $type); + $manager = $this->getManager(); + $params = $request->getParameters(); + $this->parameters->set('type', $params['type']); // types to get, page size: - $types = $this->mapAccountTypes($this->parameters->get('type')); - $pageSize = $this->parameters->get('limit'); + $types = $this->mapAccountTypes($params['type']); // get list of accounts. Count it and split it. $this->repository->resetAccountOrder(); - $collection = $this->repository->getAccountsByType($types, $this->parameters->get('sort') ?? []); - $count = $collection->count(); + $collection = $this->repository->getAccountsByType($types, $params['sort']); + $count = $collection->count(); // continue sort: - $accounts = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + $accounts = $collection->slice(($this->parameters->get('page') - 1) * $params['limit'], $params['limit']); // enrich /** @var User $admin */ - $admin = auth()->user(); - $enrichment = new AccountEnrichment(); + $admin = auth()->user(); + $enrichment = new AccountEnrichment(); $enrichment->setDate($this->parameters->get('date')); $enrichment->setUser($admin); - $accounts = $enrichment->enrich($accounts); + $accounts = $enrichment->enrich($accounts); // make paginator: - $paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.accounts.index').$this->buildParams()); + $paginator = new LengthAwarePaginator($accounts, $count, $params['limit'], $this->parameters->get('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 = new FractalCollection($accounts, $transformer, self::RESOURCE_KEY); $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); @@ -122,21 +121,21 @@ class ShowController extends Controller // get list of accounts. Count it and split it. $this->repository->resetAccountOrder(); $account->refresh(); - $manager = $this->getManager(); + $manager = $this->getManager(); // enrich /** @var User $admin */ - $admin = auth()->user(); - $enrichment = new AccountEnrichment(); + $admin = auth()->user(); + $enrichment = new AccountEnrichment(); $enrichment->setDate($this->parameters->get('date')); $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); + $resource = new Item($account, $transformer, self::RESOURCE_KEY); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } diff --git a/app/Api/V1/Requests/Models/Account/ShowRequest.php b/app/Api/V1/Requests/Models/Account/ShowRequest.php new file mode 100644 index 0000000000..3deb643254 --- /dev/null +++ b/app/Api/V1/Requests/Models/Account/ShowRequest.php @@ -0,0 +1,96 @@ +. + */ + +namespace FireflyIII\Api\V1\Requests\Models\Account; + + +use Carbon\Carbon; +use FireflyIII\Models\Preference; +use FireflyIII\Support\Facades\Preferences; +use FireflyIII\Support\Http\Api\AccountFilter; +use FireflyIII\Support\Request\ConvertsDataTypes; +use FireflyIII\User; +use Illuminate\Contracts\Validation\Validator; +use Illuminate\Foundation\Http\FormRequest; + +class ShowRequest extends FormRequest +{ + use ConvertsDataTypes; + use AccountFilter; + + public function getParameters(): array + { + $limit = $this->convertInteger('limit'); + if (0 === $limit) { + // get default for user: + /** @var User $user */ + $user = auth()->user(); + + /** @var Preference $pageSize */ + $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->convertString('sort', 'order'), + 'page' => $page, + ]; + } + + public function rules(): array + { + $keys = join(',', 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' => 'in:active,iban,name,order,-active,-iban,-name,-order', // TODO improve me. + 'type' => sprintf('in:%s', $keys), + 'limit' => 'number|min:1|max:131337', + 'page' => 'number|min:1|max:131337', + ]; + } + + public function withValidator(Validator $validator): void + { + $validator->after( + function (Validator $validator): void { + if ($validator->failed()) { + 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')); + } + } + }); + } + +} diff --git a/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php b/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php index a45297b832..46674967ec 100644 --- a/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php +++ b/app/Api/V1/Requests/Models/PiggyBank/StoreRequest.php @@ -78,7 +78,7 @@ class StoreRequest extends FormRequest 'object_group_id' => 'numeric|belongsToUser:object_groups,id', 'object_group_title' => ['min:1', 'max:255'], 'target_amount' => ['required', new IsValidZeroOrMoreAmount()], - 'start_date' => 'date|nullable', + 'start_date' => 'required|date|after:1970-01-01|before:2038-01-17', 'transaction_currency_id' => 'exists:transaction_currencies,id|required_without:transaction_currency_code', 'transaction_currency_code' => 'exists:transaction_currencies,code|required_without:transaction_currency_id', 'target_date' => 'date|nullable|after:start_date', diff --git a/resources/assets/v2/src/pages/accounts/index.js b/resources/assets/v2/src/pages/accounts/index.js index 109f7d14d4..0a391be704 100644 --- a/resources/assets/v2/src/pages/accounts/index.js +++ b/resources/assets/v2/src/pages/accounts/index.js @@ -32,6 +32,7 @@ import {showWizardButton} from "../../support/page-settings/show-wizard-button.j import {setVariable} from "../../store/set-variable.js"; import {getVariables} from "../../store/get-variables.js"; import pageNavigation from "../../support/page-navigation.js"; +import {getVariable} from "../../store/get-variable.js"; // set type from URL @@ -56,12 +57,12 @@ if(sortingColumn[0] === '-') { page = parseInt(params.page ?? 1); - showInternalsButton(); showWizardButton(); // TODO currency conversion // TODO page cleanup and recycle for transaction lists. +// TODO process startPeriod and endPeriod. let index = function () { return { @@ -73,9 +74,9 @@ let index = function () { show: false, text: '', url: '', }, wait: { show: false, text: '', - } }, + convertToPrimary: false, totalPages: 1, page: 1, pageUrl: '', @@ -259,6 +260,7 @@ let index = function () { {name: this.getPreferenceKey('sd'), default: ''}, {name: this.getPreferenceKey('filters'), default: this.filters}, {name: this.getPreferenceKey('grouped'), default: true}, + {name: 'convert_to_primary', default: false}, ]).then((res) => { // process columns: for (let k in res[0]) { @@ -283,6 +285,9 @@ let index = function () { // group accounts this.pageOptions.groupedAccounts = res[4]; + // convert to primary? + this.convertToPrimary = res[5]; + this.loadAccounts(); }); }, @@ -348,7 +353,6 @@ let index = function () { if('asc' === this.pageOptions.sortDirection && '' !== sorting) { sorting = '-' + sorting; } - //const sorting = [{column: this.pageOptions.sortingColumn, direction: this.pageOptions.sortDirection}]; // filter instructions let filters = {}; @@ -371,20 +375,20 @@ let index = function () { sort: sorting, filter: filters, active: active, - currentMoment: today, + date: format(today,'yyyy-MM-dd'), type: type, page: this.page, - startPeriod: start, - endPeriod: end + // startPeriod: start, + // endPeriod: end }; if (!this.tableColumns.balance_difference.enabled) { - delete params.startPeriod; - delete params.endPeriod; + // delete params.startPeriod; + // delete params.endPeriod; } this.accounts = []; let groupedAccounts = {}; - // one page only.o + // one page only (new Get()).index(params).then(response => { this.totalPages = response.meta.pagination.total_pages; for (let i = 0; i < response.data.length; i++) { @@ -406,9 +410,9 @@ let index = function () { liability_direction: current.attributes.liability_direction, interest: current.attributes.interest, interest_period: current.attributes.interest_period, - balance: current.attributes.balance, - pc_balance: current.attributes.pc_balance, - balances: current.attributes.balances, + // balance: current.attributes.balance, + // pc_balance: current.attributes.pc_balance, + // balances: current.attributes.balances, }; // get group info: let groupId = current.attributes.object_group_id; @@ -426,10 +430,9 @@ let index = function () { } } groupedAccounts[groupId].accounts.push(account); - - //this.accounts.push(account); } } + // order grouped accounts by order. let sortable = []; for (let set in groupedAccounts) { diff --git a/resources/lang/en_US/validation.php b/resources/lang/en_US/validation.php index a57b2f814c..73d78f9700 100644 --- a/resources/lang/en_US/validation.php +++ b/resources/lang/en_US/validation.php @@ -119,6 +119,7 @@ return [ 'between.file' => 'The :attribute must be between :min and :max kilobytes.', 'between.string' => 'The :attribute must be between :min and :max characters.', 'between.array' => 'The :attribute must have between :min and :max items.', + 'between_date' => 'The date must be between the given start and end date.', 'boolean' => 'The :attribute field must be true or false.', 'confirmed' => 'The :attribute confirmation does not match.', 'date' => 'The :attribute is not a valid date.', diff --git a/resources/views/transactions/show.twig b/resources/views/transactions/show.twig index 2bda3de9a6..bcfeced11a 100644 --- a/resources/views/transactions/show.twig +++ b/resources/views/transactions/show.twig @@ -441,7 +441,7 @@ {% for tag in journal.tags %}

+ href="{{ route('tags.show', [tag.id]) }}"> {{ tag.tag }}

{% endfor %} diff --git a/resources/views/v2/accounts/index.blade.php b/resources/views/v2/accounts/index.blade.php index 05d54582ae..1d5e8c62be 100644 --- a/resources/views/v2/accounts/index.blade.php +++ b/resources/views/v2/accounts/index.blade.php @@ -10,7 +10,7 @@

{{ __('firefly.net_worth') }}

- TODO + Not yet implemented.
@@ -20,17 +20,17 @@

{{ __('firefly.in_out_period') }}

- TODO + Not yet implemented.
-

TODO

+

Not yet implemented.

- TODO + Not yet implemented.
@@ -262,17 +262,14 @@ -