mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
commit
2500cc55e2
@ -25,10 +25,105 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Api\V2\Controllers\Autocomplete;
|
namespace FireflyIII\Api\V2\Controllers\Autocomplete;
|
||||||
|
|
||||||
use FireflyIII\Api\V2\Controllers\Controller;
|
use FireflyIII\Api\V2\Controllers\Controller;
|
||||||
|
use FireflyIII\Api\V2\Request\Autocomplete\AutocompleteRequest;
|
||||||
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
use FireflyIII\Models\Account;
|
||||||
|
use FireflyIII\Models\AccountType;
|
||||||
|
use FireflyIII\Repositories\Administration\Account\AccountRepositoryInterface as AdminAccountRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
|
use FireflyIII\Support\Http\Api\AccountFilter;
|
||||||
|
use FireflyIII\User;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use JsonException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class AccountController
|
* Class AccountController
|
||||||
*/
|
*/
|
||||||
class AccountController extends Controller
|
class AccountController extends Controller
|
||||||
{
|
{
|
||||||
|
use AccountFilter;
|
||||||
|
|
||||||
|
private array $balanceTypes;
|
||||||
|
private AdminAccountRepositoryInterface $adminRepository;
|
||||||
|
private AccountRepositoryInterface $repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AccountController constructor.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->middleware(
|
||||||
|
function ($request, $next) {
|
||||||
|
/** @var User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
$this->repository = app(AccountRepositoryInterface::class);
|
||||||
|
$this->adminRepository = app(AdminAccountRepositoryInterface::class);
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$this->balanceTypes = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE,];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Documentation for this endpoint:
|
||||||
|
* TODO endpoint is not documented.
|
||||||
|
*
|
||||||
|
* @param AutocompleteRequest $request
|
||||||
|
*
|
||||||
|
* @return JsonResponse
|
||||||
|
* @throws JsonException
|
||||||
|
* @throws FireflyException
|
||||||
|
* @throws FireflyException
|
||||||
|
*/
|
||||||
|
public function accounts(AutocompleteRequest $request): JsonResponse
|
||||||
|
{
|
||||||
|
$data = $request->getData();
|
||||||
|
$types = $data['types'];
|
||||||
|
$query = $data['query'];
|
||||||
|
$date = $data['date'] ?? today(config('app.timezone'));
|
||||||
|
$this->adminRepository->setAdministrationId($data['administration_id']);
|
||||||
|
|
||||||
|
$return = [];
|
||||||
|
$result = $this->adminRepository->searchAccount((string)$query, $types, $data['limit']);
|
||||||
|
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||||
|
|
||||||
|
/** @var Account $account */
|
||||||
|
foreach ($result as $account) {
|
||||||
|
$nameWithBalance = $account->name;
|
||||||
|
$currency = $this->repository->getAccountCurrency($account) ?? $defaultCurrency;
|
||||||
|
|
||||||
|
if (in_array($account->accountType->type, $this->balanceTypes, true)) {
|
||||||
|
$balance = app('steam')->balance($account, $date);
|
||||||
|
$nameWithBalance = sprintf('%s (%s)', $account->name, app('amount')->formatAnything($currency, $balance, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
$return[] = [
|
||||||
|
'id' => (string)$account->id,
|
||||||
|
'name' => $account->name,
|
||||||
|
'name_with_balance' => $nameWithBalance,
|
||||||
|
'type' => $account->accountType->type,
|
||||||
|
'currency_id' => $currency->id,
|
||||||
|
'currency_name' => $currency->name,
|
||||||
|
'currency_code' => $currency->code,
|
||||||
|
'currency_symbol' => $currency->symbol,
|
||||||
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// custom order.
|
||||||
|
$order = [AccountType::ASSET, AccountType::REVENUE, AccountType::EXPENSE];
|
||||||
|
usort(
|
||||||
|
$return,
|
||||||
|
function ($a, $b) use ($order) {
|
||||||
|
$pos_a = array_search($a['type'], $order, true);
|
||||||
|
$pos_b = array_search($b['type'], $order, true);
|
||||||
|
|
||||||
|
return $pos_a - $pos_b;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return response()->json($return);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ class Controller extends BaseController
|
|||||||
$page = 1;
|
$page = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
$integers = ['limit'];
|
$integers = ['limit', 'administration'];
|
||||||
$dates = ['start', 'end', 'date'];
|
$dates = ['start', 'end', 'date'];
|
||||||
|
|
||||||
if ($page < 1) {
|
if ($page < 1) {
|
||||||
|
98
app/Api/V2/Request/Autocomplete/AutocompleteRequest.php
Normal file
98
app/Api/V2/Request/Autocomplete/AutocompleteRequest.php
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* AutocompleteRequest.php
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Api\V2\Request\Autocomplete;
|
||||||
|
|
||||||
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
use FireflyIII\Models\AccountType;
|
||||||
|
use FireflyIII\Models\UserRole;
|
||||||
|
use FireflyIII\Support\Request\ChecksLogin;
|
||||||
|
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||||
|
use FireflyIII\User;
|
||||||
|
use FireflyIII\Validation\Administration\ValidatesAdministrationAccess;
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Validation\Validator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class AutocompleteRequest
|
||||||
|
*/
|
||||||
|
class AutocompleteRequest extends FormRequest
|
||||||
|
{
|
||||||
|
use ConvertsDataTypes;
|
||||||
|
use ChecksLogin;
|
||||||
|
use ValidatesAdministrationAccess;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
* @throws FireflyException
|
||||||
|
*/
|
||||||
|
public function getData(): array
|
||||||
|
{
|
||||||
|
$types = $this->convertString('types');
|
||||||
|
$array = [];
|
||||||
|
if ('' !== $types) {
|
||||||
|
$array = explode(',', $types);
|
||||||
|
}
|
||||||
|
$limit = $this->convertInteger('limit');
|
||||||
|
$limit = 0 === $limit ? 10 : $limit;
|
||||||
|
|
||||||
|
// remove 'initial balance' and another from allowed types. its internal
|
||||||
|
$array = array_diff($array, [AccountType::INITIAL_BALANCE, AccountType::RECONCILIATION]);
|
||||||
|
/** @var User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
return [
|
||||||
|
'types' => $array,
|
||||||
|
'query' => $this->convertString('query'),
|
||||||
|
'date' => $this->getCarbonDate('date'),
|
||||||
|
'limit' => $limit,
|
||||||
|
'administration_id' => (int)($this->get('administration_id', null) ?? $user->getAdministrationId()),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'limit' => 'min:0|max:1337',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure the validator instance with special rules for after the basic validation rules.
|
||||||
|
*
|
||||||
|
* @param Validator $validator
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function withValidator(Validator $validator): void
|
||||||
|
{
|
||||||
|
$validator->after(
|
||||||
|
function (Validator $validator) {
|
||||||
|
// validate if the account can access this administration
|
||||||
|
$this->validateAdministration($validator, [UserRole::CHANGE_TRANSACTIONS]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -63,4 +63,14 @@ class UserGroup extends Model
|
|||||||
{
|
{
|
||||||
return $this->hasMany(GroupMembership::class);
|
return $this->hasMany(GroupMembership::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link to accounts.
|
||||||
|
*
|
||||||
|
* @return HasMany
|
||||||
|
*/
|
||||||
|
public function accounts(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(Account::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@ namespace FireflyIII\Providers;
|
|||||||
|
|
||||||
use FireflyIII\Repositories\Account\AccountRepository;
|
use FireflyIII\Repositories\Account\AccountRepository;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\Administration\Account\AccountRepository as AdminAccountRepository;
|
||||||
|
use FireflyIII\Repositories\Administration\Account\AccountRepositoryInterface as AdminAccountRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Account\AccountTasker;
|
use FireflyIII\Repositories\Account\AccountTasker;
|
||||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||||
use FireflyIII\Repositories\Account\OperationsRepository;
|
use FireflyIII\Repositories\Account\OperationsRepository;
|
||||||
@ -73,6 +75,22 @@ class AccountServiceProvider extends ServiceProvider
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$this->app->bind(
|
||||||
|
AdminAccountRepositoryInterface::class,
|
||||||
|
function (Application $app) {
|
||||||
|
/** @var AdminAccountRepositoryInterface $repository */
|
||||||
|
$repository = app(AdminAccountRepository::class);
|
||||||
|
|
||||||
|
// phpstan thinks auth does not exist.
|
||||||
|
if ($app->auth->check()) { // @phpstan-ignore-line
|
||||||
|
$repository->setUser(auth()->user());
|
||||||
|
$repository->setAdministrationId((int) auth()->user()->user_group_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $repository;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
$this->app->bind(
|
$this->app->bind(
|
||||||
OperationsRepositoryInterface::class,
|
OperationsRepositoryInterface::class,
|
||||||
static function (Application $app) {
|
static function (Application $app) {
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* AccountRepository.php
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FireflyIII\Repositories\Administration\Account;
|
||||||
|
|
||||||
|
use FireflyIII\Support\Repositories\Administration\AdministrationTrait;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class AccountRepository
|
||||||
|
*/
|
||||||
|
class AccountRepository implements AccountRepositoryInterface
|
||||||
|
{
|
||||||
|
use AdministrationTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function searchAccount(string $query, array $types, int $limit): Collection
|
||||||
|
{
|
||||||
|
// search by group, not by user
|
||||||
|
$dbQuery = $this->userGroup->accounts()
|
||||||
|
->where('active', true)
|
||||||
|
->orderBy('accounts.order', 'ASC')
|
||||||
|
->orderBy('accounts.account_type_id', 'ASC')
|
||||||
|
->orderBy('accounts.name', 'ASC')
|
||||||
|
->with(['accountType']);
|
||||||
|
if ('' !== $query) {
|
||||||
|
// split query on spaces just in case:
|
||||||
|
$parts = explode(' ', $query);
|
||||||
|
foreach ($parts as $part) {
|
||||||
|
$search = sprintf('%%%s%%', $part);
|
||||||
|
$dbQuery->where('name', 'LIKE', $search);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (0 !== count($types)) {
|
||||||
|
$dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
|
||||||
|
$dbQuery->whereIn('account_types.type', $types);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $dbQuery->take($limit)->get(['accounts.*']);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* AccountRepositoryInterface.php
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FireflyIII\Repositories\Administration\Account;
|
||||||
|
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface AccountRepositoryInterface
|
||||||
|
*/
|
||||||
|
interface AccountRepositoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $query
|
||||||
|
* @param array $types
|
||||||
|
* @param int $limit
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function searchAccount(string $query, array $types, int $limit): Collection;
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* AdministrationTrait.php
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FireflyIII\Support\Repositories\Administration;
|
||||||
|
|
||||||
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
use FireflyIII\Models\GroupMembership;
|
||||||
|
use FireflyIII\Models\UserGroup;
|
||||||
|
use FireflyIII\User;
|
||||||
|
use Illuminate\Contracts\Auth\Authenticatable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trait AdministrationTrait
|
||||||
|
*/
|
||||||
|
trait AdministrationTrait
|
||||||
|
{
|
||||||
|
protected User $user;
|
||||||
|
protected ?int $administrationId = null;
|
||||||
|
protected ?UserGroup $userGroup = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getAdministrationId(): int
|
||||||
|
{
|
||||||
|
return $this->administrationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $administrationId
|
||||||
|
* @throws FireflyException
|
||||||
|
*/
|
||||||
|
public function setAdministrationId(int $administrationId): void
|
||||||
|
{
|
||||||
|
$this->administrationId = $administrationId;
|
||||||
|
$this->refreshAdministration();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function refreshAdministration(): void
|
||||||
|
{
|
||||||
|
if (null !== $this->administrationId) {
|
||||||
|
$memberships = GroupMembership::where('user_id', $this->user->id)
|
||||||
|
->where('user_group_id', $this->administrationId)
|
||||||
|
->count();
|
||||||
|
if (0 === $memberships) {
|
||||||
|
throw new FireflyException(sprintf('User #%d has no access to administration #%d', $this->user->id, $this->administrationId));
|
||||||
|
}
|
||||||
|
$this->userGroup = UserGroup::find($this->administrationId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new FireflyException(sprintf('Cannot validate administration for user #%d', $this->user->id));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function setUser(Authenticatable|User|null $user): void
|
||||||
|
{
|
||||||
|
if(null !== $user) {
|
||||||
|
$this->user = $user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
frontend/src/api/v2/autocomplete/accounts.js
vendored
Normal file
35
frontend/src/api/v2/autocomplete/accounts.js
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* get.js
|
||||||
|
* Copyright (c) 2022 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 {api} from "boot/axios";
|
||||||
|
|
||||||
|
export default class Accounts {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param types
|
||||||
|
* @returns {Promise<AxiosResponse<any>>}
|
||||||
|
*/
|
||||||
|
get(types, query) {
|
||||||
|
let url = 'api/v2/autocomplete/accounts';
|
||||||
|
return api.get(url, {params: {types: types, query: query, limit: 25}})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
2
frontend/src/boot/axios.js
vendored
2
frontend/src/boot/axios.js
vendored
@ -34,7 +34,7 @@ const cache = setupCache({
|
|||||||
// "export default () => {}" function below (which runs individually
|
// "export default () => {}" function below (which runs individually
|
||||||
// for each client)
|
// for each client)
|
||||||
|
|
||||||
const url = process.env.DEBUGGING ? 'https://firefly.sd.home' : '/';
|
const url = process.env.DEBUGGING ? 'https://firefly.sd.local' : '/';
|
||||||
const api = axios.create({baseURL: url, withCredentials: true, adapter: cache.adapter});
|
const api = axios.create({baseURL: url, withCredentials: true, adapter: cache.adapter});
|
||||||
|
|
||||||
export default boot(({app}) => {
|
export default boot(({app}) => {
|
||||||
|
316
frontend/src/components/transactions/Split.vue
Normal file
316
frontend/src/components/transactions/Split.vue
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
<!--
|
||||||
|
- Split.vue
|
||||||
|
- 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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="text-h6">Info for {{ $route.params.type }} {{ index }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col q-mb-xs q-pr-sm">
|
||||||
|
<q-card bordered flat>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label><strong>Main info</strong></q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-separator/>
|
||||||
|
<q-card-section>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col q-mb-md">
|
||||||
|
<TransactionDescription
|
||||||
|
:submission-error="submissionErrors.description"
|
||||||
|
:has-submission-error="hasSubmissionErrors.description"
|
||||||
|
:disabled-input="disabledInput"
|
||||||
|
:description="transaction.description"
|
||||||
|
@update:description="updateDescription"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4 q-mb-xs q-pr-xs">
|
||||||
|
<SourceAccount name="Test" :disabled-input="false" submission-error="" :has-submission-error="false" />
|
||||||
|
</div>
|
||||||
|
<div class="col-4 q-px-xs">
|
||||||
|
<q-input
|
||||||
|
v-model="transaction.amount"
|
||||||
|
:disable="disabledInput" dense
|
||||||
|
:error="hasSubmissionErrors.amount" :error-message="submissionErrors.amount"
|
||||||
|
:label="$t('firefly.amount')" bottom-slots clearable fill-mask="0"
|
||||||
|
hint="Expects #.##"
|
||||||
|
mask="#.##"
|
||||||
|
outlined reverse-fill-mask/>
|
||||||
|
</div>
|
||||||
|
<div class="col-4 q-pl-xs">
|
||||||
|
<q-input dense
|
||||||
|
v-model="transaction.destination"
|
||||||
|
:disable="disabledInput"
|
||||||
|
:error="hasSubmissionErrors.destination"
|
||||||
|
:error-message="submissionErrors.destination" :label="$t('firefly.destination_account')"
|
||||||
|
bottom-slots
|
||||||
|
clearable
|
||||||
|
outlined/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4 q-pl-xs">
|
||||||
|
Optional
|
||||||
|
</div>
|
||||||
|
<div class="col-4 q-pl-xs">
|
||||||
|
Foreign amount
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
<div class="col q-mb-xs q-pl-sm">
|
||||||
|
<q-card bordered flat>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label><strong>More meta info</strong></q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-separator/>
|
||||||
|
<q-card-section>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col q-mb-md">
|
||||||
|
<q-input
|
||||||
|
v-model="transaction.date" dense
|
||||||
|
:disable="disabledInput"
|
||||||
|
:error="hasSubmissionErrors.date" :error-message="submissionErrors.date"
|
||||||
|
:hint="$t('firefly.date')" bottom-slots outlined
|
||||||
|
type="date"/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<q-input v-model="transaction.time" :disable="disabledInput" :hint="$t('firefly.time')" bottom-slots
|
||||||
|
outlined dense
|
||||||
|
type="time"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col q-mb-md">
|
||||||
|
<q-input
|
||||||
|
v-model="transaction.category"
|
||||||
|
:disable="disabledInput" dense
|
||||||
|
:error="hasSubmissionErrors.category" :error-message="submissionErrors.category"
|
||||||
|
:label="$t('firefly.category')" bottom-slots
|
||||||
|
hint="category"
|
||||||
|
clearable outlined/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<q-select dense v-model="transaction.budget" clearable outlined hint="Budget"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col q-mb-md">
|
||||||
|
<q-select
|
||||||
|
v-model="transaction.bill"
|
||||||
|
:disable="disabledInput" dense
|
||||||
|
:error="hasSubmissionErrors.category" :error-message="submissionErrors.category"
|
||||||
|
:label="$t('firefly.bill')" bottom-slots
|
||||||
|
hint="bill"
|
||||||
|
clearable outlined/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<q-select
|
||||||
|
v-model="transaction.piggy"
|
||||||
|
:disable="disabledInput" dense
|
||||||
|
:error="hasSubmissionErrors.category" :error-message="submissionErrors.category"
|
||||||
|
:label="$t('firefly.piggy')" bottom-slots
|
||||||
|
hint="bill"
|
||||||
|
clearable outlined/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<q-input
|
||||||
|
v-model="transaction.tags"
|
||||||
|
:disable="disabledInput" dense
|
||||||
|
:error="hasSubmissionErrors.tags" :error-message="submissionErrors.category"
|
||||||
|
:label="$t('firefly.tags')" bottom-slots
|
||||||
|
hint="Tags"
|
||||||
|
clearable outlined/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col q-mb-xs q-pl-sm">
|
||||||
|
<q-card bordered flat>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label><strong>More meta info</strong></q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-separator/>
|
||||||
|
<q-card-section>
|
||||||
|
Extra opts
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
<div class="col q-mb-xs q-pl-sm">
|
||||||
|
<q-card bordered flat>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label><strong>Date info</strong></q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-separator/>
|
||||||
|
<q-card-section>
|
||||||
|
Date fields
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--
|
||||||
|
</div>
|
||||||
|
<div class="col-4 offset-4">
|
||||||
|
<q-input v-model="transaction.interest_date" filled type="date" hint="Interest date"/>
|
||||||
|
<q-input v-model="transaction.book_date" filled type="date" hint="Book date"/>
|
||||||
|
<q-input v-model="transaction.process_date" filled type="date" hint="Processing date"/>
|
||||||
|
<q-input v-model="transaction.due_date" filled type="date" hint="Due date"/>
|
||||||
|
<q-input v-model="transaction.payment_date" filled type="date" hint="Payment date"/>
|
||||||
|
<q-input v-model="transaction.invoice_date" filled type="date" hint="Invoice date"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
-->
|
||||||
|
<!--
|
||||||
|
<q-card bordered class="q-mt-md">
|
||||||
|
<q-card-section>
|
||||||
|
<div class="text-h6">Meta for {{ $route.params.type }}</div>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<q-select filled v-model="transaction.budget" :options="tempBudgets" label="Budget"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<q-input filled clearable v-model="transaction.category" :label="$t('firefly.category')" outlined/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<q-select filled v-model="transaction.subscription" :options="tempSubscriptions" label="Subscription"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
Tags
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
Bill
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
???
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
-->
|
||||||
|
<!--
|
||||||
|
<q-card bordered class="q-mt-md">
|
||||||
|
<q-card-section>
|
||||||
|
<div class="text-h6">Extr for {{ $route.params.type }}</div>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
Notes
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
attachments
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
Links
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
reference
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
url
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
location
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
-->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import TransactionDescription from "components/transactions/form/TransactionDescription.vue";
|
||||||
|
import SourceAccount from "components/transactions/form/SourceAccount.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Split",
|
||||||
|
components: {SourceAccount, TransactionDescription},
|
||||||
|
props: {
|
||||||
|
index: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
disabledInput: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
hasSubmissionErrors: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
submissionErrors: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
transaction: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateDescription(newVal) {
|
||||||
|
this.transaction.description = newVal;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
transaction: {
|
||||||
|
handler: function (val) {
|
||||||
|
const obj = {index: this.index, transaction: val}
|
||||||
|
this.$emit('update:transaction', obj);
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
167
frontend/src/components/transactions/form/SourceAccount.vue
Normal file
167
frontend/src/components/transactions/form/SourceAccount.vue
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
<!--
|
||||||
|
- SourceAccount.vue
|
||||||
|
- 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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<q-select
|
||||||
|
v-model="model"
|
||||||
|
use-input
|
||||||
|
:options="options"
|
||||||
|
@filter="filterFn"
|
||||||
|
dense
|
||||||
|
:loading="loading"
|
||||||
|
outlined
|
||||||
|
:disable="disabledInput"
|
||||||
|
:error="hasSubmissionError"
|
||||||
|
:label="$t('firefly.source_account')"
|
||||||
|
:error-message="submissionError"
|
||||||
|
bottom-slots
|
||||||
|
clearable
|
||||||
|
>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
input-debounce="0"
|
||||||
|
|
||||||
|
label="Lazy filter"
|
||||||
|
-->
|
||||||
|
<template v-slot:option="scope">
|
||||||
|
<q-item v-bind="scope.itemProps">
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>{{ scope.opt.label }}</q-item-label>
|
||||||
|
<q-item-label caption>{{ scope.opt.description }}</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:no-option>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section class="text-grey">
|
||||||
|
No results
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</template>
|
||||||
|
</q-select>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
source account is basic dropdown from API
|
||||||
|
with optional filters on account type. This depends
|
||||||
|
on transaction type which is null or invalid or withdrawal or whatever
|
||||||
|
if the index is not null the field shall be disabled and empty.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Accounts from '../../../api/v2/autocomplete/accounts'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "SourceAccount",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
model: null,
|
||||||
|
transactionTypeString: '',
|
||||||
|
options: [],
|
||||||
|
loading: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
transactionType: {
|
||||||
|
type: String,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
disabledInput: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
hasSubmissionError: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
submissionError: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
console.log('Mounted');
|
||||||
|
//this.options.value = this.stringOptions
|
||||||
|
this.getAccounts('');
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getAccounts: function (query) {
|
||||||
|
this.loading = true;
|
||||||
|
console.log('getAccounts("'+query+'")');
|
||||||
|
// default set of account types, will later be set by the transaction type.
|
||||||
|
let types = 'Asset account,Revenue account,Loan,Debt,Mortgage';
|
||||||
|
(new Accounts).get(types, query).then(response => {
|
||||||
|
this.stringOptions = [];
|
||||||
|
for (let i in response.data) {
|
||||||
|
let entry = response.data[i];
|
||||||
|
let current = {
|
||||||
|
label: entry.name,
|
||||||
|
value: entry.id,
|
||||||
|
description: entry.type
|
||||||
|
}
|
||||||
|
|
||||||
|
this.stringOptions.push(current);
|
||||||
|
}
|
||||||
|
//this.stringOptions = response.data.data;
|
||||||
|
this.options = this.stringOptions;
|
||||||
|
this.loading = false;
|
||||||
|
console.log('getAccounts done!');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
filterFn(val, update, abort) {
|
||||||
|
console.log('filterFn(' + val + ')');
|
||||||
|
if (val === '') {
|
||||||
|
update(() => {
|
||||||
|
this.getAccounts('');
|
||||||
|
//this.options = stringOptions
|
||||||
|
|
||||||
|
// here you have access to "ref" which
|
||||||
|
// is the Vue reference of the QSelect
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
update(() => {
|
||||||
|
this.getAccounts(val);
|
||||||
|
//const needle = val.toLowerCase()
|
||||||
|
//this.options = this.options.filter(v => v.label.toLowerCase().indexOf(needle) > -1)
|
||||||
|
})
|
||||||
|
// console.log('filterFn(' + val + ')');
|
||||||
|
// if (this.loading) {
|
||||||
|
// console.log('return');
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// const needle = val.toLowerCase()
|
||||||
|
// this.options = this.stringOptions.filter(v => v.label.toLowerCase().indexOf(needle) > -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -0,0 +1,70 @@
|
|||||||
|
<!--
|
||||||
|
- TransactionDescription.vue
|
||||||
|
- 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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<q-input
|
||||||
|
v-model="description"
|
||||||
|
hint=" " dense
|
||||||
|
:disable="disabledInput"
|
||||||
|
:error="hasSubmissionError" :error-message="submissionError"
|
||||||
|
:label="$t('firefly.description')" bottom-slots clearable
|
||||||
|
outlined
|
||||||
|
type="text"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "TransactionDescription",
|
||||||
|
props: {
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
disabledInput: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
hasSubmissionError: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
submissionError: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
description: {
|
||||||
|
handler: function (newVal) {
|
||||||
|
this.$emit('update:description', newVal)
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -30,7 +30,6 @@
|
|||||||
</q-banner>
|
</q-banner>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!--
|
|
||||||
<div class="row q-ma-md">
|
<div class="row q-ma-md">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<q-card>
|
<q-card>
|
||||||
@ -43,163 +42,28 @@
|
|||||||
align="left"
|
align="left"
|
||||||
class="text-teal col"
|
class="text-teal col"
|
||||||
>
|
>
|
||||||
<q-tab v-for="(transaction,index) in transactions" :name="'split-' + index" :label="getSplitLabel(index)"/>
|
<q-tab v-for="(transaction,index) in transactions" :name="'split-' + index"
|
||||||
<q-btn @click="addTransaction" flat label="Add split" icon="fas fa-plus-circle" class="text-orange"></q-btn>
|
:label="getSplitLabel(index)"/>
|
||||||
|
<q-btn @click="addTransaction" flat label="Add split" icon="fas fa-plus-circle"
|
||||||
|
class="text-orange"></q-btn>
|
||||||
</q-tabs>
|
</q-tabs>
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
-->
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<q-tab-panels v-model="tab" animated>
|
<q-tab-panels v-model="tab" animated>
|
||||||
<q-tab-panel v-for="(transaction,index) in transactions" :key="index" :name="'split-' + index">
|
<q-tab-panel v-for="(transaction,index) in transactions" :key="index" :name="'split-' + index">
|
||||||
<q-card bordered>
|
<Split
|
||||||
<q-card-section>
|
:transaction="transaction"
|
||||||
<div class="text-h6">Info for {{ $route.params.type }} {{ index }}</div>
|
:index="index"
|
||||||
</q-card-section>
|
:disabled-input="disabledInput"
|
||||||
<q-card-section>
|
:has-submission-errors="hasSubmissionErrors[index]"
|
||||||
<div class="row">
|
:submission-errors="submissionErrors[index]"
|
||||||
<div class="col-12 q-mb-xs">
|
@update:transaction="updateTransaction"
|
||||||
<q-input
|
/>
|
||||||
v-model="transaction.description"
|
|
||||||
:disable="disabledInput"
|
|
||||||
:error="hasSubmissionErrors[index].description" :error-message="submissionErrors[index].description" :label="$t('firefly.description')" bottom-slots clearable
|
|
||||||
outlined
|
|
||||||
type="text"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-4 q-mb-xs q-pr-xs">
|
|
||||||
<q-input
|
|
||||||
v-model="transaction.source"
|
|
||||||
:disable="disabledInput"
|
|
||||||
:error="hasSubmissionErrors[index].source" :error-message="submissionErrors[index].source" :label="$t('firefly.source_account')" bottom-slots
|
|
||||||
clearable outlined/>
|
|
||||||
</div>
|
|
||||||
<div class="col-4 q-px-xs">
|
|
||||||
<q-input
|
|
||||||
v-model="transaction.amount"
|
|
||||||
:disable="disabledInput"
|
|
||||||
:error="hasSubmissionErrors[index].amount" :error-message="submissionErrors[index].amount" :label="$t('firefly.amount')" bottom-slots clearable fill-mask="0"
|
|
||||||
hint="Expects #.##"
|
|
||||||
mask="#.##"
|
|
||||||
outlined reverse-fill-mask/>
|
|
||||||
</div>
|
|
||||||
<div class="col-4 q-pl-xs">
|
|
||||||
<q-input
|
|
||||||
v-model="transaction.destination"
|
|
||||||
:disable="disabledInput"
|
|
||||||
:error="hasSubmissionErrors[index].destination" :error-message="submissionErrors[index].destination" :label="$t('firefly.destination_account')" bottom-slots
|
|
||||||
clearable
|
|
||||||
outlined/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!--
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-4 offset-4">
|
|
||||||
Foreign
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
-->
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-4">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col">
|
|
||||||
<q-input
|
|
||||||
v-model="transaction.date"
|
|
||||||
:disable="disabledInput"
|
|
||||||
:error="hasSubmissionErrors[index].date" :error-message="submissionErrors[index].date" :hint="$t('firefly.date')" bottom-slots outlined
|
|
||||||
type="date"/>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<q-input v-model="transaction.time" :disable="disabledInput" :hint="$t('firefly.time')" bottom-slots outlined
|
|
||||||
type="time"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!--
|
|
||||||
<div class="col-4 offset-4">
|
|
||||||
<q-input v-model="transaction.interest_date" filled type="date" hint="Interest date"/>
|
|
||||||
<q-input v-model="transaction.book_date" filled type="date" hint="Book date"/>
|
|
||||||
<q-input v-model="transaction.process_date" filled type="date" hint="Processing date"/>
|
|
||||||
<q-input v-model="transaction.due_date" filled type="date" hint="Due date"/>
|
|
||||||
<q-input v-model="transaction.payment_date" filled type="date" hint="Payment date"/>
|
|
||||||
<q-input v-model="transaction.invoice_date" filled type="date" hint="Invoice date"/>
|
|
||||||
</div>
|
|
||||||
-->
|
|
||||||
</div>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
<!--
|
|
||||||
<q-card bordered class="q-mt-md">
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-h6">Meta for {{ $route.params.type }}</div>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-6">
|
|
||||||
<q-select filled v-model="transaction.budget" :options="tempBudgets" label="Budget"/>
|
|
||||||
</div>
|
|
||||||
<div class="col-6">
|
|
||||||
<q-input filled clearable v-model="transaction.category" :label="$t('firefly.category')" outlined/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-6">
|
|
||||||
<q-select filled v-model="transaction.subscription" :options="tempSubscriptions" label="Subscription"/>
|
|
||||||
</div>
|
|
||||||
<div class="col-6">
|
|
||||||
Tags
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-6">
|
|
||||||
Bill
|
|
||||||
</div>
|
|
||||||
<div class="col-6">
|
|
||||||
???
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
-->
|
|
||||||
<!--
|
|
||||||
<q-card bordered class="q-mt-md">
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-h6">Extr for {{ $route.params.type }}</div>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-6">
|
|
||||||
Notes
|
|
||||||
</div>
|
|
||||||
<div class="col-6">
|
|
||||||
attachments
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-6">
|
|
||||||
Links
|
|
||||||
</div>
|
|
||||||
<div class="col-6">
|
|
||||||
reference
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-6">
|
|
||||||
url
|
|
||||||
</div>
|
|
||||||
<div class="col-6">
|
|
||||||
location
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
-->
|
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
@ -219,7 +83,7 @@
|
|||||||
|
|
||||||
<div class="row q-mx-md">
|
<div class="row q-mx-md">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<q-card class="q-mt-xs">
|
<q-card class="q-mt-xs" bordered flat>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 text-right">
|
<div class="col-12 text-right">
|
||||||
@ -231,7 +95,8 @@
|
|||||||
<q-checkbox v-model="doReturnHere" :disable="disabledInput" label="Return here to create another one"
|
<q-checkbox v-model="doReturnHere" :disable="disabledInput" label="Return here to create another one"
|
||||||
left-label/>
|
left-label/>
|
||||||
<br/>
|
<br/>
|
||||||
<q-checkbox v-model="doResetForm" :disable="!doReturnHere || disabledInput" label="Reset form after submission"
|
<q-checkbox v-model="doResetForm" :disable="!doReturnHere || disabledInput"
|
||||||
|
label="Reset form after submission"
|
||||||
left-label/>
|
left-label/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -246,9 +111,11 @@
|
|||||||
import format from 'date-fns/format';
|
import format from 'date-fns/format';
|
||||||
import formatISO from 'date-fns/formatISO';
|
import formatISO from 'date-fns/formatISO';
|
||||||
import Post from "../../api/transactions/post";
|
import Post from "../../api/transactions/post";
|
||||||
|
import Split from "components/transactions/Split.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Create',
|
name: 'Create',
|
||||||
|
components: {Split},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
tab: 'split-0',
|
tab: 'split-0',
|
||||||
@ -268,35 +135,50 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
disabledInput: function () {
|
disabledInput: function () {
|
||||||
return this.submitting;
|
return this.submitting ?? false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
console.log('Created');
|
||||||
this.resetForm();
|
this.resetForm();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
resetForm: function () {
|
resetForm: function () {
|
||||||
|
console.log('ResetForm');
|
||||||
this.transactions = [];
|
this.transactions = [];
|
||||||
const info = this.getDefaultTransaction();
|
this.addTransaction();
|
||||||
this.transactions.push(info.transaction);
|
// const info = this.getDefaultTransaction();
|
||||||
this.submissionErrors.push(info.submissionError);
|
// this.transactions.push(info.transaction);
|
||||||
this.hasSubmissionErrors.push(info.hasSubmissionError);
|
// this.submissionErrors.push(info.submissionError);
|
||||||
|
// this.hasSubmissionErrors.push(info.hasSubmissionError);
|
||||||
},
|
},
|
||||||
addTransaction: function () {
|
addTransaction: function () {
|
||||||
const transaction = this.getDefaultTransaction();
|
const transaction = this.getDefaultTransaction();
|
||||||
this.transactions.push(transaction);
|
|
||||||
this.tab = 'split-' + (parseInt(this.transactions.length) - 1);
|
// push all three
|
||||||
|
this.transactions.push(transaction.transaction);
|
||||||
|
this.submissionErrors.push(transaction.submissionError);
|
||||||
|
this.hasSubmissionErrors.push(transaction.hasSubmissionError);
|
||||||
|
|
||||||
|
const index = String(this.transactions.length - 1);
|
||||||
|
console.log('AddTransaction ' + index);
|
||||||
|
this.tab = 'split-' + index;
|
||||||
},
|
},
|
||||||
getSplitLabel: function (index) {
|
getSplitLabel: function (index) {
|
||||||
if (this.transactions.hasOwnProperty(index) && null !== this.transactions[index].description && this.transactions[index].description.length > 0) {
|
console.log('Get split label (' + index + ')');
|
||||||
|
if (this.transactions.hasOwnProperty(index) &&
|
||||||
|
null !== this.transactions[index].description &&
|
||||||
|
this.transactions[index].description.length > 0) {
|
||||||
return this.transactions[index].description
|
return this.transactions[index].description
|
||||||
}
|
}
|
||||||
return this.$t('firefly.single_split') + ' ' + (index + 1);
|
return this.$t('firefly.single_split') + ' ' + (index + 1);
|
||||||
},
|
},
|
||||||
dismissBanner: function () {
|
dismissBanner: function () {
|
||||||
|
console.log('Dismiss banner');
|
||||||
this.errorMessage = '';
|
this.errorMessage = '';
|
||||||
},
|
},
|
||||||
submitTransaction: function () {
|
submitTransaction: function () {
|
||||||
|
console.log('submit transaction');
|
||||||
this.submitting = true;
|
this.submitting = true;
|
||||||
this.errorMessage = '';
|
this.errorMessage = '';
|
||||||
|
|
||||||
@ -312,7 +194,15 @@ export default {
|
|||||||
.catch(this.processErrors)
|
.catch(this.processErrors)
|
||||||
.then(this.processSuccess);
|
.then(this.processSuccess);
|
||||||
},
|
},
|
||||||
|
updateTransaction: function (obj) {
|
||||||
|
const index = obj.index;
|
||||||
|
const transaction = obj.transaction;
|
||||||
|
console.log('Update transaction ' + index);
|
||||||
|
console.log(transaction);
|
||||||
|
this.transactions[index] = transaction;
|
||||||
|
},
|
||||||
processSuccess: function (response) {
|
processSuccess: function (response) {
|
||||||
|
console.log('process success');
|
||||||
this.submitting = false;
|
this.submitting = false;
|
||||||
let message = {
|
let message = {
|
||||||
level: 'success',
|
level: 'success',
|
||||||
@ -340,6 +230,7 @@ export default {
|
|||||||
|
|
||||||
},
|
},
|
||||||
resetErrors: function () {
|
resetErrors: function () {
|
||||||
|
console.log('reset errors');
|
||||||
let length = this.transactions.length;
|
let length = this.transactions.length;
|
||||||
let transaction = this.getDefaultTransaction();
|
let transaction = this.getDefaultTransaction();
|
||||||
for (let i = 0; i < length; i++) {
|
for (let i = 0; i < length; i++) {
|
||||||
@ -348,6 +239,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
processErrors: function (error) {
|
processErrors: function (error) {
|
||||||
|
console.log('process errors');
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
let errors = error.response.data; // => the response payload
|
let errors = error.response.data; // => the response payload
|
||||||
this.errorMessage = errors.message;
|
this.errorMessage = errors.message;
|
||||||
@ -360,7 +252,8 @@ export default {
|
|||||||
this.submitting = false;
|
this.submitting = false;
|
||||||
},
|
},
|
||||||
processSingleError: function (key, errors) {
|
processSingleError: function (key, errors) {
|
||||||
// lol dumbest way to explode "transactions.0.something" ever.
|
console.log('process single error');
|
||||||
|
// lol the dumbest way to explode "transactions.0.something" ever.
|
||||||
let index = parseInt(key.split('.')[1]);
|
let index = parseInt(key.split('.')[1]);
|
||||||
let fieldName = key.split('.')[2];
|
let fieldName = key.split('.')[2];
|
||||||
switch (fieldName) {
|
switch (fieldName) {
|
||||||
@ -383,6 +276,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
buildTransaction: function () {
|
buildTransaction: function () {
|
||||||
|
console.log('build transaction');
|
||||||
const obj = {
|
const obj = {
|
||||||
transactions: []
|
transactions: []
|
||||||
};
|
};
|
||||||
@ -401,6 +295,7 @@ export default {
|
|||||||
return obj;
|
return obj;
|
||||||
},
|
},
|
||||||
getDefaultTransaction: function () {
|
getDefaultTransaction: function () {
|
||||||
|
console.log('get default transaction');
|
||||||
let date = '';
|
let date = '';
|
||||||
let time = '00:00';
|
let time = '00:00';
|
||||||
|
|
||||||
|
@ -48,6 +48,21 @@ Route::group(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* V2 API routes for auto complete
|
||||||
|
*/
|
||||||
|
Route::group(
|
||||||
|
[
|
||||||
|
'namespace' => 'FireflyIII\Api\V2\Controllers\Autocomplete',
|
||||||
|
'prefix' => 'v2/autocomplete',
|
||||||
|
'as' => 'api.v2.autocomplete.',
|
||||||
|
],
|
||||||
|
static function () {
|
||||||
|
// Auto complete routes
|
||||||
|
Route::get('accounts', ['uses' => 'AccountController@accounts', 'as' => 'accounts']);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* V2 API route for net worth endpoint(s);
|
* V2 API route for net worth endpoint(s);
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user