diff --git a/app/Api/V1/Controllers/AccountController.php b/app/Api/V1/Controllers/AccountController.php index 2dd530ed8c..f82f2881aa 100644 --- a/app/Api/V1/Controllers/AccountController.php +++ b/app/Api/V1/Controllers/AccountController.php @@ -24,16 +24,23 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers; use FireflyIII\Api\V1\Requests\AccountRequest; +use FireflyIII\Helpers\Collector\TransactionCollectorInterface; +use FireflyIII\Helpers\Filter\InternalTransferFilter; use FireflyIII\Models\Account; -use FireflyIII\Models\AccountType; +use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Support\Http\Api\AccountFilter; +use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Transformers\AccountTransformer; +use FireflyIII\Transformers\PiggyBankTransformer; +use FireflyIII\Transformers\TransactionTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; +use Illuminate\Support\Collection; use League\Fractal\Manager; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; @@ -47,7 +54,7 @@ use League\Fractal\Serializer\JsonApiSerializer; */ class AccountController extends Controller { - use AccountFilter; + use AccountFilter, TransactionFilter; /** @var CurrencyRepositoryInterface The currency repository */ private $currencyRepository; /** @var AccountRepositoryInterface The account repository */ @@ -127,6 +134,41 @@ class AccountController extends Controller return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); } + /** + * List all of them. + * + * @param Request $request + * @param Account $account + * + * @return JsonResponse] + */ + public function piggyBanks(Request $request, Account $account): JsonResponse + { + // create some objects: + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + + // types to get, page size: + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of budgets. Count it and split it. + $collection = $this->repository->getPiggyBanks($account); + $count = $collection->count(); + $piggyBanks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.accounts.piggy_banks', [$account->id]) . $this->buildParams()); + + // present to user. + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new FractalCollection($piggyBanks, new PiggyBankTransformer($this->parameters), 'piggy_banks'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } + /** * Show single instance. * @@ -170,6 +212,58 @@ class AccountController extends Controller return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); } + /** + * Show all transactions. + * + * @param Request $request + * @param Account $account + * + * @return JsonResponse + */ + public function transactions(Request $request, Account $account): JsonResponse + { + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $type = $request->get('type') ?? 'default'; + $this->parameters->set('type', $type); + + $types = $this->mapTransactionTypes($this->parameters->get('type')); + $manager = new Manager(); + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + /** @var User $admin */ + $admin = auth()->user(); + /** @var TransactionCollectorInterface $collector */ + $collector = app(TransactionCollectorInterface::class); + $collector->setUser($admin); + $collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation(); + if ($this->repository->isAsset($account)) { + $collector->setAccounts(new Collection([$account])); + } + if (!$this->repository->isAsset($account)) { + $collector->setOpposingAccounts(new Collection([$account])); + } + + 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.accounts.transactions', [$account->id]) . $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 account. * diff --git a/app/Api/V1/Controllers/BillController.php b/app/Api/V1/Controllers/BillController.php index 234e311eef..cda5100045 100644 --- a/app/Api/V1/Controllers/BillController.php +++ b/app/Api/V1/Controllers/BillController.php @@ -26,12 +26,19 @@ namespace FireflyIII\Api\V1\Controllers; use FireflyIII\Api\V1\Requests\BillRequest; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Helpers\Collector\TransactionCollectorInterface; use FireflyIII\Models\Bill; use FireflyIII\Repositories\Bill\BillRepositoryInterface; +use FireflyIII\Repositories\Journal\JournalRepositoryInterface; +use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Transformers\AttachmentTransformer; use FireflyIII\Transformers\BillTransformer; +use FireflyIII\Transformers\RuleTransformer; +use FireflyIII\Transformers\TransactionTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use League\Fractal\Manager; use League\Fractal\Pagination\IlluminatePaginatorAdapter; @@ -44,6 +51,7 @@ use League\Fractal\Serializer\JsonApiSerializer; */ class BillController extends Controller { + use TransactionFilter; /** @var BillRepositoryInterface The bill repository */ private $repository; @@ -67,6 +75,38 @@ class BillController extends Controller ); } + /** + * Display a listing of the resource. + * + * @param Request $request + * @param Bill $bill + * + * @return JsonResponse + */ + public function attachments(Request $request, Bill $bill): JsonResponse + { + $manager = new Manager(); + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $collection = $this->repository->getAttachments($bill); + + $count = $collection->count(); + $attachments = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($attachments, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.bills.attachments', [$bill->id]) . $this->buildParams()); + + // present to user. + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new FractalCollection($attachments, new AttachmentTransformer($this->parameters), 'attachments'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + /** * Remove the specified resource from storage. * @@ -105,6 +145,40 @@ class BillController extends Controller return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); } + /** + * List all of them. + * + * @param Request $request + * @param Bill $bill + * + * @return JsonResponse + */ + public function rules(Request $request, Bill $bill): JsonResponse + { + // create some objects: + $manager = new Manager; + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + + // types to get, page size: + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + + // get list of budgets. Count it and split it. + $collection = $this->repository->getRulesForBill($bill); + $count = $collection->count(); + $rules = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + + // make paginator: + $paginator = new LengthAwarePaginator($rules, $count, $pageSize, $this->parameters->get('page')); + $paginator->setPath(route('api.v1.bills.rules', [$bill->id]) . $this->buildParams()); + + // present to user. + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $resource = new FractalCollection($rules, new RuleTransformer($this->parameters), 'rules'); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } /** * Show the specified bill. @@ -149,6 +223,49 @@ class BillController extends Controller } + /** + * Show all transactions. + * + * @param Request $request + * + * @param Bill $bill + * + * @return JsonResponse + */ + public function transactions(Request $request, Bill $bill): JsonResponse + { + $pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data; + $type = $request->get('type') ?? 'default'; + $this->parameters->set('type', $type); + + $types = $this->mapTransactionTypes($this->parameters->get('type')); + $manager = new Manager(); + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + /** @var User $admin */ + $admin = auth()->user(); + /** @var TransactionCollectorInterface $collector */ + $collector = app(TransactionCollectorInterface::class); + $collector->setUser($admin); + $collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation(); + $collector->setAllAssetAccounts(); + $collector->setBills(new Collection([$bill])); + + 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.bills.transactions', [$bill->id]) . $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 a bill. diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index 2e50399817..923a72cba7 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -386,6 +386,16 @@ class AccountRepository implements AccountRepositoryInterface return $journal->date->format('Y-m-d'); } + /** + * @param Account $account + * + * @return Collection + */ + public function getPiggyBanks(Account $account): Collection + { + return $account->piggyBanks()->get(); + } + /** * @param Account $account * @@ -416,6 +426,18 @@ class AccountRepository implements AccountRepositoryInterface return $account; } + /** + * @param Account $account + * + * @return bool + */ + public function isAsset(Account $account): bool + { + $type = $account->accountType->type; + + return AccountType::ASSET === $type || AccountType::DEFAULT === $type; + } + /** * @param Account $account * diff --git a/app/Repositories/Account/AccountRepositoryInterface.php b/app/Repositories/Account/AccountRepositoryInterface.php index 77099390d3..b70582453c 100644 --- a/app/Repositories/Account/AccountRepositoryInterface.php +++ b/app/Repositories/Account/AccountRepositoryInterface.php @@ -35,7 +35,6 @@ use Illuminate\Support\Collection; */ interface AccountRepositoryInterface { - /** * Moved here from account CRUD. * @@ -167,6 +166,13 @@ interface AccountRepositoryInterface */ public function getOpeningBalanceDate(Account $account): ?string; + /** + * @param Account $account + * + * @return Collection + */ + public function getPiggyBanks(Account $account): Collection; + /** * Find or create the opposing reconciliation account. * @@ -176,6 +182,13 @@ interface AccountRepositoryInterface */ public function getReconciliation(Account $account): ?Account; + /** + * @param Account $account + * + * @return bool + */ + public function isAsset(Account $account): bool; + /** * @param Account $account * diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index 0b2f837e54..62d6acfd63 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -121,6 +121,18 @@ class BillRepository implements BillRepositoryInterface return $set; } + /** + * Get all attachments. + * + * @param Bill $bill + * + * @return Collection + */ + public function getAttachments(Bill $bill): Collection + { + return $bill->attachments()->get(); + } + /** * @return Collection */ @@ -148,7 +160,8 @@ class BillRepository implements BillRepositoryInterface public function getBillsForAccounts(Collection $accounts): Collection { $fields = ['bills.id', 'bills.created_at', 'bills.updated_at', 'bills.deleted_at', 'bills.user_id', 'bills.name', 'bills.match', 'bills.amount_min', - 'bills.amount_max', 'bills.date','bills.transaction_currency_id', 'bills.repeat_freq', 'bills.skip', 'bills.automatch', 'bills.active', 'bills.name_encrypted', + 'bills.amount_max', 'bills.date', 'bills.transaction_currency_id', 'bills.repeat_freq', 'bills.skip', 'bills.automatch', 'bills.active', + 'bills.name_encrypted', 'bills.match_encrypted',]; $ids = $accounts->pluck('id')->toArray(); $set = $this->user->bills() @@ -381,9 +394,11 @@ class BillRepository implements BillRepositoryInterface */ public function getPaidDatesInRange(Bill $bill, Carbon $start, Carbon $end): Collection { - $dates = $bill->transactionJournals()->before($end)->after($start)->get([ - 'transaction_journals.id','transaction_journals.date' - ])->pluck('date', 'id'); + $dates = $bill->transactionJournals()->before($end)->after($start)->get( + [ + 'transaction_journals.id', 'transaction_journals.date', + ] + )->pluck('date', 'id'); return $dates; } diff --git a/app/Repositories/Bill/BillRepositoryInterface.php b/app/Repositories/Bill/BillRepositoryInterface.php index a1a8e6dfcb..2046fe4c90 100644 --- a/app/Repositories/Bill/BillRepositoryInterface.php +++ b/app/Repositories/Bill/BillRepositoryInterface.php @@ -64,6 +64,15 @@ interface BillRepositoryInterface */ public function getActiveBills(): Collection; + /** + * Get all attachments. + * + * @param Bill $bill + * + * @return Collection + */ + public function getAttachments(Bill $bill): Collection; + /** * @return Collection */ diff --git a/routes/api.php b/routes/api.php index 77f7a1d6ad..6ff91d91d4 100644 --- a/routes/api.php +++ b/routes/api.php @@ -40,6 +40,10 @@ Route::group( Route::get('{account}', ['uses' => 'AccountController@show', 'as' => 'show']); Route::put('{account}', ['uses' => 'AccountController@update', 'as' => 'update']); Route::delete('{account}', ['uses' => 'AccountController@delete', 'as' => 'delete']); + + Route::get('{account}/piggy_banks', ['uses' => 'AccountController@piggyBanks', 'as' => 'piggy_banks']); + Route::get('{account}/transactions', ['uses' => 'AccountController@transactions', 'as' => 'transactions']); + } ); @@ -72,6 +76,22 @@ Route::group( } ); +Route::group( + ['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'bills', 'as' => 'api.v1.bills.'], function () { + + // Bills API routes: + Route::get('', ['uses' => 'BillController@index', 'as' => 'index']); + Route::post('', ['uses' => 'BillController@store', 'as' => 'store']); + Route::get('{bill}', ['uses' => 'BillController@show', 'as' => 'show']); + Route::put('{bill}', ['uses' => 'BillController@update', 'as' => 'update']); + Route::delete('{bill}', ['uses' => 'BillController@delete', 'as' => 'delete']); + + Route::get('{bill}/attachments', ['uses' => 'BillController@attachments', 'as' => 'attachments']); + Route::get('{bill}/rules', ['uses' => 'BillController@rules', 'as' => 'rules']); + Route::get('{bill}/transactions', ['uses' => 'BillController@transactions', 'as' => 'transactions']); +} +); + Route::group( ['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'budgets/limits', 'as' => 'api.v1.budget_limits.'], @@ -103,22 +123,6 @@ Route::group( } ); - - - - -Route::group( - ['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'bills', 'as' => 'api.v1.bills.'], function () { - - // Bills API routes: - Route::get('', ['uses' => 'BillController@index', 'as' => 'index']); - Route::post('', ['uses' => 'BillController@store', 'as' => 'store']); - Route::get('{bill}', ['uses' => 'BillController@show', 'as' => 'show']); - Route::put('{bill}', ['uses' => 'BillController@update', 'as' => 'update']); - Route::delete('{bill}', ['uses' => 'BillController@delete', 'as' => 'delete']); -} -); - Route::group( ['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'categories', 'as' => 'api.v1.categories.'], function () { @@ -177,7 +181,8 @@ Route::group( ); Route::group( - ['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'transaction_links', 'as' => 'api.v1.transaction_links.'], + ['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'transaction_links', + 'as' => 'api.v1.transaction_links.'], function () { // Transaction Links API routes: @@ -301,7 +306,6 @@ Route::group( ); - Route::group( ['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'transactions', 'as' => 'api.v1.transactions.'], function () {