mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
A new endpoint that allows you to search for transfers.
This commit is contained in:
parent
6823adc976
commit
8b11afb83f
116
app/Api/V1/Controllers/Search/TransferController.php
Normal file
116
app/Api/V1/Controllers/Search/TransferController.php
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* TransferController.php
|
||||||
|
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||||
|
*
|
||||||
|
* 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\Api\V1\Controllers\Search;
|
||||||
|
|
||||||
|
|
||||||
|
use FireflyIII\Api\V1\Controllers\Controller;
|
||||||
|
use FireflyIII\Api\V1\Requests\Search\TransferRequest;
|
||||||
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||||
|
use FireflyIII\Support\Search\TransferSearch;
|
||||||
|
use FireflyIII\Transformers\TransactionGroupTransformer;
|
||||||
|
use FireflyIII\User;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Response;
|
||||||
|
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||||
|
use League\Fractal\Resource\Collection as FractalCollection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TransferController
|
||||||
|
*/
|
||||||
|
class TransferController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return JsonResponse|Response
|
||||||
|
* @throws FireflyException
|
||||||
|
*/
|
||||||
|
public function search(TransferRequest $request)
|
||||||
|
{
|
||||||
|
// configure transfer search to search for a > b
|
||||||
|
$search = app(TransferSearch::class);
|
||||||
|
$search->setSource($request->get('source'));
|
||||||
|
$search->setDestination($request->get('destination'));
|
||||||
|
$search->setAmount($request->get('amount'));
|
||||||
|
$search->setDescription($request->get('description'));
|
||||||
|
$search->setDate($request->get('date'));
|
||||||
|
|
||||||
|
$left = $search->search();
|
||||||
|
|
||||||
|
// configure transfer search to search for b > a
|
||||||
|
$search->setSource($request->get('destination'));
|
||||||
|
$search->setDestination($request->get('source'));
|
||||||
|
$search->setAmount($request->get('amount'));
|
||||||
|
$search->setDescription($request->get('description'));
|
||||||
|
$search->setDate($request->get('date'));
|
||||||
|
|
||||||
|
$right = $search->search();
|
||||||
|
|
||||||
|
// add parameters to URL:
|
||||||
|
$this->parameters->set('source', $request->get('source'));
|
||||||
|
$this->parameters->set('destination', $request->get('destination'));
|
||||||
|
$this->parameters->set('amount', $request->get('amount'));
|
||||||
|
$this->parameters->set('description', $request->get('description'));
|
||||||
|
$this->parameters->set('date', $request->get('date'));
|
||||||
|
|
||||||
|
// get all journal ID's.
|
||||||
|
$total = $left->merge($right)->unique('id')->pluck('id')->toArray();
|
||||||
|
if (0 === count($total)) {
|
||||||
|
// forces search to be empty.
|
||||||
|
$total = [-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// collector to return results.
|
||||||
|
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
|
||||||
|
$manager = $this->getManager();
|
||||||
|
/** @var User $admin */
|
||||||
|
$admin = auth()->user();
|
||||||
|
|
||||||
|
// use new group collector:
|
||||||
|
/** @var GroupCollectorInterface $collector */
|
||||||
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
$collector
|
||||||
|
->setUser($admin)
|
||||||
|
// all info needed for the API:
|
||||||
|
->withAPIInformation()
|
||||||
|
// set page size:
|
||||||
|
->setLimit($pageSize)
|
||||||
|
// set page to retrieve
|
||||||
|
->setPage(1)
|
||||||
|
->setJournalIds($total);
|
||||||
|
|
||||||
|
$paginator = $collector->getPaginatedGroups();
|
||||||
|
$paginator->setPath(route('api.v1.search.transfers') . $this->buildParams());
|
||||||
|
$transactions = $paginator->getCollection();
|
||||||
|
|
||||||
|
/** @var TransactionGroupTransformer $transformer */
|
||||||
|
$transformer = app(TransactionGroupTransformer::class);
|
||||||
|
$transformer->setParameters($this->parameters);
|
||||||
|
|
||||||
|
$resource = new FractalCollection($transactions, $transformer, 'transactions');
|
||||||
|
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||||
|
|
||||||
|
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||||
|
}
|
||||||
|
}
|
58
app/Api/V1/Requests/Search/TransferRequest.php
Normal file
58
app/Api/V1/Requests/Search/TransferRequest.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* TransferRequest.php
|
||||||
|
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||||
|
*
|
||||||
|
* 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\Api\V1\Requests\Search;
|
||||||
|
|
||||||
|
|
||||||
|
use FireflyIII\Api\V1\Requests\Request;
|
||||||
|
use FireflyIII\Rules\IsTransferAccount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TransferRequest
|
||||||
|
*/
|
||||||
|
class TransferRequest extends Request
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Authorize logged in users.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
// Only allow authenticated users
|
||||||
|
return auth()->check();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'source' => ['required', new IsTransferAccount],
|
||||||
|
'destination' => ['required', new IsTransferAccount],
|
||||||
|
'amount' => 'required|numeric|more:0',
|
||||||
|
'description' => 'required|min:1',
|
||||||
|
'date' => 'required|date',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
73
app/Rules/IsTransferAccount.php
Normal file
73
app/Rules/IsTransferAccount.php
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* IsTransferAccount.php
|
||||||
|
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||||
|
*
|
||||||
|
* 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\Rules;
|
||||||
|
|
||||||
|
|
||||||
|
use FireflyIII\Models\TransactionType;
|
||||||
|
use FireflyIII\Validation\AccountValidator;
|
||||||
|
use Illuminate\Contracts\Validation\Rule;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class IsTransferAccount
|
||||||
|
*/
|
||||||
|
class IsTransferAccount implements Rule
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the validation error message.
|
||||||
|
*
|
||||||
|
* @return string|array
|
||||||
|
*/
|
||||||
|
public function message(): string
|
||||||
|
{
|
||||||
|
return (string)trans('validation.not_transfer_account');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the validation rule passes.
|
||||||
|
*
|
||||||
|
* @param string $attribute
|
||||||
|
* @param mixed $value
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function passes($attribute, $value): bool
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('Now in %s(%s)', __METHOD__, $value));
|
||||||
|
/** @var AccountValidator $validator */
|
||||||
|
$validator = app(AccountValidator::class);
|
||||||
|
$validator->setTransactionType(TransactionType::TRANSFER);
|
||||||
|
$validator->setUser(auth()->user());
|
||||||
|
|
||||||
|
$validAccount = $validator->validateSource(null, (string)$value);
|
||||||
|
if (true === $validAccount) {
|
||||||
|
Log::debug('Found account based on name. Return true.');
|
||||||
|
|
||||||
|
// found by name, use repos to return.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
$validAccount = $validator->validateSource((int)$value, null);
|
||||||
|
Log::debug(sprintf('Search by id (%d), result is %s.', (int)$value, var_export($validAccount, true)));
|
||||||
|
|
||||||
|
return !(false === $validAccount);
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,16 @@
|
|||||||
namespace FireflyIII\Support\Search;
|
namespace FireflyIII\Support\Search;
|
||||||
|
|
||||||
|
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface GenericSearchInterface
|
||||||
|
*/
|
||||||
interface GenericSearchInterface
|
interface GenericSearchInterface
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function search(): Collection;
|
||||||
|
|
||||||
}
|
}
|
147
app/Support/Search/TransferSearch.php
Normal file
147
app/Support/Search/TransferSearch.php
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* TransferSearch.php
|
||||||
|
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||||
|
*
|
||||||
|
* 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\Search;
|
||||||
|
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use FireflyIII\Models\Account;
|
||||||
|
use FireflyIII\Models\AccountType;
|
||||||
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
|
use FireflyIII\User;
|
||||||
|
use Illuminate\Database\Query\JoinClause;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TransferSearch
|
||||||
|
*/
|
||||||
|
class TransferSearch implements GenericSearchInterface
|
||||||
|
{
|
||||||
|
/** @var AccountRepositoryInterface */
|
||||||
|
private $accountRepository;
|
||||||
|
/** @var string */
|
||||||
|
private $amount;
|
||||||
|
/** @var Carbon */
|
||||||
|
private $date;
|
||||||
|
/** @var string */
|
||||||
|
private $description;
|
||||||
|
/** @var Account */
|
||||||
|
private $destination;
|
||||||
|
/** @var Account */
|
||||||
|
private $source;
|
||||||
|
/** @var array */
|
||||||
|
private $types;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->accountRepository = app(AccountRepositoryInterface::class);
|
||||||
|
$this->types = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function search(): Collection
|
||||||
|
{
|
||||||
|
/** @var User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
$query = $user->transactionJournals()
|
||||||
|
->leftJoin(
|
||||||
|
'transactions as source', static function (JoinClause $join) {
|
||||||
|
$join->on('transaction_journals.id', '=', 'source.transaction_journal_id');
|
||||||
|
$join->where('source.amount', '<', '0');
|
||||||
|
}
|
||||||
|
)
|
||||||
|
->leftJoin(
|
||||||
|
'transactions as destination', static function (JoinClause $join) {
|
||||||
|
$join->on('transaction_journals.id', '=', 'destination.transaction_journal_id');
|
||||||
|
$join->where('destination.amount', '>', '0');
|
||||||
|
}
|
||||||
|
)
|
||||||
|
->where('source.account_id', $this->source->id)
|
||||||
|
->where('destination.account_id', $this->destination->id)
|
||||||
|
->where('transaction_journals.description', $this->description)
|
||||||
|
->where('destination.amount', $this->amount)
|
||||||
|
->where('transaction_journals.date', $this->date->format('Y-m-d 00:00:00'))
|
||||||
|
;
|
||||||
|
|
||||||
|
return $query->get(['transaction_journals.id', 'transaction_journals.transaction_group_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $amount
|
||||||
|
*/
|
||||||
|
public function setAmount(string $amount): void
|
||||||
|
{
|
||||||
|
$this->amount = $amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $date
|
||||||
|
*/
|
||||||
|
public function setDate(string $date): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$carbon = Carbon::createFromFormat('Y-m-d', $date);
|
||||||
|
} catch (InvalidArgumentException $e) {
|
||||||
|
Log::error($e->getMessage());
|
||||||
|
$carbon = Carbon::now();
|
||||||
|
}
|
||||||
|
$this->date = $carbon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $description
|
||||||
|
*/
|
||||||
|
public function setDescription(string $description): void
|
||||||
|
{
|
||||||
|
$this->description = $description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $destination
|
||||||
|
*/
|
||||||
|
public function setDestination(string $destination): void
|
||||||
|
{
|
||||||
|
if (is_numeric($destination)) {
|
||||||
|
$this->destination = $this->accountRepository->findNull((int)$destination);
|
||||||
|
}
|
||||||
|
if (null === $this->destination) {
|
||||||
|
$this->destination = $this->accountRepository->findByName($destination, $this->types);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $source
|
||||||
|
*/
|
||||||
|
public function setSource(string $source): void
|
||||||
|
{
|
||||||
|
if (is_numeric($source)) {
|
||||||
|
$this->source = $this->accountRepository->findNull((int)$source);
|
||||||
|
}
|
||||||
|
if (null === $this->source) {
|
||||||
|
$this->source = $this->accountRepository->findByName($source, $this->types);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -45,6 +45,7 @@ return [
|
|||||||
'at_least_one_repetition' => 'Need at least one repetition.',
|
'at_least_one_repetition' => 'Need at least one repetition.',
|
||||||
'require_repeat_until' => 'Require either a number of repetitions, or an end date (repeat_until). Not both.',
|
'require_repeat_until' => 'Require either a number of repetitions, or an end date (repeat_until). Not both.',
|
||||||
'require_currency_info' => 'The content of this field is invalid without currency information.',
|
'require_currency_info' => 'The content of this field is invalid without currency information.',
|
||||||
|
'not_transfer_account' => 'This account is not an account that can be used for transfers.',
|
||||||
'require_currency_amount' => 'The content of this field is invalid without foreign amount information.',
|
'require_currency_amount' => 'The content of this field is invalid without foreign amount information.',
|
||||||
'equal_description' => 'Transaction description should not equal global description.',
|
'equal_description' => 'Transaction description should not equal global description.',
|
||||||
'file_invalid_mime' => 'File ":name" is of type ":mime" which is not accepted as a new upload.',
|
'file_invalid_mime' => 'File ":name" is of type ":mime" which is not accepted as a new upload.',
|
||||||
|
@ -342,6 +342,7 @@ Route::group(
|
|||||||
// Attachment API routes:
|
// Attachment API routes:
|
||||||
Route::get('transactions', ['uses' => 'TransactionController@search', 'as' => 'transactions']);
|
Route::get('transactions', ['uses' => 'TransactionController@search', 'as' => 'transactions']);
|
||||||
Route::get('accounts', ['uses' => 'AccountController@search', 'as' => 'accounts']);
|
Route::get('accounts', ['uses' => 'AccountController@search', 'as' => 'accounts']);
|
||||||
|
Route::get('transfers', ['uses' => 'TransferController@search', 'as' => 'transfers']);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user