Expand API for recurring transactions.

This commit is contained in:
James Cole 2018-12-07 20:20:54 +01:00
parent 8e4092e7d7
commit f0de469053
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
7 changed files with 165 additions and 13 deletions

View File

@ -24,9 +24,17 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\RecurrenceRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Models\Recurrence;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
use FireflyIII\Support\Cronjobs\RecurringCronjob;
use FireflyIII\Support\Http\Api\Transactions;
use FireflyIII\Transformers\RecurrenceTransformer;
use FireflyIII\Transformers\TransactionTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@ -36,12 +44,14 @@ use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
use Log;
/**
* Class RecurrenceController
*/
class RecurrenceController extends Controller
{
use Transactions;
/** @var RecurringRepositoryInterface The recurring transaction repository */
private $repository;
@ -156,6 +166,79 @@ class RecurrenceController extends Controller
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Show transactions for this recurrence.
*
* @param Request $request
* @param Recurrence $recurrence
*
* @return JsonResponse
*/
public function transactions(Request $request, Recurrence $recurrence): JsonResponse
{
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
$type = $request->get('type') ?? 'default';
$this->parameters->set('type', $type);
$types = $this->mapTypes($this->parameters->get('type'));
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// whatever is returned by the query, it must be part of these journals:
$journalIds = $this->repository->getJournalIds($recurrence);
/** @var User $admin */
$admin = auth()->user();
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($admin);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
$collector->setAllAssetAccounts();
$collector->setJournalIds($journalIds);
if (\in_array(TransactionType::TRANSFER, $types, true)) {
$collector->removeFilter(InternalTransferFilter::class);
}
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$collector->setRange($this->parameters->get('start'), $this->parameters->get('end'));
}
$collector->setLimit($pageSize)->setPage($this->parameters->get('page'));
$collector->setTypes($types);
$paginator = $collector->getPaginatedTransactions();
$paginator->setPath(route('api.v1.transactions.index') . $this->buildParams());
$transactions = $paginator->getCollection();
$repository = app(JournalRepositoryInterface::class);
$resource = new FractalCollection($transactions, new TransactionTransformer($this->parameters, $repository), 'transactions');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* @return JsonResponse
* @throws FireflyException
*/
public function trigger(): JsonResponse
{
$recurring = new RecurringCronjob;
try {
$result = $recurring->fire();
} catch (FireflyException $e) {
Log::error($e->getMessage());
throw new FireflyException('Could not fire recurring cron job.');
}
if (false === $result) {
return response()->json([], 204);
}
if (true === $result) {
return response()->json([], 200);
}
throw new FireflyException('Could not fire recurring cron job.');
}
/**
* Update single recurrence.
*

View File

@ -25,6 +25,7 @@ namespace FireflyIII\Api\V1\Requests;
use Carbon\Carbon;
use FireflyIII\Rules\BelongsUser;
use FireflyIII\Rules\IsBoolean;
use FireflyIII\Validation\RecurrenceValidation;
use FireflyIII\Validation\TransactionValidation;
use Illuminate\Validation\Validator;
@ -54,6 +55,14 @@ class RecurrenceRequest extends Request
*/
public function getAll(): array
{
$active = true;
$applyRules = true;
if (null !== $this->get('active')) {
$active = $this->boolean('active');
}
if (null !== $this->get('apply_rules')) {
$applyRules = $this->boolean('apply_rules');
}
$return = [
'recurrence' => [
'type' => $this->string('type'),
@ -62,8 +71,8 @@ class RecurrenceRequest extends Request
'first_date' => $this->date('first_date'),
'repeat_until' => $this->date('repeat_until'),
'repetitions' => $this->integer('nr_of_repetitions'),
'apply_rules' => $this->boolean('apply_rules'),
'active' => $this->boolean('active'),
'apply_rules' => $applyRules,
'active' => $active,
],
'meta' => [
'piggy_bank_id' => $this->integer('piggy_bank_id'),
@ -93,8 +102,8 @@ class RecurrenceRequest extends Request
'first_date' => sprintf('required|date|after:%s', $today->format('Y-m-d')),
'repeat_until' => sprintf('date|after:%s', $today->format('Y-m-d')),
'nr_of_repetitions' => 'numeric|between:1,31',
'apply_rules' => 'required|boolean',
'active' => 'required|boolean',
'apply_rules' => [new IsBoolean],
'active' => [new IsBoolean],
'tags' => 'between:1,64000',
'piggy_bank_id' => 'numeric',
'repetitions.*.type' => 'required|in:daily,weekly,ndom,monthly,yearly',

View File

@ -527,6 +527,22 @@ class TransactionCollector implements TransactionCollectorInterface
return $this;
}
/**
* @param array $journalIds
*
* @return TransactionCollectorInterface
*/
public function setJournalIds(array $journalIds): TransactionCollectorInterface
{
$this->query->where(
function (EloquentBuilder $q) use ($journalIds) {
$q->whereIn('transaction_journals.id', $journalIds);
}
);
return $this;
}
/**
* @param Collection $journals
*

View File

@ -82,13 +82,6 @@ interface TransactionCollectorInterface
*/
public function count(): int;
/**
* Get all transactions.
*
* @return Collection
*/
public function getTransactions(): Collection;
/**
* Get a paginated result.
*
@ -103,6 +96,13 @@ interface TransactionCollectorInterface
*/
public function getQuery(): EloquentBuilder;
/**
* Get all transactions.
*
* @return Collection
*/
public function getTransactions(): Collection;
/**
* Set to ignore the cache.
*
@ -198,6 +198,15 @@ interface TransactionCollectorInterface
*/
public function setCategory(Category $category): TransactionCollectorInterface;
/**
* Set the journal IDs to filter on.
*
* @param array $journalIds
*
* @return TransactionCollectorInterface
*/
public function setJournalIds(array $journalIds): TransactionCollectorInterface;
/**
* Set the journals to filter on.
*

View File

@ -175,6 +175,22 @@ class RecurringRepository implements RecurringRepositoryInterface
return $query->get(['transaction_journals.*'])->count();
}
/**
* Get journal ID's for journals created by this recurring transaction.
*
* @param Recurrence $recurrence
*
* @return array
*/
public function getJournalIds(Recurrence $recurrence): array
{
return TransactionJournalMeta::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id')
->where('transaction_journals.user_id', $this->user->id)
->where('journal_meta.name', '=', 'recurrence_id')
->where('journal_meta.data', '=', json_encode((string)$recurrence->id))
->get(['journal_meta.transaction_journal_id'])->pluck('transaction_journal_id')->toArray();
}
/**
* Get the notes.
*
@ -500,6 +516,8 @@ class RecurringRepository implements RecurringRepositoryInterface
return $return;
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* Get the number of daily occurrences for a recurring transaction until date $end is reached. Will skip every $skipMod-1 occurrences.
*
@ -528,6 +546,7 @@ class RecurringRepository implements RecurringRepositoryInterface
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* Get the number of daily occurrences for a recurring transaction until date $end is reached. Will skip every $skipMod-1 occurrences.
*
@ -575,6 +594,7 @@ class RecurringRepository implements RecurringRepositoryInterface
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* Get the number of daily occurrences for a recurring transaction until date $end is reached. Will skip every $skipMod-1 occurrences.
*
@ -607,7 +627,6 @@ class RecurringRepository implements RecurringRepositoryInterface
return $return;
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* Get the number of daily occurrences for a recurring transaction until date $end is reached. Will skip every $skipMod-1 occurrences.
*
@ -652,6 +671,8 @@ class RecurringRepository implements RecurringRepositoryInterface
return $return;
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* Calculates the number of daily occurrences for a recurring transaction, starting at the date, until $count is reached. It will skip
* over $skipMod -1 recurrences.
@ -681,6 +702,7 @@ class RecurringRepository implements RecurringRepositoryInterface
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* Calculates the number of monthly occurrences for a recurring transaction, starting at the date, until $count is reached. It will skip
* over $skipMod -1 recurrences.
@ -720,6 +742,7 @@ class RecurringRepository implements RecurringRepositoryInterface
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* Calculates the number of NDOM occurrences for a recurring transaction, starting at the date, until $count is reached. It will skip
* over $skipMod -1 recurrences.
@ -759,6 +782,7 @@ class RecurringRepository implements RecurringRepositoryInterface
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* Calculates the number of weekly occurrences for a recurring transaction, starting at the date, until $count is reached. It will skip
* over $skipMod -1 recurrences.
@ -802,6 +826,7 @@ class RecurringRepository implements RecurringRepositoryInterface
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* Calculates the number of yearly occurrences for a recurring transaction, starting at the date, until $count is reached. It will skip
* over $skipMod -1 recurrences.
@ -838,7 +863,6 @@ class RecurringRepository implements RecurringRepositoryInterface
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* Get the number of daily occurrences for a recurring transaction until date $end is reached. Will skip every $skipMod-1 occurrences.
*

View File

@ -89,6 +89,15 @@ interface RecurringRepositoryInterface
*/
public function getJournalCount(Recurrence $recurrence, Carbon $start = null, Carbon $end = null): int;
/**
* Get journal ID's for journals created by this recurring transaction.
*
* @param Recurrence $recurrence
*
* @return array
*/
public function getJournalIds(Recurrence $recurrence): array;
/**
* Get the notes.
*

View File

@ -200,9 +200,11 @@ Route::group(
// Recurrence API routes:
Route::get('', ['uses' => 'RecurrenceController@index', 'as' => 'index']);
Route::post('', ['uses' => 'RecurrenceController@store', 'as' => 'store']);
Route::post('trigger', ['uses' => 'RecurrenceController@trigger', 'as' => 'trigger']);
Route::get('{recurrence}', ['uses' => 'RecurrenceController@show', 'as' => 'show']);
Route::put('{recurrence}', ['uses' => 'RecurrenceController@update', 'as' => 'update']);
Route::delete('{recurrence}', ['uses' => 'RecurrenceController@delete', 'as' => 'delete']);
Route::get('{recurrence}/transactions', ['uses' => 'RecurrenceController@transactions', 'as' => 'transactions']);
}
);