diff --git a/app/Api/V1/Controllers/LinkTypeController.php b/app/Api/V1/Controllers/LinkTypeController.php index 6d3afaad13..c7e525028e 100644 --- a/app/Api/V1/Controllers/LinkTypeController.php +++ b/app/Api/V1/Controllers/LinkTypeController.php @@ -25,10 +25,16 @@ namespace FireflyIII\Api\V1\Controllers; use FireflyIII\Api\V1\Requests\LinkTypeRequest; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Helpers\Collector\TransactionCollectorInterface; +use FireflyIII\Helpers\Filter\InternalTransferFilter; use FireflyIII\Models\LinkType; +use FireflyIII\Models\TransactionType; +use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface; +use FireflyIII\Support\Http\Api\Transactions; use FireflyIII\Transformers\LinkTypeTransformer; +use FireflyIII\Transformers\TransactionTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -46,6 +52,7 @@ use League\Fractal\Serializer\JsonApiSerializer; */ class LinkTypeController extends Controller { + use Transactions; /** @var LinkTypeRepositoryInterface The link type repository */ private $repository; @@ -174,6 +181,58 @@ class LinkTypeController extends Controller } + /** + * Delete the resource. + * + * @param LinkType $linkType + * + * @return JsonResponse + */ + public function transactions(Request $request, LinkType $linkType): 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($linkType); + + /** @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'); + } + + + /** * Update object. * diff --git a/app/Api/V1/Controllers/PiggyBankController.php b/app/Api/V1/Controllers/PiggyBankController.php index 8ab73dbcdc..f5283a2a42 100644 --- a/app/Api/V1/Controllers/PiggyBankController.php +++ b/app/Api/V1/Controllers/PiggyBankController.php @@ -27,6 +27,7 @@ use FireflyIII\Api\V1\Requests\PiggyBankRequest; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\PiggyBank; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; +use FireflyIII\Transformers\PiggyBankEventTransformer; use FireflyIII\Transformers\PiggyBankTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; @@ -117,6 +118,38 @@ class PiggyBankController extends Controller } + /** + * List single resource. + * + * @param Request $request + * @param PiggyBank $piggyBank + * + * @return JsonResponse + */ + public function piggyBankEvents(Request $request, PiggyBank $piggyBank): JsonResponse + { + // types to get, page size: + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $manager = new Manager(); + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $collection = $piggyBank->piggyBankEvents()->get(); + $count = $collection->count(); + $events = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($events, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.piggy_banks.events', [$piggyBank->id]) . $this->buildParams()); + + + $resource = new FractalCollection($events, new PiggyBankEventTransformer($this->parameters), 'piggy_bank_events'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } + /** * List single resource. * diff --git a/app/Api/V1/Controllers/RuleController.php b/app/Api/V1/Controllers/RuleController.php index c4e74e3721..445b44133f 100644 --- a/app/Api/V1/Controllers/RuleController.php +++ b/app/Api/V1/Controllers/RuleController.php @@ -138,10 +138,6 @@ class RuleController extends Controller public function show(Request $request, Rule $rule): JsonResponse { $manager = new Manager(); - // add include parameter: - $include = $request->get('include') ?? ''; - $manager->parseIncludes($include); - $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; $manager->setSerializer(new JsonApiSerializer($baseUrl)); diff --git a/app/Api/V1/Controllers/JournalLinkController.php b/app/Api/V1/Controllers/TransactionLinkController.php similarity index 92% rename from app/Api/V1/Controllers/JournalLinkController.php rename to app/Api/V1/Controllers/TransactionLinkController.php index c428fc0146..708a2a7f69 100644 --- a/app/Api/V1/Controllers/JournalLinkController.php +++ b/app/Api/V1/Controllers/TransactionLinkController.php @@ -1,6 +1,6 @@ getForUser(auth()->user(), 'listPageSize', 50)->data; - $linkType = $this->repository->findByName($name); - // get list of accounts. Count it and split it. + // get list of transaction links. Count it and split it. $collection = $this->repository->getJournalLinks($linkType); $count = $collection->count(); $journalLinks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); // make paginator: $paginator = new LengthAwarePaginator($journalLinks, $count, $pageSize, $this->parameters->get('page')); - $paginator->setPath(route('api.v1.journal_links.index') . $this->buildParams()); + $paginator->setPath(route('api.v1.transaction_links.index') . $this->buildParams()); // present to user. $manager->setSerializer(new JsonApiSerializer($baseUrl)); - $resource = new FractalCollection($journalLinks, new JournalLinkTransformer($this->parameters), 'journal_links'); + $resource = new FractalCollection($journalLinks, new JournalLinkTransformer($this->parameters), 'transaction_links'); $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); @@ -144,7 +144,7 @@ class JournalLinkController extends Controller $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; $manager->setSerializer(new JsonApiSerializer($baseUrl)); - $resource = new Item($journalLink, new JournalLinkTransformer($this->parameters), 'journal_links'); + $resource = new Item($journalLink, new JournalLinkTransformer($this->parameters), 'transaction_links'); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); @@ -175,7 +175,7 @@ class JournalLinkController extends Controller $data['direction'] = 'inward'; $journalLink = $this->repository->storeLink($data, $inward, $outward); - $resource = new Item($journalLink, new JournalLinkTransformer($this->parameters), 'journal_links'); + $resource = new Item($journalLink, new JournalLinkTransformer($this->parameters), 'transaction_links'); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); @@ -208,7 +208,7 @@ class JournalLinkController extends Controller $data['direction'] = 'inward'; $journalLink = $this->repository->updateLink($journalLink, $data); - $resource = new Item($journalLink, new JournalLinkTransformer($this->parameters), 'journal_links'); + $resource = new Item($journalLink, new JournalLinkTransformer($this->parameters), 'transaction_links'); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); diff --git a/app/Api/V1/Requests/JournalLinkRequest.php b/app/Api/V1/Requests/JournalLinkRequest.php index 2c31e0d889..530af0d6de 100644 --- a/app/Api/V1/Requests/JournalLinkRequest.php +++ b/app/Api/V1/Requests/JournalLinkRequest.php @@ -71,8 +71,8 @@ class JournalLinkRequest extends Request return [ 'link_type_id' => 'exists:link_types,id|required_without:link_type_name', 'link_type_name' => 'exists:link_types,name|required_without:link_type_id', - 'inward_id' => 'required|belongsToUser:transaction_journals,id', - 'outward_id' => 'required|belongsToUser:transaction_journals,id', + 'inward_id' => 'required|belongsToUser:transaction_journals,id|different:outward_id', + 'outward_id' => 'required|belongsToUser:transaction_journals,id|different:inward_id', 'notes' => 'between:0,65000', ]; } @@ -124,8 +124,12 @@ class JournalLinkRequest extends Request } if ($repository->findLink($inward, $outward)) { - $validator->errors()->add('outward_id', 'Already have a link between inward and outward.'); - $validator->errors()->add('inward_id', 'Already have a link between inward and outward.'); + // only if not updating: + $link = $this->route()->parameter('journalLink'); + if (null === $link) { + $validator->errors()->add('outward_id', 'Already have a link between inward and outward.'); + $validator->errors()->add('inward_id', 'Already have a link between inward and outward.'); + } } } } diff --git a/app/Repositories/LinkType/LinkTypeRepository.php b/app/Repositories/LinkType/LinkTypeRepository.php index f7e8c7fcee..dc3cc71deb 100644 --- a/app/Repositories/LinkType/LinkTypeRepository.php +++ b/app/Repositories/LinkType/LinkTypeRepository.php @@ -156,6 +156,22 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface return LinkType::orderBy('name', 'ASC')->get(); } + /** + * Return array of all journal ID's for this type of link. + * + * @param LinkType $linkType + * + * @return array + */ + public function getJournalIds(LinkType $linkType): array + { + $links = $linkType->transactionJournalLinks()->get(['source_id', 'destination_id']); + $sources = $links->pluck('source_id')->toArray(); + $destinations = $links->pluck('destination_id')->toArray(); + + return array_unique(array_merge($sources, $destinations)); + } + /** * Returns all the journal links (of a specific type). * diff --git a/app/Repositories/LinkType/LinkTypeRepositoryInterface.php b/app/Repositories/LinkType/LinkTypeRepositoryInterface.php index 02080dcc0e..85c37c4644 100644 --- a/app/Repositories/LinkType/LinkTypeRepositoryInterface.php +++ b/app/Repositories/LinkType/LinkTypeRepositoryInterface.php @@ -97,6 +97,15 @@ interface LinkTypeRepositoryInterface */ public function get(): Collection; + /** + * Return array of all journal ID's for this type of link. + * + * @param LinkType $linkType + * + * @return array + */ + public function getJournalIds(LinkType $linkType): array; + /** * @param LinkType|null $linkType * diff --git a/app/Transformers/JournalLinkTransformer.php b/app/Transformers/JournalLinkTransformer.php index 43e5f386dd..b7cd15dc79 100644 --- a/app/Transformers/JournalLinkTransformer.php +++ b/app/Transformers/JournalLinkTransformer.php @@ -24,11 +24,8 @@ declare(strict_types=1); namespace FireflyIII\Transformers; -use FireflyIII\Helpers\Collector\TransactionCollectorInterface; use FireflyIII\Models\Note; use FireflyIII\Models\TransactionJournalLink; -use Illuminate\Support\Collection; -use League\Fractal\Resource\Item; use League\Fractal\TransformerAbstract; use Symfony\Component\HttpFoundation\ParameterBag; @@ -38,19 +35,6 @@ use Symfony\Component\HttpFoundation\ParameterBag; */ class JournalLinkTransformer extends TransformerAbstract { - /** - * List of resources possible to include - * - * @var array - */ - protected $availableIncludes = ['inward', 'outward', 'link_type']; - /** - * List of resources to automatically include - * - * @var array - */ - protected $defaultIncludes = ['inward', 'outward', 'link_type']; - /** @var ParameterBag */ protected $parameters; @@ -66,54 +50,6 @@ class JournalLinkTransformer extends TransformerAbstract $this->parameters = $parameters; } - /** - * @param TransactionJournalLink $link - * - * @return Item - */ - public function includeInward(TransactionJournalLink $link): Item - { - // need to use the collector to get the transaction :( - // journals always use collector and limited using URL parameters. - /** @var TransactionCollectorInterface $collector */ - $collector = app(TransactionCollectorInterface::class); - $collector->setUser($link->source->user); - $collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation(); - $collector->setJournals(new Collection([$link->source])); - $transactions = $collector->getTransactions(); - - return $this->item($transactions->first(), new TransactionTransformer($this->parameters), 'transactions'); - } - - /** - * @param TransactionJournalLink $link - * - * @return Item - */ - public function includeLinkType(TransactionJournalLink $link): Item - { - return $this->item($link->linkType, new LinkTypeTransformer($this->parameters), 'link_types'); - } - - /** - * @param TransactionJournalLink $link - * - * @return Item - */ - public function includeOutward(TransactionJournalLink $link): Item - { - // need to use the collector to get the transaction :( - // journals always use collector and limited using URL parameters. - /** @var TransactionCollectorInterface $collector */ - $collector = app(TransactionCollectorInterface::class); - $collector->setUser($link->source->user); - $collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation(); - $collector->setJournals(new Collection([$link->destination])); - $transactions = $collector->getTransactions(); - - return $this->item($transactions->first(), new TransactionTransformer($this->parameters), 'transactions'); - } - /** * @param TransactionJournalLink $link * @@ -121,7 +57,7 @@ class JournalLinkTransformer extends TransformerAbstract */ public function transform(TransactionJournalLink $link): array { - $notes = ''; + $notes = null; /** @var Note $note */ $note = $link->notes()->first(); if (null !== $note) { @@ -132,11 +68,13 @@ class JournalLinkTransformer extends TransformerAbstract 'id' => (int)$link->id, 'updated_at' => $link->updated_at->toAtomString(), 'created_at' => $link->created_at->toAtomString(), + 'inward_id' => $link->source_id, + 'outward_id' => $link->destination_id, 'notes' => $notes, 'links' => [ [ 'rel' => 'self', - 'uri' => '/journal_links/' . $link->id, + 'uri' => '/transaction_links/' . $link->id, ], ], ]; diff --git a/app/Transformers/PiggyBankTransformer.php b/app/Transformers/PiggyBankTransformer.php index af41ba7087..c5883fa4ce 100644 --- a/app/Transformers/PiggyBankTransformer.php +++ b/app/Transformers/PiggyBankTransformer.php @@ -40,8 +40,6 @@ use Symfony\Component\HttpFoundation\ParameterBag; */ class PiggyBankTransformer extends TransformerAbstract { - - /** @var ParameterBag */ protected $parameters; diff --git a/routes/api.php b/routes/api.php index 323aedf164..81e9fcbf26 100644 --- a/routes/api.php +++ b/routes/api.php @@ -147,12 +147,7 @@ Route::group( ['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'journal_links', 'as' => 'api.v1.journal_links.'], function () { - // Journal Link API routes: - Route::get('', ['uses' => 'JournalLinkController@index', 'as' => 'index']); - Route::post('', ['uses' => 'JournalLinkController@store', 'as' => 'store']); - Route::get('{journalLink}', ['uses' => 'JournalLinkController@show', 'as' => 'show']); - Route::put('{journalLink}', ['uses' => 'JournalLinkController@update', 'as' => 'update']); - Route::delete('{journalLink}', ['uses' => 'JournalLinkController@delete', 'as' => 'delete']); + } ); @@ -166,6 +161,21 @@ Route::group( Route::get('{linkType}', ['uses' => 'LinkTypeController@show', 'as' => 'show']); Route::put('{linkType}', ['uses' => 'LinkTypeController@update', 'as' => 'update']); Route::delete('{linkType}', ['uses' => 'LinkTypeController@delete', 'as' => 'delete']); + Route::get('{linkType}/transactions', ['uses' => 'LinkTypeController@transactions', 'as' => 'transactions']); + } +); + +Route::group( + ['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'transaction_links', 'as' => 'api.v1.transaction_links.'], + function () { + + // Transaction Links API routes: + Route::get('', ['uses' => 'TransactionLinkController@index', 'as' => 'index']); + Route::post('', ['uses' => 'TransactionLinkController@store', 'as' => 'store']); + Route::get('{journalLink}', ['uses' => 'TransactionLinkController@show', 'as' => 'show']); + Route::put('{journalLink}', ['uses' => 'TransactionLinkController@update', 'as' => 'update']); + Route::delete('{journalLink}', ['uses' => 'TransactionLinkController@delete', 'as' => 'delete']); + } ); @@ -177,6 +187,7 @@ Route::group( Route::get('', ['uses' => 'PiggyBankController@index', 'as' => 'index']); Route::post('', ['uses' => 'PiggyBankController@store', 'as' => 'store']); Route::get('{piggyBank}', ['uses' => 'PiggyBankController@show', 'as' => 'show']); + Route::get('{piggyBank}/events', ['uses' => 'PiggyBankController@piggyBankEvents', 'as' => 'events']); Route::put('{piggyBank}', ['uses' => 'PiggyBankController@update', 'as' => 'update']); Route::delete('{piggyBank}', ['uses' => 'PiggyBankController@delete', 'as' => 'delete']); }