mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-12-25 04:31:24 +00:00
Compare commits
26 Commits
develop-20
...
develop-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cefbaafa19 | ||
|
|
a8c88800c4 | ||
|
|
9d1a127200 | ||
|
|
3fdde2d1c8 | ||
|
|
cdc0b8dd2c | ||
|
|
1a1e06e6e8 | ||
|
|
6d39b8468c | ||
|
|
bdee3947b2 | ||
|
|
2317037655 | ||
|
|
dcea6b757b | ||
|
|
bd7fe92818 | ||
|
|
850e47d8db | ||
|
|
96fe62400f | ||
|
|
5d07fcdcb6 | ||
|
|
fd5d2d57a8 | ||
|
|
8e7d42201f | ||
|
|
f26bd3cb31 | ||
|
|
36915cdace | ||
|
|
8a5cecd2a0 | ||
|
|
78da1b22bb | ||
|
|
6d970a9794 | ||
|
|
8bb7739f05 | ||
|
|
7788bb4b33 | ||
|
|
2ecb4bb3b7 | ||
|
|
4a783d3c3c | ||
|
|
a7d6f26051 |
22
.github/workflows/release.yml
vendored
22
.github/workflows/release.yml
vendored
@@ -122,10 +122,14 @@ jobs:
|
||||
- name: Run CI
|
||||
run: |
|
||||
rm -rf vendor composer.lock
|
||||
composer validate --strict
|
||||
composer update --no-dev --no-scripts --no-plugins -q
|
||||
sudo chown -R runner:docker resources/lang
|
||||
.ci/phpcs.sh
|
||||
- name: Import GPG key
|
||||
uses: crazy-max/ghaction-import-gpg@v6
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
passphrase: ${{ secrets.PASSPHRASE }}
|
||||
- name: Release
|
||||
run: |
|
||||
# do some configuration
|
||||
@@ -141,7 +145,6 @@ jobs:
|
||||
tarName=FireflyIII-$version.tar.gz
|
||||
|
||||
# update composer (again)
|
||||
composer validate --strict
|
||||
composer update --no-dev --no-scripts --no-plugins
|
||||
composer dump-autoload
|
||||
|
||||
@@ -191,6 +194,10 @@ jobs:
|
||||
sha256sum -b $zipName > $zipName.sha256
|
||||
sha256sum -b $tarName > $tarName.sha256
|
||||
|
||||
# add signatures:
|
||||
gpg --armor --detach-sign $zipName
|
||||
gpg --armor --detach-sign $tarName
|
||||
|
||||
# create a development (nightly) release:
|
||||
if [[ "develop" == "$version" ]]; then
|
||||
echo 'Develop release.'
|
||||
@@ -198,7 +205,7 @@ jobs:
|
||||
rm output.txt
|
||||
echo "Bi-weekly development release of Firefly III with the latest fixes, translations and features. Docker users can find this release under the \`develop\` tag." >> output.txt
|
||||
echo "" >> output.txt
|
||||
echo "This release was created on **$(date +'%Y-%m-%d')** and may contain unexpected bugs. Data loss is rare but is not impossible." >> output.txt
|
||||
echo "This release was created on **$(date +'%Y-%m-%d')** and may contain unexpected bugs. Data loss is rare but is not impossible. The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/)." >> output.txt
|
||||
echo "" >> output.txt
|
||||
echo "* Please read the installation instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/installation/docker/), [Portainer](https://docs.firefly-iii.org/how-to/firefly-iii/installation/portainer/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/installation/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/installation/self-managed/)" >> output.txt
|
||||
echo "* Or read the upgrade instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/docker/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/self-managed/)" >> output.txt
|
||||
@@ -221,6 +228,10 @@ jobs:
|
||||
gh release upload $releaseName $zipName.sha256
|
||||
gh release upload $releaseName $tarName.sha256
|
||||
|
||||
# add signatures to release
|
||||
gh release upload $releaseName $zipName.asc
|
||||
gh release upload $releaseName $tarName.asc
|
||||
|
||||
# get current HEAD and add as file to the release
|
||||
HEAD=$(git rev-parse HEAD)
|
||||
echo $HEAD > HEAD.txt
|
||||
@@ -234,6 +245,7 @@ jobs:
|
||||
echo '' >> output.txt
|
||||
echo "* Installation instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/installation/docker/), [Portainer](https://docs.firefly-iii.org/how-to/firefly-iii/installation/portainer/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/installation/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/installation/self-managed/)" >> output.txt
|
||||
echo "* Or read the upgrade instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/docker/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/self-managed/)" >> output.txt
|
||||
echo "* The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/)."
|
||||
|
||||
echo "Create default release."
|
||||
git tag -a $releaseName -m "Here be changelog"
|
||||
@@ -248,6 +260,10 @@ jobs:
|
||||
gh release upload $releaseName $zipName.sha256
|
||||
gh release upload $releaseName $tarName.sha256
|
||||
|
||||
# add signatures to release
|
||||
gh release upload $releaseName $zipName.asc
|
||||
gh release upload $releaseName $tarName.asc
|
||||
|
||||
# get current HEAD and add as file to the release
|
||||
HEAD=$(git rev-parse HEAD)
|
||||
echo $HEAD > HEAD.txt
|
||||
|
||||
@@ -73,7 +73,8 @@ class UpdateController extends Controller
|
||||
$data = $request->getAll();
|
||||
|
||||
// Fixes 8750.
|
||||
foreach ($data['transactions'] as $index => $info) {
|
||||
$transactions = $data['transactions'] ?? [];
|
||||
foreach ($transactions as $index => $info) {
|
||||
unset($data['transactions'][$index]['type']);
|
||||
}
|
||||
|
||||
|
||||
@@ -84,6 +84,10 @@ class PreferencesController extends Controller
|
||||
{
|
||||
$manager = $this->getManager();
|
||||
|
||||
if ('currencyPreference' === $preference->name) {
|
||||
throw new FireflyException('Please use api/v1/currencies/default instead.');
|
||||
}
|
||||
|
||||
/** @var PreferenceTransformer $transformer */
|
||||
$transformer = app(PreferenceTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
@@ -103,6 +107,11 @@ class PreferencesController extends Controller
|
||||
{
|
||||
$manager = $this->getManager();
|
||||
$data = $request->getAll();
|
||||
|
||||
if ('currencyPreference' === $data['name']) {
|
||||
throw new FireflyException('Please use api/v1/currencies/default instead.');
|
||||
}
|
||||
|
||||
$pref = app('preferences')->set($data['name'], $data['data']);
|
||||
|
||||
/** @var PreferenceTransformer $transformer */
|
||||
@@ -122,6 +131,10 @@ class PreferencesController extends Controller
|
||||
*/
|
||||
public function update(PreferenceUpdateRequest $request, Preference $preference): JsonResponse
|
||||
{
|
||||
if ('currencyPreference' === $preference->name) {
|
||||
throw new FireflyException('Please use api/v1/currencies/default instead.');
|
||||
}
|
||||
|
||||
$manager = $this->getManager();
|
||||
$data = $request->getAll();
|
||||
$pref = app('preferences')->set($preference->name, $data['data']);
|
||||
|
||||
@@ -71,8 +71,9 @@ class StoreRequest extends FormRequest
|
||||
if (is_array($triggers)) {
|
||||
foreach ($triggers as $trigger) {
|
||||
$return[] = [
|
||||
'type' => $trigger['type'],
|
||||
'value' => $trigger['value'],
|
||||
'type' => $trigger['type'] ?? '',
|
||||
'value' => $trigger['value'] ?? null,
|
||||
'prohibited' => $this->convertBoolean((string)($trigger['prohibited'] ?? 'false')),
|
||||
'active' => $this->convertBoolean((string)($trigger['active'] ?? 'true')),
|
||||
'stop_processing' => $this->convertBoolean((string)($trigger['stop_processing'] ?? 'false')),
|
||||
];
|
||||
|
||||
@@ -81,10 +81,12 @@ class UpdateRequest extends FormRequest
|
||||
if (is_array($triggers)) {
|
||||
foreach ($triggers as $trigger) {
|
||||
$active = array_key_exists('active', $trigger) ? $trigger['active'] : true;
|
||||
$prohibited = array_key_exists('prohibited', $trigger) ? $trigger['prohibited'] : false;
|
||||
$stopProcessing = array_key_exists('stop_processing', $trigger) ? $trigger['stop_processing'] : false;
|
||||
$return[] = [
|
||||
'type' => $trigger['type'],
|
||||
'value' => $trigger['value'],
|
||||
'prohibited' => $prohibited,
|
||||
'active' => $active,
|
||||
'stop_processing' => $stopProcessing,
|
||||
];
|
||||
|
||||
@@ -122,6 +122,9 @@ class Controller extends BaseController
|
||||
$obj = null;
|
||||
}
|
||||
}
|
||||
if (null !== $date && 'end' === $field) {
|
||||
$obj->endOfDay();
|
||||
}
|
||||
$bag->set($field, $obj);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ 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\Enums\UserRoleEnum;
|
||||
use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Transformers\V2\AccountTransformer;
|
||||
@@ -58,7 +57,7 @@ class IndexController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO see autocomplete/accountcontroller for list.
|
||||
* TODO see autocomplete/account controller for list.
|
||||
*/
|
||||
public function index(IndexRequest $request): JsonResponse
|
||||
{
|
||||
@@ -80,25 +79,4 @@ class IndexController extends Controller
|
||||
->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)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V2\Request\Chart;
|
||||
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
@@ -39,6 +40,7 @@ class BalanceChartRequest extends FormRequest
|
||||
use ChecksLogin;
|
||||
use ConvertsDataTypes;
|
||||
use ValidatesUserGroupTrait;
|
||||
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
|
||||
|
||||
/**
|
||||
* Get all data from the request.
|
||||
|
||||
@@ -44,14 +44,11 @@ class IndexRequest extends FormRequest
|
||||
|
||||
public function getAccountTypes(): array
|
||||
{
|
||||
$type = (string)$this->get('type', 'default');
|
||||
$type = (string) $this->get('type', 'default');
|
||||
|
||||
return $this->mapAccountTypes($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all data from the request.
|
||||
*/
|
||||
public function getDate(): Carbon
|
||||
{
|
||||
return $this->getCarbonDate('date');
|
||||
@@ -63,7 +60,9 @@ class IndexRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'date' => 'date|after:1900-01-01|before:2099-12-31',
|
||||
'date' => 'date|after:1900-01-01|before:2099-12-31',
|
||||
'start' => 'date|after:1900-01-01|before:2099-12-31|before:end|required_with:end',
|
||||
'end' => 'date|after:1900-01-01|before:2099-12-31|after:start|required_with:start',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace FireflyIII\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Vite;
|
||||
use Barryvdh\Debugbar\Facades\Debugbar;
|
||||
|
||||
/**
|
||||
* Class SecureHeaders
|
||||
@@ -43,6 +44,9 @@ class SecureHeaders
|
||||
// generate and share nonce.
|
||||
$nonce = base64_encode(random_bytes(16));
|
||||
Vite::useCspNonce($nonce);
|
||||
if (class_exists('Barryvdh\Debugbar\Facades\Debugbar')) {
|
||||
Debugbar::getJavascriptRenderer()->setCspNonce($nonce);
|
||||
}
|
||||
app('view')->share('JS_NONCE', $nonce);
|
||||
|
||||
$response = $next($request);
|
||||
@@ -55,14 +59,29 @@ class SecureHeaders
|
||||
"base-uri 'self'",
|
||||
"font-src 'self' data:",
|
||||
sprintf("connect-src 'self' %s", $trackingScriptSrc),
|
||||
sprintf("img-src 'self' 'nonce-%1s'", $nonce),
|
||||
sprintf("img-src 'self' data: 'nonce-%1s' ", $nonce),
|
||||
"manifest-src 'self'",
|
||||
];
|
||||
|
||||
// overrule in development mode
|
||||
if (true === env('IS_LOCAL_DEV')) {
|
||||
$csp = [
|
||||
"default-src 'none'",
|
||||
"object-src 'none'",
|
||||
sprintf("script-src 'unsafe-eval' 'strict-dynamic' 'nonce-%1s' https://firefly.sd.internal/_debugbar/assets", $nonce),
|
||||
"style-src 'unsafe-inline' 'self' https://10.0.0.15:5173/",
|
||||
"base-uri 'self'",
|
||||
"font-src 'self' data: https://10.0.0.15:5173/",
|
||||
sprintf("connect-src 'self' %s https://10.0.0.15:5173/ wss://10.0.0.15:5173/", $trackingScriptSrc),
|
||||
sprintf("img-src 'self' data: 'nonce-%1s'", $nonce),
|
||||
"manifest-src 'self'",
|
||||
];
|
||||
}
|
||||
|
||||
$route = $request->route();
|
||||
$customUrl = '';
|
||||
$authGuard = (string)config('firefly.authentication_guard');
|
||||
$logoutUrl = (string)config('firefly.custom_logout_url');
|
||||
$authGuard = (string) config('firefly.authentication_guard');
|
||||
$logoutUrl = (string) config('firefly.custom_logout_url');
|
||||
if ('remote_user_guard' === $authGuard && '' !== $logoutUrl) {
|
||||
$customUrl = $logoutUrl;
|
||||
}
|
||||
@@ -110,8 +129,8 @@ class SecureHeaders
|
||||
*/
|
||||
private function getTrackingScriptSource(): string
|
||||
{
|
||||
if ('' !== (string)config('firefly.tracker_site_id') && '' !== (string)config('firefly.tracker_url')) {
|
||||
return (string)config('firefly.tracker_url');
|
||||
if ('' !== (string) config('firefly.tracker_site_id') && '' !== (string) config('firefly.tracker_url')) {
|
||||
return (string) config('firefly.tracker_url');
|
||||
}
|
||||
|
||||
return '';
|
||||
|
||||
@@ -295,4 +295,16 @@ class UserGroupRepository implements UserGroupRepositoryInterface
|
||||
$this->user->user_group_id = $userGroup->id;
|
||||
$this->user->save();
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function getMembershipsFromGroupId(int $groupId): Collection
|
||||
{
|
||||
return $this->user->groupMemberships()->where('user_group_id', $groupId)->get();
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function getById(int $id): ?UserGroup
|
||||
{
|
||||
return UserGroup::find($id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,8 +36,12 @@ interface UserGroupRepositoryInterface
|
||||
{
|
||||
public function destroy(UserGroup $userGroup): void;
|
||||
|
||||
public function getMembershipsFromGroupId(int $groupId): Collection;
|
||||
|
||||
public function get(): Collection;
|
||||
|
||||
public function getById(int $id): ?UserGroup;
|
||||
|
||||
public function getAll(): Collection;
|
||||
|
||||
public function setUser(null|Authenticatable|User $user): void;
|
||||
|
||||
@@ -27,11 +27,13 @@ namespace FireflyIII\Repositories\UserGroups\Account;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountMeta;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Services\Internal\Update\AccountUpdateService;
|
||||
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* Class AccountRepository
|
||||
@@ -121,7 +123,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
if (!in_array($type, $list, true)) {
|
||||
return null;
|
||||
}
|
||||
$currencyId = (int)$this->getMetaValue($account, 'currency_id');
|
||||
$currencyId = (int) $this->getMetaValue($account, 'currency_id');
|
||||
if ($currencyId > 0) {
|
||||
return TransactionCurrency::find($currencyId);
|
||||
}
|
||||
@@ -143,7 +145,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
return null;
|
||||
}
|
||||
if (1 === $result->count()) {
|
||||
return (string)$result->first()->data;
|
||||
return (string) $result->first()->data;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -228,7 +230,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
|
||||
continue;
|
||||
}
|
||||
if ($index !== (int)$account->order) {
|
||||
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();
|
||||
@@ -305,4 +307,34 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
|
||||
return $service->update($account, $data);
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function getMetaValues(Collection $accounts, array $fields): Collection
|
||||
{
|
||||
$query = AccountMeta::whereIn('account_id', $accounts->pluck('id')->toArray());
|
||||
if (count($fields) > 0) {
|
||||
$query->whereIn('name', $fields);
|
||||
}
|
||||
|
||||
return $query->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data']);
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function getAccountTypes(Collection $accounts): Collection
|
||||
{
|
||||
return AccountType::leftJoin('accounts', 'accounts.account_type_id', '=', 'account_types.id')
|
||||
->whereIn('accounts.id', $accounts->pluck('id')->toArray())
|
||||
->get(['accounts.id', 'account_types.type'])
|
||||
;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function getLastActivity(Collection $accounts): array
|
||||
{
|
||||
return Transaction::whereIn('account_id', $accounts->pluck('id')->toArray())
|
||||
->leftJoin('transaction_journals', 'transaction_journals.id', 'transactions.transaction_journal_id')
|
||||
->groupBy('transactions.account_id')
|
||||
->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) as date_max')])->toArray() // @phpstan-ignore-line
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,12 @@ interface AccountRepositoryInterface
|
||||
{
|
||||
public function countAccounts(array $types): int;
|
||||
|
||||
public function getAccountTypes(Collection $accounts): Collection;
|
||||
|
||||
public function getLastActivity(Collection $accounts): array;
|
||||
|
||||
public function getMetaValues(Collection $accounts, array $fields): Collection;
|
||||
|
||||
public function find(int $accountId): ?Account;
|
||||
|
||||
public function findByAccountNumber(string $number, array $types): ?Account;
|
||||
|
||||
@@ -24,12 +24,13 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Support\Http\Api;
|
||||
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Models\GroupMembership;
|
||||
use FireflyIII\Models\UserGroup;
|
||||
use FireflyIII\Repositories\UserGroup\UserGroupRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Trait ValidatesUserGroupTrait
|
||||
@@ -42,63 +43,64 @@ trait ValidatesUserGroupTrait
|
||||
*/
|
||||
protected function validateUserGroup(Request $request): UserGroup
|
||||
{
|
||||
app('log')->debug(sprintf('validateUserGroup: %s', static::class));
|
||||
Log::debug(sprintf('validateUserGroup: %s', static::class));
|
||||
if (!auth()->check()) {
|
||||
app('log')->debug('validateUserGroup: user is not logged in, return NULL.');
|
||||
Log::debug('validateUserGroup: user is not logged in, return NULL.');
|
||||
|
||||
throw new AuthenticationException();
|
||||
}
|
||||
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$groupId = 0;
|
||||
$user = auth()->user();
|
||||
$groupId = 0;
|
||||
if (!$request->has('user_group_id')) {
|
||||
$groupId = $user->user_group_id;
|
||||
app('log')->debug(sprintf('validateUserGroup: no user group submitted, use default group #%d.', $groupId));
|
||||
Log::debug(sprintf('validateUserGroup: no user group submitted, use default group #%d.', $groupId));
|
||||
}
|
||||
if ($request->has('user_group_id')) {
|
||||
$groupId = (int)$request->get('user_group_id');
|
||||
app('log')->debug(sprintf('validateUserGroup: user group submitted, search for memberships in group #%d.', $groupId));
|
||||
$groupId = (int) $request->get('user_group_id');
|
||||
Log::debug(sprintf('validateUserGroup: user group submitted, search for memberships in group #%d.', $groupId));
|
||||
}
|
||||
|
||||
/** @var null|GroupMembership $membership */
|
||||
$membership = $user->groupMemberships()->where('user_group_id', $groupId)->first();
|
||||
/** @var UserGroupRepositoryInterface $repository */
|
||||
$repository = app(UserGroupRepositoryInterface::class);
|
||||
$repository->setUser($user);
|
||||
$memberships = $repository->getMembershipsFromGroupId($groupId);
|
||||
|
||||
if (null === $membership) {
|
||||
app('log')->debug(sprintf('validateUserGroup: user has no access to group #%d.', $groupId));
|
||||
if (0 === $memberships->count()) {
|
||||
Log::debug(sprintf('validateUserGroup: user has no access to group #%d.', $groupId));
|
||||
|
||||
throw new AuthorizationException((string)trans('validation.no_access_group'));
|
||||
throw new AuthorizationException((string) trans('validation.no_access_group'));
|
||||
}
|
||||
|
||||
// need to get the group from the membership:
|
||||
/** @var null|UserGroup $group */
|
||||
$group = $membership->userGroup;
|
||||
$group = $repository->getById($groupId);
|
||||
if (null === $group) {
|
||||
app('log')->debug(sprintf('validateUserGroup: group #%d does not exist.', $groupId));
|
||||
Log::debug(sprintf('validateUserGroup: group #%d does not exist.', $groupId));
|
||||
|
||||
throw new AuthorizationException((string)trans('validation.belongs_user_or_user_group'));
|
||||
throw new AuthorizationException((string) trans('validation.belongs_user_or_user_group'));
|
||||
}
|
||||
app('log')->debug(sprintf('validateUserGroup: validate access of user to group #%d ("%s").', $groupId, $group->title));
|
||||
$roles = property_exists($this, 'acceptedRoles') ? $this->acceptedRoles : [];
|
||||
Log::debug(sprintf('validateUserGroup: validate access of user to group #%d ("%s").', $groupId, $group->title));
|
||||
$roles = property_exists($this, 'acceptedRoles') ? $this->acceptedRoles : [];
|
||||
if (0 === count($roles)) {
|
||||
app('log')->debug('validateUserGroup: no roles defined, so no access.');
|
||||
Log::debug('validateUserGroup: no roles defined, so no access.');
|
||||
|
||||
throw new AuthorizationException((string)trans('validation.no_accepted_roles_defined'));
|
||||
throw new AuthorizationException((string) trans('validation.no_accepted_roles_defined'));
|
||||
}
|
||||
app('log')->debug(sprintf('validateUserGroup: have %d roles to check.', count($roles)), $roles);
|
||||
Log::debug(sprintf('validateUserGroup: have %d roles to check.', count($roles)), $roles);
|
||||
|
||||
/** @var UserRoleEnum $role */
|
||||
foreach ($roles as $role) {
|
||||
if ($user->hasRoleInGroupOrOwner($group, $role)) {
|
||||
app('log')->debug(sprintf('validateUserGroup: User has role "%s" in group #%d, return the group.', $role->value, $groupId));
|
||||
Log::debug(sprintf('validateUserGroup: User has role "%s" in group #%d, return the group.', $role->value, $groupId));
|
||||
|
||||
return $group;
|
||||
}
|
||||
app('log')->debug(sprintf('validateUserGroup: User does NOT have role "%s" in group #%d, continue searching.', $role->value, $groupId));
|
||||
Log::debug(sprintf('validateUserGroup: User does NOT have role "%s" in group #%d, continue searching.', $role->value, $groupId));
|
||||
}
|
||||
|
||||
app('log')->debug('validateUserGroup: User does NOT have enough rights to access endpoint.');
|
||||
Log::debug('validateUserGroup: User does NOT have enough rights to access endpoint.');
|
||||
|
||||
throw new AuthorizationException((string)trans('validation.belongs_user_or_user_group'));
|
||||
throw new AuthorizationException((string) trans('validation.belongs_user_or_user_group'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,14 +464,15 @@ class Navigation
|
||||
$increment = 'addDay';
|
||||
$format = $this->preferredCarbonFormat($start, $end);
|
||||
$displayFormat = (string)trans('config.month_and_day_js', [], $locale);
|
||||
$diff = $start->diffInMonths($end, true);
|
||||
// increment by month (for year)
|
||||
if ($start->diffInMonths($end, true) > 1) {
|
||||
if ($diff >= 1.0001) {
|
||||
$increment = 'addMonth';
|
||||
$displayFormat = (string)trans('config.month_js');
|
||||
}
|
||||
|
||||
// increment by year (for multi-year)
|
||||
if ($start->diffInMonths($end, true) > 12) {
|
||||
if ($diff >= 12.0001) {
|
||||
$increment = 'addYear';
|
||||
$displayFormat = (string)trans('config.year_js');
|
||||
}
|
||||
@@ -494,11 +495,15 @@ class Navigation
|
||||
public function preferredCarbonFormat(Carbon $start, Carbon $end): string
|
||||
{
|
||||
$format = 'Y-m-d';
|
||||
if ((int)$start->diffInMonths($end, true) > 1) {
|
||||
$diff = $start->diffInMonths($end, true);
|
||||
Log::debug(sprintf('preferredCarbonFormat(%s, %s) = %f', $start->format('Y-m-d'), $end->format('Y-m-d'), $diff));
|
||||
if ($diff >= 1.001) {
|
||||
Log::debug(sprintf('Return Y-m because %s', $diff));
|
||||
$format = 'Y-m';
|
||||
}
|
||||
|
||||
if ((int)$start->diffInMonths($end, true) > 12) {
|
||||
if ($diff >= 12.001) {
|
||||
Log::debug(sprintf('Return Y because %s', $diff));
|
||||
$format = 'Y';
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ class Preferences
|
||||
}
|
||||
|
||||
return Preference::where('user_id', $user->id)
|
||||
->where('name', '!=', 'currencyPreference')
|
||||
->where(function (Builder $q) use ($user): void {
|
||||
$q->whereNull('user_group_id');
|
||||
$q->orWhere('user_group_id', $user->user_group_id);
|
||||
|
||||
@@ -159,6 +159,10 @@ class OperatorQuerySearch implements SearchInterface
|
||||
foreach ($query1->getNodes() as $searchNode) {
|
||||
$this->handleSearchNode($searchNode);
|
||||
}
|
||||
|
||||
// add missing information
|
||||
$this->collector->withBillInformation();
|
||||
|
||||
$this->collector->setSearchWords($this->words);
|
||||
$this->collector->excludeSearchWords($this->prohibitedWords);
|
||||
}
|
||||
|
||||
@@ -34,8 +34,7 @@ use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
||||
*/
|
||||
class RuleTransformer extends AbstractTransformer
|
||||
{
|
||||
/** @var RuleRepositoryInterface */
|
||||
private $ruleRepository;
|
||||
private RuleRepositoryInterface $ruleRepository;
|
||||
|
||||
/**
|
||||
* CurrencyTransformer constructor.
|
||||
@@ -109,8 +108,16 @@ class RuleTransformer extends AbstractTransformer
|
||||
if ('user_action' === $ruleTrigger->trigger_type) {
|
||||
continue;
|
||||
}
|
||||
$triggerType = (string) $ruleTrigger->trigger_type;
|
||||
$triggerValue = (string)$ruleTrigger->trigger_value;
|
||||
$needsContext = config(sprintf('search.operators.%s.needs_context', $ruleTrigger->trigger_type), true);
|
||||
$prohibited = false;
|
||||
|
||||
if (str_starts_with($triggerType, '-')) {
|
||||
$prohibited = true;
|
||||
$triggerType = substr($triggerType, 1);
|
||||
}
|
||||
|
||||
$needsContext = config(sprintf('search.operators.%s.needs_context', $triggerType), true);
|
||||
if (false === $needsContext) {
|
||||
$triggerValue = 'true';
|
||||
}
|
||||
@@ -119,8 +126,9 @@ class RuleTransformer extends AbstractTransformer
|
||||
'id' => (string)$ruleTrigger->id,
|
||||
'created_at' => $ruleTrigger->created_at->toAtomString(),
|
||||
'updated_at' => $ruleTrigger->updated_at->toAtomString(),
|
||||
'type' => $ruleTrigger->trigger_type,
|
||||
'type' => $triggerType,
|
||||
'value' => $triggerValue,
|
||||
'prohibited' => $prohibited,
|
||||
'order' => $ruleTrigger->order,
|
||||
'active' => $ruleTrigger->active,
|
||||
'stop_processing' => $ruleTrigger->stop_processing,
|
||||
|
||||
@@ -27,13 +27,12 @@ namespace FireflyIII\Transformers\V2;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountMeta;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class AccountTransformer
|
||||
@@ -42,118 +41,45 @@ class AccountTransformer extends AbstractTransformer
|
||||
{
|
||||
private array $accountMeta;
|
||||
private array $accountTypes;
|
||||
private array $balances;
|
||||
private array $balanceDifferences;
|
||||
private array $convertedBalances;
|
||||
private array $currencies;
|
||||
private TransactionCurrency $default;
|
||||
private array $lastActivity;
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
* This method collects meta-data for one or all accounts in the transformer's collection.
|
||||
*/
|
||||
public function collectMetaData(Collection $objects): Collection
|
||||
{
|
||||
// TODO separate methods
|
||||
$this->currencies = [];
|
||||
$this->accountMeta = [];
|
||||
$this->accountTypes = [];
|
||||
$this->lastActivity = [];
|
||||
$this->balances = app('steam')->balancesByAccounts($objects, $this->getDate());
|
||||
$this->convertedBalances = app('steam')->balancesByAccountsConverted($objects, $this->getDate());
|
||||
$this->currencies = [];
|
||||
$this->accountMeta = [];
|
||||
$this->accountTypes = [];
|
||||
$this->lastActivity = [];
|
||||
$this->convertedBalances = [];
|
||||
$this->balanceDifferences = [];
|
||||
|
||||
/** @var CurrencyRepositoryInterface $repository */
|
||||
$repository = app(CurrencyRepositoryInterface::class);
|
||||
$this->default = app('amount')->getDefaultCurrency();
|
||||
// get balances of all accounts
|
||||
$this->getMetaBalances($objects);
|
||||
|
||||
// get currencies:
|
||||
$accountIds = $objects->pluck('id')->toArray();
|
||||
// TODO move query to repository
|
||||
$meta = AccountMeta::whereIn('account_id', $accountIds)
|
||||
->whereIn('name', ['currency_id', 'account_role', 'account_number'])
|
||||
->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data'])
|
||||
;
|
||||
$currencyIds = $meta->where('name', 'currency_id')->pluck('data')->toArray();
|
||||
// get default currency:
|
||||
$this->getDefaultCurrency();
|
||||
|
||||
// collect currency and other meta-data:
|
||||
$this->collectAccountMetaData($objects);
|
||||
|
||||
$currencies = $repository->getByIds($currencyIds);
|
||||
foreach ($currencies as $currency) {
|
||||
$id = $currency->id;
|
||||
$this->currencies[$id] = $currency;
|
||||
}
|
||||
foreach ($meta as $entry) {
|
||||
$id = $entry->account_id;
|
||||
$this->accountMeta[$id][$entry->name] = $entry->data;
|
||||
}
|
||||
// get account types:
|
||||
// select accounts.id, account_types.type from account_types left join accounts on accounts.account_type_id = account_types.id;
|
||||
// TODO move query to repository
|
||||
$accountTypes = AccountType::leftJoin('accounts', 'accounts.account_type_id', '=', 'account_types.id')
|
||||
->whereIn('accounts.id', $accountIds)
|
||||
->get(['accounts.id', 'account_types.type'])
|
||||
;
|
||||
$this->collectAccountTypes($objects);
|
||||
|
||||
/** @var AccountType $row */
|
||||
foreach ($accountTypes as $row) {
|
||||
$this->accountTypes[$row->id] = (string)config(sprintf('firefly.shortNamesByFullName.%s', $row->type));
|
||||
// get last activity:
|
||||
$this->getLastActivity($objects);
|
||||
|
||||
// TODO add balance difference
|
||||
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
|
||||
$this->getBalanceDifference($objects, $this->parameters->get('start'), $this->parameters->get('end'));
|
||||
}
|
||||
|
||||
// get last activity
|
||||
// TODO move query to repository
|
||||
$array = Transaction::whereIn('account_id', $accountIds)
|
||||
->leftJoin('transaction_journals', 'transaction_journals.id', 'transactions.transaction_journal_id')
|
||||
->groupBy('transactions.account_id')
|
||||
->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) as date_max')])->toArray() // @phpstan-ignore-line
|
||||
;
|
||||
foreach ($array as $row) {
|
||||
$this->lastActivity[(int)$row['account_id']] = Carbon::parse($row['date_max'], config('app.timezone'));
|
||||
}
|
||||
|
||||
// TODO needs separate method.
|
||||
/** @var null|array $sort */
|
||||
$sort = $this->parameters->get('sort');
|
||||
if (null !== $sort && count($sort) > 0) {
|
||||
foreach ($sort as $column => $direction) {
|
||||
// account_number + iban
|
||||
if ('iban' === $column) {
|
||||
$meta = $this->accountMeta;
|
||||
$objects = $objects->sort(function (Account $left, Account $right) use ($meta, $direction) {
|
||||
$leftIban = trim(sprintf('%s%s', $left->iban, $meta[$left->id]['account_number'] ?? ''));
|
||||
$rightIban = trim(sprintf('%s%s', $right->iban, $meta[$right->id]['account_number'] ?? ''));
|
||||
if ('asc' === $direction) {
|
||||
return strcasecmp($leftIban, $rightIban);
|
||||
}
|
||||
|
||||
return strcasecmp($rightIban, $leftIban);
|
||||
});
|
||||
}
|
||||
if ('balance' === $column) {
|
||||
$balances = $this->convertedBalances;
|
||||
$objects = $objects->sort(function (Account $left, Account $right) use ($balances, $direction) {
|
||||
$leftBalance = (float)($balances[$left->id]['native_balance'] ?? 0);
|
||||
$rightBalance = (float)($balances[$right->id]['native_balance'] ?? 0);
|
||||
if ('asc' === $direction) {
|
||||
return $leftBalance <=> $rightBalance;
|
||||
}
|
||||
|
||||
return $rightBalance <=> $leftBalance;
|
||||
});
|
||||
}
|
||||
if ('last_activity' === $column) {
|
||||
$dates = $this->lastActivity;
|
||||
$objects = $objects->sort(function (Account $left, Account $right) use ($dates, $direction) {
|
||||
$leftDate = $dates[$left->id] ?? Carbon::create(1900, 1, 1, 0, 0, 0);
|
||||
$rightDate = $dates[$right->id] ?? Carbon::create(1900, 1, 1, 0, 0, 0);
|
||||
if ('asc' === $direction) {
|
||||
return $leftDate->gt($rightDate) ? 1 : -1;
|
||||
}
|
||||
|
||||
return $rightDate->gt($leftDate) ? 1 : -1;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// $objects = $objects->sortByDesc('name');
|
||||
return $objects;
|
||||
return $this->sortAccounts($objects);
|
||||
}
|
||||
|
||||
private function getDate(): Carbon
|
||||
@@ -171,44 +97,56 @@ class AccountTransformer extends AbstractTransformer
|
||||
*/
|
||||
public function transform(Account $account): array
|
||||
{
|
||||
$id = $account->id;
|
||||
$id = $account->id;
|
||||
|
||||
// various meta
|
||||
$accountRole = $this->accountMeta[$id]['account_role'] ?? null;
|
||||
$accountType = $this->accountTypes[$id];
|
||||
$order = $account->order;
|
||||
$accountRole = $this->accountMeta[$id]['account_role'] ?? null;
|
||||
$accountType = $this->accountTypes[$id];
|
||||
$order = $account->order;
|
||||
|
||||
// no currency? use default
|
||||
$currency = $this->default;
|
||||
if (array_key_exists($id, $this->accountMeta) && 0 !== (int)($this->accountMeta[$id]['currency_id'] ?? 0)) {
|
||||
$currency = $this->currencies[(int)$this->accountMeta[$id]['currency_id']];
|
||||
$currency = $this->default;
|
||||
if (array_key_exists($id, $this->accountMeta) && 0 !== (int) ($this->accountMeta[$id]['currency_id'] ?? 0)) {
|
||||
$currency = $this->currencies[(int) $this->accountMeta[$id]['currency_id']];
|
||||
}
|
||||
// amounts and calculation.
|
||||
$balance = $this->balances[$id] ?? null;
|
||||
$nativeBalance = $this->convertedBalances[$id]['native_balance'] ?? null;
|
||||
$balance = $this->balances[$id]['balance'] ?? null;
|
||||
$nativeBalance = $this->convertedBalances[$id]['native_balance'] ?? null;
|
||||
|
||||
// no order for some accounts:
|
||||
if (!in_array(strtolower($accountType), ['liability', 'liabilities', 'asset'], true)) {
|
||||
$order = null;
|
||||
}
|
||||
|
||||
// balance difference
|
||||
$diffStart = null;
|
||||
$diffEnd = null;
|
||||
$balanceDiff = null;
|
||||
$nativeBalanceDiff = null;
|
||||
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
|
||||
$diffStart = $this->parameters->get('start')->toAtomString();
|
||||
$diffEnd = $this->parameters->get('end')->toAtomString();
|
||||
$balanceDiff = $this->balanceDifferences[$id]['balance'] ?? null;
|
||||
$nativeBalanceDiff = $this->balanceDifferences[$id]['native_balance'] ?? null;
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => (string)$account->id,
|
||||
'id' => (string) $account->id,
|
||||
'created_at' => $account->created_at->toAtomString(),
|
||||
'updated_at' => $account->updated_at->toAtomString(),
|
||||
'active' => $account->active,
|
||||
'order' => $order,
|
||||
'name' => $account->name,
|
||||
'iban' => '' === (string)$account->iban ? null : $account->iban,
|
||||
'iban' => '' === (string) $account->iban ? null : $account->iban,
|
||||
'account_number' => $this->accountMeta[$id]['account_number'] ?? null,
|
||||
'type' => strtolower($accountType),
|
||||
'account_role' => $accountRole,
|
||||
'currency_id' => (string)$currency->id,
|
||||
'currency_id' => (string) $currency->id,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
|
||||
'native_currency_id' => (string)$this->default->id,
|
||||
'native_currency_id' => (string) $this->default->id,
|
||||
'native_currency_code' => $this->default->code,
|
||||
'native_currency_symbol' => $this->default->symbol,
|
||||
'native_currency_decimal_places' => $this->default->decimal_places,
|
||||
@@ -218,6 +156,12 @@ class AccountTransformer extends AbstractTransformer
|
||||
'native_current_balance' => $nativeBalance,
|
||||
'current_balance_date' => $this->getDate()->endOfDay()->toAtomString(),
|
||||
|
||||
// balance difference
|
||||
'balance_difference' => $balanceDiff,
|
||||
'native_balance_difference' => $nativeBalanceDiff,
|
||||
'balance_difference_start' => $diffStart,
|
||||
'balance_difference_end' => $diffEnd,
|
||||
|
||||
// more meta
|
||||
'last_activity' => array_key_exists($id, $this->lastActivity) ? $this->lastActivity[$id]->toAtomString() : null,
|
||||
|
||||
@@ -246,4 +190,179 @@ class AccountTransformer extends AbstractTransformer
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function getMetaBalances(Collection $accounts): void
|
||||
{
|
||||
try {
|
||||
$this->convertedBalances = app('steam')->balancesByAccountsConverted($accounts, $this->getDate());
|
||||
} catch (FireflyException $e) {
|
||||
Log::error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function getDefaultCurrency(): void
|
||||
{
|
||||
$this->default = app('amount')->getDefaultCurrency();
|
||||
}
|
||||
|
||||
private function collectAccountMetaData(Collection $accounts): void
|
||||
{
|
||||
/** @var CurrencyRepositoryInterface $repository */
|
||||
$repository = app(CurrencyRepositoryInterface::class);
|
||||
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$metaFields = $accountRepository->getMetaValues($accounts, ['currency_id', 'account_role', 'account_number']);
|
||||
$currencyIds = $metaFields->where('name', 'currency_id')->pluck('data')->toArray();
|
||||
|
||||
$currencies = $repository->getByIds($currencyIds);
|
||||
foreach ($currencies as $currency) {
|
||||
$id = $currency->id;
|
||||
$this->currencies[$id] = $currency;
|
||||
}
|
||||
foreach ($metaFields as $entry) {
|
||||
$id = $entry->account_id;
|
||||
$this->accountMeta[$id][$entry->name] = $entry->data;
|
||||
}
|
||||
}
|
||||
|
||||
private function collectAccountTypes(Collection $accounts): void
|
||||
{
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$accountTypes = $accountRepository->getAccountTypes($accounts);
|
||||
|
||||
/** @var AccountType $row */
|
||||
foreach ($accountTypes as $row) {
|
||||
$this->accountTypes[$row->id] = (string) config(sprintf('firefly.shortNamesByFullName.%s', $row->type));
|
||||
}
|
||||
}
|
||||
|
||||
private function getLastActivity(Collection $accounts): void
|
||||
{
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$lastActivity = $accountRepository->getLastActivity($accounts);
|
||||
foreach ($lastActivity as $row) {
|
||||
$this->lastActivity[(int) $row['account_id']] = Carbon::parse($row['date_max'], config('app.timezone'));
|
||||
}
|
||||
}
|
||||
|
||||
private function sortAccounts(Collection $accounts): Collection
|
||||
{
|
||||
/** @var null|array $sort */
|
||||
$sort = $this->parameters->get('sort');
|
||||
|
||||
if (null === $sort || 0 === count($sort)) {
|
||||
return $accounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var string $column
|
||||
* @var string $direction
|
||||
*/
|
||||
foreach ($sort as $column => $direction) {
|
||||
// account_number + iban
|
||||
if ('iban' === $column) {
|
||||
$accounts = $this->sortByIban($accounts, $direction);
|
||||
}
|
||||
if ('balance' === $column) {
|
||||
$accounts = $this->sortByBalance($accounts, $direction);
|
||||
}
|
||||
if ('last_activity' === $column) {
|
||||
$accounts = $this->sortByLastActivity($accounts, $direction);
|
||||
}
|
||||
if ('balance_difference' === $column) {
|
||||
$accounts = $this->sortByBalanceDifference($accounts, $direction);
|
||||
}
|
||||
}
|
||||
|
||||
return $accounts;
|
||||
}
|
||||
|
||||
private function sortByIban(Collection $accounts, string $direction): Collection
|
||||
{
|
||||
$meta = $this->accountMeta;
|
||||
|
||||
return $accounts->sort(function (Account $left, Account $right) use ($meta, $direction) {
|
||||
$leftIban = trim(sprintf('%s%s', $left->iban, $meta[$left->id]['account_number'] ?? ''));
|
||||
$rightIban = trim(sprintf('%s%s', $right->iban, $meta[$right->id]['account_number'] ?? ''));
|
||||
if ('asc' === $direction) {
|
||||
return strcasecmp($leftIban, $rightIban);
|
||||
}
|
||||
|
||||
return strcasecmp($rightIban, $leftIban);
|
||||
});
|
||||
}
|
||||
|
||||
private function sortByBalance(Collection $accounts, string $direction): Collection
|
||||
{
|
||||
$balances = $this->convertedBalances;
|
||||
|
||||
return $accounts->sort(function (Account $left, Account $right) use ($balances, $direction) {
|
||||
$leftBalance = (float) ($balances[$left->id]['native_balance'] ?? 0);
|
||||
$rightBalance = (float) ($balances[$right->id]['native_balance'] ?? 0);
|
||||
if ('asc' === $direction) {
|
||||
return $leftBalance <=> $rightBalance;
|
||||
}
|
||||
|
||||
return $rightBalance <=> $leftBalance;
|
||||
});
|
||||
}
|
||||
|
||||
private function sortByLastActivity(Collection $accounts, string $direction): Collection
|
||||
{
|
||||
$dates = $this->lastActivity;
|
||||
|
||||
return $accounts->sort(function (Account $left, Account $right) use ($dates, $direction) {
|
||||
$leftDate = $dates[$left->id] ?? Carbon::create(1900, 1, 1, 0, 0, 0);
|
||||
$rightDate = $dates[$right->id] ?? Carbon::create(1900, 1, 1, 0, 0, 0);
|
||||
if ('asc' === $direction) {
|
||||
return $leftDate->gt($rightDate) ? 1 : -1;
|
||||
}
|
||||
|
||||
return $rightDate->gt($leftDate) ? 1 : -1;
|
||||
});
|
||||
}
|
||||
|
||||
private function getBalanceDifference(Collection $accounts, Carbon $start, Carbon $end): void
|
||||
{
|
||||
// collect balances, start and end for both native and converted.
|
||||
// yes the b is usually used for boolean by idiots but here it's for balance.
|
||||
$bStart = [];
|
||||
$bEnd = [];
|
||||
|
||||
try {
|
||||
$bStart = app('steam')->balancesByAccountsConverted($accounts, $start);
|
||||
$bEnd = app('steam')->balancesByAccountsConverted($accounts, $end);
|
||||
} catch (FireflyException $e) {
|
||||
Log::error($e->getMessage());
|
||||
}
|
||||
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$id = $account->id;
|
||||
if (array_key_exists($id, $bStart) && array_key_exists($id, $bEnd)) {
|
||||
$this->balanceDifferences[$id] = [
|
||||
'balance' => bcsub($bEnd[$id]['balance'], $bStart[$id]['balance']),
|
||||
'native_balance' => bcsub($bEnd[$id]['native_balance'], $bStart[$id]['native_balance']),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function sortByBalanceDifference(Collection $accounts, string $direction): Collection
|
||||
{
|
||||
$balances = $this->balanceDifferences;
|
||||
|
||||
return $accounts->sort(function (Account $left, Account $right) use ($balances, $direction) {
|
||||
$leftBalance = (float) ($balances[$left->id]['native_balance'] ?? 0);
|
||||
$rightBalance = (float) ($balances[$right->id]['native_balance'] ?? 0);
|
||||
if ('asc' === $direction) {
|
||||
return $leftBalance <=> $rightBalance;
|
||||
}
|
||||
|
||||
return $rightBalance <=> $leftBalance;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,10 +381,7 @@ class User extends Authenticatable
|
||||
$dbRolesTitles = $dbRoles->pluck('title')->toArray();
|
||||
|
||||
/** @var Collection $groupMemberships */
|
||||
$groupMemberships = $this->groupMemberships()
|
||||
->whereIn('user_role_id', $dbRolesIds)
|
||||
->where('user_group_id', $userGroup->id)->get()
|
||||
;
|
||||
$groupMemberships = $this->groupMemberships()->whereIn('user_role_id', $dbRolesIds)->where('user_group_id', $userGroup->id)->get();
|
||||
if (0 === $groupMemberships->count()) {
|
||||
app('log')->error(sprintf(
|
||||
'User #%d "%s" does not have roles %s in user group #%d "%s"',
|
||||
|
||||
@@ -101,6 +101,7 @@
|
||||
"psr/log": "<4",
|
||||
"ramsey/uuid": "^4.7",
|
||||
"rcrowe/twigbridge": "^0.14",
|
||||
"twig/twig": "3.8.0",
|
||||
"spatie/laravel-html": "^3.2",
|
||||
"spatie/laravel-ignition": "^2",
|
||||
"spatie/period": "^2.4",
|
||||
|
||||
45
composer.lock
generated
45
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "90c32aa5ed2c60408abbdd7edf8100db",
|
||||
"content-hash": "be35d14f371e7f8bc178ae6f7a615e5c",
|
||||
"packages": [
|
||||
{
|
||||
"name": "bacon/bacon-qr-code",
|
||||
@@ -3366,16 +3366,16 @@
|
||||
},
|
||||
{
|
||||
"name": "nesbot/carbon",
|
||||
"version": "3.2.4",
|
||||
"version": "3.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/briannesbitt/Carbon.git",
|
||||
"reference": "82c28278c1c8f7b82dcdab25692237f052ffc8d8"
|
||||
"reference": "7219739c4e01d4680c980545821733b6ed8ee880"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/82c28278c1c8f7b82dcdab25692237f052ffc8d8",
|
||||
"reference": "82c28278c1c8f7b82dcdab25692237f052ffc8d8",
|
||||
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/7219739c4e01d4680c980545821733b6ed8ee880",
|
||||
"reference": "7219739c4e01d4680c980545821733b6ed8ee880",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3468,7 +3468,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-04-05T09:58:10+00:00"
|
||||
"time": "2024-04-18T16:35:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nette/schema",
|
||||
@@ -8610,37 +8610,30 @@
|
||||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
"version": "v3.9.2",
|
||||
"version": "v3.8.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/Twig.git",
|
||||
"reference": "856cb5a6cfd6f3e4dc1f6c9a8f54e259503f7cf3"
|
||||
"reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/856cb5a6cfd6f3e4dc1f6c9a8f54e259503f7cf3",
|
||||
"reference": "856cb5a6cfd6f3e4dc1f6c9a8f54e259503f7cf3",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d",
|
||||
"reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"symfony/deprecation-contracts": "^2.5|^3",
|
||||
"symfony/polyfill-ctype": "^1.8",
|
||||
"symfony/polyfill-mbstring": "^1.3",
|
||||
"symfony/polyfill-php80": "^1.22"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/container": "^1.0|^2.0",
|
||||
"symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0"
|
||||
"symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/Resources/core.php",
|
||||
"src/Resources/debug.php",
|
||||
"src/Resources/escaper.php",
|
||||
"src/Resources/string_loader.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Twig\\": "src/"
|
||||
}
|
||||
@@ -8673,7 +8666,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/twigphp/Twig/issues",
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.9.2"
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.8.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -8685,7 +8678,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-04-17T14:16:25+00:00"
|
||||
"time": "2023-11-21T18:54:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "vlucas/phpdotenv",
|
||||
@@ -10398,16 +10391,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-strict-rules",
|
||||
"version": "1.5.3",
|
||||
"version": "1.5.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
|
||||
"reference": "568210bd301f94a0d4b1e5a0808c374c1b9cf11b"
|
||||
"reference": "2e193a07651a6f4be3baa44ddb21d822681f5918"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/568210bd301f94a0d4b1e5a0808c374c1b9cf11b",
|
||||
"reference": "568210bd301f94a0d4b1e5a0808c374c1b9cf11b",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/2e193a07651a6f4be3baa44ddb21d822681f5918",
|
||||
"reference": "2e193a07651a6f4be3baa44ddb21d822681f5918",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -10441,9 +10434,9 @@
|
||||
"description": "Extra strict and opinionated rules for PHPStan",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
|
||||
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.3"
|
||||
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.5"
|
||||
},
|
||||
"time": "2024-04-06T07:43:25+00:00"
|
||||
"time": "2024-04-19T15:12:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
|
||||
326
config/debugbar.php
Normal file
326
config/debugbar.php
Normal file
@@ -0,0 +1,326 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Debugbar Settings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Debugbar is enabled by default, when debug is set to true in app.php.
|
||||
| You can override the value by setting enable to true or false instead of null.
|
||||
|
|
||||
| You can provide an array of URI's that must be ignored (eg. 'api/*')
|
||||
|
|
||||
*/
|
||||
|
||||
'enabled' => env('DEBUGBAR_ENABLED', null),
|
||||
'except' => [
|
||||
'telescope*',
|
||||
'horizon*',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Storage settings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| DebugBar stores data for session/ajax requests.
|
||||
| You can disable this, so the debugbar stores data in headers/session,
|
||||
| but this can cause problems with large data collectors.
|
||||
| By default, file storage (in the storage folder) is used. Redis and PDO
|
||||
| can also be used. For PDO, run the package migrations first.
|
||||
|
|
||||
| Warning: Enabling storage.open will allow everyone to access previous
|
||||
| request, do not enable open storage in publicly available environments!
|
||||
| Specify a callback if you want to limit based on IP or authentication.
|
||||
| Leaving it to null will allow localhost only.
|
||||
*/
|
||||
'storage' => [
|
||||
'enabled' => true,
|
||||
'open' => env('DEBUGBAR_OPEN_STORAGE'), // bool/callback.
|
||||
'driver' => 'file', // redis, file, pdo, socket, custom
|
||||
'path' => storage_path('debugbar'), // For file driver
|
||||
'connection' => null, // Leave null for default connection (Redis/PDO)
|
||||
'provider' => '', // Instance of StorageInterface for custom driver
|
||||
'hostname' => '127.0.0.1', // Hostname to use with the "socket" driver
|
||||
'port' => 2304, // Port to use with the "socket" driver
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Editor
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Choose your preferred editor to use when clicking file name.
|
||||
|
|
||||
| Supported: "phpstorm", "vscode", "vscode-insiders", "vscode-remote",
|
||||
| "vscode-insiders-remote", "vscodium", "textmate", "emacs",
|
||||
| "sublime", "atom", "nova", "macvim", "idea", "netbeans",
|
||||
| "xdebug", "espresso"
|
||||
|
|
||||
*/
|
||||
|
||||
'editor' => env('DEBUGBAR_EDITOR') ?: env('IGNITION_EDITOR', 'phpstorm'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Remote Path Mapping
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If you are using a remote dev server, like Laravel Homestead, Docker, or
|
||||
| even a remote VPS, it will be necessary to specify your path mapping.
|
||||
|
|
||||
| Leaving one, or both of these, empty or null will not trigger the remote
|
||||
| URL changes and Debugbar will treat your editor links as local files.
|
||||
|
|
||||
| "remote_sites_path" is an absolute base path for your sites or projects
|
||||
| in Homestead, Vagrant, Docker, or another remote development server.
|
||||
|
|
||||
| Example value: "/home/vagrant/Code"
|
||||
|
|
||||
| "local_sites_path" is an absolute base path for your sites or projects
|
||||
| on your local computer where your IDE or code editor is running on.
|
||||
|
|
||||
| Example values: "/Users/<name>/Code", "C:\Users\<name>\Documents\Code"
|
||||
|
|
||||
*/
|
||||
|
||||
'remote_sites_path' => env('DEBUGBAR_REMOTE_SITES_PATH'),
|
||||
'local_sites_path' => env('DEBUGBAR_LOCAL_SITES_PATH', env('IGNITION_LOCAL_SITES_PATH')),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Vendors
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Vendor files are included by default, but can be set to false.
|
||||
| This can also be set to 'js' or 'css', to only include javascript or css vendor files.
|
||||
| Vendor files are for css: font-awesome (including fonts) and highlight.js (css files)
|
||||
| and for js: jquery and highlight.js
|
||||
| So if you want syntax highlighting, set it to true.
|
||||
| jQuery is set to not conflict with existing jQuery scripts.
|
||||
|
|
||||
*/
|
||||
|
||||
'include_vendors' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Capture Ajax Requests
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The Debugbar can capture Ajax requests and display them. If you don't want this (ie. because of errors),
|
||||
| you can use this option to disable sending the data through the headers.
|
||||
|
|
||||
| Optionally, you can also send ServerTiming headers on ajax requests for the Chrome DevTools.
|
||||
|
|
||||
| Note for your request to be identified as ajax requests they must either send the header
|
||||
| X-Requested-With with the value XMLHttpRequest (most JS libraries send this), or have application/json as a Accept header.
|
||||
|
|
||||
| By default `ajax_handler_auto_show` is set to true allowing ajax requests to be shown automatically in the Debugbar.
|
||||
| Changing `ajax_handler_auto_show` to false will prevent the Debugbar from reloading.
|
||||
*/
|
||||
|
||||
'capture_ajax' => true,
|
||||
'add_ajax_timing' => false,
|
||||
'ajax_handler_auto_show' => true,
|
||||
'ajax_handler_enable_tab' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Custom Error Handler for Deprecated warnings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When enabled, the Debugbar shows deprecated warnings for Symfony components
|
||||
| in the Messages tab.
|
||||
|
|
||||
*/
|
||||
'error_handler' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Clockwork integration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The Debugbar can emulate the Clockwork headers, so you can use the Chrome
|
||||
| Extension, without the server-side code. It uses Debugbar collectors instead.
|
||||
|
|
||||
*/
|
||||
'clockwork' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DataCollectors
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Enable/disable DataCollectors
|
||||
|
|
||||
*/
|
||||
|
||||
'collectors' => [
|
||||
'phpinfo' => true, // Php version
|
||||
'messages' => true, // Messages
|
||||
'time' => true, // Time Datalogger
|
||||
'memory' => true, // Memory usage
|
||||
'exceptions' => true, // Exception displayer
|
||||
'log' => true, // Logs from Monolog (merged in messages if enabled)
|
||||
'db' => true, // Show database (PDO) queries and bindings
|
||||
'views' => true, // Views with their data
|
||||
'route' => true, // Current route information
|
||||
'auth' => false, // Display Laravel authentication status
|
||||
'gate' => true, // Display Laravel Gate checks
|
||||
'session' => true, // Display session data
|
||||
'symfony_request' => true, // Only one can be enabled..
|
||||
'mail' => true, // Catch mail messages
|
||||
'laravel' => false, // Laravel version and environment
|
||||
'events' => false, // All events fired
|
||||
'default_request' => false, // Regular or special Symfony request logger
|
||||
'logs' => false, // Add the latest log messages
|
||||
'files' => false, // Show the included files
|
||||
'config' => false, // Display config settings
|
||||
'cache' => false, // Display cache events
|
||||
'models' => true, // Display models
|
||||
'livewire' => true, // Display Livewire (when available)
|
||||
'jobs' => false, // Display dispatched jobs
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Extra options
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Configure some DataCollectors
|
||||
|
|
||||
*/
|
||||
|
||||
'options' => [
|
||||
'time' => [
|
||||
'memory_usage' => false, // Calculated by subtracting memory start and end, it may be inaccurate
|
||||
],
|
||||
'messages' => [
|
||||
'trace' => true, // Trace the origin of the debug message
|
||||
],
|
||||
'memory' => [
|
||||
'reset_peak' => false, // run memory_reset_peak_usage before collecting
|
||||
'with_baseline' => false, // Set boot memory usage as memory peak baseline
|
||||
'precision' => 0, // Memory rounding precision
|
||||
],
|
||||
'auth' => [
|
||||
'show_name' => true, // Also show the users name/email in the debugbar
|
||||
'show_guards' => true, // Show the guards that are used
|
||||
],
|
||||
'db' => [
|
||||
'with_params' => true, // Render SQL with the parameters substituted
|
||||
'backtrace' => true, // Use a backtrace to find the origin of the query in your files.
|
||||
'backtrace_exclude_paths' => [], // Paths to exclude from backtrace. (in addition to defaults)
|
||||
'timeline' => false, // Add the queries to the timeline
|
||||
'duration_background' => true, // Show shaded background on each query relative to how long it took to execute.
|
||||
'explain' => [ // Show EXPLAIN output on queries
|
||||
'enabled' => false,
|
||||
'types' => ['SELECT'], // Deprecated setting, is always only SELECT
|
||||
],
|
||||
'hints' => false, // Show hints for common mistakes
|
||||
'show_copy' => false, // Show copy button next to the query,
|
||||
'slow_threshold' => false, // Only track queries that last longer than this time in ms
|
||||
'memory_usage' => false, // Show queries memory usage
|
||||
'soft_limit' => 100, // After the soft limit, no parameters/backtrace are captured
|
||||
'hard_limit' => 500, // After the hard limit, queries are ignored
|
||||
],
|
||||
'mail' => [
|
||||
'timeline' => false, // Add mails to the timeline
|
||||
'show_body' => true,
|
||||
],
|
||||
'views' => [
|
||||
'timeline' => false, // Add the views to the timeline (Experimental)
|
||||
'data' => false, // true for all data, 'keys' for only names, false for no parameters.
|
||||
'group' => 50, // Group duplicate views. Pass value to auto-group, or true/false to force
|
||||
'exclude_paths' => [ // Add the paths which you don't want to appear in the views
|
||||
'vendor/filament', // Exclude Filament components by default
|
||||
],
|
||||
],
|
||||
'route' => [
|
||||
'label' => true, // show complete route on bar
|
||||
],
|
||||
'session' => [
|
||||
'hiddens' => [], // hides sensitive values using array paths
|
||||
],
|
||||
'symfony_request' => [
|
||||
'hiddens' => [], // hides sensitive values using array paths, example: request_request.password
|
||||
],
|
||||
'events' => [
|
||||
'data' => false, // collect events data, listeners
|
||||
],
|
||||
'logs' => [
|
||||
'file' => null,
|
||||
],
|
||||
'cache' => [
|
||||
'values' => true, // collect cache values
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Inject Debugbar in Response
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Usually, the debugbar is added just before </body>, by listening to the
|
||||
| Response after the App is done. If you disable this, you have to add them
|
||||
| in your template yourself. See http://phpdebugbar.com/docs/rendering.html
|
||||
|
|
||||
*/
|
||||
|
||||
'inject' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DebugBar route prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Sometimes you want to set route prefix to be used by DebugBar to load
|
||||
| its resources from. Usually the need comes from misconfigured web server or
|
||||
| from trying to overcome bugs like this: http://trac.nginx.org/nginx/ticket/97
|
||||
|
|
||||
*/
|
||||
'route_prefix' => '_debugbar',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DebugBar route middleware
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Additional middleware to run on the Debugbar routes
|
||||
*/
|
||||
'route_middleware' => [],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DebugBar route domain
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By default DebugBar route served from the same domain that request served.
|
||||
| To override default domain, specify it as a non-empty value.
|
||||
*/
|
||||
'route_domain' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DebugBar theme
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Switches between light and dark theme. If set to auto it will respect system preferences
|
||||
| Possible values: auto, light, dark
|
||||
*/
|
||||
'theme' => env('DEBUGBAR_THEME', 'auto'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Backtrace stack limit
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By default, the DebugBar limits the number of frames returned by the 'debug_backtrace()' function.
|
||||
| If you need larger stacktraces, you can increase this number. Setting it to 0 will result in no limit.
|
||||
*/
|
||||
'debug_backtrace_limit' => 50,
|
||||
];
|
||||
@@ -117,8 +117,8 @@ return [
|
||||
'expression_engine' => false,
|
||||
// see cer.php for exchange rates feature flag.
|
||||
],
|
||||
'version' => 'develop/2024-04-18',
|
||||
'api_version' => '2.0.13',
|
||||
'version' => 'develop/2024-04-22',
|
||||
'api_version' => '2.0.14',
|
||||
'db_version' => 24,
|
||||
|
||||
// generic settings
|
||||
@@ -924,7 +924,7 @@ return [
|
||||
'sorting' => [
|
||||
'allowed' => [
|
||||
'transactions' => ['description', 'amount'],
|
||||
'accounts' => ['name', 'active', 'iban', 'balance', 'last_activity'],
|
||||
'accounts' => ['name', 'active', 'iban', 'balance', 'last_activity', 'balance_difference'],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
176
package-lock.json
generated
176
package-lock.json
generated
@@ -2402,9 +2402,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.3.tgz",
|
||||
"integrity": "sha512-X9alQ3XM6I9IlSlmC8ddAvMSyG1WuHk5oUnXGw+yUBs3BFoTizmG1La/Gr8fVJvDWAq+zlYTZ9DBgrlKRVY06g==",
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.16.1.tgz",
|
||||
"integrity": "sha512-92/y0TqNLRYOTXpm6Z7mnpvKAG9P7qmK7yJeRJSdzElNCUnsgbpAsGqerUboYRIQKzgfq4pWu9xVkgpWLfmNsw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -2415,9 +2415,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.3.tgz",
|
||||
"integrity": "sha512-eQK5JIi+POhFpzk+LnjKIy4Ks+pwJ+NXmPxOCSvOKSNRPONzKuUvWE+P9JxGZVxrtzm6BAYMaL50FFuPe0oWMQ==",
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.16.1.tgz",
|
||||
"integrity": "sha512-ttWB6ZCfRLuDIUiE0yiu5gcqOsYjA5F7kEV1ggHMj20FwLZ8A1FMeahZJFl/pnOmcnD2QL0z4AcDuo27utGU8A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2428,9 +2428,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.3.tgz",
|
||||
"integrity": "sha512-Od4vE6f6CTT53yM1jgcLqNfItTsLt5zE46fdPaEmeFHvPs5SjZYlLpHrSiHEKR1+HdRfxuzXHjDOIxQyC3ptBA==",
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.16.1.tgz",
|
||||
"integrity": "sha512-QLDvPLetbqjHojTGFw9+nuSP3YY/iz2k1cep6crYlr97sS+ZJ0W43b8Z0zC00+lnFZj6JSNxiA4DjboNQMuh1A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2441,9 +2441,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.3.tgz",
|
||||
"integrity": "sha512-0IMAO21axJeNIrvS9lSe/PGthc8ZUS+zC53O0VhF5gMxfmcKAP4ESkKOCwEi6u2asUrt4mQv2rjY8QseIEb1aw==",
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.16.1.tgz",
|
||||
"integrity": "sha512-TAUK/D8khRrRIa1KwRzo8JNKk3tcqaeXWdtsiLgA8zmACWwlWLjPCJ4DULGHQrMkeBjp1Cd3Yuwx04lZgFx5Vg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2454,9 +2454,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.3.tgz",
|
||||
"integrity": "sha512-ge2DC7tHRHa3caVEoSbPRJpq7azhG+xYsd6u2MEnJ6XzPSzQsTKyXvh6iWjXRf7Rt9ykIUWHtl0Uz3T6yXPpKw==",
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.16.1.tgz",
|
||||
"integrity": "sha512-KO+WGZjrh6zyFTD1alIFkfdtxf8B4BC+hqd3kBZHscPLvE5FR/6QKsyuCT0JlERxxYBSUKNUQ/UHyX5uwO1x2A==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -2467,9 +2467,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.14.3.tgz",
|
||||
"integrity": "sha512-ljcuiDI4V3ySuc7eSk4lQ9wU8J8r8KrOUvB2U+TtK0TiW6OFDmJ+DdIjjwZHIw9CNxzbmXY39wwpzYuFDwNXuw==",
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.16.1.tgz",
|
||||
"integrity": "sha512-NqxbllzIB1WoAo4ThUXVtd21iiM5IHMTTXmXySKBLVcZvkU0HIZmatlP7hLzb5yQubcmdIeWmncd2NdsjocEiw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -2480,9 +2480,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.3.tgz",
|
||||
"integrity": "sha512-Eci2us9VTHm1eSyn5/eEpaC7eP/mp5n46gTRB3Aar3BgSvDQGJZuicyq6TsH4HngNBgVqC5sDYxOzTExSU+NjA==",
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.16.1.tgz",
|
||||
"integrity": "sha512-snma5NvV8y7IECQ5rq0sr0f3UUu+92NVmG/913JXJMcXo84h9ak9TA5UI9Cl2XRM9j3m37QwDBtEYnJzRkSmxA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2493,9 +2493,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.3.tgz",
|
||||
"integrity": "sha512-UrBoMLCq4E92/LCqlh+blpqMz5h1tJttPIniwUgOFJyjWI1qrtrDhhpHPuFxULlUmjFHfloWdixtDhSxJt5iKw==",
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.16.1.tgz",
|
||||
"integrity": "sha512-KOvqGprlD84ueivhCi2flvcUwDRD20mAsE3vxQNVEI2Di9tnPGAfEu6UcrSPZbM+jG2w1oSr43hrPo0RNg6GGg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2506,9 +2506,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.3.tgz",
|
||||
"integrity": "sha512-5aRjvsS8q1nWN8AoRfrq5+9IflC3P1leMoy4r2WjXyFqf3qcqsxRCfxtZIV58tCxd+Yv7WELPcO9mY9aeQyAmw==",
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.16.1.tgz",
|
||||
"integrity": "sha512-/gsNwtiGLqYwN4vP+EIdUC6Q6LTlpupWqokqIndvZcjn9ig/5P01WyaYCU2wvfL/2Z82jp5kX8c1mDBOvCP3zg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -2519,9 +2519,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.3.tgz",
|
||||
"integrity": "sha512-sk/Qh1j2/RJSX7FhEpJn8n0ndxy/uf0kI/9Zc4b1ELhqULVdTfN6HL31CDaTChiBAOgLcsJ1sgVZjWv8XNEsAQ==",
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.16.1.tgz",
|
||||
"integrity": "sha512-uU8zuGkQfGqfD9w6VRJZI4IuG4JIfNxxJgEmLMAmPVHREKGsxFVfgHy5c6CexQF2vOfgjB33OsET3Vdn2lln9A==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -2532,9 +2532,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.3.tgz",
|
||||
"integrity": "sha512-jOO/PEaDitOmY9TgkxF/TQIjXySQe5KVYB57H/8LRP/ux0ZoO8cSHCX17asMSv3ruwslXW/TLBcxyaUzGRHcqg==",
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.16.1.tgz",
|
||||
"integrity": "sha512-lsjLtDgtcGFEuBP6yrXwkRN5/wKlvUZtfbKZZu0yaoNpiBL4epgnO21osAALIspVRnl4qZgyLFd8xjCYYWgwfw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -2545,9 +2545,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.3.tgz",
|
||||
"integrity": "sha512-8ybV4Xjy59xLMyWo3GCfEGqtKV5M5gCSrZlxkPGvEPCGDLNla7v48S662HSGwRd6/2cSneMQWiv+QzcttLrrOA==",
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.16.1.tgz",
|
||||
"integrity": "sha512-N2ZizKhUryqqrMfdCnjhJhZRgv61C6gK+hwVtCIKC8ts8J+go+vqENnGexwg21nHIOvLN5mBM8a7DI2vlyIOPg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2558,9 +2558,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.3.tgz",
|
||||
"integrity": "sha512-s+xf1I46trOY10OqAtZ5Rm6lzHre/UiLA1J2uOhCFXWkbZrJRkYBPO6FhvGfHmdtQ3Bx793MNa7LvoWFAm93bg==",
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.16.1.tgz",
|
||||
"integrity": "sha512-5ICeMxqg66FrOA2AbnBQ2TJVxfvZsKLxmof0ibvPLaYtbsJqnTUtJOofgWb46Gjd4uZcA4rdsp4JCxegzQPqCg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2571,9 +2571,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.3.tgz",
|
||||
"integrity": "sha512-+4h2WrGOYsOumDQ5S2sYNyhVfrue+9tc9XcLWLh+Kw3UOxAvrfOrSMFon60KspcDdytkNDh7K2Vs6eMaYImAZg==",
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.16.1.tgz",
|
||||
"integrity": "sha512-1vIP6Ce02L+qWD7uZYRiFiuAJo3m9kARatWmFSnss0gZnVj2Id7OPUU9gm49JPGasgcR3xMqiH3fqBJ8t00yVg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2584,9 +2584,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.3.tgz",
|
||||
"integrity": "sha512-T1l7y/bCeL/kUwh9OD4PQT4aM7Bq43vX05htPJJ46RTI4r5KNt6qJRzAfNfM+OYMNEVBWQzR2Gyk+FXLZfogGw==",
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.16.1.tgz",
|
||||
"integrity": "sha512-Y3M92DcVsT6LoP+wrKpoUWPaazaP1fzbNkp0a0ZSj5Y//+pQVfVe/tQdsYQQy7dwXR30ZfALUIc9PCh9Izir6w==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -2597,9 +2597,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.3.tgz",
|
||||
"integrity": "sha512-/BypzV0H1y1HzgYpxqRaXGBRqfodgoBBCcsrujT6QRcakDQdfU+Lq9PENPh5jB4I44YWq+0C2eHsHya+nZY1sA==",
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.16.1.tgz",
|
||||
"integrity": "sha512-x0fvpHMuF7fK5r8oZxSi8VYXkrVmRgubXpO/wcf15Lk3xZ4Jvvh5oG+u7Su1776A7XzVKZhD2eRc4t7H50gL3w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2708,9 +2708,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/eslint": {
|
||||
"version": "8.56.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.9.tgz",
|
||||
"integrity": "sha512-W4W3KcqzjJ0sHg2vAq9vfml6OhsJ53TcUjUqfzzZf/EChUtwspszj/S0pzMxnfRcO55/iGq47dscXw71Fxc4Zg==",
|
||||
"version": "8.56.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz",
|
||||
"integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "*",
|
||||
@@ -3373,9 +3373,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/alpinejs": {
|
||||
"version": "3.13.8",
|
||||
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.8.tgz",
|
||||
"integrity": "sha512-XolbBJryCndomtaHd/KHQjQeD/L72FJxy/YhLLFD4Lr7zzGcpcbg+UgXteMR2pYg1KhRUr6V4O3GfN1zJAmRWw==",
|
||||
"version": "3.13.9",
|
||||
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.9.tgz",
|
||||
"integrity": "sha512-BTEJ3fcUpqKlB+aFYSWGVEp8IP3vA96x7wUaNSdb5SJxV5i7s+/Bduxy9D6TokcqpW5MygsnG1eBEFfr4AnQSw==",
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "~3.1.1"
|
||||
}
|
||||
@@ -4020,9 +4020,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001610",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001610.tgz",
|
||||
"integrity": "sha512-QFutAY4NgaelojVMjY63o6XlZyORPaLfyMnsl3HgnWdJUcX6K0oaJymHjH8PT5Gk7sTm8rvC/c5COUQKXqmOMA==",
|
||||
"version": "1.0.30001612",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001612.tgz",
|
||||
"integrity": "sha512-lFgnZ07UhaCcsSZgWW0K5j4e69dK1u/ltrL9lTUiFOwNHs12S3UMIEYgBV0Z6C6hRDev7iRnMzzYmKabYdXF9g==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -5095,9 +5095,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.740",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.740.tgz",
|
||||
"integrity": "sha512-Yvg5i+iyv7Xm18BRdVPVm8lc7kgxM3r6iwqCH2zB7QZy1kZRNmd0Zqm0zcD9XoFREE5/5rwIuIAOT+/mzGcnZg==",
|
||||
"version": "1.4.745",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.745.tgz",
|
||||
"integrity": "sha512-tRbzkaRI5gbUn5DEvF0dV4TQbMZ5CLkWeTAXmpC9IrYT+GE+x76i9p+o3RJ5l9XmdQlI1pPhVtE9uNcJJ0G0EA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/elliptic": {
|
||||
@@ -6305,9 +6305,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/i18next-http-backend": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.5.0.tgz",
|
||||
"integrity": "sha512-Z/aQsGZk1gSxt2/DztXk92DuDD20J+rNudT7ZCdTrNOiK8uQppfvdjq9+DFQfpAnFPn3VZS+KQIr1S/W1KxhpQ==",
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.5.1.tgz",
|
||||
"integrity": "sha512-+rNX1tghdVxdfjfPt0bI1sNg5ahGW9kA7OboG7b4t03Fp69NdDlRIze6yXhIbN8rbHxJ8IP4dzRm/okZ15lkQg==",
|
||||
"dependencies": {
|
||||
"cross-fetch": "4.0.0"
|
||||
}
|
||||
@@ -6498,9 +6498,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz",
|
||||
"integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==",
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz",
|
||||
"integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
@@ -8991,9 +8991,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.14.3",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.3.tgz",
|
||||
"integrity": "sha512-ag5tTQKYsj1bhrFC9+OEWqb5O6VYgtQDO9hPDBMmIbePwhfSr+ExlcU741t8Dhw5DkPCQf6noz0jb36D6W9/hw==",
|
||||
"version": "4.16.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.16.1.tgz",
|
||||
"integrity": "sha512-5CaD3MPDlPKfhqzRvWXK96G6ELJfPZNb3LHiZxTHgDdC6jvwfGz2E8nY+9g1ONk4ttHsK1WaFP19Js4PSr1E3g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.5"
|
||||
@@ -9006,22 +9006,22 @@
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.14.3",
|
||||
"@rollup/rollup-android-arm64": "4.14.3",
|
||||
"@rollup/rollup-darwin-arm64": "4.14.3",
|
||||
"@rollup/rollup-darwin-x64": "4.14.3",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.14.3",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.14.3",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.14.3",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.14.3",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.14.3",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.14.3",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.14.3",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.14.3",
|
||||
"@rollup/rollup-linux-x64-musl": "4.14.3",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.14.3",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.14.3",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.14.3",
|
||||
"@rollup/rollup-android-arm-eabi": "4.16.1",
|
||||
"@rollup/rollup-android-arm64": "4.16.1",
|
||||
"@rollup/rollup-darwin-arm64": "4.16.1",
|
||||
"@rollup/rollup-darwin-x64": "4.16.1",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.16.1",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.16.1",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.16.1",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.16.1",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.16.1",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.16.1",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.16.1",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.16.1",
|
||||
"@rollup/rollup-linux-x64-musl": "4.16.1",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.16.1",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.16.1",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.16.1",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
@@ -10098,9 +10098,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.2.9",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.9.tgz",
|
||||
"integrity": "sha512-uOQWfuZBlc6Y3W/DTuQ1Sr+oIXWvqljLvS881SVmAj00d5RdgShLcuXWxseWPd4HXwiYBFW/vXHfKFeqj9uQnw==",
|
||||
"version": "5.2.10",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.10.tgz",
|
||||
"integrity": "sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.20.1",
|
||||
|
||||
@@ -22,95 +22,10 @@ var count = 0;
|
||||
|
||||
$(document).ready(function () {
|
||||
updateListButtons();
|
||||
addSort();
|
||||
$('.clone-transaction').click(cloneTransaction);
|
||||
$('.clone-transaction-and-edit').click(cloneTransactionAndEdit);
|
||||
});
|
||||
|
||||
var fixHelper = function (e, tr) {
|
||||
"use strict";
|
||||
var $originals = tr.children();
|
||||
var $helper = tr.clone();
|
||||
$helper.children().each(function (index) {
|
||||
// Set helper cell sizes to match the original sizes
|
||||
$(this).width($originals.eq(index).width());
|
||||
});
|
||||
return $helper;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function addSort() {
|
||||
if (typeof $(".table-sortable>tbody").sortable !== "undefined") {
|
||||
$('.table-sortable>tbody').sortable(
|
||||
{
|
||||
items: "tr:not(.unsortable)",
|
||||
handle: '.object-handle',
|
||||
stop: sortStop,
|
||||
start: function (event, ui) {
|
||||
// Build a placeholder cell that spans all the cells in the row
|
||||
var cellCount = 0;
|
||||
$('td, th', ui.helper).each(function () {
|
||||
// For each TD or TH try and get it's colspan attribute, and add that or 1 to the total
|
||||
var colspan = 1;
|
||||
var colspanAttr = $(this).attr('colspan');
|
||||
if (colspanAttr > 1) {
|
||||
colspan = colspanAttr;
|
||||
}
|
||||
cellCount += colspan;
|
||||
});
|
||||
|
||||
// Add the placeholder UI - note that this is the item's content, so TD rather than TR
|
||||
ui.placeholder.html('<td colspan="' + cellCount + '"> </td>');
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param event
|
||||
* @param ui
|
||||
* @returns {boolean|undefined}
|
||||
*/
|
||||
function sortStop(event, ui) {
|
||||
"use strict";
|
||||
var current = $(ui.item);
|
||||
var thisDate = current.data('date');
|
||||
var originalBG = current.css('backgroundColor');
|
||||
|
||||
|
||||
if (current.prev().data('date') !== thisDate && current.next().data('date') !== thisDate) {
|
||||
// animate something with color:
|
||||
current.animate({backgroundColor: "#d9534f"}, 200, function () {
|
||||
$(this).animate({backgroundColor: originalBG}, 200);
|
||||
return undefined;
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
//return false;
|
||||
// do update
|
||||
var list = $('tr[data-date="' + thisDate + '"]');
|
||||
var submit = [];
|
||||
$.each(list, function (i, v) {
|
||||
var row = $(v);
|
||||
var id = row.data('id');
|
||||
submit.push(id);
|
||||
});
|
||||
|
||||
// do extra animation when done?
|
||||
$.post('transactions/reorder', {items: submit, date: thisDate, _token: token});
|
||||
|
||||
current.animate({backgroundColor: "#5cb85c"}, 200, function () {
|
||||
$(this).animate({backgroundColor: originalBG}, 200);
|
||||
return undefined;
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -103,6 +103,12 @@
|
||||
.skin-firefly-iii .btn-info {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-firefly-iii .btn-danger {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-firefly-iii .btn-warning {
|
||||
color: #fff;
|
||||
}
|
||||
.skin-firefly-iii .btn-default {
|
||||
background-color: #55606a;
|
||||
color: #bec5cb;
|
||||
@@ -251,6 +257,11 @@
|
||||
}
|
||||
.skin-firefly-iii .table > thead > tr > th,
|
||||
.skin-firefly-iii .table > tbody > tr > th,
|
||||
.skin-firefly-iii .table > tfoot > tr > th {
|
||||
border-bottom: 2px #c9d1d9 solid;
|
||||
}
|
||||
.skin-firefly-iii .table > thead > tr > th,
|
||||
.skin-firefly-iii .table > tbody > tr > th,
|
||||
.skin-firefly-iii .table > tfoot > tr > th,
|
||||
.skin-firefly-iii .table > thead > tr > td,
|
||||
.skin-firefly-iii .table > tbody > tr > td,
|
||||
@@ -480,6 +491,7 @@
|
||||
.skin-firefly-iii .nav-tabs-custom > .nav-tabs > li.active > a {
|
||||
border-left-color: #353c42;
|
||||
border-right-color: #353c42;
|
||||
color: #bec5cb;
|
||||
}
|
||||
.skin-firefly-iii .nav-tabs-custom > .nav-tabs.pull-right > li:first-of-type.active > a {
|
||||
border-left-color: #353c42;
|
||||
@@ -511,7 +523,7 @@
|
||||
background-color: #272c30;
|
||||
}
|
||||
.skin-firefly-iii .input-group .input-group-addon {
|
||||
border-right: 1px solid #272c30;
|
||||
border: 1px solid #272c30;
|
||||
}
|
||||
.skin-firefly-iii .form-control {
|
||||
border-color: #272c30;
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -30,6 +30,11 @@ import '../../css/grid-ff3-theme.css';
|
||||
import Get from "../../api/v2/model/account/get.js";
|
||||
import Put from "../../api/v2/model/account/put.js";
|
||||
import AccountRenderer from "../../support/renderers/AccountRenderer.js";
|
||||
import {showInternalsButton} from "../../support/page-settings/show-internals-button.js";
|
||||
import {showWizardButton} from "../../support/page-settings/show-wizard-button.js";
|
||||
import {getVariable} from "../../store/get-variable.js";
|
||||
import {setVariable} from "../../store/set-variable.js";
|
||||
|
||||
|
||||
// set type from URL
|
||||
const beforeQuery = window.location.href.split('?');
|
||||
@@ -47,6 +52,9 @@ sortingColumn = params.column ?? '';
|
||||
sortDirection = params.direction ?? '';
|
||||
|
||||
|
||||
showInternalsButton();
|
||||
showWizardButton();
|
||||
|
||||
let index = function () {
|
||||
return {
|
||||
// notifications
|
||||
@@ -59,10 +67,64 @@ let index = function () {
|
||||
show: false, text: '',
|
||||
|
||||
}
|
||||
}, totalPages: 1, page: 1, // available columns:
|
||||
},
|
||||
totalPages: 1,
|
||||
page: 1,
|
||||
|
||||
// available columns:
|
||||
// visible is hard coded, enabled is user-configurable.
|
||||
tableColumns: {
|
||||
drag_and_drop: {
|
||||
visible: true,
|
||||
enabled: true,
|
||||
},
|
||||
active: {
|
||||
visible: true,
|
||||
enabled: true,
|
||||
},
|
||||
name: {
|
||||
enabled: true
|
||||
visible: true,
|
||||
enabled: true,
|
||||
},
|
||||
type: {
|
||||
visible: true,
|
||||
enabled: true,
|
||||
},
|
||||
liability_type: {
|
||||
visible: type === 'liabilities',
|
||||
enabled: true,
|
||||
},
|
||||
liability_direction: {
|
||||
visible: type === 'liabilities',
|
||||
enabled: true,
|
||||
},
|
||||
liability_interest: {
|
||||
visible: type === 'liabilities',
|
||||
enabled: true,
|
||||
},
|
||||
number: {
|
||||
visible: true,
|
||||
enabled: true,
|
||||
},
|
||||
current_balance: {
|
||||
visible: type !== 'liabilities',
|
||||
enabled: true,
|
||||
},
|
||||
amount_due: {
|
||||
visible: type === 'liabilities',
|
||||
enabled: true,
|
||||
},
|
||||
last_activity: {
|
||||
visible: true,
|
||||
enabled: true,
|
||||
},
|
||||
balance_difference: {
|
||||
visible: true,
|
||||
enabled: true,
|
||||
},
|
||||
menu: {
|
||||
visible: true,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
editors: {},
|
||||
@@ -77,7 +139,7 @@ let index = function () {
|
||||
sort(column) {
|
||||
this.sortingColumn = column;
|
||||
this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc';
|
||||
const url = './accounts/'+type+'?column='+column+'&direction='+this.sortDirection;
|
||||
const url = './accounts/' + type + '?column=' + column + '&direction=' + this.sortDirection;
|
||||
|
||||
window.history.pushState({}, "", url);
|
||||
|
||||
@@ -92,11 +154,34 @@ let index = function () {
|
||||
format(date) {
|
||||
return format(date, i18next.t('config.date_time_fns'));
|
||||
},
|
||||
saveColumnSettings() {
|
||||
let newSettings = {};
|
||||
for (let key in this.tableColumns) {
|
||||
if (this.tableColumns.hasOwnProperty(key)) {
|
||||
newSettings[key] = this.tableColumns[key].enabled;
|
||||
}
|
||||
}
|
||||
console.log('New settings', newSettings);
|
||||
setVariable('accts_columns_' + type, newSettings);
|
||||
},
|
||||
|
||||
init() {
|
||||
this.notifications.wait.show = true;
|
||||
this.notifications.wait.text = i18next.t('firefly.wait_loading_data')
|
||||
this.loadAccounts();
|
||||
|
||||
const key = 'accts_columns_' + type;
|
||||
const defaultValue = {"drag_and_drop": false};
|
||||
|
||||
getVariable(key, defaultValue).then((response) => {
|
||||
for (let k in response) {
|
||||
if (response.hasOwnProperty(k) && this.tableColumns.hasOwnProperty(k)) {
|
||||
this.tableColumns[k].enabled = response[k] ?? true;
|
||||
}
|
||||
}
|
||||
}).then(() => {
|
||||
this.loadAccounts();
|
||||
});
|
||||
|
||||
},
|
||||
renderObjectValue(field, account) {
|
||||
let renderer = new AccountRenderer();
|
||||
@@ -140,14 +225,33 @@ let index = function () {
|
||||
this.accounts[index].nameEditorVisible = true;
|
||||
},
|
||||
loadAccounts() {
|
||||
|
||||
// sort instructions
|
||||
const sorting = [{column: this.sortingColumn, direction: this.sortDirection}];
|
||||
|
||||
// get start and end from the store:
|
||||
const start = new Date(window.store.get('start'));
|
||||
const end = new Date(window.store.get('end'));
|
||||
|
||||
let params = {
|
||||
sorting: sorting,
|
||||
type: type,
|
||||
page: this.page,
|
||||
start: start,
|
||||
end: end
|
||||
};
|
||||
|
||||
if(!this.tableColumns.balance_difference.enabled){
|
||||
delete params.start;
|
||||
delete params.end;
|
||||
}
|
||||
|
||||
this.notifications.wait.show = true;
|
||||
this.notifications.wait.text = i18next.t('firefly.wait_loading_data')
|
||||
this.accounts = [];
|
||||
// sort instructions
|
||||
// &sorting[0][column]=description&sorting[0][direction]=asc
|
||||
const sorting = [{column: this.sortingColumn, direction: this.sortDirection}];
|
||||
|
||||
// one page only.o
|
||||
(new Get()).index({sorting: sorting, type: type, page: this.page}).then(response => {
|
||||
(new Get()).index(params).then(response => {
|
||||
for (let i = 0; i < response.data.data.length; i++) {
|
||||
if (response.data.data.hasOwnProperty(i)) {
|
||||
let current = response.data.data[i];
|
||||
@@ -165,7 +269,10 @@ let index = function () {
|
||||
native_current_balance: current.attributes.native_current_balance,
|
||||
native_currency_code: current.attributes.native_currency_code,
|
||||
last_activity: null === current.attributes.last_activity ? '' : format(new Date(current.attributes.last_activity), i18next.t('config.month_and_day_fns')),
|
||||
balance_difference: current.attributes.balance_difference,
|
||||
native_balance_difference: current.attributes.native_balance_difference
|
||||
};
|
||||
console.log(current.attributes.balance_difference);
|
||||
this.accounts.push(account);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import {format} from "date-fns";
|
||||
import {getVariable} from "../../store/get-variable.js";
|
||||
import formatMoney from "../../util/format-money.js";
|
||||
import {getCacheKey} from "../../support/get-cache-key.js";
|
||||
import {cleanupCache} from "../../support/cleanup-cache.js";
|
||||
|
||||
let afterPromises = false;
|
||||
export default () => ({
|
||||
@@ -38,6 +39,7 @@ export default () => ({
|
||||
const start = new Date(window.store.get('start'));
|
||||
const end = new Date(window.store.get('end'));
|
||||
const boxesCacheKey = getCacheKey('dashboard-boxes-data', start, end);
|
||||
cleanupCache();
|
||||
|
||||
const cacheValid = window.store.get('cacheValid');
|
||||
let cachedData = window.store.get(boxesCacheKey);
|
||||
|
||||
@@ -69,6 +69,7 @@ let transactions = function () {
|
||||
resetButton: false,
|
||||
rulesButton: true,
|
||||
webhooksButton: true,
|
||||
categorySelectVisible: false
|
||||
},
|
||||
|
||||
// form behaviour during transaction
|
||||
@@ -343,7 +344,13 @@ let transactions = function () {
|
||||
// destination can never be revenue account
|
||||
this.filters.destination = ['Expense account', 'Loan', 'Debt', 'Mortgage', 'Asset account'];
|
||||
},
|
||||
|
||||
keyUpFromCategory(e) {
|
||||
if (e.key === 'Enter' && false === this.formStates.categorySelectVisible) {
|
||||
this.submitTransaction();
|
||||
return;
|
||||
}
|
||||
this.formStates.categorySelectVisible = document.querySelector('input.ac-category').nextSibling.classList.contains('show');
|
||||
},
|
||||
submitTransaction() {
|
||||
// reset all messages:
|
||||
this.notifications.error.show = false;
|
||||
@@ -422,7 +429,10 @@ let transactions = function () {
|
||||
if (this.formStates.returnHereButton) {
|
||||
this.notifications.success.show = true;
|
||||
this.notifications.success.url = 'transactions/show/' + this.groupProperties.id;
|
||||
this.notifications.success.text = i18next.t('firefly.stored_journal_js', {description: this.groupProperties.title, interpolation: { escapeValue: false }});
|
||||
this.notifications.success.text = i18next.t('firefly.stored_journal_js', {
|
||||
description: this.groupProperties.title,
|
||||
interpolation: {escapeValue: false}
|
||||
});
|
||||
this.formStates.isSubmitting = false;
|
||||
// reset group title again
|
||||
this.groupProperties.title = null;
|
||||
|
||||
@@ -72,12 +72,15 @@ let transactions = function () {
|
||||
resetButton: true,
|
||||
rulesButton: true,
|
||||
webhooksButton: true,
|
||||
|
||||
|
||||
},
|
||||
|
||||
// form behaviour during transaction
|
||||
formBehaviour: {
|
||||
formType: 'edit',
|
||||
foreignCurrencyEnabled: true,
|
||||
categorySelectVisible: false,
|
||||
},
|
||||
|
||||
// form data (except transactions) is stored in formData
|
||||
@@ -111,6 +114,14 @@ let transactions = function () {
|
||||
}
|
||||
},
|
||||
|
||||
keyUpFromCategory(e) {
|
||||
if (e.key === 'Enter' && false === this.formBehaviour.categorySelectVisible) {
|
||||
this.submitTransaction();
|
||||
return;
|
||||
}
|
||||
this.formBehaviour.categorySelectVisible = document.querySelector('input.ac-category').nextSibling.classList.contains('show');
|
||||
},
|
||||
|
||||
// submit the transaction form.
|
||||
// TODO pretty much duplicate of create.js
|
||||
submitTransaction() {
|
||||
|
||||
@@ -23,217 +23,12 @@ import dates from "../shared/dates.js";
|
||||
import i18next from "i18next";
|
||||
import {format} from "date-fns";
|
||||
import formatMoney from "../../util/format-money.js";
|
||||
import Put from "../../api/v2/model/transaction/put.js";
|
||||
|
||||
import {createGrid, ModuleRegistry} from "@ag-grid-community/core";
|
||||
|
||||
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/transaction/get.js";
|
||||
|
||||
import AmountEditor from "../../support/ag-grid/AmountEditor.js";
|
||||
|
||||
import TransactionDataSource from "../../support/ag-grid/TransactionDataSource.js";
|
||||
import {InfiniteRowModelModule} from '@ag-grid-community/infinite-row-model';
|
||||
import DateTimeEditor from "../../support/ag-grid/DateTimeEditor.js";
|
||||
|
||||
const ds = new TransactionDataSource();
|
||||
|
||||
// set type from URL
|
||||
const urlParts = window.location.href.split('/');
|
||||
const type = urlParts[urlParts.length - 1];
|
||||
ds.setType(type);
|
||||
|
||||
let dataTable;
|
||||
const editableFields = ['description', 'amount', 'date'];
|
||||
|
||||
const onCellEditRequestMethod = (event) => {
|
||||
console.log('onCellEditRequestMethod');
|
||||
const data = event.data;
|
||||
const field = event.colDef.field;
|
||||
let newValue = event.newValue;
|
||||
if (!editableFields.includes(field)) {
|
||||
console.log('Field ' + field + ' is not editable.');
|
||||
return;
|
||||
}
|
||||
|
||||
// this needs to be better
|
||||
if ('amount' === field) {
|
||||
newValue = event.newValue.amount;
|
||||
console.log('New value is now' + newValue);
|
||||
}
|
||||
|
||||
console.log('New value for field "' + field + '" in transaction journal #' + data.transaction_journal_id + ' of group #' + data.id + ' is "' + newValue + '"');
|
||||
data[field] = newValue;
|
||||
let rowNode = dataTable.getRowNode(String(event.rowIndex));
|
||||
rowNode.updateData(data);
|
||||
|
||||
// then push update to Firefly III over API:
|
||||
let submission = {
|
||||
transactions: [
|
||||
{
|
||||
transaction_journal_id: data.transaction_journal_id,
|
||||
}
|
||||
]
|
||||
};
|
||||
submission.transactions[0][field] = newValue;
|
||||
|
||||
let putter = new Put();
|
||||
putter.put(submission, {id: data.id});
|
||||
|
||||
|
||||
};
|
||||
|
||||
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 },
|
||||
// { description: "Ford", model: "F-Series", price: 33850, electric: false },
|
||||
// { description: "Toyota", model: "Corolla", price: 29600, electric: false },
|
||||
// ],
|
||||
// Column Definitions: Defines & controls grid columns.
|
||||
columnDefs: [
|
||||
{
|
||||
field: "icon",
|
||||
editable: false,
|
||||
headerName: '',
|
||||
sortable: false,
|
||||
width: 40,
|
||||
cellRenderer: function (params) {
|
||||
if (params.getValue()) {
|
||||
return '<a href="./transactions/show/' + parseInt(params.value.id) + '"><em class="' + params.value.classes + '"></em></a>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: "description",
|
||||
cellDataType: 'text',
|
||||
editable: true,
|
||||
// cellRenderer: function (params) {
|
||||
// if (params.getValue()) {
|
||||
// return '<a href="#">' + params.getValue() + '</a>';
|
||||
// }
|
||||
// return '';
|
||||
// }
|
||||
|
||||
},
|
||||
{
|
||||
field: "amount",
|
||||
editable: function (params) {
|
||||
// only when NO foreign amount.
|
||||
return null === params.data.amount.foreign_amount && null === params.data.amount.foreign_currency_code;
|
||||
},
|
||||
cellEditor: AmountEditor,
|
||||
cellRenderer(params) {
|
||||
if (params.getValue()) {
|
||||
let returnString = '';
|
||||
let amount = parseFloat(params.getValue().amount);
|
||||
let obj = params.getValue();
|
||||
let stringClass = 'text-danger';
|
||||
if (obj.type === 'withdrawal') {
|
||||
amount = amount * -1;
|
||||
}
|
||||
if (obj.type === 'deposit') {
|
||||
stringClass = 'text-success';
|
||||
}
|
||||
if (obj.type === 'transfer') {
|
||||
stringClass = 'text-info';
|
||||
}
|
||||
returnString += '<span class="' + stringClass + '">' + formatMoney(amount, params.getValue().currency_code) + '</span>';
|
||||
|
||||
// foreign amount:
|
||||
if (obj.foreign_amount) {
|
||||
let foreignAmount = parseFloat(params.getValue().foreign_amount);
|
||||
if (obj.type === 'withdrawal') {
|
||||
foreignAmount = foreignAmount * -1;
|
||||
}
|
||||
returnString += ' (<span class="' + stringClass + '">' + formatMoney(foreignAmount, obj.foreign_currency_code) + '</span>)';
|
||||
}
|
||||
return returnString;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
},
|
||||
{
|
||||
field: "date",
|
||||
editable: true,
|
||||
cellDataType: 'date',
|
||||
cellEditor: DateTimeEditor,
|
||||
cellEditorPopup: true,
|
||||
cellEditorPopupPosition: 'under',
|
||||
cellRenderer(params) {
|
||||
if (params.getValue()) {
|
||||
return format(params.getValue(), i18next.t('config.date_time_fns_short'));
|
||||
}
|
||||
return '';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: "from",
|
||||
cellDataType: 'text',
|
||||
cellRenderer: function (params) {
|
||||
if (params.getValue()) {
|
||||
let obj = params.getValue();
|
||||
return '<a href="./accounts/show/' + obj.id + '">' + obj.name + '</a>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: "to",
|
||||
cellDataType: 'text',
|
||||
cellRenderer: function (params) {
|
||||
if (params.getValue()) {
|
||||
let obj = params.getValue();
|
||||
return '<a href="./accounts/show/' + obj.id + '">' + obj.name + '</a>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: "category",
|
||||
cellDataType: 'text',
|
||||
cellRenderer: function (params) {
|
||||
if (params.getValue()) {
|
||||
let obj = params.getValue();
|
||||
if (null !== obj.id) {
|
||||
return '<a href="./categories/show/' + obj.id + '">' + obj.name + '</a>';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: "budget",
|
||||
cellDataType: 'text',
|
||||
cellRenderer: function (params) {
|
||||
if (params.getValue()) {
|
||||
let obj = params.getValue();
|
||||
if (null !== obj.id) {
|
||||
return '<a href="./budgets/show/' + obj.id + '">' + obj.name + '</a>';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
ModuleRegistry.registerModules([InfiniteRowModelModule]);
|
||||
let index = function () {
|
||||
return {
|
||||
// notifications
|
||||
@@ -276,36 +71,56 @@ let index = function () {
|
||||
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')
|
||||
// TODO need date range.
|
||||
// TODO handle page number
|
||||
//this.getTransactions(this.page);
|
||||
this.getTransactions(this.page);``
|
||||
|
||||
// Your Javascript code to create the grid
|
||||
dataTable = createGrid(document.querySelector('#grid'), gridOptions);
|
||||
// dataTable = createGrid(document.querySelector('#grid'), gridOptions);
|
||||
|
||||
|
||||
},
|
||||
// getTransactions(page) {
|
||||
// const urlParts = window.location.href.split('/');
|
||||
// const type = urlParts[urlParts.length - 1];
|
||||
// let getter = new Get();
|
||||
//
|
||||
// getter.list({page: page, type: type}).then(response => {
|
||||
// this.parseTransactions(response.data.data)
|
||||
//
|
||||
// // set meta data
|
||||
// this.totalPages = response.data.meta.pagination.total_pages;
|
||||
// this.perPage = response.data.meta.pagination.per_page;
|
||||
// this.page = response.data.meta.pagination.current_page;
|
||||
// }).catch(error => {
|
||||
// // to do this is auto generated
|
||||
// this.notifications.wait.show = false;
|
||||
// this.notifications.error.show = true;
|
||||
// this.notifications.error.text = error.response.data.message;
|
||||
// });
|
||||
// },
|
||||
getTransactions(page) {
|
||||
this.notifications.wait.show = true;
|
||||
this.notifications.wait.text = i18next.t('firefly.wait_loading_data')
|
||||
this.transactions = [];
|
||||
const urlParts = window.location.href.split('/');
|
||||
const type = urlParts[urlParts.length - 1];
|
||||
let getter = new Get();
|
||||
|
||||
getter.list({page: page, type: type}).then(response => {
|
||||
this.parseTransactions(response.data.data)
|
||||
|
||||
// set meta data
|
||||
this.totalPages = response.data.meta.pagination.total_pages;
|
||||
this.perPage = response.data.meta.pagination.per_page;
|
||||
this.page = response.data.meta.pagination.current_page;
|
||||
}).catch(error => {
|
||||
// to do this is auto generated
|
||||
this.notifications.wait.show = false;
|
||||
this.notifications.error.show = true;
|
||||
this.notifications.error.text = error.response.data.message;
|
||||
});
|
||||
},
|
||||
previousPage() {
|
||||
if(this.page > 1) {
|
||||
this.page--;
|
||||
}
|
||||
this.getTransactions(this.page);
|
||||
},
|
||||
nextPage() {
|
||||
if(this.page < this.totalPages) {
|
||||
this.page++;
|
||||
}
|
||||
this.getTransactions(this.page);
|
||||
},
|
||||
gotoPage(i) {
|
||||
if(i > 0 && i <= this.totalPages) {
|
||||
this.page = i;
|
||||
}
|
||||
this.getTransactions(this.page);
|
||||
},
|
||||
|
||||
parseTransactions(data) {
|
||||
// no parse, just save
|
||||
for (let i in data) {
|
||||
@@ -321,7 +136,7 @@ let index = function () {
|
||||
|
||||
|
||||
transaction.split = isSplit;
|
||||
tranaction.icon = 'fa fa-solid fa-arrow-left';
|
||||
transaction.icon = 'fa fa-solid fa-arrow-left';
|
||||
transaction.firstSplit = firstSplit;
|
||||
transaction.group_title = current.attributes.group_title;
|
||||
transaction.id = current.id;
|
||||
|
||||
@@ -22,31 +22,27 @@ import Get from "../api/v1/preferences/index.js";
|
||||
import Post from "../api/v1/preferences/post.js";
|
||||
|
||||
export function getVariable(name, defaultValue = null) {
|
||||
|
||||
const validCache = window.store.get('cacheValid');
|
||||
// currently unused, window.X can be used by the blade template
|
||||
// to make things available quicker than if the store has to grab it through the API.
|
||||
// then again, it's not that slow.
|
||||
if (validCache && window.hasOwnProperty(name)) {
|
||||
// console.log('Get from window');
|
||||
return Promise.resolve(window[name]);
|
||||
}
|
||||
// load from store2, if it's present.
|
||||
const fromStore = window.store.get(name);
|
||||
if (validCache && typeof fromStore !== 'undefined') {
|
||||
// console.log('Get "' + name + '" from store');
|
||||
return Promise.resolve(fromStore);
|
||||
}
|
||||
let getter = (new Get);
|
||||
|
||||
return getter.getByName(name).then((response) => {
|
||||
// console.log('Get "' + name + '" from API');
|
||||
return Promise.resolve(parseResponse(name, response));
|
||||
}).catch(() => {
|
||||
}).catch((error) => {
|
||||
// preference does not exist (yet).
|
||||
// POST it and then return it anyway.
|
||||
let poster = (new Post);
|
||||
poster.post(name, defaultValue).then((response) => {
|
||||
|
||||
return poster.post(name, defaultValue).then((response) => {
|
||||
return Promise.resolve(parseResponse(name, response));
|
||||
});
|
||||
});
|
||||
@@ -55,7 +51,6 @@ export function getVariable(name, defaultValue = null) {
|
||||
export function parseResponse(name, response) {
|
||||
let value = response.data.data.attributes.data;
|
||||
window.store.set(name, value);
|
||||
// console.log('Store "' + name + '" in localStorage');
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
34
resources/assets/v2/src/support/cleanup-cache.js
Normal file
34
resources/assets/v2/src/support/cleanup-cache.js
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* load-translations.js
|
||||
* Copyright (c) 2023 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 {format} from "date-fns";
|
||||
import store from "store";
|
||||
|
||||
function cleanupCache() {
|
||||
const localValue = store.get('lastActivity');
|
||||
|
||||
store.each(function(value, key) {
|
||||
if(key.startsWith('dcx') && !key.includes(localValue)) {
|
||||
store.remove(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export {cleanupCache};
|
||||
@@ -23,7 +23,7 @@ import store from "store";
|
||||
|
||||
function getCacheKey(string, start, end) {
|
||||
const localValue = store.get('lastActivity');
|
||||
const cacheKey = format(start, 'y-MM-dd') + '_' + format(end, 'y-MM-dd') + '_' + string + localValue;
|
||||
const cacheKey = 'dcx' + format(start, 'yMMdd')+ format(end, 'yMMdd') + string + localValue;
|
||||
console.log('getCacheKey: ' + cacheKey);
|
||||
return String(cacheKey);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* show-internals-button.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/.
|
||||
*/
|
||||
|
||||
|
||||
export function showInternalsButton() {
|
||||
console.log('showInternalsButton');
|
||||
document.querySelector('.toggle-page-internals').classList.remove('d-none')
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* show-settings-button.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/.
|
||||
*/
|
||||
|
||||
|
||||
export function showWizardButton() {
|
||||
document.querySelector('.toggle-page-wizard').classList.remove('d-none')
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
import {defineConfig} from 'vite';
|
||||
import laravel from 'laravel-vite-plugin';
|
||||
import manifestSRI from 'vite-plugin-manifest-sri';
|
||||
import * as fs from "fs";
|
||||
|
||||
const host = '127.0.0.1';
|
||||
|
||||
@@ -72,6 +73,16 @@ export default defineConfig({
|
||||
|
||||
server: {
|
||||
usePolling: true,
|
||||
watch: {
|
||||
usePolling: true,
|
||||
},
|
||||
host: '10.0.0.15',
|
||||
// hmr: {
|
||||
// protocol: 'wss',
|
||||
// },
|
||||
// https: {
|
||||
// key: fs.readFileSync(`/sites/vm/tls-certificates/wildcard.sd.internal.key`),
|
||||
// cert: fs.readFileSync(`/sites/vm/tls-certificates/wildcard.sd.internal.crt`),
|
||||
// },
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Изход',
|
||||
'logout_other_sessions' => 'Изход от всички други сесии',
|
||||
'toggleNavigation' => 'Превключване на навигацията',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Търсене...',
|
||||
'version' => 'Версия',
|
||||
'dashboard' => 'Основно табло',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Tanca la sessió',
|
||||
'logout_other_sessions' => 'Tanca les altres sessions',
|
||||
'toggleNavigation' => 'Alternar navegació',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Cerca...',
|
||||
'version' => 'Versió',
|
||||
'dashboard' => 'Panell de control',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Odhlásit se',
|
||||
'logout_other_sessions' => 'Logout all other sessions',
|
||||
'toggleNavigation' => 'Vyp/zap. navigaci',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Hledat…',
|
||||
'version' => 'Verze',
|
||||
'dashboard' => 'Přehled',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Logout',
|
||||
'logout_other_sessions' => 'Logout all other sessions',
|
||||
'toggleNavigation' => 'Toggle navigation',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Search...',
|
||||
'version' => 'Version',
|
||||
'dashboard' => 'Dashboard',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Abmelden',
|
||||
'logout_other_sessions' => 'Alle anderen Sitzungen abmelden',
|
||||
'toggleNavigation' => 'Navigation umschalten',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Suchen...',
|
||||
'version' => 'Version',
|
||||
'dashboard' => 'Übersicht',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Αποσύνδεση',
|
||||
'logout_other_sessions' => 'Αποσυνδέσετε όλες τις άλλες συνεδρίες',
|
||||
'toggleNavigation' => 'Εναλλαγή περιήγησης',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Αναζήτηση...',
|
||||
'version' => 'Έκδοση',
|
||||
'dashboard' => 'Επισκόπηση',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Logout',
|
||||
'logout_other_sessions' => 'Logout all other sessions',
|
||||
'toggleNavigation' => 'Toggle navigation',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Search...',
|
||||
'version' => 'Version',
|
||||
'dashboard' => 'Dashboard',
|
||||
|
||||
@@ -2090,6 +2090,7 @@ return [
|
||||
'logout' => 'Logout',
|
||||
'logout_other_sessions' => 'Logout all other sessions',
|
||||
'toggleNavigation' => 'Toggle navigation',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Search...',
|
||||
'version' => 'Version',
|
||||
'dashboard' => 'Dashboard',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Cerrar sesión',
|
||||
'logout_other_sessions' => 'Desconectar todas las demás sesiones',
|
||||
'toggleNavigation' => 'Activar navegación',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Buscar...',
|
||||
'version' => 'Versión',
|
||||
'dashboard' => 'Panel de control',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Kirjaudu ulos',
|
||||
'logout_other_sessions' => 'Kirjaudu ulos kaikista muista istunnoista',
|
||||
'toggleNavigation' => 'Vaihda navigointia',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Hae ...',
|
||||
'version' => 'Versio',
|
||||
'dashboard' => 'Etusivu',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Se déconnecter',
|
||||
'logout_other_sessions' => 'Déconnecter toutes les autres sessions',
|
||||
'toggleNavigation' => 'Activer navigation',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Rechercher...',
|
||||
'version' => 'Version',
|
||||
'dashboard' => 'Tableau de bord',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Kijelentkezés',
|
||||
'logout_other_sessions' => 'Minden más munkamenet kijelentkeztetése',
|
||||
'toggleNavigation' => 'Navigációs mód átkapcsolása',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Keresés...',
|
||||
'version' => 'Verzió',
|
||||
'dashboard' => 'Műszerfal',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Keluar',
|
||||
'logout_other_sessions' => 'Logout all other sessions',
|
||||
'toggleNavigation' => 'Toggle navigation',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Pencarian...',
|
||||
'version' => 'Versi',
|
||||
'dashboard' => 'Dasbor',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Esci',
|
||||
'logout_other_sessions' => 'Esci da tutte le altre sessioni',
|
||||
'toggleNavigation' => 'Attiva / disattiva la navigazione',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Cerca...',
|
||||
'version' => 'Versione',
|
||||
'dashboard' => 'Cruscotto',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'ログアウト',
|
||||
'logout_other_sessions' => 'すべてのセッションからログアウト',
|
||||
'toggleNavigation' => 'ナビゲーションを切り替え',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => '検索...',
|
||||
'version' => 'バージョン',
|
||||
'dashboard' => 'ダッシュボード',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => '로그아웃',
|
||||
'logout_other_sessions' => '다른 모든 세션 로그아웃',
|
||||
'toggleNavigation' => '토글 내비게이션',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => '검색...',
|
||||
'version' => '버전',
|
||||
'dashboard' => '대시보드',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Logg ut',
|
||||
'logout_other_sessions' => 'Logg ut alle andre økter',
|
||||
'toggleNavigation' => 'Vis/Skjul navigation',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Søk...',
|
||||
'version' => 'Versjon',
|
||||
'dashboard' => 'Startskjerm',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Uitloggen',
|
||||
'logout_other_sessions' => 'Alle andere sessies afmelden',
|
||||
'toggleNavigation' => 'Navigatie aan of uit',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Zoeken...',
|
||||
'version' => 'Versie',
|
||||
'dashboard' => 'Dashboard',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Logg ut',
|
||||
'logout_other_sessions' => 'Logg ut alle andre økter',
|
||||
'toggleNavigation' => 'Vis/Skjul navigation',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Søk...',
|
||||
'version' => 'Versjon',
|
||||
'dashboard' => 'Startskjerm',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Wyloguj',
|
||||
'logout_other_sessions' => 'Wyloguj z pozostałych sesji',
|
||||
'toggleNavigation' => 'Przełącz nawigację',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Szukaj...',
|
||||
'version' => 'Wersja',
|
||||
'dashboard' => 'Kokpit',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Desconectar',
|
||||
'logout_other_sessions' => 'Sair de todas as outras sessões',
|
||||
'toggleNavigation' => 'Alternar navegação',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Pesquisar...',
|
||||
'version' => 'Versão',
|
||||
'dashboard' => 'Painel de Controle',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Terminar Sessão',
|
||||
'logout_other_sessions' => 'Terminar todas as outras sessões',
|
||||
'toggleNavigation' => 'Mostrar/esconder navegação',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Pesquisar...',
|
||||
'version' => 'Versão',
|
||||
'dashboard' => 'Painel de controlo',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Ieșire',
|
||||
'logout_other_sessions' => 'Deconectează toate celelalte sesiuni',
|
||||
'toggleNavigation' => 'Navigare',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Cautare...',
|
||||
'version' => 'Versiunea',
|
||||
'dashboard' => 'Panou de control',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Выход',
|
||||
'logout_other_sessions' => 'Завершить все другие сессии',
|
||||
'toggleNavigation' => 'Переключить навигацию',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Поиск...',
|
||||
'version' => 'Версия',
|
||||
'dashboard' => 'Сводка',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Odhlásiť sa',
|
||||
'logout_other_sessions' => 'Odhlásiť všetky ostatné sedenia',
|
||||
'toggleNavigation' => 'Vyp/zap. navigáciu',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Vyhľadať...',
|
||||
'version' => 'Verzia',
|
||||
'dashboard' => 'Prehľad',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Odjava',
|
||||
'logout_other_sessions' => 'Odjavi vse druge seje',
|
||||
'toggleNavigation' => 'Preklopi navigacijo',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Iskanje...',
|
||||
'version' => 'Različica',
|
||||
'dashboard' => 'Nadzorna plošča',
|
||||
|
||||
@@ -2145,6 +2145,7 @@ return [
|
||||
'logout' => 'Logga ut',
|
||||
'logout_other_sessions' => 'Logga ut alla andra sessioner',
|
||||
'toggleNavigation' => 'Växla navigering',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Sök...',
|
||||
'version' => 'Version',
|
||||
'dashboard' => 'Kontrollpanel',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Logout',
|
||||
'logout_other_sessions' => 'Logout all other sessions',
|
||||
'toggleNavigation' => 'Toggle navigation',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Search...',
|
||||
'version' => 'Version',
|
||||
'dashboard' => 'Dashboard',
|
||||
|
||||
@@ -2145,6 +2145,7 @@ return [
|
||||
'logout' => 'Çıkış Yap',
|
||||
'logout_other_sessions' => 'Logout all other sessions',
|
||||
'toggleNavigation' => 'Navigasyonu aç / kapat',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Aranıyor...',
|
||||
'version' => 'Versiyon',
|
||||
'dashboard' => 'Gösterge paneli',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Вихід із системи',
|
||||
'logout_other_sessions' => 'Вийти з усіх інших сеансів',
|
||||
'toggleNavigation' => 'Переключити навігацію',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Пошук...',
|
||||
'version' => 'Версія',
|
||||
'dashboard' => 'Головна панель',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => 'Đăng xuất',
|
||||
'logout_other_sessions' => 'Logout all other sessions',
|
||||
'toggleNavigation' => 'Chuyển đổi đổigle navigation',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => 'Tìm kiếm...',
|
||||
'version' => 'Phiên bản',
|
||||
'dashboard' => 'Bảng điều khiển',
|
||||
|
||||
@@ -2145,6 +2145,7 @@ return [
|
||||
'logout' => '退出登录',
|
||||
'logout_other_sessions' => '退出所有其他已登录设备',
|
||||
'toggleNavigation' => '切换导览',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => '搜索…',
|
||||
'version' => '版本',
|
||||
'dashboard' => '仪表盘',
|
||||
|
||||
@@ -2144,6 +2144,7 @@ return [
|
||||
'logout' => '登出',
|
||||
'logout_other_sessions' => 'Logout all other sessions',
|
||||
'toggleNavigation' => '切換導覽',
|
||||
'toggle_dropdown' => 'Toggle dropdown',
|
||||
'searchPlaceholder' => '搜尋…',
|
||||
'version' => '版本',
|
||||
'dashboard' => '監控面板',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<table class="table table-condensed table-hover table-responsive table-sortable">
|
||||
<table class="table table-condensed table-hover table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
{% if showCategory or showBudget %}
|
||||
@@ -49,7 +49,7 @@
|
||||
<tbody>
|
||||
{% for group in groups %}
|
||||
{% if group.count > 1 %}
|
||||
<tr style="border-top:1px #aaa solid;" class="unsortable">
|
||||
<tr style="border-top:1px #aaa solid;">
|
||||
<td colspan="2" style="border-top:1px #aaa solid;">
|
||||
<small><strong>
|
||||
<a href="{{ route('transactions.show', [group.id]) }}"
|
||||
@@ -104,29 +104,29 @@
|
||||
data-id="{{ group.id }}">
|
||||
<td style=" {{ style|raw }}" class="hidden-xs">
|
||||
{% if transaction.transaction_type_type == 'Withdrawal' %}
|
||||
<span class="object-handle fa fa-long-arrow-left fa-fw"
|
||||
<span class="fa fa-long-arrow-left fa-fw"
|
||||
title="{{ trans('firefly.Withdrawal') }}"></span>
|
||||
{% endif %}
|
||||
|
||||
{% if transaction.transaction_type_type == 'Deposit' %}
|
||||
<span class="object-handle fa fa-long-arrow-right fa-fw"
|
||||
<span class="fa fa-long-arrow-right fa-fw"
|
||||
title="{{ trans('firefly.Deposit') }}"></span>
|
||||
{% endif %}
|
||||
|
||||
{% if transaction.transaction_type_type == 'Transfer' %}
|
||||
<span class="object-handle fa fa-exchange fa-fw" title="{{ trans('firefly.Transfer') }}"></span>
|
||||
<span class="fa fa-exchange fa-fw" title="{{ trans('firefly.Transfer') }}"></span>
|
||||
{% endif %}
|
||||
|
||||
{% if transaction.transaction_type_type == 'Reconciliation' %}
|
||||
<span class="object-handle fa-fw fa fa-calculator"
|
||||
<span class="fa-fw fa fa-calculator"
|
||||
title="{{ trans('firefly.reconciliation_transaction') }}"></span>
|
||||
{% endif %}
|
||||
{% if transaction.transaction_type_type == 'Opening balance' %}
|
||||
<span class="object-handle fa-fw fa fa-star-o"
|
||||
<span class="fa-fw fa fa-star-o"
|
||||
title="{{ trans('firefly.Opening balance') }}"></span>
|
||||
{% endif %}
|
||||
{% if transaction.transaction_type_type == 'Liability credit' %}
|
||||
<span class="object-handle fa-fw fa fa-star-o"
|
||||
<span class="fa-fw fa fa-star-o"
|
||||
title="{{ trans('firefly.Liability credit') }}"></span>
|
||||
{% endif %}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<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>
|
||||
<h3 class="card-title">Net worth</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
some chart
|
||||
@@ -17,7 +17,7 @@
|
||||
<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>
|
||||
<h3 class="card-title">In + out this period</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
Same
|
||||
@@ -27,7 +27,7 @@
|
||||
<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>
|
||||
<h3 class="card-title">Something else</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
Same
|
||||
@@ -56,42 +56,56 @@
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>
|
||||
<td x-show="tableColumns.drag_and_drop.visible && tableColumns.drag_and_drop.enabled"> </td>
|
||||
<td x-show="tableColumns.active.visible && tableColumns.active.enabled">
|
||||
<a href="#" x-on:click.prevent="sort('active')">Active?</a>
|
||||
<em x-show="sortingColumn === 'active' && sortDirection === 'asc'" class="fa-solid fa-arrow-down-wide-short"></em>
|
||||
<em x-show="sortingColumn === 'active' && sortDirection === 'desc'" class="fa-solid fa-arrow-up-wide-short"></em>
|
||||
</td>
|
||||
<td>
|
||||
<td x-show="tableColumns.name.visible && tableColumns.name.enabled">
|
||||
<a href="#" x-on:click.prevent="sort('name')">Name</a>
|
||||
<em x-show="sortingColumn === 'name' && sortDirection === 'asc'" class="fa-solid fa-arrow-down-z-a"></em>
|
||||
<em x-show="sortingColumn === 'name' && sortDirection === 'desc'" class="fa-solid fa-arrow-up-z-a"></em>
|
||||
</td>
|
||||
<td>Type</td>
|
||||
<td>
|
||||
<td x-show="tableColumns.type.visible && tableColumns.type.enabled">Type</td>
|
||||
<td x-show="tableColumns.liability_type.visible && tableColumns.liability_type.enabled">Liability type</td>
|
||||
<td x-show="tableColumns.liability_direction.visible && tableColumns.liability_direction.enabled">Liability direction</td>
|
||||
<td x-show="tableColumns.liability_interest.visible && tableColumns.liability_interest.enabled">Liability interest</td>
|
||||
<td x-show="tableColumns.number.visible && tableColumns.number.enabled">
|
||||
<a href="#" x-on:click.prevent="sort('iban')">Account number</a>
|
||||
<em x-show="sortingColumn === 'iban' && sortDirection === 'asc'" class="fa-solid fa-arrow-down-z-a"></em>
|
||||
<em x-show="sortingColumn === 'iban' && sortDirection === 'desc'" class="fa-solid fa-arrow-up-z-a"></em>
|
||||
</td>
|
||||
<td>
|
||||
<td x-show="tableColumns.current_balance.visible && tableColumns.current_balance.enabled">
|
||||
<a href="#" x-on:click.prevent="sort('balance')">Current balance</a>
|
||||
<em x-show="sortingColumn === 'balance' && sortDirection === 'asc'" class="fa-solid fa-arrow-down-wide-short"></em>
|
||||
<em x-show="sortingColumn === 'balance' && sortDirection === 'desc'" class="fa-solid fa-arrow-up-wide-short"></em>
|
||||
</td>
|
||||
<td>
|
||||
<td x-show="tableColumns.amount_due.visible && tableColumns.amount_due.enabled">
|
||||
<a href="#" x-on:click.prevent="sort('amount_due')">Current balance</a>
|
||||
<em x-show="sortingColumn === 'amount_due' && sortDirection === 'asc'" class="fa-solid fa-arrow-down-wide-short"></em>
|
||||
<em x-show="sortingColumn === 'amount_due' && sortDirection === 'desc'" class="fa-solid fa-arrow-up-wide-short"></em>
|
||||
</td>
|
||||
<td x-show="tableColumns.last_activity.visible && tableColumns.last_activity.enabled">
|
||||
<a href="#" x-on:click.prevent="sort('last_activity')">Last activity</a>
|
||||
<em x-show="sortingColumn === 'last_activity' && sortDirection === 'asc'" class="fa-solid fa-arrow-down-wide-short"></em>
|
||||
<em x-show="sortingColumn === 'last_activity' && sortDirection === 'desc'" class="fa-solid fa-arrow-up-wide-short"></em>
|
||||
</td>
|
||||
<td>Balance difference</td>
|
||||
<td> </td>
|
||||
<td x-show="tableColumns.balance_difference.visible && tableColumns.balance_difference.enabled">
|
||||
<a href="#" x-on:click.prevent="sort('balance_difference')">Balance difference</a>
|
||||
<em x-show="sortingColumn === 'balance_difference' && sortDirection === 'asc'" class="fa-solid fa-arrow-down-wide-short"></em>
|
||||
<em x-show="sortingColumn === 'balance_difference' && sortDirection === 'desc'" class="fa-solid fa-arrow-up-wide-short"></em>
|
||||
</td>
|
||||
<td x-show="tableColumns.menu.visible && tableColumns.menu.enabled"> </td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template x-for="(account, index) in accounts" :key="index">
|
||||
<tr>
|
||||
<td>TODO</td>
|
||||
<td>
|
||||
<td x-show="tableColumns.drag_and_drop.visible && tableColumns.drag_and_drop.enabled">
|
||||
<em class="fa-solid fa-bars"></em>
|
||||
</td>
|
||||
<td x-show="tableColumns.active.visible && tableColumns.active.enabled">
|
||||
<template x-if="account.active">
|
||||
<em class="text-success fa-solid fa-check"></em>
|
||||
</template>
|
||||
@@ -99,7 +113,7 @@
|
||||
<em class="text-danger fa-solid fa-xmark"></em>
|
||||
</template>
|
||||
</td>
|
||||
<td>
|
||||
<td x-show="tableColumns.name.visible && tableColumns.name.enabled">
|
||||
<!-- render content using a function -->
|
||||
<span x-html="renderObjectValue('name', account)" x-show="!account.nameEditorVisible"></span>
|
||||
|
||||
@@ -119,7 +133,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<td x-show="tableColumns.type.visible && tableColumns.type.enabled">
|
||||
<template x-if="null === account.role || '' === account.role">
|
||||
<span><em>{{ __('firefly.no_account_role') }}</em></span>
|
||||
</template>
|
||||
@@ -127,7 +141,10 @@
|
||||
<span x-text="accountRole(account.role)"></span>"
|
||||
</template>
|
||||
</td>
|
||||
<td>
|
||||
<td x-show="tableColumns.liability_type.visible && tableColumns.liability_type.enabled"></td>
|
||||
<td x-show="tableColumns.liability_direction.visible && tableColumns.liability_direction.enabled"></td>
|
||||
<td x-show="tableColumns.liability_interest.visible && tableColumns.liability_interest.enabled"></td>
|
||||
<td x-show="tableColumns.number.visible && tableColumns.number.enabled">
|
||||
<!-- IBAN and no account nr -->
|
||||
<template x-if="'' === account.account_number && '' !== account.iban">
|
||||
<span x-text="account.iban"></span>
|
||||
@@ -144,14 +161,33 @@
|
||||
</span>
|
||||
</template>
|
||||
</td>
|
||||
<td>
|
||||
<td x-show="tableColumns.current_balance.visible && tableColumns.current_balance.enabled">
|
||||
<span x-text="formatMoney(account.current_balance, account.currency_code)"></span>
|
||||
</td>
|
||||
<td>
|
||||
<td x-show="tableColumns.amount_due.visible && tableColumns.amount_due.enabled">
|
||||
TODO
|
||||
</td>
|
||||
<td x-show="tableColumns.last_activity.visible && tableColumns.last_activity.enabled">
|
||||
<span x-text="account.last_activity"></span>
|
||||
</td>
|
||||
<td>TODO 2 </td>
|
||||
<td> </td>
|
||||
<td x-show="tableColumns.balance_difference.visible && tableColumns.balance_difference.enabled">
|
||||
<template x-if="null !== account.balance_difference">
|
||||
<span x-text="formatMoney(account.balance_difference, account.currency_code)"></span>
|
||||
</template>
|
||||
</td>
|
||||
<td x-show="tableColumns.menu.visible && tableColumns.menu.enabled">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<a :href="'./accounts/edit/' + account.id" class="btn btn-sm btn-light"><em class="fa-solid fa-pencil"></em></a>
|
||||
<button type="button" class="btn btn-light dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<span class="visually-hidden">{{ __('firefly.toggle_dropdown') }}</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" :href="'./accounts/show/' + account.id"><em class="fa-solid fa-eye"></em> {{ __('firefly.show') }}</a></li>
|
||||
<li><a class="dropdown-item" :href="'./accounts/reconcile/' + account.id"><em class="fa-solid fa-calculator"></em> {{ __('firefly.reconcile_selected') }}</a></li>
|
||||
<li><a class="dropdown-item" :href="'./accounts/delete/' + account.id"><em class="fa-solid fa-trash"></em> {{ __('firefly.delete') }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
@@ -167,6 +203,55 @@
|
||||
Nav
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Internal settings modal -->
|
||||
<div class="modal fade" id="internalsModal" tabindex="-1" aria-labelledby="internalsModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="internalsModalLabel">TODO Page settings</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<h2>Visible columns</h2>
|
||||
<template x-for="(column, key) in tableColumns" :key="key">
|
||||
<div class="form-check" x-show="column.visible">
|
||||
<input class="form-check-input" type="checkbox" x-model="column.enabled" @change="saveColumnSettings"> <span x-text="key"></span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
- Group accounts <br>
|
||||
- Columns to show<br>
|
||||
- Show / hide inactive accounts (dropdown: both, active inactive only)<br>
|
||||
- default sort field<br>
|
||||
- default sort direction<br>
|
||||
- show info boxes (once they contain info)<br>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="wizardModal" tabindex="-1" aria-labelledby="wizardModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="wizardModalLabel">TODO Would you like to know more?</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Need to learn what's on this page?<br>
|
||||
Take me to the help pages (opens in a new window or tab)
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal"><em class="fa-solid fa-hat-wizard"></em> Show me around</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"> <em class="fa-solid fa-right-from-bracket"></em> Take me to the documentation</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -111,12 +111,12 @@
|
||||
<div class="row">
|
||||
<div class="col text-end">
|
||||
<div class="btn-group">
|
||||
<button @click="addSplit()" class="btn btn-secondary"
|
||||
:disabled="formStates.isSubmitting">{{ __('firefly.add_another_split') }}</button>
|
||||
<template x-if="1 !== entries.length">
|
||||
<button :disabled="formStates.isSubmitting" class="btn btn-danger text-white"
|
||||
@click="removeSplit(index)">{{ __('firefly.transaction_remove_split') }}</button>
|
||||
</template>
|
||||
<button @click="addSplit()" class="btn btn-info"
|
||||
:disabled="formStates.isSubmitting">{{ __('firefly.add_another_split') }}</button>
|
||||
<button class="btn btn-success text-white" :disabled="formStates.isSubmitting"
|
||||
@click="submitTransaction()">{{ __('firefly.submit') }}</button>
|
||||
</div>
|
||||
|
||||
@@ -35,7 +35,10 @@
|
||||
|
||||
<!-- begin date range drop down -->
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link daterange-holder" data-bs-toggle="dropdown" href="#"></a>
|
||||
<a class="nav-link daterange-holder d-none d-sm-block" data-bs-toggle="dropdown" href="#"></a>
|
||||
<a class="nav-link daterange-icon d-block d-sm-none" data-bs-toggle="dropdown" href="#">
|
||||
<em class="fa-regular fa-calendar-days"></em>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-lg dropdown-menu-end">
|
||||
<a href="#" class="dropdown-item daterange-current" @click="changeDateRange">
|
||||
|
||||
|
||||
@@ -14,17 +14,17 @@
|
||||
</template>
|
||||
</h3>
|
||||
<template x-if="loading">
|
||||
<p>
|
||||
<p class="d-none d-xs-block">
|
||||
<em class="fa-solid fa-spinner fa-spin"></em>
|
||||
</p>
|
||||
</template>
|
||||
<template x-if="!loading && 0 !== balanceBox.amounts.length">
|
||||
<p>
|
||||
<p class="d-none d-sm-block">
|
||||
<a href="{{ route('reports.report.default', ['allAssetAccounts',$start->format('Ymd'),$end->format('Ymd')]) }}">{{ __('firefly.in_out_period') }}</a>
|
||||
</p>
|
||||
</template>
|
||||
<template x-if="!loading && 0 === balanceBox.amounts.length">
|
||||
<p>
|
||||
<p class="d-none d-sm-block">
|
||||
TODO (no money in or out)
|
||||
</p>
|
||||
</template>
|
||||
@@ -33,7 +33,7 @@
|
||||
<i class="fa-solid fa-scale-balanced"></i>
|
||||
</span>
|
||||
|
||||
<div class="small-box-footer hover-footer">
|
||||
<div class="small-box-footer hover-footer d-none d-xl-block">
|
||||
<template x-if="0 === balanceBox.subtitles.length">
|
||||
<span> </span>
|
||||
</template>
|
||||
@@ -66,21 +66,21 @@
|
||||
</h3>
|
||||
</template>
|
||||
<template x-if="loading">
|
||||
<p>
|
||||
<p class="d-none d-sm-block">
|
||||
<em class="fa-solid fa-spinner fa-spin"></em>
|
||||
</p>
|
||||
</template>
|
||||
<template x-if="!loading && billBox.unpaid.length > 0">
|
||||
<p><a href="{{ route('bills.index') }}">{{ __('firefly.bills_to_pay') }}</a></p>
|
||||
<p class="d-none d-sm-block"><a href="{{ route('bills.index') }}">{{ __('firefly.bills_to_pay') }}</a></p>
|
||||
</template>
|
||||
<template x-if="0 === billBox.unpaid.length && !loading">
|
||||
<p>TODO No subscriptions are waiting to be paid</p>
|
||||
<p class="d-none d-sm-block">TODO No subscriptions are waiting to be paid</p>
|
||||
</template>
|
||||
</div>
|
||||
<span class="small-box-icon">
|
||||
<em class="fa-regular fa-calendar"></em>
|
||||
</span>
|
||||
<span class="small-box-footer">
|
||||
<span class="small-box-footer d-none d-xl-block">
|
||||
<template x-if="0 === billBox.paid.length">
|
||||
<span> </span>
|
||||
</template>
|
||||
@@ -117,21 +117,21 @@
|
||||
</h3>
|
||||
|
||||
<template x-if="loading">
|
||||
<p>
|
||||
<p class="d-none d-sm-block">
|
||||
<em class="fa-solid fa-spinner fa-spin"></em>
|
||||
</p>
|
||||
</template>
|
||||
<template x-if="!loading && 0 !== leftBox.left.length">
|
||||
<p><a href="{{ route('budgets.index') }}">{{ __('firefly.left_to_spend') }}</a></p>
|
||||
<p class="d-none d-sm-block"><a href="{{ route('budgets.index') }}">{{ __('firefly.left_to_spend') }}</a></p>
|
||||
</template>
|
||||
<template x-if="!loading && 0 === leftBox.left.length">
|
||||
<p>TODO no money is budgeted in this period</p>
|
||||
<p class="d-none d-sm-block">TODO no money is budgeted in this period</p>
|
||||
</template>
|
||||
</div>
|
||||
<span class="small-box-icon">
|
||||
<em class="fa-solid fa-money-check-dollar"></em>
|
||||
</span>
|
||||
<span class="small-box-footer">
|
||||
<span class="small-box-footer d-none d-xl-block">
|
||||
<template x-if="0 !== leftBox.perDay.length">
|
||||
<span>{{ __('firefly.per_day') }}:</span>
|
||||
</template>
|
||||
@@ -163,12 +163,12 @@
|
||||
</h3>
|
||||
|
||||
<template x-if="loading">
|
||||
<p>
|
||||
<p class="d-none d-sm-block">
|
||||
<em class="fa-solid fa-spinner fa-spin"></em>
|
||||
</p>
|
||||
</template>
|
||||
<template x-if="!loading">
|
||||
<p>
|
||||
<p class="d-none d-sm-block">
|
||||
<a href="{{ route('reports.report.default', ['allAssetAccounts','currentYearStart','currentYearEnd']) }}">{{ __('firefly.net_worth') }}</a>
|
||||
</p>
|
||||
</template>
|
||||
@@ -176,7 +176,7 @@
|
||||
<span class="small-box-icon">
|
||||
<i class="fa-solid fa-chart-line"></i>
|
||||
</span>
|
||||
<span class="small-box-footer">
|
||||
<span class="small-box-footer d-none d-xl-block">
|
||||
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<input type="search"
|
||||
class="form-control ac-category"
|
||||
:id="'category_name_' + index"
|
||||
@keyup.enter="submitTransaction()"
|
||||
@keyup="keyUpFromCategory"
|
||||
x-model="transaction.category_name"
|
||||
:data-index="index"
|
||||
placeholder="{{ __('firefly.category') }}">
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
<footer class="app-footer">
|
||||
<!--begin::To the end-->
|
||||
<div class="float-end d-none d-sm-inline">
|
||||
<a href="{{ route('debug') }}">v{{ $FF_VERSION }}</a>
|
||||
<a href="{{ route('debug') }}">
|
||||
@if(str_starts_with($FF_VERSION, 'develop'))
|
||||
<span class="text-danger">{{ $FF_VERSION }}</span>
|
||||
@else
|
||||
v{{ $FF_VERSION }}
|
||||
@endif
|
||||
</a>
|
||||
</div>
|
||||
<!--end::To the end-->
|
||||
<!--begin::Copyright-->
|
||||
|
||||
@@ -2,7 +2,16 @@
|
||||
<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 toggle-page-internals d-none">
|
||||
<a class="nav-link" href="#" data-bs-toggle="modal" data-bs-target="#internalsModal">
|
||||
<em class="fa-solid fa-sliders"></em>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item toggle-page-wizard d-none">
|
||||
<a class="nav-link" href="#" data-bs-toggle="modal" data-bs-target="#wizardModal">
|
||||
<em class="fa-solid fa-hat-wizard"></em>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link" data-bs-toggle="dropdown" href="#">
|
||||
|
||||
@@ -18,11 +18,6 @@
|
||||
:optionalDateFields="$optionalDateFields"></x-transaction-split>
|
||||
</template>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col text-end">
|
||||
<button class="btn btn-success" :disabled="formStates.isSubmitting" @click="submitTransaction()">Update</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -40,60 +40,38 @@
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
Nav
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-xl-10 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">Transactions</h3>
|
||||
</div>
|
||||
<div class="col text-end">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div id="grid" class="ag-theme-alpine-auto-dark ag-theme-firefly-iii" style="height: 500px">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template x-if="!notifications.wait.show">
|
||||
<nav aria-label="TODO Page navigation">
|
||||
<ul class="pagination">
|
||||
<template x-if="page > 1">
|
||||
<li class="page-item"><a class="page-link" @click.prevent="previousPage">Previous</a></li>
|
||||
</template>
|
||||
<template x-for="i in totalPages">
|
||||
<li :class="{'page-item': true, 'active': i === page}">
|
||||
<a class="page-link" href="#" x-text="i" @click.prevent="gotoPage(i)"></a>
|
||||
</li>
|
||||
</template>
|
||||
<template x-if="page < totalPages">
|
||||
<li class="page-item"><a class="page-link" @click.prevent="nextPage" href="#">Next</a></li>
|
||||
</template>
|
||||
</ul>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3 class="card-title">Transactions</h3>
|
||||
</div>
|
||||
<div class="col text-end">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-secondary btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Options
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="#">
|
||||
<em class="text-danger fa-regular fa-circle-xmark"></em>
|
||||
Category</a></li>
|
||||
<li><a class="dropdown-item" href="#">
|
||||
<em class="text-success fa-regular fa-circle-check"></em>
|
||||
Budget</a></li>
|
||||
<li><a class="dropdown-item" href="#">Another action</a></li>
|
||||
<li><a class="dropdown-item" href="#">Something else here</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="#">Separated link</a></li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<h3 class="card-title">Transactions</h3>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
|
||||
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -145,7 +123,8 @@
|
||||
<template x-if="transaction.split && transaction.firstSplit">
|
||||
<span>I AM FIRST SPLIT</span>
|
||||
</template>
|
||||
<a :href="'./transactions/show/' + transaction.id" x-text="transaction.description"></a>
|
||||
<a :href="'./transactions/show/' + transaction.id"
|
||||
x-text="transaction.description"></a>
|
||||
</td>
|
||||
</template>
|
||||
<template x-if="tableColumns.source.enabled">
|
||||
@@ -164,16 +143,19 @@
|
||||
<td>
|
||||
|
||||
<template x-if="'withdrawal' === transaction.type">
|
||||
<span class="text-danger" x-text="formatMoney(transaction.amount*-1, transaction.currency_code)"></span>
|
||||
<span class="text-danger"
|
||||
x-text="formatMoney(transaction.amount*-1, transaction.currency_code)"></span>
|
||||
</template>
|
||||
|
||||
|
||||
<template x-if="'deposit' === transaction.type">
|
||||
<span class="text-success" x-text="formatMoney(transaction.amount, transaction.currency_code)"></span>
|
||||
<span class="text-success"
|
||||
x-text="formatMoney(transaction.amount, transaction.currency_code)"></span>
|
||||
</template>
|
||||
|
||||
<template x-if="'transfer' === transaction.type">
|
||||
<span class="text-info" x-text="formatMoney(transaction.amount, transaction.currency_code)"></span>
|
||||
<span class="text-info"
|
||||
x-text="formatMoney(transaction.amount, transaction.currency_code)"></span>
|
||||
</template>
|
||||
<template
|
||||
x-if="'transfer' !== transaction.type && 'deposit' !== transaction.type && 'withdrawal' !== transaction.type">
|
||||
|
||||
Reference in New Issue
Block a user