mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Merge remote-tracking branch 'upstream/develop' into feat/expression-engine
This commit is contained in:
commit
41fc1e8f82
2
.github/CODEOWNERS
vendored
Normal file
2
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# code owners for this Firefly III related repository
|
||||
* @JC5 @SDx3
|
101
app/Api/V2/Controllers/Model/Account/IndexController.php
Normal file
101
app/Api/V2/Controllers/Model/Account/IndexController.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/*
|
||||
* IndexController.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V2\Controllers\Model\Account;
|
||||
|
||||
use FireflyIII\Api\V2\Controllers\Controller;
|
||||
use FireflyIII\Api\V2\Request\Model\Account\IndexRequest;
|
||||
use FireflyIII\Api\V2\Request\Model\Transaction\InfiniteListRequest;
|
||||
use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Transformers\V2\AccountTransformer;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
|
||||
class IndexController extends Controller
|
||||
{
|
||||
public const string RESOURCE_KEY = 'accounts';
|
||||
|
||||
private AccountRepositoryInterface $repository;
|
||||
|
||||
/**
|
||||
* AccountController constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
// new way of user group validation
|
||||
$userGroup = $this->validateUserGroup($request);
|
||||
if (null !== $userGroup) {
|
||||
$this->repository->setUserGroup($userGroup);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO see autocomplete/accountcontroller for list.
|
||||
*/
|
||||
public function index(IndexRequest $request): JsonResponse
|
||||
{
|
||||
$this->repository->resetAccountOrder();
|
||||
$types = $request->getAccountTypes();
|
||||
$accounts = $this->repository->getAccountsByType($types);
|
||||
$pageSize = $this->parameters->get('limit');
|
||||
$count = $accounts->count();
|
||||
$accounts = $accounts->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
||||
$paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page'));
|
||||
$transformer = new AccountTransformer();
|
||||
$transformer->setParameters($this->parameters); // give params to transformer
|
||||
|
||||
return response()
|
||||
->json($this->jsonApiList('accounts', $paginator, $transformer))
|
||||
->header('Content-Type', self::CONTENT_TYPE)
|
||||
;
|
||||
}
|
||||
|
||||
public function infiniteList(InfiniteListRequest $request): JsonResponse
|
||||
{
|
||||
$this->repository->resetAccountOrder();
|
||||
|
||||
// get accounts of the specified type, and return.
|
||||
$types = $request->getAccountTypes();
|
||||
|
||||
// get from repository
|
||||
$accounts = $this->repository->getAccountsInOrder($types, $request->getSortInstructions('accounts'), $request->getStartRow(), $request->getEndRow());
|
||||
$total = $this->repository->countAccounts($types);
|
||||
$count = $request->getEndRow() - $request->getStartRow();
|
||||
$paginator = new LengthAwarePaginator($accounts, $total, $count, $this->parameters->get('page'));
|
||||
$transformer = new AccountTransformer();
|
||||
$transformer->setParameters($this->parameters); // give params to transformer
|
||||
|
||||
return response()
|
||||
->json($this->jsonApiList(self::RESOURCE_KEY, $paginator, $transformer))
|
||||
->header('Content-Type', self::CONTENT_TYPE)
|
||||
;
|
||||
}
|
||||
}
|
@ -35,6 +35,47 @@ use Illuminate\Http\JsonResponse;
|
||||
*/
|
||||
class TransactionController extends Controller
|
||||
{
|
||||
public function infiniteList(InfiniteListRequest $request): JsonResponse
|
||||
{
|
||||
// get sort instructions
|
||||
$instructions = $request->getSortInstructions('transactions');
|
||||
|
||||
// collect transactions:
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setUserGroup(auth()->user()->userGroup)
|
||||
->withAPIInformation()
|
||||
->setStartRow($request->getStartRow())
|
||||
->setEndRow($request->getEndRow())
|
||||
->setTypes($request->getTransactionTypes())
|
||||
->setSorting($instructions)
|
||||
;
|
||||
|
||||
$start = $this->parameters->get('start');
|
||||
$end = $this->parameters->get('end');
|
||||
if (null !== $start) {
|
||||
$collector->setStart($start);
|
||||
}
|
||||
if (null !== $end) {
|
||||
$collector->setEnd($end);
|
||||
}
|
||||
|
||||
$paginator = $collector->getPaginatedGroups();
|
||||
$params = $request->buildParams();
|
||||
$paginator->setPath(
|
||||
sprintf(
|
||||
'%s?%s',
|
||||
route('api.v2.infinite.transactions.list'),
|
||||
$params
|
||||
)
|
||||
);
|
||||
|
||||
return response()
|
||||
->json($this->jsonApiList('transactions', $paginator, new TransactionGroupTransformer()))
|
||||
->header('Content-Type', self::CONTENT_TYPE)
|
||||
;
|
||||
}
|
||||
|
||||
public function list(ListRequest $request): JsonResponse
|
||||
{
|
||||
// collect transactions:
|
||||
@ -75,45 +116,4 @@ class TransactionController extends Controller
|
||||
->header('Content-Type', self::CONTENT_TYPE)
|
||||
;
|
||||
}
|
||||
|
||||
public function infiniteList(InfiniteListRequest $request): JsonResponse
|
||||
{
|
||||
// get sort instructions
|
||||
$instructions = $request->getSortInstructions();
|
||||
|
||||
// collect transactions:
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setUserGroup(auth()->user()->userGroup)
|
||||
->withAPIInformation()
|
||||
->setStartRow($request->getStartRow())
|
||||
->setEndRow($request->getEndRow())
|
||||
->setTypes($request->getTransactionTypes())
|
||||
->setSorting($instructions)
|
||||
;
|
||||
|
||||
$start = $this->parameters->get('start');
|
||||
$end = $this->parameters->get('end');
|
||||
if (null !== $start) {
|
||||
$collector->setStart($start);
|
||||
}
|
||||
if (null !== $end) {
|
||||
$collector->setEnd($end);
|
||||
}
|
||||
|
||||
$paginator = $collector->getPaginatedGroups();
|
||||
$params = $request->buildParams();
|
||||
$paginator->setPath(
|
||||
sprintf(
|
||||
'%s?%s',
|
||||
route('api.v2.infinite.transactions.list'),
|
||||
$params
|
||||
)
|
||||
);
|
||||
|
||||
return response()
|
||||
->json($this->jsonApiList('transactions', $paginator, new TransactionGroupTransformer()))
|
||||
->header('Content-Type', self::CONTENT_TYPE)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
67
app/Api/V2/Request/Model/Account/IndexRequest.php
Normal file
67
app/Api/V2/Request/Model/Account/IndexRequest.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/*
|
||||
* IndexRequest.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V2\Request\Model\Account;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Support\Http\Api\AccountFilter;
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
/**
|
||||
* Class IndexRequest
|
||||
*
|
||||
* Lots of code stolen from the SingleDateRequest.
|
||||
*/
|
||||
class IndexRequest extends FormRequest
|
||||
{
|
||||
use AccountFilter;
|
||||
use ChecksLogin;
|
||||
use ConvertsDataTypes;
|
||||
|
||||
/**
|
||||
* Get all data from the request.
|
||||
*/
|
||||
public function getDate(): Carbon
|
||||
{
|
||||
return $this->getCarbonDate('date');
|
||||
}
|
||||
|
||||
public function getAccountTypes(): array
|
||||
{
|
||||
$type = (string)$this->get('type', 'default');
|
||||
|
||||
return $this->mapAccountTypes($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'date' => 'date|after:1900-01-01|before:2099-12-31',
|
||||
];
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Api\V2\Request\Model\Transaction;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Support\Http\Api\AccountFilter;
|
||||
use FireflyIII\Support\Http\Api\TransactionFilter;
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
@ -36,6 +37,7 @@ use Illuminate\Foundation\Http\FormRequest;
|
||||
*/
|
||||
class InfiniteListRequest extends FormRequest
|
||||
{
|
||||
use AccountFilter;
|
||||
use ChecksLogin;
|
||||
use ConvertsDataTypes;
|
||||
use TransactionFilter;
|
||||
@ -81,6 +83,13 @@ class InfiniteListRequest extends FormRequest
|
||||
return $this->getCarbonDate('end');
|
||||
}
|
||||
|
||||
public function getAccountTypes(): array
|
||||
{
|
||||
$type = (string)$this->get('type', 'default');
|
||||
|
||||
return $this->mapAccountTypes($type);
|
||||
}
|
||||
|
||||
public function getPage(): int
|
||||
{
|
||||
$page = $this->convertInteger('page');
|
||||
@ -88,9 +97,9 @@ class InfiniteListRequest extends FormRequest
|
||||
return 0 === $page || $page > 65536 ? 1 : $page;
|
||||
}
|
||||
|
||||
public function getSortInstructions(): array
|
||||
public function getSortInstructions(string $key): array
|
||||
{
|
||||
$allowed = config('firefly.sorting.allowed.transactions');
|
||||
$allowed = config(sprintf('firefly.sorting.allowed.%s', $key));
|
||||
$set = $this->get('sorting', []);
|
||||
$result = [];
|
||||
if (0 === count($set)) {
|
||||
|
@ -117,6 +117,7 @@ class GracefulNotFoundHandler extends ExceptionHandler
|
||||
return redirect(route('tags.index'));
|
||||
|
||||
case 'categories.show':
|
||||
case 'categories.edit':
|
||||
case 'categories.show.all':
|
||||
$request->session()->reflash();
|
||||
|
||||
|
@ -72,31 +72,6 @@ class StandardMessageGenerator implements MessageGeneratorInterface
|
||||
$this->run();
|
||||
}
|
||||
|
||||
public function getVersion(): int
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
public function setObjects(Collection $objects): void
|
||||
{
|
||||
$this->objects = $objects;
|
||||
}
|
||||
|
||||
public function setTrigger(int $trigger): void
|
||||
{
|
||||
$this->trigger = $trigger;
|
||||
}
|
||||
|
||||
public function setUser(User $user): void
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
public function setWebhooks(Collection $webhooks): void
|
||||
{
|
||||
$this->webhooks = $webhooks;
|
||||
}
|
||||
|
||||
private function getWebhooks(): Collection
|
||||
{
|
||||
return $this->user->webhooks()->where('active', true)->where('trigger', $this->trigger)->get(['webhooks.*']);
|
||||
@ -206,6 +181,11 @@ class StandardMessageGenerator implements MessageGeneratorInterface
|
||||
$this->storeMessage($webhook, $basicMessage);
|
||||
}
|
||||
|
||||
public function getVersion(): int
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
private function collectAccounts(TransactionGroup $transactionGroup): Collection
|
||||
{
|
||||
$accounts = new Collection();
|
||||
@ -232,4 +212,24 @@ class StandardMessageGenerator implements MessageGeneratorInterface
|
||||
$webhookMessage->save();
|
||||
app('log')->debug(sprintf('Stored new webhook message #%d', $webhookMessage->id));
|
||||
}
|
||||
|
||||
public function setObjects(Collection $objects): void
|
||||
{
|
||||
$this->objects = $objects;
|
||||
}
|
||||
|
||||
public function setTrigger(int $trigger): void
|
||||
{
|
||||
$this->trigger = $trigger;
|
||||
}
|
||||
|
||||
public function setUser(User $user): void
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
public function setWebhooks(Collection $webhooks): void
|
||||
{
|
||||
$this->webhooks = $webhooks;
|
||||
}
|
||||
}
|
||||
|
@ -33,10 +33,11 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
*/
|
||||
trait CollectorProperties
|
||||
{
|
||||
public const string TEST = 'Test';
|
||||
|
||||
/** @var array<int, string> */
|
||||
public array $sorting;
|
||||
public const string TEST = 'Test';
|
||||
private ?int $endRow;
|
||||
private ?int $endRow;
|
||||
private bool $expandGroupSearch;
|
||||
private array $fields;
|
||||
private bool $hasAccountInfo;
|
||||
@ -52,7 +53,7 @@ trait CollectorProperties
|
||||
private ?int $page;
|
||||
private array $postFilters;
|
||||
private HasMany $query;
|
||||
private ?int $startRow;
|
||||
private ?int $startRow;
|
||||
private array $stringFields;
|
||||
/*
|
||||
* This array is used to collect ALL tags the user may search for (using 'setTags').
|
||||
|
@ -25,6 +25,7 @@ namespace FireflyIII\Helpers\Collector;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Carbon\Exceptions\InvalidFormatException;
|
||||
use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\Extensions\AccountCollection;
|
||||
use FireflyIII\Helpers\Collector\Extensions\AmountCollection;
|
||||
@ -782,6 +783,35 @@ class GroupCollector implements GroupCollectorInterface
|
||||
return $currentCollection;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function sortCollection(Collection $collection): Collection
|
||||
{
|
||||
/**
|
||||
* @var string $field
|
||||
* @var string $direction
|
||||
*/
|
||||
foreach ($this->sorting as $field => $direction) {
|
||||
$func = 'ASC' === $direction ? 'sortBy' : 'sortByDesc';
|
||||
$collection = $collection->{$func}(function (array $product, int $key) use ($field) { // @phpstan-ignore-line
|
||||
// depends on $field:
|
||||
if ('description' === $field) {
|
||||
if (1 === count($product['transactions'])) {
|
||||
return array_values($product['transactions'])[0][$field];
|
||||
}
|
||||
if (count($product['transactions']) > 1) {
|
||||
return $product['title'];
|
||||
}
|
||||
|
||||
return 'zzz';
|
||||
}
|
||||
|
||||
exit('here we are');
|
||||
});
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as getGroups but everything is in a paginator.
|
||||
*/
|
||||
@ -792,6 +822,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
$this->setLimit(50);
|
||||
}
|
||||
if (null !== $this->startRow && null !== $this->endRow) {
|
||||
/** @var int $total */
|
||||
$total = $this->endRow - $this->startRow;
|
||||
|
||||
return new LengthAwarePaginator($set, $this->total, $total, 1);
|
||||
@ -931,6 +962,14 @@ class GroupCollector implements GroupCollectorInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function setSorting(array $instructions): GroupCollectorInterface
|
||||
{
|
||||
$this->sorting = $instructions;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setStartRow(int $startRow): self
|
||||
{
|
||||
$this->startRow = $startRow;
|
||||
@ -1091,41 +1130,4 @@ class GroupCollector implements GroupCollectorInterface
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function sortCollection(Collection $collection): Collection
|
||||
{
|
||||
/**
|
||||
* @var string $field
|
||||
* @var string $direction
|
||||
*/
|
||||
foreach ($this->sorting as $field => $direction) {
|
||||
$func = 'ASC' === $direction ? 'sortBy' : 'sortByDesc';
|
||||
$collection = $collection->{$func}(function (array $product, int $key) use ($field) { // @phpstan-ignore-line
|
||||
// depends on $field:
|
||||
if ('description' === $field) {
|
||||
if (1 === count($product['transactions'])) {
|
||||
return array_values($product['transactions'])[0][$field];
|
||||
}
|
||||
if (count($product['transactions']) > 1) {
|
||||
return $product['title'];
|
||||
}
|
||||
|
||||
return 'zzz';
|
||||
}
|
||||
|
||||
exit('here we are');
|
||||
});
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function setSorting(array $instructions): GroupCollectorInterface
|
||||
{
|
||||
$this->sorting = $instructions;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -285,13 +285,6 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function getPaginatedGroups(): LengthAwarePaginator;
|
||||
|
||||
public function setSorting(array $instructions): self;
|
||||
|
||||
/**
|
||||
* Sort the collection on a column.
|
||||
*/
|
||||
public function sortCollection(Collection $collection): Collection;
|
||||
|
||||
public function hasAnyTag(): self;
|
||||
|
||||
/**
|
||||
@ -560,6 +553,8 @@ interface GroupCollectorInterface
|
||||
|
||||
public function setSepaCT(string $sepaCT): self;
|
||||
|
||||
public function setSorting(array $instructions): self;
|
||||
|
||||
/**
|
||||
* Set source accounts.
|
||||
*/
|
||||
@ -620,6 +615,11 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function setXorAccounts(Collection $accounts): self;
|
||||
|
||||
/**
|
||||
* Sort the collection on a column.
|
||||
*/
|
||||
public function sortCollection(Collection $collection): Collection;
|
||||
|
||||
/**
|
||||
* Automatically include all stuff required to make API calls work.
|
||||
*/
|
||||
|
@ -51,7 +51,7 @@ class NetWorth implements NetWorthInterface
|
||||
|
||||
private CurrencyRepositoryInterface $currencyRepos;
|
||||
private User $user;
|
||||
private ?UserGroup $userGroup;
|
||||
private ?UserGroup $userGroup;
|
||||
|
||||
/**
|
||||
* This method collects the user's net worth in ALL the user's currencies
|
||||
|
@ -69,6 +69,11 @@ abstract class Controller extends BaseController
|
||||
$authGuard = config('firefly.authentication_guard');
|
||||
$logoutUrl = config('firefly.custom_logout_url');
|
||||
|
||||
// overrule v2 layout back to v1.
|
||||
if ('true' === request()->get('force_default_layout') && 'v2' === config('firefly.layout')) {
|
||||
app('view')->getFinder()->setPaths([realpath(base_path('resources/views'))]); // @phpstan-ignore-line
|
||||
}
|
||||
|
||||
app('view')->share('authGuard', $authGuard);
|
||||
app('view')->share('logoutUrl', $logoutUrl);
|
||||
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Http\Middleware\IsDemoUser;
|
||||
use FireflyIII\Models\AccountType;
|
||||
|
@ -34,6 +34,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class ReconcileController
|
||||
@ -73,7 +74,6 @@ class ReconcileController extends Controller
|
||||
$accountCurrency = $this->accountRepos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency();
|
||||
$amount = '0';
|
||||
$clearedAmount = '0';
|
||||
$route = '';
|
||||
|
||||
if (null === $start && null === $end) {
|
||||
throw new FireflyException('Invalid dates submitted.');
|
||||
@ -103,14 +103,11 @@ class ReconcileController extends Controller
|
||||
$clearedJournals = $collector->getExtractedJournals();
|
||||
}
|
||||
|
||||
app('log')->debug('Start transaction loop');
|
||||
|
||||
/** @var array $journal */
|
||||
foreach ($journals as $journal) {
|
||||
$amount = $this->processJournal($account, $accountCurrency, $journal, $amount);
|
||||
}
|
||||
app('log')->debug(sprintf('Final amount is %s', $amount));
|
||||
app('log')->debug('End transaction loop');
|
||||
|
||||
/** @var array $journal */
|
||||
foreach ($clearedJournals as $journal) {
|
||||
@ -118,31 +115,17 @@ class ReconcileController extends Controller
|
||||
$clearedAmount = $this->processJournal($account, $accountCurrency, $journal, $clearedAmount);
|
||||
}
|
||||
}
|
||||
Log::debug(sprintf('Start balance: "%s"', $startBalance));
|
||||
Log::debug(sprintf('End balance: "%s"', $endBalance));
|
||||
Log::debug(sprintf('Cleared amount: "%s"', $clearedAmount));
|
||||
Log::debug(sprintf('Amount: "%s"', $amount));
|
||||
$difference = bcadd(bcadd(bcsub($startBalance, $endBalance), $clearedAmount), $amount);
|
||||
$diffCompare = bccomp($difference, '0');
|
||||
$countCleared = count($clearedJournals);
|
||||
|
||||
$reconSum = bcadd(bcadd($startBalance, $amount), $clearedAmount);
|
||||
|
||||
try {
|
||||
$view = view(
|
||||
'accounts.reconcile.overview',
|
||||
compact(
|
||||
'account',
|
||||
'start',
|
||||
'diffCompare',
|
||||
'difference',
|
||||
'end',
|
||||
'clearedAmount',
|
||||
'startBalance',
|
||||
'endBalance',
|
||||
'amount',
|
||||
'route',
|
||||
'countCleared',
|
||||
'reconSum',
|
||||
'selectedIds'
|
||||
)
|
||||
)->render();
|
||||
$view = view('accounts.reconcile.overview', compact('account', 'start', 'diffCompare', 'difference', 'end', 'clearedAmount', 'startBalance', 'endBalance', 'amount', 'route', 'countCleared', 'reconSum', 'selectedIds'))->render();
|
||||
} catch (\Throwable $e) {
|
||||
app('log')->debug(sprintf('View error: %s', $e->getMessage()));
|
||||
app('log')->error($e->getTraceAsString());
|
||||
@ -151,14 +134,42 @@ class ReconcileController extends Controller
|
||||
throw new FireflyException($view, 0, $e);
|
||||
}
|
||||
|
||||
$return = [
|
||||
'post_url' => $route,
|
||||
'html' => $view,
|
||||
];
|
||||
$return = ['post_url' => $route, 'html' => $view];
|
||||
|
||||
return response()->json($return);
|
||||
}
|
||||
|
||||
private function processJournal(Account $account, TransactionCurrency $currency, array $journal, string $amount): string
|
||||
{
|
||||
$toAdd = '0';
|
||||
app('log')->debug(sprintf('User submitted %s #%d: "%s"', $journal['transaction_type_type'], $journal['transaction_journal_id'], $journal['description']));
|
||||
|
||||
// not much magic below we need to cover using tests.
|
||||
|
||||
if ($account->id === $journal['source_account_id']) {
|
||||
if ($currency->id === $journal['currency_id']) {
|
||||
$toAdd = $journal['amount'];
|
||||
}
|
||||
if (null !== $journal['foreign_currency_id'] && $journal['foreign_currency_id'] === $currency->id) {
|
||||
$toAdd = $journal['foreign_amount'];
|
||||
}
|
||||
}
|
||||
if ($account->id === $journal['destination_account_id']) {
|
||||
if ($currency->id === $journal['currency_id']) {
|
||||
$toAdd = bcmul($journal['amount'], '-1');
|
||||
}
|
||||
if (null !== $journal['foreign_currency_id'] && $journal['foreign_currency_id'] === $currency->id) {
|
||||
$toAdd = bcmul($journal['foreign_amount'], '-1');
|
||||
}
|
||||
}
|
||||
|
||||
app('log')->debug(sprintf('Going to add %s to %s', $toAdd, $amount));
|
||||
$amount = bcadd($amount, $toAdd);
|
||||
app('log')->debug(sprintf('Result is %s', $amount));
|
||||
|
||||
return $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of transactions in a modal.
|
||||
*
|
||||
@ -176,6 +187,7 @@ class ReconcileController extends Controller
|
||||
}
|
||||
$startDate = clone $start;
|
||||
$startDate->subDay();
|
||||
$end->endOfDay();
|
||||
|
||||
$currency = $this->accountRepos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency();
|
||||
$startBalance = app('steam')->bcround(app('steam')->balance($account, $startDate), $currency->decimal_places);
|
||||
@ -214,37 +226,6 @@ class ReconcileController extends Controller
|
||||
return response()->json(['html' => $html, 'startBalance' => $startBalance, 'endBalance' => $endBalance]);
|
||||
}
|
||||
|
||||
private function processJournal(Account $account, TransactionCurrency $currency, array $journal, string $amount): string
|
||||
{
|
||||
$toAdd = '0';
|
||||
app('log')->debug(sprintf('User submitted %s #%d: "%s"', $journal['transaction_type_type'], $journal['transaction_journal_id'], $journal['description']));
|
||||
|
||||
// not much magic below we need to cover using tests.
|
||||
|
||||
if ($account->id === $journal['source_account_id']) {
|
||||
if ($currency->id === $journal['currency_id']) {
|
||||
$toAdd = $journal['amount'];
|
||||
}
|
||||
if (null !== $journal['foreign_currency_id'] && $journal['foreign_currency_id'] === $currency->id) {
|
||||
$toAdd = $journal['foreign_amount'];
|
||||
}
|
||||
}
|
||||
if ($account->id === $journal['destination_account_id']) {
|
||||
if ($currency->id === $journal['currency_id']) {
|
||||
$toAdd = bcmul($journal['amount'], '-1');
|
||||
}
|
||||
if (null !== $journal['foreign_currency_id'] && $journal['foreign_currency_id'] === $currency->id) {
|
||||
$toAdd = bcmul($journal['foreign_amount'], '-1');
|
||||
}
|
||||
}
|
||||
|
||||
app('log')->debug(sprintf('Going to add %s to %s', $toAdd, $amount));
|
||||
$amount = bcadd($amount, $toAdd);
|
||||
app('log')->debug(sprintf('Result is %s', $amount));
|
||||
|
||||
return $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* "fix" amounts to make it easier on the reconciliation overview:
|
||||
*/
|
||||
|
@ -75,52 +75,45 @@ class IndexController extends Controller
|
||||
$objectType = 'transfer';
|
||||
}
|
||||
|
||||
// add a split for the (future) v2 release.
|
||||
$periods = [];
|
||||
$groups = [];
|
||||
$subTitle = 'TODO page subtitle in v2';
|
||||
$subTitleIcon = config('firefly.transactionIconsByType.'.$objectType);
|
||||
$types = config('firefly.transactionTypesByType.'.$objectType);
|
||||
$page = (int)$request->get('page');
|
||||
$pageSize = (int)app('preferences')->get('listPageSize', 50)->data;
|
||||
|
||||
$subTitleIcon = config('firefly.transactionIconsByType.'.$objectType);
|
||||
$types = config('firefly.transactionTypesByType.'.$objectType);
|
||||
$page = (int)$request->get('page');
|
||||
$pageSize = (int)app('preferences')->get('listPageSize', 50)->data;
|
||||
|
||||
if ('v2' !== (string)config('firefly.layout')) {
|
||||
if (null === $start) {
|
||||
$start = session('start');
|
||||
$end = session('end');
|
||||
}
|
||||
if (null === $end) {
|
||||
// get last transaction ever?
|
||||
$last = $this->repository->getLast();
|
||||
$end = null !== $last ? $last->date : session('end');
|
||||
}
|
||||
|
||||
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
|
||||
$startStr = $start->isoFormat($this->monthAndDayFormat);
|
||||
$endStr = $end->isoFormat($this->monthAndDayFormat);
|
||||
$subTitle = (string)trans(sprintf('firefly.title_%s_between', $objectType), ['start' => $startStr, 'end' => $endStr]);
|
||||
$path = route('transactions.index', [$objectType, $start->format('Y-m-d'), $end->format('Y-m-d')]);
|
||||
$firstJournal = $this->repository->firstNull();
|
||||
$startPeriod = null === $firstJournal ? new Carbon() : $firstJournal->date;
|
||||
$endPeriod = clone $end;
|
||||
$periods = $this->getTransactionPeriodOverview($objectType, $startPeriod, $endPeriod);
|
||||
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
|
||||
$collector->setRange($start, $end)
|
||||
->setTypes($types)
|
||||
->setLimit($pageSize)
|
||||
->setPage($page)
|
||||
->withBudgetInformation()
|
||||
->withCategoryInformation()
|
||||
->withAccountInformation()
|
||||
->withAttachmentInformation()
|
||||
;
|
||||
$groups = $collector->getPaginatedGroups();
|
||||
$groups->setPath($path);
|
||||
if (null === $start) {
|
||||
$start = session('start');
|
||||
$end = session('end');
|
||||
}
|
||||
if (null === $end) {
|
||||
// get last transaction ever?
|
||||
$last = $this->repository->getLast();
|
||||
$end = null !== $last ? $last->date : session('end');
|
||||
}
|
||||
|
||||
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
|
||||
$startStr = $start->isoFormat($this->monthAndDayFormat);
|
||||
$endStr = $end->isoFormat($this->monthAndDayFormat);
|
||||
$subTitle = (string)trans(sprintf('firefly.title_%s_between', $objectType), ['start' => $startStr, 'end' => $endStr]);
|
||||
$path = route('transactions.index', [$objectType, $start->format('Y-m-d'), $end->format('Y-m-d')]);
|
||||
$firstJournal = $this->repository->firstNull();
|
||||
$startPeriod = null === $firstJournal ? new Carbon() : $firstJournal->date;
|
||||
$endPeriod = clone $end;
|
||||
$periods = $this->getTransactionPeriodOverview($objectType, $startPeriod, $endPeriod);
|
||||
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
|
||||
$collector->setRange($start, $end)
|
||||
->setTypes($types)
|
||||
->setLimit($pageSize)
|
||||
->setPage($page)
|
||||
->withBudgetInformation()
|
||||
->withCategoryInformation()
|
||||
->withAccountInformation()
|
||||
->withAttachmentInformation()
|
||||
;
|
||||
$groups = $collector->getPaginatedGroups();
|
||||
$groups->setPath($path);
|
||||
|
||||
return view('transactions.index', compact('subTitle', 'objectType', 'subTitleIcon', 'groups', 'periods', 'start', 'end'));
|
||||
}
|
||||
|
@ -202,7 +202,7 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
|
||||
$availableBudget->user()->associate($this->user);
|
||||
$availableBudget->transactionCurrency()->associate($currency);
|
||||
$availableBudget->start_date = $start->startOfDay()->format('Y-m-d'); // @phpstan-ignore-line
|
||||
$availableBudget->end_date = $end->endOfDay()->format('Y-m-d'); // @phpstan-ignore-line
|
||||
$availableBudget->end_date = $end->endOfDay()->format('Y-m-d'); // @phpstan-ignore-line
|
||||
}
|
||||
$availableBudget->amount = $amount;
|
||||
$availableBudget->save();
|
||||
|
@ -375,6 +375,12 @@ class RecurringRepository implements RecurringRepositoryInterface
|
||||
app('log')->debug('Now in getXOccurrencesSince()');
|
||||
$skipMod = $repetition->repetition_skip + 1;
|
||||
$occurrences = [];
|
||||
|
||||
// to fix #8616, take a few days from both dates, then filter the list to make sure no entries
|
||||
// from today or before are saved.
|
||||
$date->subDays(4);
|
||||
$afterDate->subDays(4);
|
||||
|
||||
if ('daily' === $repetition->repetition_type) {
|
||||
$occurrences = $this->getXDailyOccurrencesSince($date, $afterDate, $count, $skipMod);
|
||||
}
|
||||
@ -407,7 +413,7 @@ class RecurringRepository implements RecurringRepositoryInterface
|
||||
}
|
||||
$filtered = [];
|
||||
foreach ($occurrences as $date) {
|
||||
if ($date->lte($max)) {
|
||||
if ($date->lte($max) && $date->gt(today())) {
|
||||
$filtered[] = $date;
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,17 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
{
|
||||
use UserGroupTrait;
|
||||
|
||||
#[\Override]
|
||||
public function countAccounts(array $types): int
|
||||
{
|
||||
$query = $this->userGroup->accounts();
|
||||
if (0 !== count($types)) {
|
||||
$query->accountTypeIn($types);
|
||||
}
|
||||
|
||||
return $query->count();
|
||||
}
|
||||
|
||||
public function findByAccountNumber(string $number, array $types): ?Account
|
||||
{
|
||||
$dbQuery = $this->userGroup
|
||||
@ -161,6 +172,71 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
return $query->get(['accounts.*']);
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function getAccountsInOrder(array $types, array $sort, int $startRow, int $endRow): Collection
|
||||
{
|
||||
$query = $this->userGroup->accounts();
|
||||
if (0 !== count($types)) {
|
||||
$query->accountTypeIn($types);
|
||||
}
|
||||
$query->skip($startRow);
|
||||
$query->take($endRow - $startRow);
|
||||
|
||||
// add sort parameters. At this point they're filtered to allowed fields to sort by:
|
||||
if (0 !== count($sort)) {
|
||||
foreach ($sort as $label => $direction) {
|
||||
$query->orderBy(sprintf('accounts.%s', $label), $direction);
|
||||
}
|
||||
}
|
||||
|
||||
if (0 === count($sort)) {
|
||||
$query->orderBy('accounts.order', 'ASC');
|
||||
$query->orderBy('accounts.active', 'DESC');
|
||||
$query->orderBy('accounts.name', 'ASC');
|
||||
}
|
||||
|
||||
return $query->get(['accounts.*']);
|
||||
}
|
||||
|
||||
public function getActiveAccountsByType(array $types): Collection
|
||||
{
|
||||
$query = $this->userGroup->accounts();
|
||||
if (0 !== count($types)) {
|
||||
$query->accountTypeIn($types);
|
||||
}
|
||||
$query->where('active', true);
|
||||
$query->orderBy('accounts.account_type_id', 'ASC');
|
||||
$query->orderBy('accounts.order', 'ASC');
|
||||
$query->orderBy('accounts.name', 'ASC');
|
||||
|
||||
return $query->get(['accounts.*']);
|
||||
}
|
||||
|
||||
public function resetAccountOrder(): void
|
||||
{
|
||||
$sets = [
|
||||
[AccountType::DEFAULT, AccountType::ASSET],
|
||||
[AccountType::LOAN, AccountType::DEBT, AccountType::CREDITCARD, AccountType::MORTGAGE],
|
||||
];
|
||||
foreach ($sets as $set) {
|
||||
$list = $this->getAccountsByType($set);
|
||||
$index = 1;
|
||||
foreach ($list as $account) {
|
||||
if (false === $account->active) {
|
||||
$account->order = 0;
|
||||
|
||||
continue;
|
||||
}
|
||||
if ($index !== (int)$account->order) {
|
||||
app('log')->debug(sprintf('Account #%d ("%s"): order should %d be but is %d.', $account->id, $account->name, $index, $account->order));
|
||||
$account->order = $index;
|
||||
$account->save();
|
||||
}
|
||||
++$index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getAccountsByType(array $types, ?array $sort = []): Collection
|
||||
{
|
||||
$res = array_intersect([AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT], $types);
|
||||
@ -187,20 +263,6 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
return $query->get(['accounts.*']);
|
||||
}
|
||||
|
||||
public function getActiveAccountsByType(array $types): Collection
|
||||
{
|
||||
$query = $this->userGroup->accounts();
|
||||
if (0 !== count($types)) {
|
||||
$query->accountTypeIn($types);
|
||||
}
|
||||
$query->where('active', true);
|
||||
$query->orderBy('accounts.account_type_id', 'ASC');
|
||||
$query->orderBy('accounts.order', 'ASC');
|
||||
$query->orderBy('accounts.name', 'ASC');
|
||||
|
||||
return $query->get(['accounts.*']);
|
||||
}
|
||||
|
||||
public function searchAccount(string $query, array $types, int $limit): Collection
|
||||
{
|
||||
// search by group, not by user
|
||||
|
@ -35,6 +35,8 @@ use Illuminate\Support\Collection;
|
||||
*/
|
||||
interface AccountRepositoryInterface
|
||||
{
|
||||
public function countAccounts(array $types): int;
|
||||
|
||||
public function find(int $accountId): ?Account;
|
||||
|
||||
public function findByAccountNumber(string $number, array $types): ?Account;
|
||||
@ -49,6 +51,11 @@ interface AccountRepositoryInterface
|
||||
|
||||
public function getAccountsByType(array $types, ?array $sort = []): Collection;
|
||||
|
||||
/**
|
||||
* Used in the infinite accounts list.
|
||||
*/
|
||||
public function getAccountsInOrder(array $types, array $sort, int $startRow, int $endRow): Collection;
|
||||
|
||||
public function getActiveAccountsByType(array $types): Collection;
|
||||
|
||||
/**
|
||||
@ -56,6 +63,11 @@ interface AccountRepositoryInterface
|
||||
*/
|
||||
public function getMetaValue(Account $account, string $field): ?string;
|
||||
|
||||
/**
|
||||
* Reset order types of the mentioned accounts.
|
||||
*/
|
||||
public function resetAccountOrder(): void;
|
||||
|
||||
public function searchAccount(string $query, array $types, int $limit): Collection;
|
||||
|
||||
public function setUser(User $user): void;
|
||||
|
@ -39,7 +39,7 @@ class RemoteUserGuard implements Guard
|
||||
{
|
||||
protected Application $application;
|
||||
protected UserProvider $provider;
|
||||
protected ?User $user;
|
||||
protected ?User $user;
|
||||
|
||||
/**
|
||||
* Create a new authentication guard.
|
||||
|
@ -79,13 +79,14 @@ trait CalculateXOccurrencesSince
|
||||
while ($total < $count) {
|
||||
$domCorrected = min($dayOfMonth, $mutator->daysInMonth);
|
||||
$mutator->day = $domCorrected;
|
||||
app('log')->debug(sprintf('Mutator is now %s', $mutator->format('Y-m-d')));
|
||||
if (0 === $attempts % $skipMod && $mutator->gte($afterDate)) {
|
||||
app('log')->debug('Is added to the list.');
|
||||
$return[] = clone $mutator;
|
||||
++$total;
|
||||
}
|
||||
++$attempts;
|
||||
$mutator = $mutator->endOfMonth()->addDay();
|
||||
app('log')->debug(sprintf('Mutator is now %s', $mutator->format('Y-m-d')));
|
||||
}
|
||||
|
||||
return $return;
|
||||
|
@ -72,16 +72,18 @@ class OperatorQuerySearch implements SearchInterface
|
||||
private GroupCollectorInterface $collector;
|
||||
private CurrencyRepositoryInterface $currencyRepository;
|
||||
private array $excludeTags;
|
||||
private array $includeAnyTags;
|
||||
// added to fix #8632
|
||||
private array $includeTags;
|
||||
private array $invalidOperators;
|
||||
private int $limit;
|
||||
private Collection $operators;
|
||||
private int $page;
|
||||
private array $prohibitedWords;
|
||||
private float $startTime;
|
||||
private TagRepositoryInterface $tagRepository;
|
||||
private array $validOperators;
|
||||
private array $words;
|
||||
private array $invalidOperators;
|
||||
private int $limit;
|
||||
private Collection $operators;
|
||||
private int $page;
|
||||
private array $prohibitedWords;
|
||||
private float $startTime;
|
||||
private TagRepositoryInterface $tagRepository;
|
||||
private array $validOperators;
|
||||
private array $words;
|
||||
|
||||
/**
|
||||
* OperatorQuerySearch constructor.
|
||||
@ -93,6 +95,7 @@ class OperatorQuerySearch implements SearchInterface
|
||||
$this->page = 1;
|
||||
$this->words = [];
|
||||
$this->excludeTags = [];
|
||||
$this->includeAnyTags = [];
|
||||
$this->includeTags = [];
|
||||
$this->prohibitedWords = [];
|
||||
$this->invalidOperators = [];
|
||||
@ -1112,8 +1115,9 @@ class OperatorQuerySearch implements SearchInterface
|
||||
$this->collector->findNothing();
|
||||
}
|
||||
if ($tags->count() > 0) {
|
||||
$ids = array_values($tags->pluck('id')->toArray());
|
||||
$this->includeTags = array_unique(array_merge($this->includeTags, $ids));
|
||||
// changed from includeTags to includeAnyTags for #8632
|
||||
$ids = array_values($tags->pluck('id')->toArray());
|
||||
$this->includeAnyTags = array_unique(array_merge($this->includeAnyTags, $ids));
|
||||
}
|
||||
|
||||
break;
|
||||
@ -1125,8 +1129,9 @@ class OperatorQuerySearch implements SearchInterface
|
||||
$this->collector->findNothing();
|
||||
}
|
||||
if ($tags->count() > 0) {
|
||||
$ids = array_values($tags->pluck('id')->toArray());
|
||||
$this->includeTags = array_unique(array_merge($this->includeTags, $ids));
|
||||
// changed from includeTags to includeAnyTags for #8632
|
||||
$ids = array_values($tags->pluck('id')->toArray());
|
||||
$this->includeAnyTags = array_unique(array_merge($this->includeAnyTags, $ids));
|
||||
}
|
||||
|
||||
break;
|
||||
@ -2725,6 +2730,19 @@ class OperatorQuerySearch implements SearchInterface
|
||||
}
|
||||
$this->collector->setAllTags($collection);
|
||||
}
|
||||
// if include ANY tags, include them: (see #8632)
|
||||
if (count($this->includeAnyTags) > 0) {
|
||||
app('log')->debug(sprintf('%d include ANY tag(s)', count($this->includeAnyTags)));
|
||||
$collection = new Collection();
|
||||
foreach ($this->includeAnyTags as $tagId) {
|
||||
$tag = $this->tagRepository->find($tagId);
|
||||
if (null !== $tag) {
|
||||
app('log')->debug(sprintf('Include ANY tag "%s"', $tag->tag));
|
||||
$collection->push($tag);
|
||||
}
|
||||
}
|
||||
$this->collector->setTags($collection);
|
||||
}
|
||||
}
|
||||
|
||||
public function getWords(): array
|
||||
|
@ -114,7 +114,7 @@ class AccountTransformer extends AbstractTransformer
|
||||
|
||||
// no currency? use default
|
||||
$currency = $this->default;
|
||||
if (0 !== (int)$this->accountMeta[$id]['currency_id']) {
|
||||
if (array_key_exists($id, $this->accountMeta) && 0 !== (int)$this->accountMeta[$id]['currency_id']) {
|
||||
$currency = $this->currencies[(int)$this->accountMeta[$id]['currency_id']];
|
||||
}
|
||||
// amounts and calculation.
|
||||
|
@ -47,7 +47,7 @@ use Illuminate\Support\Facades\DB;
|
||||
class TransactionGroupTransformer extends AbstractTransformer
|
||||
{
|
||||
private array $accountTypes = []; // account types collection.
|
||||
private ExchangeRateConverter $converter; // collection of all journals and some important meta-data.
|
||||
private ExchangeRateConverter $converter; // collection of all journals and some important meta-data.
|
||||
private array $currencies = [];
|
||||
private TransactionCurrency $default; // collection of all currencies for this transformer.
|
||||
private array $journals = [];
|
||||
|
@ -112,7 +112,7 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"barryvdh/laravel-debugbar": "^3.9",
|
||||
"barryvdh/laravel-ide-helper": "2.*",
|
||||
"barryvdh/laravel-ide-helper": "3.*",
|
||||
"ergebnis/phpstan-rules": "^2.1",
|
||||
"fakerphp/faker": "1.*",
|
||||
"filp/whoops": "2.*",
|
||||
|
1797
composer.lock
generated
1797
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -115,9 +115,9 @@ return [
|
||||
'handle_debts' => true,
|
||||
// see cer.php for exchange rates feature flag.
|
||||
],
|
||||
'version' => '6.1.10',
|
||||
'version' => 'develop/2024-03-07',
|
||||
'api_version' => '2.0.12',
|
||||
'db_version' => 22,
|
||||
'db_version' => 23,
|
||||
|
||||
// generic settings
|
||||
'maxUploadSize' => 1073741824, // 1 GB
|
||||
@ -917,6 +917,7 @@ return [
|
||||
'sorting' => [
|
||||
'allowed' => [
|
||||
'transactions' => ['description', 'amount'],
|
||||
'accounts' => ['name'],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
54
database/migrations/2024_03_03_174645_add_indices.php
Normal file
54
database/migrations/2024_03_03_174645_add_indices.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class () extends Migration {
|
||||
private const string QUERY_ERROR = 'Could not execute query (table "%s", field "%s"): %s';
|
||||
private const string EXPL = 'If the index already exists (see error), this is not an problem. Otherwise, please open a GitHub discussion.';
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
// add missing indices
|
||||
$set = [
|
||||
'account_meta' => ['account_id'],
|
||||
'accounts' => ['user_id', 'user_group_id', 'account_type_id'],
|
||||
'budgets' => ['user_id', 'user_group_id'],
|
||||
'journal_meta' => ['transaction_journal_id', 'data', 'name'],
|
||||
'category_transaction_journal' => ['transaction_journal_id'],
|
||||
'categories' => ['user_id', 'user_group_id'],
|
||||
'transaction_currencies' => ['code'],
|
||||
'transaction_groups' => ['user_id', 'user_group_id'],
|
||||
'transaction_journals' => ['user_id', 'user_group_id', 'date', 'transaction_group_id', 'transaction_type_id', 'transaction_currency_id', 'bill_id'],
|
||||
'transactions' => ['account_id', 'transaction_journal_id', 'transaction_currency_id', 'foreign_currency_id'],
|
||||
];
|
||||
|
||||
foreach ($set as $table => $fields) {
|
||||
foreach ($fields as $field) {
|
||||
try {
|
||||
Schema::table(
|
||||
$table,
|
||||
static function (Blueprint $blueprint) use ($field): void {
|
||||
$blueprint->index($field);
|
||||
}
|
||||
);
|
||||
} catch (QueryException $e) {
|
||||
app('log')->error(sprintf(self::QUERY_ERROR, $table, $field, $e->getMessage()));
|
||||
app('log')->error(self::EXPL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void {}
|
||||
};
|
6
package-lock.json
generated
6
package-lock.json
generated
@ -460,9 +460,9 @@
|
||||
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA=="
|
||||
},
|
||||
"node_modules/alpinejs": {
|
||||
"version": "3.13.5",
|
||||
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.5.tgz",
|
||||
"integrity": "sha512-1d2XeNGN+Zn7j4mUAKXtAgdc4/rLeadyTMWeJGXF5DzwawPBxwTiBhFFm6w/Ei8eJxUZeyNWWSD9zknfdz1kEw==",
|
||||
"version": "3.13.6",
|
||||
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.6.tgz",
|
||||
"integrity": "sha512-/F7pVR+11r1A0KVw+eY1DcjTFlRQn9arD3p5/2Q4vq0N2WDC/dHpg+Pz7ZMiVQlHE7ZmZmcqTRm1wYTdDLMiEg==",
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "~3.1.1"
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
import{f as n}from"./vendor-50e42c6c.js";function e(){return{id:"",name:"",alpine_name:""}}function o(){return{description:[],amount:[],currency_code:[],foreign_amount:[],foreign_currency_code:[],source_account:[],destination_account:[],budget_id:[],category_name:[],piggy_bank_id:[],bill_id:[],tags:[],notes:[],internal_reference:[],external_url:[],latitude:[],longitude:[],zoom_level:[],date:[],interest_date:[],book_date:[],process_date:[],due_date:[],payment_date:[],invoice_date:[]}}function d(){let t=n(new Date,"yyyy-MM-dd HH:mm");return{description:"",amount:"",currency_code:"EUR",foreign_amount:"",foreign_currency_code:"",source_account:e(),destination_account:e(),budget_id:null,category_name:"",piggy_bank_id:null,bill_id:null,tags:[],notes:"",internal_reference:"",external_url:"",hasLocation:!1,latitude:null,longitude:null,zoomLevel:null,date:t,interest_date:"",book_date:"",process_date:"",due_date:"",payment_date:"",invoice_date:"",errors:o()}}export{d as c,o as d};
|
||||
import{f as n}from"./vendor-f5e18451.js";function e(){return{id:"",name:"",alpine_name:""}}function o(){return{description:[],amount:[],currency_code:[],foreign_amount:[],foreign_currency_code:[],source_account:[],destination_account:[],budget_id:[],category_name:[],piggy_bank_id:[],bill_id:[],tags:[],notes:[],internal_reference:[],external_url:[],latitude:[],longitude:[],zoom_level:[],date:[],interest_date:[],book_date:[],process_date:[],due_date:[],payment_date:[],invoice_date:[]}}function d(){let t=n(new Date,"yyyy-MM-dd HH:mm");return{description:"",amount:"",currency_code:"EUR",foreign_amount:"",foreign_currency_code:"",source_account:e(),destination_account:e(),budget_id:null,category_name:"",piggy_bank_id:null,bill_id:null,tags:[],notes:"",internal_reference:"",external_url:"",hasLocation:!1,latitude:null,longitude:null,zoomLevel:null,date:t,interest_date:"",book_date:"",process_date:"",due_date:"",payment_date:"",invoice_date:"",errors:o()}}export{d as c,o as d};
|
1
public/build/assets/dashboard-41a4b449.js
Normal file
1
public/build/assets/dashboard-41a4b449.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
import{a as s}from"./format-money-2e5851ad.js";let t=class{list(a){return s.get("/api/v2/subscriptions",{params:a})}paid(a){return s.get("/api/v2/subscriptions/sum/paid",{params:a})}unpaid(a){return s.get("/api/v2/subscriptions/sum/unpaid",{params:a})}};class e{list(a){return s.get("/api/v2/piggy-banks",{params:a})}}export{t as G,e as a};
|
||||
import{a as s}from"./format-money-00d5f4b9.js";let t=class{list(a){return s.get("/api/v2/subscriptions",{params:a})}paid(a){return s.get("/api/v2/subscriptions/sum/paid",{params:a})}unpaid(a){return s.get("/api/v2/subscriptions/sum/unpaid",{params:a})}};class e{list(a){return s.get("/api/v2/piggy-banks",{params:a})}}export{t as G,e as a};
|
@ -1 +1 @@
|
||||
import{a as t}from"./format-money-2e5851ad.js";class n{list(a){return t.get("/api/v2/transactions",{params:a})}infiniteList(a){return t.get("/api/v2/infinite/transactions",{params:a})}show(a,i){return t.get("/api/v2/transactions/"+a,{params:i})}}export{n as G};
|
||||
import{a as t}from"./format-money-00d5f4b9.js";class n{list(a){return t.get("/api/v2/transactions",{params:a})}infiniteList(a){return t.get("/api/v2/infinite/transactions",{params:a})}show(a,i){return t.get("/api/v2/transactions/"+a,{params:i})}}export{n as G};
|
1
public/build/assets/get-5b2cde80.js
Normal file
1
public/build/assets/get-5b2cde80.js
Normal file
@ -0,0 +1 @@
|
||||
import{a as s}from"./format-money-00d5f4b9.js";import{f as n}from"./vendor-f5e18451.js";class c{show(a,t){return s.get("/api/v2/accounts/"+a,{params:t})}index(a){return s.get("/api/v2/accounts",{params:a})}transactions(a,t){const r={page:t.page??1};return t.hasOwnProperty("start")&&(r.start=n(t.start,"y-MM-dd")),t.hasOwnProperty("end")&&(r.end=n(t.end,"y-MM-dd")),s.get("/api/v2/accounts/"+a+"/transactions",{params:r})}}export{c as G};
|
File diff suppressed because one or more lines are too long
1
public/build/assets/index-943f66ab.js
Normal file
1
public/build/assets/index-943f66ab.js
Normal file
File diff suppressed because one or more lines are too long
1
public/build/assets/index-bcaedc78.js
Normal file
1
public/build/assets/index-bcaedc78.js
Normal file
@ -0,0 +1 @@
|
||||
import{d as l,f as d}from"./format-money-00d5f4b9.js";import{f as c,i as o}from"./vendor-f5e18451.js";/* empty css */import{G as f}from"./get-5b2cde80.js";const i=window.location.href.split("/"),u=i[i.length-1];let h=function(){return{notifications:{error:{show:!1,text:"",url:""},success:{show:!1,text:"",url:""},wait:{show:!1,text:""}},totalPages:1,page:1,tableColumns:{name:{enabled:!0}},accounts:[],formatMoney(t,a){return d(t,a)},format(t){return c(t,o.t("config.date_time_fns"))},init(){this.notifications.wait.show=!0,this.notifications.wait.text=o.t("firefly.wait_loading_data"),this.loadAccounts()},loadAccounts(){new f().index({type:u,page:this.page}).then(t=>{for(let a=0;a<t.data.data.length;a++)if(t.data.data.hasOwnProperty(a)){let e=t.data.data[a],r={id:parseInt(e.id),name:e.attributes.name};this.accounts.push(r)}this.notifications.wait.show=!1})}}},n={index:h,dates:l};function s(){Object.keys(n).forEach(t=>{console.log(`Loading page component "${t}"`);let a=n[t]();Alpine.data(t,()=>a)}),Alpine.start()}document.addEventListener("firefly-iii-bootstrapped",()=>{console.log("Loaded through event listener."),s()});window.bootstrapped&&(console.log("Loaded through window variable."),s());
|
@ -1 +1 @@
|
||||
import{c as o}from"./create-empty-split-81f71b2e.js";import{f as _}from"./vendor-50e42c6c.js";function l(a,r){let n=[];for(let i in a)if(a.hasOwnProperty(i)){let e=a[i],t=o();t.transaction_journal_id=e.transaction_journal_id,t.transaction_group_id=r,t.bill_id=e.bill_id,t.bill_name=e.bill_name,t.budget_id=e.budget_id,t.budget_name=e.budget_name,t.category_name=e.category_name,t.category_id=e.category_id,t.piggy_bank_id=e.piggy_bank_id,t.piggy_bank_name=e.piggy_bank_name,t.book_date=e.book_date,t.due_date=e.due_date,t.interest_date=e.interest_date,t.invoice_date=e.invoice_date,t.payment_date=e.payment_date,t.process_date=e.process_date,t.external_url=e.external_url,t.internal_reference=e.internal_reference,t.notes=e.notes,t.tags=e.tags,t.amount=parseFloat(e.amount).toFixed(e.currency_decimal_places),t.currency_code=e.currency_code,e.foreign_amount!==null&&(t.forein_currency_code=e.foreign_currency_code,t.foreign_amount=parseFloat(e.foreign_amount).toFixed(e.foreign_currency_decimal_places)),t.date=_(new Date(e.date),"yyyy-MM-dd HH:mm"),t.description=e.description,t.destination_account={id:e.destination_id,name:e.destination_name,type:e.destination_type,alpine_name:e.destination_name},t.source_account={id:e.source_id,name:e.source_name,type:e.source_type,alpine_name:e.source_name},e.latitude!==null&&(t.hasLocation=!0,t.latitude=e.latitude,t.longitude=e.longitude,t.zoomLevel=e.zoom_level),n.push(t)}return n}export{l as p};
|
||||
import{c as o}from"./create-empty-split-c97f45c5.js";import{f as _}from"./vendor-f5e18451.js";function l(a,r){let n=[];for(let i in a)if(a.hasOwnProperty(i)){let e=a[i],t=o();t.transaction_journal_id=e.transaction_journal_id,t.transaction_group_id=r,t.bill_id=e.bill_id,t.bill_name=e.bill_name,t.budget_id=e.budget_id,t.budget_name=e.budget_name,t.category_name=e.category_name,t.category_id=e.category_id,t.piggy_bank_id=e.piggy_bank_id,t.piggy_bank_name=e.piggy_bank_name,t.book_date=e.book_date,t.due_date=e.due_date,t.interest_date=e.interest_date,t.invoice_date=e.invoice_date,t.payment_date=e.payment_date,t.process_date=e.process_date,t.external_url=e.external_url,t.internal_reference=e.internal_reference,t.notes=e.notes,t.tags=e.tags,t.amount=parseFloat(e.amount).toFixed(e.currency_decimal_places),t.currency_code=e.currency_code,e.foreign_amount!==null&&(t.forein_currency_code=e.foreign_currency_code,t.foreign_amount=parseFloat(e.foreign_amount).toFixed(e.foreign_currency_decimal_places)),t.date=_(new Date(e.date),"yyyy-MM-dd HH:mm"),t.description=e.description,t.destination_account={id:e.destination_id,name:e.destination_name,type:e.destination_type,alpine_name:e.destination_name},t.source_account={id:e.source_id,name:e.source_name,type:e.source_type,alpine_name:e.source_name},e.latitude!==null&&(t.hasLocation=!0,t.latitude=e.latitude,t.longitude=e.longitude,t.zoomLevel=e.zoom_level),n.push(t)}return n}export{l as p};
|
@ -1 +1 @@
|
||||
import{a as p}from"./format-money-2e5851ad.js";class u{put(t,a){let r="/api/v2/transactions/"+parseInt(a.id);return p.put(r,t)}}export{u as P};
|
||||
import{a as p}from"./format-money-00d5f4b9.js";class u{put(t,a){let r="/api/v2/transactions/"+parseInt(a.id);return p.put(r,t)}}export{u as P};
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
public/build/assets/vendor-52daf6b6.css
Normal file
1
public/build/assets/vendor-52daf6b6.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,67 +1,80 @@
|
||||
{
|
||||
"_create-empty-split-81f71b2e.js": {
|
||||
"file": "assets/create-empty-split-81f71b2e.js",
|
||||
"_create-empty-split-c97f45c5.js": {
|
||||
"file": "assets/create-empty-split-c97f45c5.js",
|
||||
"imports": [
|
||||
"_vendor-50e42c6c.js"
|
||||
"_vendor-f5e18451.js"
|
||||
],
|
||||
"integrity": "sha384-iCRJ+fmYZVSXY4L5+Q8vhQKqcjSi6lnglG7yW8deRAjJwiVaANm0EzriQPOAgQMS"
|
||||
"integrity": "sha384-wKgs+6aM6DzW9ZXDeQVeN/TZtIlkASgtnnFzSjaSdQ1oFOyhKBuZBr3j2nGoXWRI"
|
||||
},
|
||||
"_format-money-2e5851ad.js": {
|
||||
"file": "assets/format-money-2e5851ad.js",
|
||||
"_format-money-00d5f4b9.js": {
|
||||
"file": "assets/format-money-00d5f4b9.js",
|
||||
"imports": [
|
||||
"_vendor-50e42c6c.js"
|
||||
"_vendor-f5e18451.js"
|
||||
],
|
||||
"integrity": "sha384-kzF0fVkQWinHDsR1R190XjenILJ0VYvD2H1rQJW23dJvcA18qRrGRyb9q+3RADZU"
|
||||
"integrity": "sha384-NwibVlqlZ8kjqF9U76HNU1p/xODJOtH4zTBi6i4OGG4x9PcqhTEbQtoXC4WnYE2u"
|
||||
},
|
||||
"_get-28b9aa25.js": {
|
||||
"file": "assets/get-28b9aa25.js",
|
||||
"_get-0097c30a.js": {
|
||||
"file": "assets/get-0097c30a.js",
|
||||
"imports": [
|
||||
"_format-money-2e5851ad.js"
|
||||
"_format-money-00d5f4b9.js"
|
||||
],
|
||||
"integrity": "sha384-pKOfaG0JCi2+ROk2Tt8poQvr+uMK7u4BUCUKRcvEU/bREr4YReZnqSNBWtvdP3dW"
|
||||
"integrity": "sha384-FLOxTBwEkGf7PbJFJGi1kmcf110PY99rnGSOOmJSlnPd4fnsEZXYu4e7Q0gAxmC8"
|
||||
},
|
||||
"_get-e0a81c64.js": {
|
||||
"file": "assets/get-e0a81c64.js",
|
||||
"_get-59bb27e9.js": {
|
||||
"file": "assets/get-59bb27e9.js",
|
||||
"imports": [
|
||||
"_format-money-2e5851ad.js"
|
||||
"_format-money-00d5f4b9.js"
|
||||
],
|
||||
"integrity": "sha384-3OBZFmOXNyobpHvnLUfs5EdHG9fjwBAhQRszBlMtAO55MsE+OrB0/xnuNfMN+R7N"
|
||||
"integrity": "sha384-bmtl6LrtsrNh0rSgcWohzOXmDozXSaQ9BLaXchj8IiJAeqULMGFOx/z6olr9Z6I9"
|
||||
},
|
||||
"_parse-downloaded-splits-83822d1b.js": {
|
||||
"file": "assets/parse-downloaded-splits-83822d1b.js",
|
||||
"_get-5b2cde80.js": {
|
||||
"file": "assets/get-5b2cde80.js",
|
||||
"imports": [
|
||||
"_create-empty-split-81f71b2e.js",
|
||||
"_vendor-50e42c6c.js"
|
||||
"_format-money-00d5f4b9.js",
|
||||
"_vendor-f5e18451.js"
|
||||
],
|
||||
"integrity": "sha384-dPB0/W3hHYE/2m/HPqibSEe2N2XD7wMTePDsRd6fv03fKwgFu6mwOjUx1t3q6Gn6"
|
||||
"integrity": "sha384-Tr9M0BbSz6qynIKHsBlaiZlTWRqPh5K8oIoFNE9eqXvooEeAW2o0thWfuXBrV5s+"
|
||||
},
|
||||
"_put-bb9ad93a.js": {
|
||||
"file": "assets/put-bb9ad93a.js",
|
||||
"_parse-downloaded-splits-9e5b1aaf.js": {
|
||||
"file": "assets/parse-downloaded-splits-9e5b1aaf.js",
|
||||
"imports": [
|
||||
"_format-money-2e5851ad.js"
|
||||
"_create-empty-split-c97f45c5.js",
|
||||
"_vendor-f5e18451.js"
|
||||
],
|
||||
"integrity": "sha384-jk8bB684G+XotWRFO6k57GF1QGy6BUgFBb55JPsvU3clT6DBNA42ELW8vPyX9HbB"
|
||||
"integrity": "sha384-+u7ce40ZJSnmX7AM9MgiCCgIjXt+TkLmWC/p/A7bocN5A5HLNNEgM5UUkecfGDbU"
|
||||
},
|
||||
"_splice-errors-into-transactions-6727b386.js": {
|
||||
"file": "assets/splice-errors-into-transactions-6727b386.js",
|
||||
"_put-d6ed6223.js": {
|
||||
"file": "assets/put-d6ed6223.js",
|
||||
"imports": [
|
||||
"_format-money-2e5851ad.js",
|
||||
"_get-28b9aa25.js",
|
||||
"_vendor-50e42c6c.js"
|
||||
"_format-money-00d5f4b9.js"
|
||||
],
|
||||
"integrity": "sha384-x5WY5RRt7J6TLl+7nLZi1TBBKgUmjNokUh2ezaofCTQp0sEkHQrmJYAGUX/ZwPY4"
|
||||
"integrity": "sha384-AqFeuR/08blTrxpgzmtTKao9mbSJhBT6N/0clyeAgTtSUyH4w0gKJ2MTvt8lOujk"
|
||||
},
|
||||
"_vendor-50e42c6c.js": {
|
||||
"_splice-errors-into-transactions-bba1ef64.js": {
|
||||
"file": "assets/splice-errors-into-transactions-bba1ef64.js",
|
||||
"imports": [
|
||||
"_format-money-00d5f4b9.js",
|
||||
"_get-0097c30a.js",
|
||||
"_vendor-f5e18451.js"
|
||||
],
|
||||
"integrity": "sha384-ld/QxFbIFbqpxeHPa3i4Rs58YFPmt265gYQThhJqTDxuXYD0vBS5fiMzKYccFI7g"
|
||||
},
|
||||
"_vendor-f5e18451.js": {
|
||||
"assets": [
|
||||
"assets/layers-1dbbe9d0.png",
|
||||
"assets/layers-2x-066daca8.png",
|
||||
"assets/marker-icon-574c3a5c.png"
|
||||
],
|
||||
"css": [
|
||||
"assets/vendor-6fbf50c2.css"
|
||||
"assets/vendor-52daf6b6.css"
|
||||
],
|
||||
"file": "assets/vendor-50e42c6c.js",
|
||||
"integrity": "sha384-dsSpdnRISp7bIdL3fpyygvNfUizujkwhA11RPZehiEUvEt52ESLYt+Hlp3c9/iZp"
|
||||
"file": "assets/vendor-f5e18451.js",
|
||||
"integrity": "sha384-BzNMP5uYsiVrJylPJoLh/L+4WMPug3bTJN6sHZQpTe/UM3l1gwxier4DHUeuKNwq"
|
||||
},
|
||||
"grid-ff3-theme.css": {
|
||||
"file": "assets/grid-ff3-theme-badb0a41.css",
|
||||
"src": "grid-ff3-theme.css",
|
||||
"integrity": "sha384-YOSw8Sq/038Q+it1zgud/qsZiSuNu8tf099eME8psK3OAg1G3FfV063w7Q+LGQj0"
|
||||
},
|
||||
"node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.ttf": {
|
||||
"file": "assets/fa-brands-400-5656d596.ttf",
|
||||
@ -108,66 +121,76 @@
|
||||
"src": "node_modules/leaflet/dist/images/marker-icon.png",
|
||||
"integrity": "sha384-wg83fCOXjBtqzFAWhTL9Sd9vmLUNhfEEzfmNUX9zwv2igKlz/YQbdapF4ObdxF+R"
|
||||
},
|
||||
"resources/assets/v2/pages/dashboard/dashboard.js": {
|
||||
"file": "assets/dashboard-5f195194.js",
|
||||
"resources/assets/v2/pages/accounts/index.js": {
|
||||
"css": [
|
||||
"assets/grid-ff3-theme-badb0a41.css"
|
||||
],
|
||||
"file": "assets/index-bcaedc78.js",
|
||||
"imports": [
|
||||
"_format-money-2e5851ad.js",
|
||||
"_vendor-50e42c6c.js",
|
||||
"_get-e0a81c64.js",
|
||||
"_get-28b9aa25.js"
|
||||
"_format-money-00d5f4b9.js",
|
||||
"_vendor-f5e18451.js",
|
||||
"_get-5b2cde80.js"
|
||||
],
|
||||
"isEntry": true,
|
||||
"src": "resources/assets/v2/pages/accounts/index.js",
|
||||
"integrity": "sha384-76KRUQFORlmJ/leXDqMLlPq4/4wiW8vFbtL5qUk+bE86/oKpY8CIuk17zFU1emoS"
|
||||
},
|
||||
"resources/assets/v2/pages/dashboard/dashboard.js": {
|
||||
"file": "assets/dashboard-41a4b449.js",
|
||||
"imports": [
|
||||
"_format-money-00d5f4b9.js",
|
||||
"_vendor-f5e18451.js",
|
||||
"_get-5b2cde80.js",
|
||||
"_get-59bb27e9.js",
|
||||
"_get-0097c30a.js"
|
||||
],
|
||||
"isEntry": true,
|
||||
"src": "resources/assets/v2/pages/dashboard/dashboard.js",
|
||||
"integrity": "sha384-/MzTdILeI9XL/Q9uFEP734/m9XlPpRFcLFQxe8i+di4fuU218Q4W+NSG3DGcKQ11"
|
||||
"integrity": "sha384-1kybqwSzdo4Qfy5jIkvNgbYJwALV6jnXqKM/aGUAM1sjIJvfGnxK2DjHX4lZN3IZ"
|
||||
},
|
||||
"resources/assets/v2/pages/transactions/create.js": {
|
||||
"file": "assets/create-5a4939e8.js",
|
||||
"file": "assets/create-b721a410.js",
|
||||
"imports": [
|
||||
"_format-money-2e5851ad.js",
|
||||
"_create-empty-split-81f71b2e.js",
|
||||
"_splice-errors-into-transactions-6727b386.js",
|
||||
"_vendor-50e42c6c.js",
|
||||
"_get-28b9aa25.js"
|
||||
"_format-money-00d5f4b9.js",
|
||||
"_create-empty-split-c97f45c5.js",
|
||||
"_splice-errors-into-transactions-bba1ef64.js",
|
||||
"_vendor-f5e18451.js",
|
||||
"_get-0097c30a.js"
|
||||
],
|
||||
"isEntry": true,
|
||||
"src": "resources/assets/v2/pages/transactions/create.js",
|
||||
"integrity": "sha384-o9vKV/iUV+9XIsX8rG9jk8bMPSxuV827U7ScOca2b6I6f/lyJBUDmzoMqMIfaqce"
|
||||
"integrity": "sha384-cAEOcjbtExGHfptyeQTlY4AINMi2a/DH22mgeTzvjnzY2KbZ0UydbBze9XmXNq9v"
|
||||
},
|
||||
"resources/assets/v2/pages/transactions/edit.js": {
|
||||
"file": "assets/edit-4c7ba005.js",
|
||||
"file": "assets/edit-195027dc.js",
|
||||
"imports": [
|
||||
"_format-money-2e5851ad.js",
|
||||
"_get-e0a81c64.js",
|
||||
"_parse-downloaded-splits-83822d1b.js",
|
||||
"_splice-errors-into-transactions-6727b386.js",
|
||||
"_vendor-50e42c6c.js",
|
||||
"_create-empty-split-81f71b2e.js",
|
||||
"_put-bb9ad93a.js",
|
||||
"_get-28b9aa25.js"
|
||||
"_format-money-00d5f4b9.js",
|
||||
"_get-59bb27e9.js",
|
||||
"_parse-downloaded-splits-9e5b1aaf.js",
|
||||
"_splice-errors-into-transactions-bba1ef64.js",
|
||||
"_vendor-f5e18451.js",
|
||||
"_create-empty-split-c97f45c5.js",
|
||||
"_put-d6ed6223.js",
|
||||
"_get-0097c30a.js"
|
||||
],
|
||||
"isEntry": true,
|
||||
"src": "resources/assets/v2/pages/transactions/edit.js",
|
||||
"integrity": "sha384-MOmzHUGTHwnuic0yw3wUp8MJO+Y9iLdXGy+V5iWtiZLpG2ts8z8R8Ms9Gp9L7Rc0"
|
||||
},
|
||||
"resources/assets/v2/pages/transactions/index.css": {
|
||||
"file": "assets/index-badb0a41.css",
|
||||
"src": "resources/assets/v2/pages/transactions/index.css",
|
||||
"integrity": "sha384-YOSw8Sq/038Q+it1zgud/qsZiSuNu8tf099eME8psK3OAg1G3FfV063w7Q+LGQj0"
|
||||
"integrity": "sha384-yZLBSDqjmV3bx4u+464kapaHom6+82OkAyZfuITONyH8DbtMsQ1HrNPTJbeBZ3qN"
|
||||
},
|
||||
"resources/assets/v2/pages/transactions/index.js": {
|
||||
"css": [
|
||||
"assets/index-badb0a41.css"
|
||||
"assets/grid-ff3-theme-badb0a41.css"
|
||||
],
|
||||
"file": "assets/index-90222973.js",
|
||||
"file": "assets/index-943f66ab.js",
|
||||
"imports": [
|
||||
"_format-money-2e5851ad.js",
|
||||
"_vendor-50e42c6c.js",
|
||||
"_put-bb9ad93a.js",
|
||||
"_get-e0a81c64.js"
|
||||
"_format-money-00d5f4b9.js",
|
||||
"_vendor-f5e18451.js",
|
||||
"_put-d6ed6223.js",
|
||||
"_get-59bb27e9.js"
|
||||
],
|
||||
"isEntry": true,
|
||||
"src": "resources/assets/v2/pages/transactions/index.js",
|
||||
"integrity": "sha384-Sl6bKzSJxtyjnJoamDiBSZwdCi+AA8ZV/s40dXKFznm6RfYLiBLSS2qmQMlrVPZT"
|
||||
"integrity": "sha384-gaqmZQLTNbfSpNmb87PYF8FD5c7jq3Ng+4ghR3Q3aKxOLhOLyBXoVb7ryCnkpfcF"
|
||||
},
|
||||
"resources/assets/v2/pages/transactions/show.css": {
|
||||
"file": "assets/show-8b1429e5.css",
|
||||
@ -178,17 +201,17 @@
|
||||
"css": [
|
||||
"assets/show-8b1429e5.css"
|
||||
],
|
||||
"file": "assets/show-3ef1a32a.js",
|
||||
"file": "assets/show-63e01089.js",
|
||||
"imports": [
|
||||
"_format-money-2e5851ad.js",
|
||||
"_vendor-50e42c6c.js",
|
||||
"_get-e0a81c64.js",
|
||||
"_parse-downloaded-splits-83822d1b.js",
|
||||
"_create-empty-split-81f71b2e.js"
|
||||
"_format-money-00d5f4b9.js",
|
||||
"_vendor-f5e18451.js",
|
||||
"_get-59bb27e9.js",
|
||||
"_parse-downloaded-splits-9e5b1aaf.js",
|
||||
"_create-empty-split-c97f45c5.js"
|
||||
],
|
||||
"isEntry": true,
|
||||
"src": "resources/assets/v2/pages/transactions/show.js",
|
||||
"integrity": "sha384-SbD7zS5YgCmKGz4yqp3mEIvEye39FouSpd2+WCld8l7Wij23HCUdfGgF/cD+b21y"
|
||||
"integrity": "sha384-x17vRUDjWuxqceuMDNz/325LQPW85BoFU7068e9G3TPT01C1vfmQAfPcYxlWaYxn"
|
||||
},
|
||||
"resources/assets/v2/sass/app.scss": {
|
||||
"file": "assets/app-fb7b26ec.css",
|
||||
@ -197,8 +220,8 @@
|
||||
"integrity": "sha384-asG3EmbviAZntc1AzgJpoF+jBChn+oq/7eQfYWrCdJ1Ku/c7rJ82sstr6Eptxqgd"
|
||||
},
|
||||
"vendor.css": {
|
||||
"file": "assets/vendor-6fbf50c2.css",
|
||||
"file": "assets/vendor-52daf6b6.css",
|
||||
"src": "vendor.css",
|
||||
"integrity": "sha384-0YVccvAWchjHnv20Seb0KZBtTsv9I25fn2FuQ23kgKg6ST+hYNb95i7F6T/1sC58"
|
||||
"integrity": "sha384-Fc+BMCTQkokqH7JnKp0yiICeLn8STx2f8o4TbcqhtXweJCOlM/cHwVILvX9LkK9t"
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -2,7 +2,7 @@
|
||||
"config": {
|
||||
"html_language": "de",
|
||||
"date_time_fns": "dd. MMM. yyyy um HH:mm:ss",
|
||||
"date_time_fns_short": "dd. MMM. yyyy um HH:mm"
|
||||
"date_time_fns_short": "MMMM do, yyyy @ HH:mm"
|
||||
},
|
||||
"firefly": {
|
||||
"spent": "Ausgegeben",
|
||||
|
@ -2,7 +2,7 @@
|
||||
"config": {
|
||||
"html_language": "de",
|
||||
"date_time_fns": "dd. MMM. yyyy um HH:mm:ss",
|
||||
"date_time_fns_short": "dd. MMM. yyyy um HH:mm"
|
||||
"date_time_fns_short": "MMMM do, yyyy @ HH:mm"
|
||||
},
|
||||
"firefly": {
|
||||
"spent": "Ausgegeben",
|
||||
|
@ -17,7 +17,7 @@
|
||||
"money_flowing_in": "In",
|
||||
"money_flowing_out": "Ut",
|
||||
"category": "Kategori",
|
||||
"unknown_category_plain": "No category",
|
||||
"unknown_category_plain": "Ingen kategori",
|
||||
"all_money": "All your money",
|
||||
"unknown_source_plain": "Unknown source account",
|
||||
"unknown_dest_plain": "Unknown destination account",
|
||||
|
@ -17,7 +17,7 @@
|
||||
"money_flowing_in": "In",
|
||||
"money_flowing_out": "Ut",
|
||||
"category": "Kategori",
|
||||
"unknown_category_plain": "No category",
|
||||
"unknown_category_plain": "Ingen kategori",
|
||||
"all_money": "All your money",
|
||||
"unknown_source_plain": "Unknown source account",
|
||||
"unknown_dest_plain": "Unknown destination account",
|
||||
|
@ -82,7 +82,7 @@ The most exciting features are:
|
||||
Then the things that make you go "yeah OK, makes sense".
|
||||
|
||||
* A [double-entry](https://en.wikipedia.org/wiki/Double-entry_bookkeeping_system) bookkeeping system.
|
||||
* Save towards a goal using [piggy banks](https://docs.firefly-iii.org/explanation/financial-concepts/piggies/).
|
||||
* Save towards a goal using [piggy banks](https://docs.firefly-iii.org/explanation/financial-concepts/piggy-banks/).
|
||||
* View [income and expense reports](https://docs.firefly-iii.org/how-to/firefly-iii/finances/reports/).
|
||||
|
||||
And the things you would hope for but not expect:
|
||||
|
@ -5,8 +5,8 @@
|
||||
"flash_warning": "Achtung!",
|
||||
"flash_success": "Geschafft!",
|
||||
"close": "Schlie\u00dfen",
|
||||
"select_dest_account": "Bitte einen g\u00fcltigen Zielkontonamen ausw\u00e4hlen oder eingeben",
|
||||
"select_source_account": "Bitte einen g\u00fcltigen Quellkontonamen ausw\u00e4hlen oder eingeben",
|
||||
"select_dest_account": "Please select or type a valid destination account name",
|
||||
"select_source_account": "Please select or type a valid source account name",
|
||||
"split_transaction_title": "Beschreibung der Splittbuchung",
|
||||
"errors_submission": "Bei Ihren Eingaben stimmt etwas nicht. Bitte \u00fcberpr\u00fcfen Sie die unten stehenden Fehler.",
|
||||
"split": "Teilen",
|
||||
|
@ -26,17 +26,22 @@ export default class Get {
|
||||
/**
|
||||
*
|
||||
* @param identifier
|
||||
* @param date
|
||||
* @param params
|
||||
* @returns {Promise<AxiosResponse<any>>}
|
||||
*/
|
||||
get(identifier, date) {
|
||||
let params = {date: format(date, 'y-MM-dd').slice(0, 10)};
|
||||
if (!date) {
|
||||
return api.get('/api/v2/accounts/' + identifier);
|
||||
}
|
||||
show(identifier, params) {
|
||||
return api.get('/api/v2/accounts/' + identifier, {params: params});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param params
|
||||
* @returns {Promise<AxiosResponse<any>>}
|
||||
*/
|
||||
index(params) {
|
||||
return api.get('/api/v2/accounts', {params: params});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param identifier
|
||||
|
111
resources/assets/v2/pages/accounts/index.js
Normal file
111
resources/assets/v2/pages/accounts/index.js
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* show.js
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
import '../../boot/bootstrap.js';
|
||||
import dates from "../shared/dates.js";
|
||||
import i18next from "i18next";
|
||||
import {format} from "date-fns";
|
||||
import formatMoney from "../../util/format-money.js";
|
||||
|
||||
import '@ag-grid-community/styles/ag-grid.css';
|
||||
import '@ag-grid-community/styles/ag-theme-alpine.css';
|
||||
import '../../css/grid-ff3-theme.css';
|
||||
import Get from "../../api/v2/model/account/get.js";
|
||||
|
||||
// set type from URL
|
||||
const urlParts = window.location.href.split('/');
|
||||
const type = urlParts[urlParts.length - 1];
|
||||
|
||||
let index = function () {
|
||||
return {
|
||||
// notifications
|
||||
notifications: {
|
||||
error: {
|
||||
show: false, text: '', url: '',
|
||||
}, success: {
|
||||
show: false, text: '', url: '',
|
||||
}, wait: {
|
||||
show: false, text: '',
|
||||
|
||||
}
|
||||
}, totalPages: 1, page: 1, // available columns:
|
||||
tableColumns: {
|
||||
name: {
|
||||
enabled: true
|
||||
},
|
||||
},
|
||||
|
||||
accounts: [],
|
||||
|
||||
formatMoney(amount, currencyCode) {
|
||||
return formatMoney(amount, currencyCode);
|
||||
},
|
||||
|
||||
format(date) {
|
||||
return format(date, i18next.t('config.date_time_fns'));
|
||||
},
|
||||
|
||||
init() {
|
||||
this.notifications.wait.show = true;
|
||||
this.notifications.wait.text = i18next.t('firefly.wait_loading_data')
|
||||
this.loadAccounts();
|
||||
},
|
||||
|
||||
loadAccounts() {
|
||||
// one page only.
|
||||
(new Get()).index({type: type, page: this.page}).then(response => {
|
||||
for (let i = 0; i < response.data.data.length; i++) {
|
||||
if (response.data.data.hasOwnProperty(i)) {
|
||||
let current = response.data.data[i];
|
||||
let account = {
|
||||
id: parseInt(current.id),
|
||||
name: current.attributes.name,
|
||||
};
|
||||
this.accounts.push(account);
|
||||
}
|
||||
}
|
||||
this.notifications.wait.show = false;
|
||||
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let comps = {index, dates};
|
||||
|
||||
function loadPage() {
|
||||
Object.keys(comps).forEach(comp => {
|
||||
console.log(`Loading page component "${comp}"`);
|
||||
let data = comps[comp]();
|
||||
Alpine.data(comp, () => data);
|
||||
});
|
||||
Alpine.start();
|
||||
}
|
||||
|
||||
// wait for load until bootstrapped event is received.
|
||||
document.addEventListener('firefly-iii-bootstrapped', () => {
|
||||
console.log('Loaded through event listener.');
|
||||
loadPage();
|
||||
});
|
||||
// or is bootstrapped before event is triggered.
|
||||
if (window.bootstrapped) {
|
||||
console.log('Loaded through window variable.');
|
||||
loadPage();
|
||||
}
|
@ -191,7 +191,7 @@ export default () => ({
|
||||
if (account.hasOwnProperty(i)) {
|
||||
let accountId = account[i];
|
||||
// grab account info for box:
|
||||
(new Get).get(accountId, new Date(window.store.get('end'))).then((response) => {
|
||||
(new Get).show(accountId, new Date(window.store.get('end'))).then((response) => {
|
||||
let parent = response.data.data;
|
||||
|
||||
// get groups for account:
|
||||
|
@ -44,12 +44,6 @@ const urlParts = window.location.href.split('/');
|
||||
const type = urlParts[urlParts.length - 1];
|
||||
ds.setType(type);
|
||||
|
||||
document.addEventListener('cellEditRequest', () => {
|
||||
console.log('Loaded through event listener.');
|
||||
//loadPage();
|
||||
});
|
||||
let rowImmutableStore = [];
|
||||
|
||||
let dataTable;
|
||||
const editableFields = ['description', 'amount', 'date'];
|
||||
|
||||
@ -90,22 +84,18 @@ const onCellEditRequestMethod = (event) => {
|
||||
|
||||
};
|
||||
|
||||
document.addEventListener('cellValueChanged', () => {
|
||||
console.log('I just realized a cell value has changed.');
|
||||
});
|
||||
document.addEventListener('onCellValueChanged', () => {
|
||||
console.log('I just realized a cell value has changed.');
|
||||
});
|
||||
|
||||
let doOnCellValueChanged = function (e) {
|
||||
console.log('I just realized a cell value has changed.');
|
||||
};
|
||||
|
||||
const gridOptions = {
|
||||
rowModelType: 'infinite',
|
||||
datasource: ds,
|
||||
cacheOverflowSize: 1,
|
||||
cacheBlockSize: 20,
|
||||
onCellEditRequest: onCellEditRequestMethod,
|
||||
readOnlyEdit: true,
|
||||
getRowId: function (params) {
|
||||
console.log('getRowId', params.data.id);
|
||||
return params.data.id;
|
||||
},
|
||||
|
||||
// Row Data: The data to be displayed.
|
||||
// rowData: [
|
||||
// { description: "Tesla", model: "Model Y", price: 64950, electric: true },
|
||||
|
@ -64,7 +64,7 @@ return [
|
||||
// 'date_time' => '%B %e, %Y, @ %T',
|
||||
'date_time_js' => 'Do MMMM YYYY um HH:mm:ss',
|
||||
'date_time_fns' => 'dd. MMM. yyyy um HH:mm:ss',
|
||||
'date_time_fns_short' => 'dd. MMM. yyyy um HH:mm',
|
||||
'date_time_fns_short' => 'MMMM do, yyyy @ HH:mm',
|
||||
|
||||
// 'specific_day' => '%e %B %Y',
|
||||
'specific_day_js' => 'D. MMMM YYYY',
|
||||
|
@ -317,8 +317,8 @@ return [
|
||||
'update_new_version_alert' => 'Eine neue Version von Firefly III ist verfügbar. Sie verwenden :your_version, die neueste Version ist :new_version, die am :date veröffentlicht wurde.',
|
||||
'update_version_beta' => 'Seien Sie vorsichtig bei der Verwendung dieser BETA-Version. Sie könnte noch Fehler enthaltern.',
|
||||
'update_version_alpha' => 'Seien Sie vorsichtig bei der Verwendung dieser APLPHA-Version. Sie kann Fehler enthaltern.',
|
||||
'update_current_dev_older' => 'Sie verwenden die Entwicklungsversion „:version”, die älter ist als die neueste Version :new_version. Bitte aktualisieren Sie!',
|
||||
'update_current_dev_newer' => 'Sie verwenden die Entwicklungsversion „:version”, die neuer ist als die letzte Version :new_version.',
|
||||
'update_current_dev_older' => 'You are running development release ":version", which is older than the latest release :new_version. Please update!',
|
||||
'update_current_dev_newer' => 'You are running development release ":version", which is newer than the latest release :new_version.',
|
||||
'update_current_version_alert' => 'Sie verwenden Version :version. Dies ist die neueste verfügbare Version.',
|
||||
'update_newer_version_alert' => 'Sie verwenden :your_version. Ihre Version ist neuer als die neueste Version (:new_version).',
|
||||
'update_check_error' => 'Bei der Suche nach Aktualisierungen ist ein Fehler aufgetreten: :error',
|
||||
@ -896,12 +896,12 @@ return [
|
||||
'rule_trigger_budget_is' => 'Budget ist „:trigger_value”',
|
||||
'rule_trigger_tag_is_choice' => 'Irgendein Schlagwort lautet..',
|
||||
'rule_trigger_tag_is' => 'Irgendein Schlagwort lautet ":trigger_value"',
|
||||
'rule_trigger_tag_contains_choice' => 'Beliebiges Schlagwort enthält …',
|
||||
'rule_trigger_tag_contains' => 'Beliebiges Schlagwort enthält „:trigger_value”',
|
||||
'rule_trigger_tag_ends_choice' => 'Beliebiges Schlagwort endet auf ...',
|
||||
'rule_trigger_tag_ends' => 'Beliebiges Schlagwort endet auf „:trigger_value”',
|
||||
'rule_trigger_tag_starts_choice' => 'Beliebiges Schlagwort beginnt mit ...',
|
||||
'rule_trigger_tag_starts' => 'Beliebiges Schlagwort beginnt mit „:trigger_value”',
|
||||
'rule_trigger_tag_contains_choice' => 'Any tag contains..',
|
||||
'rule_trigger_tag_contains' => 'Any tag contains ":trigger_value"',
|
||||
'rule_trigger_tag_ends_choice' => 'Any tag ends with..',
|
||||
'rule_trigger_tag_ends' => 'Any tag ends with ":trigger_value"',
|
||||
'rule_trigger_tag_starts_choice' => 'Any tag starts with..',
|
||||
'rule_trigger_tag_starts' => 'Any tag starts with ":trigger_value"',
|
||||
'rule_trigger_currency_is_choice' => 'Buchungswährung ist..',
|
||||
'rule_trigger_currency_is' => 'Buchungswährung ist „:trigger_value”',
|
||||
'rule_trigger_foreign_currency_is_choice' => 'Fremdwährung der Buchung ist..',
|
||||
@ -1586,8 +1586,8 @@ return [
|
||||
'submission_options' => 'Übermittlungsoptionen',
|
||||
'apply_rules_checkbox' => 'Regeln anwenden',
|
||||
'fire_webhooks_checkbox' => 'Webhooks abfeuern',
|
||||
'select_source_account' => 'Bitte einen gültigen Quellkontonamen auswählen oder eingeben',
|
||||
'select_dest_account' => 'Bitte einen gültigen Zielkontonamen auswählen oder eingeben',
|
||||
'select_source_account' => 'Please select or type a valid source account name',
|
||||
'select_dest_account' => 'Please select or type a valid destination account name',
|
||||
|
||||
// convert stuff:
|
||||
'convert_is_already_type_Withdrawal' => 'Diese Buchung ist bereits eine Ausgabe',
|
||||
@ -2459,7 +2459,7 @@ return [
|
||||
'block_code_bounced' => 'E-Mail-Nachricht(en) wurden abgewiesen',
|
||||
'block_code_expired' => 'Demo-Konto abgelaufen',
|
||||
'no_block_code' => 'Kein Grund für Block oder Benutzer nicht blockiert',
|
||||
'demo_user_export' => 'Der Demo-Benutzer kann keine Daten exportieren',
|
||||
'demo_user_export' => 'The demo user cannot export data',
|
||||
'block_code_email_changed' => 'Der Benutzer hat die neue E-Mail-Adresse noch nicht bestätigt',
|
||||
'admin_update_email' => 'Im Gegensatz zur Profilseite wird der Benutzer NICHT benachrichtigt, dass seine E-Mail-Adresse geändert wurde!',
|
||||
'update_user' => 'Benutzer aktualisieren',
|
||||
|
@ -1941,7 +1941,7 @@ return [
|
||||
'categories' => 'Kategorier',
|
||||
'edit_category' => 'Redigera kategori ":name"',
|
||||
'no_category' => '(utan kategori)',
|
||||
'unknown_category_plain' => 'No category',
|
||||
'unknown_category_plain' => 'Ingen kategori',
|
||||
'category' => 'Kategori',
|
||||
'delete_category' => 'Ta bort kategori ":name"',
|
||||
'deleted_category' => 'Kategori ":name" togs bort',
|
||||
|
@ -34,6 +34,6 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'failed' => 'Credentials นี้ไม่ตรงกับบันทึกของเรา',
|
||||
'throttle' => 'พยายามเข้าสู่ระบบมากเกินไป โปรดลองใหม่อีกครั้งใน :seconds วินาที',
|
||||
'failed' => 'ข้อมูลนี้ไม่ตรงกับในระบบ',
|
||||
'throttle' => 'คุณได้พยายามเข้าระบบหลายครั้งเกินไป กรุณาลองใหม่ใน :seconds วินาทีข้างหน้า.',
|
||||
];
|
||||
|
@ -38,7 +38,7 @@ return [
|
||||
'see_help_icon' => 'อย่างไรก็ตาม ไอคอน <i class="fa fa-question-circle"></i> ในมุมบนขวาจะช่วยอธิบายเพิ่มเติมให้คุณ',
|
||||
'index' => 'ยินดีต้อนรับเข้าสู่ <strong>Firefly III</strong>! ในหน้านี้แสดงรายละเอียดทางการเงินอย่างรวดเร็วให้คุณรับชม สำหรับรายละเอียดเพิ่มเติม สามารถดูได้ที่บัญชี → <a href=":asset"> บัญชีสินทรัพย์ </a> และแน่นอน จะมีส่วนของหน้า <a href=":budgets"> งบประมาณ </a> และหน้า <a href=":reports"> รายงาน </a> หรือ คุณสามารถที่จะดูทุกเมนูได้ตามสบาย',
|
||||
'accounts-index' => 'บัญชีทรัพย์สินคือบัญชีส่วนตัวของคุณ, ส่วนบัญชีค่าใช้จ่ายคือบัญชีสำหรับให้คุณทำการสร้างรายจ่ายขึ้นมาในบัญชีนี้ เช่น การเก็บเงิน หรือ ให้เพื่อน เป็นต้น, บัญชีรายได้คือบัญชีที่ได้รับเงินจากส่วนต่างๆ เช่น เงินได้จากงานของคุณ, เงินได้จากภาครัฐ หรือ รายรับอื่นๆ, ส่วนบัญชีหนี้สิน คือ บัญชีสำหรับกรณีมีหนี้ และ การกู้ยืมต่างๆ เช่น หนี้จากบัตรเครดิต หรือ หนี้จากกองทุนยืมเรียน ซึ่งในหน้านี้คุณสามารถแก้ไขหรือลบได้',
|
||||
'budgets-index' => 'This page shows you an overview of your budgets. The top bar shows the amount that is available to be budgeted. This can be customized for any period by clicking the amount on the right. The amount you\'ve actually spent is shown in the bar below. Below that are the expenses per budget and what you\'ve budgeted for them.',
|
||||
'budgets-index' => 'หน้านี้แสดงภาพรวมงบประมาณของคุณ แถบด้านบนแสดงจำนวนเงินที่สามารถจัดงบประมาณได้ ซึ่งสามารถปรับแต่งในช่วงเวลาใดก็ได้โดยคลิกจำนวนเงินทางด้านขวา จำนวนเงินที่คุณใช้จ่ายจริงจะแสดงอยู่ในแถบด้านล่าง ด้านล่างนี้คือค่าใช้จ่ายต่องบประมาณและสิ่งที่คุณตั้งงบประมาณไว้',
|
||||
'reports-index-start' => 'Firefly III รองรับจำนวนรายงานหลายรูปแบบ อ่านเพิ่มเติมโดยคลิ๊กไปที่ไอคอน <i class="fa fa-question-circle"></i> ที่อยู่ด้านบนขวาของมุม',
|
||||
'reports-index-examples' => 'ลองดูตัวอย่างของเรา: <a href=":one">รายละเอียดการเงินรายเดือน</a>, <a href=":two">รายละเอียดการเงินรายปี</a> และ <a href=":three">รายละเอียดงบประมาณโดยรวม</a>',
|
||||
'currencies-index' => 'Firefly III รองรับค่าเงินหลายประเภท ถึงแม้ว่าค่าเงินเดิมจะตั้งค่าไว้ที่ Euro สามารถที่จะเปลี่ยนเป็น US Dollar ได้ หรือค่าเงินอื่นๆได้ โดยคุณจะเห็นหน้าของการตั้งค่าสกุลเงิน โดยจะมีค่าเงินต่างๆ อยู่แล้ว ซึ่งคุณสามารถเพิ่มค่าเงินของคุณเข้าไปได้ โดยที่การเปลี่ยนค่าเงินตั้งต้น จะไม่ส่งผลต่อการเปลี่ยนค่าเงินในธุรกรรมที่ถูกบันทึกไปก่อนหน้านี้ อย่างไรก็ตาม Firefly III รองรับค่าเงินหลายประเภทในเวลาเดียวกัน',
|
||||
|
@ -56,10 +56,10 @@ return [
|
||||
*/
|
||||
|
||||
// invite
|
||||
'invitation_created_subject' => 'An invitation has been created',
|
||||
'invitation_created_body' => 'Admin user ":email" created a user invitation which can be used by whoever is behind email address ":invitee". The invite will be valid for 48hrs.',
|
||||
'invite_user_subject' => 'You\'ve been invited to create a Firefly III account.',
|
||||
'invitation_introduction' => 'You\'ve been invited to create a Firefly III account on **:host**. Firefly III is a personal, self-hosted, private personal finance manager. All the cool kids are using it.',
|
||||
'invitation_created_subject' => 'คำเชิญถูกสร้างขึ้นสร้างแล้ว',
|
||||
'invitation_created_body' => 'ผู้ดูแลระบบ ":email" ได้สร้างคำเชิญให้ผู้ใช้ตามที่อยู่อีเมล ":invitee" คำเชิญจะมีอายุการใช้งาน 48 ชั่วโมง',
|
||||
'invite_user_subject' => 'คุณได้รับเชิญให้สร้างบัญชี Firefly III',
|
||||
'invitation_introduction' => 'คุณได้รับเชิญให้สร้างบัญชี Firefly III บน **:host** Firefly III เป็นผู้จัดการการเงินส่วนบุคคลส่วนตัวที่โฮสต์เอง',
|
||||
'invitation_invited_by' => 'You\'ve been invited by ":admin" and this invitation was sent to ":invitee". That\'s you, right?',
|
||||
'invitation_url' => 'The invitation is valid for 48 hours and can be redeemed by surfing to [Firefly III](:url). Enjoy!',
|
||||
|
||||
|
@ -38,29 +38,29 @@ return [
|
||||
'index_intro' => 'ยินดีต้อนรับสู่หน้าหลักของ Firefly III โปรดสละเวลาอ่านบทแนะนำนี้เพื่อทำความเข้าใจว่า Firefly III ทำงานอย่างไร',
|
||||
'index_accounts-chart' => 'กราฟนี้แสดงยอดคงเหลือปัจจุบันของบัญชีสินทรัพย์ของคุณ คุณสามารถเลือกบัญชีที่แสดงได้ที่นี่ในการตั้งค่าของคุณ',
|
||||
'index_box_out_holder' => 'กล่องเล็กๆ นี้และกล่องข้างๆ จะทำให้คุณเห็นภาพรวมโดยย่อเกี่ยวกับสถานการณ์ทางการเงินของคุณ',
|
||||
'index_help' => 'If you ever need help with a page or a form, press this button.',
|
||||
'index_outro' => 'Most pages of Firefly III will start with a little tour like this one. Please contact me when you have questions or comments. Enjoy!',
|
||||
'index_sidebar-toggle' => 'To create new transactions, accounts or other things, use the menu under this icon.',
|
||||
'index_cash_account' => 'These are the accounts created so far. You can use the cash account to track cash expenses but it\'s not mandatory of course.',
|
||||
'index_help' => 'หากคุณต้องการความช่วยเหลือเกี่ยวกับหน้าหรือแบบฟอร์ม ให้กดปุ่มนี้',
|
||||
'index_outro' => 'หน้าเพจส่วนใหญ่ของ Firefly III จะเริ่มต้นด้วยการทัวร์เล็กๆ น้อยๆ แบบนี้ โปรดติดต่อฉันเมื่อคุณมีคำถามหรือความคิดเห็น Enjoy!',
|
||||
'index_sidebar-toggle' => 'หากต้องการสร้างธุรกรรม บัญชี หรือสิ่งอื่นๆ ใหม่ ให้ใช้เมนูใต้ไอคอนนี้',
|
||||
'index_cash_account' => 'นี่คือบัญชีที่สร้างขึ้นจนถึงตอนนี้ คุณสามารถใช้บัญชีเงินสดเพื่อติดตามค่าใช้จ่ายเงินสดได้ แต่ไม่ได้บังคับแน่นอน',
|
||||
|
||||
// transactions
|
||||
'transactions_create_basic_info' => 'Enter the basic information of your transaction. Source, destination, date and description.',
|
||||
'transactions_create_amount_info' => 'Enter the amount of the transaction. If necessary the fields will auto-update for foreign amount info.',
|
||||
'transactions_create_optional_info' => 'All of these fields are optional. Adding meta-data here will make your transactions better organised.',
|
||||
'transactions_create_split' => 'If you want to split a transaction, add more splits with this button',
|
||||
'transactions_create_basic_info' => 'ป้อนข้อมูลพื้นฐานของการทำธุรกรรมของคุณ อย่างเช่น แหล่งที่มา ปลายทาง วันที่ และคำอธิบาย',
|
||||
'transactions_create_amount_info' => 'ป้อนจำนวนเงินของธุรกรรม หากจำเป็น ฟิลด์จะอัปเดตอัตโนมัติสำหรับข้อมูลจำนวนเงินต่างประเทศ',
|
||||
'transactions_create_optional_info' => 'ฟิลด์ทั้งหมดนี้ไม่จำเป็นต้องกรอก การเพิ่มข้อมูลเมตาที่นี่จะทำให้ธุรกรรมของคุณจัดระเบียบได้ดีขึ้น',
|
||||
'transactions_create_split' => 'หากคุณต้องการแยกธุรกรรม ให้เพิ่มด้วยปุ่มนี้',
|
||||
|
||||
// create account:
|
||||
'accounts_create_iban' => 'Give your accounts a valid IBAN. This could make a data import very easy in the future.',
|
||||
'accounts_create_asset_opening_balance' => 'Assets accounts may have an "opening balance", indicating the start of this account\'s history in Firefly III.',
|
||||
'accounts_create_asset_currency' => 'Firefly III supports multiple currencies. Asset accounts have one main currency, which you must set here.',
|
||||
'accounts_create_asset_virtual' => 'It can sometimes help to give your account a virtual balance: an extra amount always added to or removed from the actual balance.',
|
||||
'accounts_create_iban' => 'ให้ IBAN ที่ถูกต้องแก่บัญชีของคุณ ซึ่งอาจทำให้การนำเข้าข้อมูลในอนาคตเป็นเรื่องง่ายมาก',
|
||||
'accounts_create_asset_opening_balance' => 'บัญชีสินทรัพย์อาจมี "ยอดดุลยกมา" ซึ่งบ่งบอกถึงจุดเริ่มต้นของประวัติของบัญชีนี้ใน Firefly III',
|
||||
'accounts_create_asset_currency' => 'Firefly III รองรับหลายสกุลเงิน บัญชีสินทรัพย์จำเป็นต้องมีสกุลเงินหลักหนึ่งสกุล ซึ่งคุณต้องตั้งค่าที่นี่',
|
||||
'accounts_create_asset_virtual' => 'บางครั้งการมอบยอดเสมือนให้กับบัญชีของคุณอาจช่วยได้: จำนวนเงินพิเศษจะถูกบวกเข้าหรือลบออกจากยอดจริงเสมอ',
|
||||
|
||||
// budgets index
|
||||
'budgets_index_intro' => 'Budgets are used to manage your finances and form one of the core functions of Firefly III.',
|
||||
'budgets_index_see_expenses_bar' => 'Spending money will slowly fill this bar.',
|
||||
'budgets_index_navigate_periods' => 'Navigate through periods to easily set budgets ahead of time.',
|
||||
'budgets_index_new_budget' => 'Create new budgets as you see fit.',
|
||||
'budgets_index_list_of_budgets' => 'Use this table to set the amounts for each budget and see how you are doing.',
|
||||
'budgets_index_intro' => 'งบประมาณใช้เพื่อจัดการการเงินของคุณคือหนึ่งในหน้าที่หลักของ Firefly III',
|
||||
'budgets_index_see_expenses_bar' => 'การใช้จ่ายเงินจะค่อยๆทำให้แถบนี้เต็ม',
|
||||
'budgets_index_navigate_periods' => 'เลื่อนไปตามช่วงเวลาเพื่อตั้งงบประมาณล่วงหน้าได้อย่างง่ายดาย',
|
||||
'budgets_index_new_budget' => 'สร้างงบประมาณใหม่ตามที่คุณต้องการ',
|
||||
'budgets_index_list_of_budgets' => 'ใช้ตารางนี้เพื่อกำหนดจำนวนเงินสำหรับงบประมาณแต่ละอย่างและดูว่าคุณใช้จ่ายไปแล้วเท่าไร',
|
||||
'budgets_index_outro' => 'To learn more about budgeting, checkout the help icon in the top right corner.',
|
||||
|
||||
/*
|
||||
|
109
resources/views/v2/accounts/index.blade.php
Normal file
109
resources/views/v2/accounts/index.blade.php
Normal file
@ -0,0 +1,109 @@
|
||||
@extends('layout.v2')
|
||||
@section('vite')
|
||||
@vite(['resources/assets/v2/sass/app.scss', 'resources/assets/v2/pages/accounts/index.js'])
|
||||
@endsection
|
||||
@section('content')
|
||||
<div class="app-content">
|
||||
<div class="container-fluid" x-data="index">
|
||||
<x-messages></x-messages>
|
||||
<div class="row mb-3">
|
||||
<div class="col-xl-4 col-lg-6 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Info</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
some chart
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-4 col-lg-6 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Info</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
Same
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-4 col-lg-6 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Info</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
Same
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
Nav
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-xl-12 col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3 class="card-title">Accounts (ungrouped)</h3>
|
||||
</div>
|
||||
<div class="col text-end">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Active?</td>
|
||||
<td>Name</td>
|
||||
<td>Type</td>
|
||||
<td>Account number</td>
|
||||
<td>Current balance</td>
|
||||
<td>Last activity</td>
|
||||
<td>Balance difference</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template x-for="(account, index) in accounts" :key="index">
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
<td>
|
||||
<a :href="'./accounts/show/' + account.id">
|
||||
<span x-text="account.name"></span>
|
||||
</a>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
Nav
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
@ -1,3 +1,9 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ route(Route::current()->getName(), Route::current()->parameters()) }}?force_default_layout=true">
|
||||
<i class="fa-solid fa-landmark"></i>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link" data-bs-toggle="dropdown" href="#">
|
||||
<i class="fa-solid fa-gears"></i>
|
||||
|
@ -104,6 +104,7 @@ Route::group(
|
||||
'as' => 'api.v2.accounts.',
|
||||
],
|
||||
static function (): void {
|
||||
Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
|
||||
Route::get('{account}', ['uses' => 'ShowController@show', 'as' => 'show']);
|
||||
}
|
||||
);
|
||||
@ -173,7 +174,6 @@ Route::group(
|
||||
);
|
||||
|
||||
// V2 API route for budgets and budget limits:
|
||||
// TODO Validate from here down.
|
||||
Route::group(
|
||||
[
|
||||
'namespace' => 'FireflyIII\Api\V2\Controllers\Model',
|
||||
|
@ -1204,11 +1204,11 @@ Route::group(
|
||||
// show groups:
|
||||
// TODO improve these routes
|
||||
Route::get('{what}/all', ['uses' => 'Transaction\IndexController@indexAll', 'as' => 'index.all'])->where(
|
||||
['what' => 'withdrawal|deposit|transfers|transfer']
|
||||
['what' => 'withdrawal|deposit|transfers|transfer|all']
|
||||
);
|
||||
|
||||
Route::get('{what}/{start_date?}/{end_date?}', ['uses' => 'Transaction\IndexController@index', 'as' => 'index'])->where(
|
||||
['what' => 'withdrawal|deposit|transfers|transfer']
|
||||
['what' => 'withdrawal|deposit|transfers|transfer|all']
|
||||
)->where(['start_date' => DATEFORMAT])
|
||||
->where(['end_date' => DATEFORMAT])
|
||||
;
|
||||
|
@ -44,11 +44,15 @@ export default defineConfig({
|
||||
'resources/assets/v2/sass/app.scss',
|
||||
'resources/assets/v2/pages/dashboard/dashboard.js',
|
||||
|
||||
// accounts
|
||||
'resources/assets/v2/pages/accounts/index.js',
|
||||
|
||||
// transactions
|
||||
'resources/assets/v2/pages/transactions/create.js',
|
||||
'resources/assets/v2/pages/transactions/edit.js',
|
||||
'resources/assets/v2/pages/transactions/show.js',
|
||||
'resources/assets/v2/pages/transactions/index.js',
|
||||
|
||||
],
|
||||
refresh: true,
|
||||
}),
|
||||
|
Loading…
Reference in New Issue
Block a user