More auto-complete migration.

This commit is contained in:
James Cole
2020-07-19 17:24:29 +02:00
parent fd6f1fbbf0
commit 8a4999406b
16 changed files with 248 additions and 229 deletions

View File

@@ -39,9 +39,7 @@ class BillController extends Controller
private BillRepositoryInterface $repository;
/**
* TransactionController constructor.
*
* @codeCoverageIgnore
* BillController constructor.
*/
public function __construct()
{

View File

@@ -0,0 +1,79 @@
<?php
/**
* BudgetController.php
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest;
use FireflyIII\Models\Budget;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
/**
* Class BudgetController
*/
class BudgetController extends Controller
{
private BudgetRepositoryInterface $repository;
/**
* BudgetController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->repository = app(BudgetRepositoryInterface::class);
$this->repository->setUser($user);
return $next($request);
}
);
}
/**
* @param AutocompleteRequest $request
*
* @return JsonResponse
*/
public function budgets(AutocompleteRequest $request): JsonResponse
{
$data = $request->getData();
$result = $this->repository->searchBudget($data['query']);
$filtered = $result->map(
static function (Budget $item) {
return [
'id' => $item->id,
'name' => $item->name,
];
}
);
return response()->json($filtered);
}
}

View File

@@ -0,0 +1,79 @@
<?php
/**
* CategoryController.php
* Copyright (c) 2020 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Autocomplete;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest;
use FireflyIII\Models\Category;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
/**
* Class CategoryController
*/
class CategoryController extends Controller
{
private CategoryRepositoryInterface $repository;
/**
* BudgetController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->repository = app(CategoryRepositoryInterface::class);
$this->repository->setUser($user);
return $next($request);
}
);
}
/**
* @param AutocompleteRequest $request
*
* @return JsonResponse
*/
public function budgets(AutocompleteRequest $request): JsonResponse
{
$data = $request->getData();
$result = $this->repository->searchCategory($data['query']);
$filtered = $result->map(
static function (Category $item) {
return [
'id' => $item->id,
'name' => $item->name,
];
}
);
return response()->json($filtered);
}
}

View File

@@ -23,7 +23,6 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Json;
use Amount;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
@@ -54,62 +53,6 @@ use Log;
class AutoCompleteController extends Controller
{
/**
* @param Request $request
*
* @return JsonResponse
*/
public function accounts(Request $request): JsonResponse
{
$accountTypes = explode(',', $request->get('types') ?? '');
$search = $request->get('search');
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
// filter the account types:
$allowedAccountTypes = [AccountType::ASSET, AccountType::EXPENSE, AccountType::REVENUE, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE,];
$balanceTypes = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE,];
$filteredAccountTypes = [];
foreach ($accountTypes as $type) {
if (in_array($type, $allowedAccountTypes, true)) {
$filteredAccountTypes[] = $type;
}
}
if (0 === count($filteredAccountTypes)) {
$filteredAccountTypes = $allowedAccountTypes;
}
Log::debug(sprintf('Now in accounts("%s"). Filtering results.', $search), $filteredAccountTypes);
$return = [];
$result = $repository->searchAccount((string) $search, $filteredAccountTypes);
$defaultCurrency = app('amount')->getDefaultCurrency();
/** @var Account $account */
foreach ($result as $account) {
$nameWithBalance = $account->name;
$currency = $repository->getAccountCurrency($account) ?? $defaultCurrency;
if (in_array($account->accountType->type, $balanceTypes, true)) {
$balance = app('steam')->balance($account, new Carbon);
$nameWithBalance = sprintf('%s (%s)', $account->name, app('amount')->formatAnything($currency, $balance, false));
}
$return[] = [
'id' => $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_decimal_places' => $currency->decimal_places,
];
}
return response()->json($return);
}
/**
* Searches in the titles of all transaction journals.
* The result is limited to the top 15 unique results.
@@ -187,53 +130,6 @@ class AutoCompleteController extends Controller
return response()->json($array);
}
/**
* An auto-complete specifically for asset accounts and liabilities, used when mass updating and for rules mostly.
*
* @param Request $request
*
* @return JsonResponse
*/
public function assetAccounts(Request $request): JsonResponse
{
$search = $request->get('search');
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
// filter the account types:
$allowedAccountTypes = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE];
Log::debug(sprintf('Now in expenseAccounts(%s). Filtering results.', $search), $allowedAccountTypes);
$return = [];
$result = $repository->searchAccount((string) $search, $allowedAccountTypes);
/** @var Account $account */
foreach ($result as $account) {
$return[] = [
'id' => $account->id,
'name' => $account->name,
'type' => $account->accountType->type,
];
}
return response()->json($return);
}
/**
* @param Request $request
*
* @return JsonResponse
* @codeCoverageIgnore
*/
public function bills(Request $request): JsonResponse
{
$query = (string) $request->get('search');
/** @var BillRepositoryInterface $repository */
$repository = app(BillRepositoryInterface::class);
$result = $repository->searchBill($query);
return response()->json($result->toArray());
}
/**
* @param Request $request
@@ -312,37 +208,6 @@ class AutoCompleteController extends Controller
return response()->json($result);
}
/**
* An auto-complete specifically for expense accounts, used when mass updating mostly.
*
* @param Request $request
*
* @return JsonResponse
*/
public function expenseAccounts(Request $request): JsonResponse
{
$search = $request->get('search');
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
// filter the account types:
$allowedAccountTypes = [AccountType::EXPENSE];
Log::debug(sprintf('Now in expenseAccounts(%s). Filtering results.', $search), $allowedAccountTypes);
$return = [];
$result = $repository->searchAccount((string) $search, $allowedAccountTypes);
/** @var Account $account */
foreach ($result as $account) {
$return[] = [
'id' => $account->id,
'name' => $account->name,
'type' => $account->accountType->type,
];
}
return response()->json($return);
}
/**
* An auto-complete specifically for expense accounts, used when mass updating mostly.
@@ -404,37 +269,6 @@ class AutoCompleteController extends Controller
return response()->json($response);
}
/**
* An auto-complete specifically for revenue accounts, used when converting transactions mostly.
*
* @param Request $request
*
* @return JsonResponse
*/
public function revenueAccounts(Request $request): JsonResponse
{
$search = $request->get('search');
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
// filter the account types:
$allowedAccountTypes = [AccountType::REVENUE];
Log::debug('Now in revenueAccounts(). Filtering results.', $allowedAccountTypes);
$return = [];
$result = $repository->searchAccount((string) $search, $allowedAccountTypes);
/** @var Account $account */
foreach ($result as $account) {
$return[] = [
'id' => $account->id,
'name' => $account->name,
'type' => $account->accountType->type,
];
}
return response()->json($return);
}
/**
* @param Request $request

View File

@@ -77,15 +77,25 @@ class EditController extends Controller
$repository = app(AccountRepositoryInterface::class);
$allowedOpposingTypes = config('firefly.allowed_opposing_types');
$accountToTypes = config('firefly.account_to_transaction');
$defaultCurrency = app('amount')->getDefaultCurrency();
$cash = $repository->getCashAccount();
$previousUri = $this->rememberPreviousUri('transactions.edit.uri');
$parts = parse_url($previousUri);
$search = sprintf('?%s', $parts['query'] ?? '');
$previousUri = str_replace($search, '', $previousUri);
$expectedSourceTypes = config('firefly.expected_source_types');
$allowedSourceDests = config('firefly.source_dests');
//
$defaultCurrency = app('amount')->getDefaultCurrency();
$cash = $repository->getCashAccount();
$previousUri = $this->rememberPreviousUri('transactions.edit.uri');
$parts = parse_url($previousUri);
$search = sprintf('?%s', $parts['query'] ?? '');
$previousUri = str_replace($search, '', $previousUri);
return view('transactions.edit', compact('cash', 'transactionGroup', 'allowedOpposingTypes', 'accountToTypes', 'defaultCurrency', 'previousUri'));
return view(
'transactions.edit',
compact(
'cash', 'allowedSourceDests', 'expectedSourceTypes', 'transactionGroup', 'allowedOpposingTypes', 'accountToTypes', 'defaultCurrency',
'previousUri'
)
);
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -143,7 +143,7 @@ function initCategoryAC() {
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: 'json/categories?uid=' + uid,
url: 'api/v1/autocomplete/categories?uid=' + uid,
filter: function (list) {
return $.map(list, function (object) {
return {name: object.name};
@@ -151,7 +151,7 @@ function initCategoryAC() {
}
},
remote: {
url: 'json/categories?search=%QUERY&uid=' + uid,
url: 'api/v1/autocomplete/categories?query=%QUERY&uid=' + uid,
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (object) {

View File

@@ -228,7 +228,7 @@ function updateActionInput(selectList) {
switch (selectList.val()) {
case 'set_category':
console.log('Select list value is ' + selectList.val() + ', so input needs auto complete.');
createAutoComplete(inputResult, 'json/categories');
createAutoComplete(inputResult, 'api/v1/autocomplete/categories');
break;
case 'clear_category':
case 'clear_budget':
@@ -240,7 +240,7 @@ function updateActionInput(selectList) {
break;
case 'set_budget':
console.log('Select list value is ' + selectList.val() + ', so input needs auto complete.');
createAutoComplete(inputResult, 'json/budgets');
createAutoComplete(inputResult, 'api/v1/autocomplete/budgets');
break;
case 'add_tag':
case 'remove_tag':
@@ -253,27 +253,27 @@ function updateActionInput(selectList) {
break;
case 'set_source_account':
console.log('Select list value is ' + selectList.val() + ', so input needs auto complete.');
createAutoComplete(inputResult, 'json/accounts');
createAutoComplete(inputResult, 'api/v1/autocomplete/accounts');
break;
case 'set_destination_account':
console.log('Select list value is ' + selectList.val() + ', so input needs auto complete.');
createAutoComplete(inputResult, 'json/accounts');
createAutoComplete(inputResult, 'api/v1/autocomplete/accounts');
break;
case 'convert_withdrawal':
console.log('Select list value is ' + selectList.val() + ', so input needs expense accounts auto complete.');
createAutoComplete(inputResult, 'json/expense-accounts');
createAutoComplete(inputResult, 'api/v1/autocomplete/accounts?types=Expense account&');
break;
case 'convert_deposit':
console.log('Select list value is ' + selectList.val() + ', so input needs revenue accounts auto complete.');
createAutoComplete(inputResult, 'json/revenue-accounts');
createAutoComplete(inputResult, 'api/v1/autocomplete/accounts?types=Revenue account&');
break;
case 'convert_transfer':
console.log('Select list value is ' + selectList.val() + ', so input needs asset accounts auto complete.');
createAutoComplete(inputResult, 'json/asset-accounts');
createAutoComplete(inputResult, 'api/v1/autocomplete/accounts?types=Asset account&');
break;
case 'link_to_bill':
console.log('Select list value is ' + selectList.val() + ', so input needs auto complete.');
createAutoComplete(inputResult, 'json/bills');
createAutoComplete(inputResult, 'api/v1/autocomplete/bills');
break;
case 'update_piggy':
console.log('Select list value is ' + selectList.val() + ', so input needs auto complete.');
@@ -312,7 +312,7 @@ function updateTriggerInput(selectList) {
case 'to_account_is':
case 'to_account_contains':
console.log('Select list value is ' + selectList.val() + ', so input needs auto complete.');
createAutoComplete(inputResult, 'json/accounts');
createAutoComplete(inputResult, 'api/v1/autocomplete/accounts');
break;
case 'tag_is':
console.log('Select list value is ' + selectList.val() + ', so input needs auto complete.');
@@ -320,11 +320,11 @@ function updateTriggerInput(selectList) {
break;
case 'budget_is':
console.log('Select list value is ' + selectList.val() + ', so input needs auto complete.');
createAutoComplete(inputResult, 'json/budgets');
createAutoComplete(inputResult, 'api/v1/autocomplete/budgets');
break;
case 'category_is':
console.log('Select list value is ' + selectList.val() + ', so input needs auto complete.');
createAutoComplete(inputResult, 'json/categories');
createAutoComplete(inputResult, 'api/v1/autocomplete/categories');
break;
case 'transaction_type':
console.log('Select list value is ' + selectList.val() + ', so input needs auto complete.');
@@ -377,11 +377,17 @@ function createAutoComplete(input, URI) {
console.log('Now in createAutoComplete("' + URI + '").');
input.typeahead('destroy');
// append URI:
var lastChar = URI[URI.length -1];
var urlParamSplit = '?';
if('&' === lastChar) {
urlParamSplit = '';
}
var source = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: URI + '?uid=' + uid,
url: URI + urlParamSplit + 'uid=' + uid,
filter: function (list) {
return $.map(list, function (item) {
return {name: item.name};
@@ -389,7 +395,7 @@ function createAutoComplete(input, URI) {
}
},
remote: {
url: URI + '?search=%QUERY&uid=' + uid,
url: URI + urlParamSplit + 'query=%QUERY&uid=' + uid,
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (item) {

View File

@@ -109,7 +109,7 @@ $(document).ready(function () {
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: 'json/categories?uid=' + uid,
url: 'api/v1/autocomplete/categories?uid=' + uid,
filter: function (list) {
return $.map(list, function (obj) {
return obj;
@@ -117,7 +117,7 @@ $(document).ready(function () {
}
},
remote: {
url: 'json/categories?search=%QUERY&uid=' + uid,
url: 'api/v1/autocomplete/categories?query=%QUERY&uid=' + uid,
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (obj) {

View File

@@ -86,7 +86,7 @@ $(document).ready(function () {
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: 'json/categories?uid=' + uid,
url: 'api/v1/autocomplete/categories?uid=' + uid,
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
@@ -94,7 +94,7 @@ $(document).ready(function () {
}
},
remote: {
url: 'json/categories?search=%QUERY&uid=' + uid,
url: 'api/v1/autocomplete/categories?query=%QUERY&uid=' + uid,
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (name) {

View File

@@ -82,7 +82,7 @@
return this.error.length > 0;
},
loadBudgets: function () {
let URI = document.getElementsByTagName('base')[0].href + "json/budgets";
let URI = document.getElementsByTagName('base')[0].href + 'api/v1/autocomplete/budgets';
axios.get(URI, {}).then((res) => {
this.budgets = [
{

View File

@@ -85,7 +85,7 @@
},
mounted() {
this.target = this.$refs.input;
this.categoryAutoCompleteURI = document.getElementsByTagName('base')[0].href + "json/categories?search=";
this.categoryAutoCompleteURI = document.getElementsByTagName('base')[0].href + "api/v1/autocomplete/categories?query=";
},
methods: {
hasError: function () {

View File

@@ -307,21 +307,7 @@
},
deleteTransaction(index, event) {
event.preventDefault();
for (const key in this.transactions) {
if (
this.transactions.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
// TODO empty iff?
}
}
this.transactions.splice(index, 1);
for (const key in this.transactions) {
if (
this.transactions.hasOwnProperty(key) && /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294) {
// TODO empty iff?
}
}
},
clearDestination(index) {
// console.log('clearDestination(' + index + ')');
@@ -380,6 +366,12 @@
}
},
ucFirst(string) {
if (typeof string === 'string') {
return string.charAt(0).toUpperCase() + string.slice(1);
}
return null;
},
processIncomingGroupRow(transaction) {
// console.log(transaction);
this.setTransactionType(transaction.type);
@@ -390,6 +382,11 @@
newTags.push({text: transaction.tags[key], tiClasses: []});
}
}
console.log('source allowed types for a ' + transaction.type);
//console.log(window.expectedSourceTypes.source[transaction.type]);
console.log(window.expectedSourceTypes.source[this.ucFirst(transaction.type)]);
console.log('destination allowed types for a ' + transaction.type);
console.log(window.expectedSourceTypes.destination[this.ucFirst(transaction.type)]);
this.transactions.push({
transaction_journal_id: transaction.transaction_journal_id,
@@ -445,7 +442,7 @@
currency_name: transaction.currency_name,
currency_code: transaction.currency_code,
currency_decimal_places: transaction.currency_decimal_places,
allowed_types: [transaction.source_type]
allowed_types: window.expectedSourceTypes.source[this.ucFirst(transaction.type)]
},
destination_account: {
id: transaction.destination_id,
@@ -455,10 +452,22 @@
currency_name: transaction.currency_name,
currency_code: transaction.currency_code,
currency_decimal_places: transaction.currency_decimal_places,
allowed_types: [transaction.destination_type]
allowed_types: window.expectedSourceTypes.destination[this.ucFirst(transaction.type)]
}
});
},
limitSourceType: function (type) {
// let i;
// for (i = 0; i < this.transactions.length; i++) {
// this.transactions[i].source_account.allowed_types = [type];
// }
},
limitDestinationType: function (type) {
// let i;
// for (i = 0; i < this.transactions.length; i++) {
// this.transactions[i].destination_account.allowed_types = [type];
// }
},
convertData: function () {
let data = {
'transactions': [],

View File

@@ -12,6 +12,8 @@
var allowedOpposingTypes = {{ allowedOpposingTypes|json_encode|raw }};
var accountToTypes = {{ accountToTypes|json_encode|raw }};
var defaultCurrency = {{ defaultCurrency.toArray()|json_encode|raw }};
var allowedSourceDests = {{ allowedSourceDests|json_encode|raw }};
var expectedSourceTypes = {{ expectedSourceTypes|json_encode|raw }};
var cashAccountId = {{ cash.id }};
var previousUri = '{{ previousUri }}';
</script>

View File

@@ -63,6 +63,8 @@ Route::group(
// Auto complete routes
Route::get('accounts', ['uses' => 'AccountController@accounts', 'as' => 'accounts']);
Route::get('bills', ['uses' => 'BillController@bills', 'as' => 'bills']);
Route::get('budgets', ['uses' => 'BudgetController@budgets', 'as' => 'budgets']);
Route::get('categories', ['uses' => 'CategoryController@categories', 'as' => 'categories']);
}
);