Expand API with available budgets.

This commit is contained in:
James Cole 2018-06-24 08:33:06 +02:00
parent ad6a9a7df7
commit 91701473af
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
12 changed files with 522 additions and 27 deletions

View File

@ -69,13 +69,15 @@ class AttachmentController extends Controller
/**
* Remove the specified resource from storage.
*
* @param int $id
* @param Attachment $attachment
*
* @return \Illuminate\Http\Response
* @return JsonResponse
*/
public function destroy($id)
public function delete(Attachment $attachment): JsonResponse
{
//
$this->repository->destroy($attachment);
return response()->json([], 204);
}
/**

View File

@ -0,0 +1,183 @@
<?php
/**
* AvailableBudgetController.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\AvailableBudgetRequest;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Transformers\AvailableBudgetTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/**
* Class AvailableBudgetController
*/
class AvailableBudgetController extends Controller
{
/** @var CurrencyRepositoryInterface */
private $currencyRepository;
/** @var BudgetRepositoryInterface */
private $repository;
/**
* AccountController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->repository = app(BudgetRepositoryInterface::class);
$this->currencyRepository = app(CurrencyRepositoryInterface::class);
$this->repository->setUser($user);
return $next($request);
}
);
}
/**
* Remove the specified resource from storage.
*
* @param AvailableBudget $availableBudget
*
* @return JsonResponse
*/
public function delete(AvailableBudget $availableBudget): JsonResponse
{
$this->repository->destroyAvailableBudget($availableBudget);
return response()->json([], 204);
}
/**
* Display a listing of the resource.
*
* @param Request $request
*
* @return JsonResponse
*/
public function index(Request $request): 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 accounts. Count it and split it.
$collection = $this->repository->getAvailableBudgets();
$count = $collection->count();
$availableBudgets = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// make paginator:
$paginator = new LengthAwarePaginator($availableBudgets, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.available_budgets.index') . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new FractalCollection($availableBudgets, new AvailableBudgetTransformer($this->parameters), 'available_budgets');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Display the specified resource.
*
* @param Request $request
* @param AvailableBudget $availableBudget
*
* @return JsonResponse
*/
public function show(Request $request, AvailableBudget $availableBudget): JsonResponse
{
$manager = new Manager;
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($availableBudget, new AvailableBudgetTransformer($this->parameters), 'available_budgets');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Store a newly created resource in storage.
*
* @param AvailableBudgetRequest $request
*
* @return JsonResponse
*/
public function store(AvailableBudgetRequest $request): JsonResponse
{
$data = $request->getAll();
$currency = $this->currencyRepository->findNull($data['transaction_currency_id']);
$availableBudget = $this->repository->setAvailableBudget($currency, $data['start_date'], $data['end_date'], $data['amount']);
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($availableBudget, new AvailableBudgetTransformer($this->parameters), 'available_budgets');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* Update the specified resource in storage.
*
* @param AvailableBudgetRequest $request
* @param AvailableBudget $availableBudget
*
* @return JsonResponse
*/
public function update(AvailableBudgetRequest $request, AvailableBudget $availableBudget): JsonResponse
{
$data = $request->getAll();
$this->repository->updateAvailableBudget($availableBudget, $data);
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($availableBudget, new AvailableBudgetTransformer($this->parameters), 'available_budgets');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
}

View File

@ -0,0 +1,69 @@
<?php
/**
* AvailableBudgetRequest.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
/**
* Class AvailableBudgetRequest
*/
class AvailableBudgetRequest extends Request
{
/**
* @return bool
*/
public function authorize(): bool
{
// Only allow authenticated users
return auth()->check();
}
/**
* @return array
*/
public function getAll(): array
{
return [
'transaction_currency_id' => $this->integer('transaction_currency_id'),
'amount' => $this->string('amount'),
'start_date' => $this->date('start_date'),
'end_date' => $this->date('end_date'),
];
}
/**
* @return array
*/
public function rules(): array
{
$rules = [
'transaction_currency_id' => 'required|numeric|exists:transaction_currencies,id',
'amount' => 'required|numeric|more:0',
'start_date' => 'required|date|before:end_date',
'end_date' => 'required|date|after:start_date',
];
return $rules;
}
}

View File

@ -22,13 +22,25 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Carbon\Carbon;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class AvailableBudget.
*
* @property int $id
* @property Carbon $created_at
* @property Carbon $updated_at
* @property User $user
* @property TransactionCurrency $transactionCurrency
* @property int $transaction_currency_id
* @property Carbon $start_date
* @property Carbon $end_date
* @property string $amount
*/
class AvailableBudget extends Model
{
@ -49,11 +61,29 @@ class AvailableBudget extends Model
/** @var array */
protected $fillable = ['user_id', 'transaction_currency_id', 'amount', 'start_date', 'end_date'];
/**
* @param string $value
*
* @return AvailableBudget
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
public static function routeBinder(string $value): AvailableBudget
{
if (auth()->check()) {
$availableBudgetId = (int)$value;
$availableBudget = auth()->user()->availableBudgets()->find($availableBudgetId);
if (null !== $availableBudget) {
return $availableBudget;
}
}
throw new NotFoundHttpException;
}
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function transactionCurrency()
public function transactionCurrency(): BelongsTo
{
return $this->belongsTo(TransactionCurrency::class);
}

View File

@ -23,6 +23,8 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\Budget;
use Carbon\Carbon;
use Exception;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\AvailableBudget;
@ -156,15 +158,30 @@ class BudgetRepository implements BudgetRepositoryInterface
* @param Budget $budget
*
* @return bool
* @throws \Exception
*/
public function destroy(Budget $budget): bool
{
$budget->delete();
try {
$budget->delete();
} catch (Exception $e) {
Log::error(sprintf('Could not delete budget: %s', $e->getMessage()));
}
return true;
}
/**
* @param AvailableBudget $availableBudget
*/
public function destroyAvailableBudget(AvailableBudget $availableBudget): void
{
try {
$availableBudget->delete();
} catch (Exception $e) {
Log::error(sprintf('Could not delete available budget: %s', $e->getMessage()));
}
}
/**
* Filters entries from the result set generated by getBudgetPeriodReport.
*
@ -355,6 +372,16 @@ class BudgetRepository implements BudgetRepositoryInterface
return $amount;
}
/**
* Returns all available budget objects.
*
* @return Collection
*/
public function getAvailableBudgets(): Collection
{
return $this->user->availableBudgets()->get();
}
/**
* @param Budget $budget
* @param Carbon $start
@ -527,9 +554,9 @@ class BudgetRepository implements BudgetRepositoryInterface
* @param Carbon $end
* @param string $amount
*
* @return bool
* @return AvailableBudget
*/
public function setAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end, string $amount): bool
public function setAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end, string $amount): AvailableBudget
{
$availableBudget = $this->user->availableBudgets()
->where('transaction_currency_id', $currency->id)
@ -545,7 +572,7 @@ class BudgetRepository implements BudgetRepositoryInterface
$availableBudget->amount = $amount;
$availableBudget->save();
return true;
return $availableBudget;
}
/**
@ -652,6 +679,35 @@ class BudgetRepository implements BudgetRepositoryInterface
return $budget;
}
/**
* @param AvailableBudget $availableBudget
* @param array $data
*
* @return AvailableBudget
* @throws FireflyException
*/
public function updateAvailableBudget(AvailableBudget $availableBudget, array $data): AvailableBudget
{
$existing = $this->user->availableBudgets()
->where('transaction_currency_id', $data['transaction_currency_id'])
->where('start_date', $data['start_date']->format('Y-m-d 00:00:00'))
->where('end_date', $data['end_date']->format('Y-m-d 00:00:00'))
->where('id', '!=', $availableBudget->id)
->first();
if (null !== $existing) {
throw new FireflyException(sprintf('An entry already exists for these parameters: available budget object with ID #%d', $existing->id));
}
$availableBudget->transaction_currency_id = $data['transaction_currency_id'];
$availableBudget->start_date = $data['start_date'];
$availableBudget->end_date = $data['end_date'];
$availableBudget->amount = $data['amount'];
$availableBudget->save();
return $availableBudget;
}
/**
* @param Budget $budget
* @param Carbon $start

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\Budget;
use Carbon\Carbon;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\TransactionCurrency;
@ -69,6 +70,11 @@ interface BudgetRepositoryInterface
*/
public function destroy(Budget $budget): bool;
/**
* @param AvailableBudget $availableBudget
*/
public function destroyAvailableBudget(AvailableBudget $availableBudget): void;
/**
* Filters entries from the result set generated by getBudgetPeriodReport.
*
@ -141,6 +147,13 @@ interface BudgetRepositoryInterface
*/
public function getAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end): string;
/**
* Returns all available budget objects.
*
* @return Collection
*/
public function getAvailableBudgets(): Collection;
/**
* @param Budget $budget
* @param Carbon $start
@ -194,9 +207,9 @@ interface BudgetRepositoryInterface
* @param Carbon $end
* @param string $amount
*
* @return bool
* @return AvailableBudget
*/
public function setAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end, string $amount): bool;
public function setAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end, string $amount): AvailableBudget;
/**
* @param User $user
@ -213,6 +226,7 @@ interface BudgetRepositoryInterface
*/
public function spentInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): string;
/**
* @param Collection $accounts
* @param Carbon $start
@ -237,6 +251,14 @@ interface BudgetRepositoryInterface
*/
public function update(Budget $budget, array $data): Budget;
/**
* @param AvailableBudget $availableBudget
* @param array $data
*
* @return AvailableBudget
*/
public function updateAvailableBudget(AvailableBudget $availableBudget, array $data): AvailableBudget;
/**
* @param Budget $budget
* @param Carbon $start

View File

@ -132,7 +132,7 @@ class AccountTransformer extends TransformerAbstract
*/
public function includeUser(Account $account): Item
{
return $this->item($account->user, new UserTransformer($this->parameters), 'user');
return $this->item($account->user, new UserTransformer($this->parameters), 'users');
}
/**

View File

@ -74,7 +74,7 @@ class AttachmentTransformer extends TransformerAbstract
*/
public function includeUser(Attachment $attachment): Item
{
return $this->item($attachment->user, new UserTransformer($this->parameters), 'user');
return $this->item($attachment->user, new UserTransformer($this->parameters), 'users');
}
/**

View File

@ -0,0 +1,117 @@
<?php
/**
* AvailableBudgetTransformer.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Transformers;
use FireflyIII\Models\AvailableBudget;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
use Symfony\Component\HttpFoundation\ParameterBag;
class AvailableBudgetTransformer extends TransformerAbstract
{
/**
* List of resources possible to include
*
* @var array
*/
protected $availableIncludes = ['transaction_currency', 'user'];
/**
* List of resources to automatically include
*
* @var array
*/
protected $defaultIncludes = ['transaction_currency'];
/** @var ParameterBag */
protected $parameters;
/**
* CurrencyTransformer constructor.
*
* @codeCoverageIgnore
*
* @param ParameterBag $parameters
*/
public function __construct(ParameterBag $parameters)
{
$this->parameters = $parameters;
}
/**
* Attach the currency.
*
* @codeCoverageIgnore
*
* @param AvailableBudget $availableBudget
*
* @return Item
*/
public function includeTransactionCurrency(AvailableBudget $availableBudget): Item
{
return $this->item($availableBudget->transactionCurrency, new CurrencyTransformer($this->parameters), 'transaction_currencies');
}
/**
* Attach the user.
*
* @codeCoverageIgnore
*
* @param AvailableBudget $availableBudget
*
* @return Item
*/
public function includeUser(AvailableBudget $availableBudget): Item
{
return $this->item($availableBudget->user, new UserTransformer($this->parameters), 'users');
}
/**
* Transform the note.
*
* @param AvailableBudget $availableBudget
*
* @return array
*/
public function transform(AvailableBudget $availableBudget): array
{
$data = [
'id' => (int)$availableBudget->id,
'updated_at' => $availableBudget->updated_at->toAtomString(),
'created_at' => $availableBudget->created_at->toAtomString(),
'start_date' => $availableBudget->start_date->format('Y-m-d'),
'end_date' => $availableBudget->end_date->format('Y-m-d'),
'amount' => round($availableBudget->amount, $availableBudget->transactionCurrency->decimal_places),
'links' => [
[
'rel' => 'self',
'uri' => '/available_budgets/' . $availableBudget->id,
],
],
];
return $data;
}
}

View File

@ -84,7 +84,7 @@ class RecurrenceTransformer extends TransformerAbstract
*/
public function includeUser(Recurrence $recurrence): Item
{
return $this->item($recurrence->user, new UserTransformer($this->parameters), 'user');
return $this->item($recurrence->user, new UserTransformer($this->parameters), 'users');
}
/**

View File

@ -83,16 +83,16 @@ use FireflyIII\TransactionRules\Triggers\UserAction;
*/
return [
'configuration' => [
'configuration' => [
'single_user_mode' => true,
'is_demo_site' => false,
],
'encryption' => null === env('USE_ENCRYPTION') || env('USE_ENCRYPTION') === true,
'version' => '4.7.4',
'api_version' => '0.3',
'db_version' => 4,
'maxUploadSize' => 15242880,
'allowedMimes' => [
'encryption' => null === env('USE_ENCRYPTION') || env('USE_ENCRYPTION') === true,
'version' => '4.7.4',
'api_version' => '0.3',
'db_version' => 4,
'maxUploadSize' => 15242880,
'allowedMimes' => [
/* plain files */
'text/plain',
@ -154,8 +154,8 @@ return [
'application/vnd.oasis.opendocument.database',
'application/vnd.oasis.opendocument.image',
],
'list_length' => 10,
'export_formats' => [
'list_length' => 10,
'export_formats' => [
'csv' => CsvExporter::class,
],
'default_export_format' => 'csv',
@ -260,6 +260,7 @@ return [
// models
'account' => \FireflyIII\Models\Account::class,
'attachment' => \FireflyIII\Models\Attachment::class,
'availableBudget' => \FireflyIII\Models\AvailableBudget::class,
'bill' => \FireflyIII\Models\Bill::class,
'budget' => \FireflyIII\Models\Budget::class,
'budgetLimit' => \FireflyIII\Models\BudgetLimit::class,
@ -271,7 +272,7 @@ return [
'piggyBank' => \FireflyIII\Models\PiggyBank::class,
'tj' => \FireflyIII\Models\TransactionJournal::class,
'tag' => \FireflyIII\Models\Tag::class,
'recurrence' => \FireflyIII\Models\Recurrence::class,
'recurrence' => \FireflyIII\Models\Recurrence::class,
'rule' => \FireflyIII\Models\Rule::class,
'ruleGroup' => \FireflyIII\Models\RuleGroup::class,
'exportJob' => \FireflyIII\Models\ExportJob::class,
@ -280,7 +281,7 @@ return [
'user' => \FireflyIII\User::class,
// strings
'import_provider' => \FireflyIII\Support\Binder\ImportProvider::class,
'import_provider' => \FireflyIII\Support\Binder\ImportProvider::class,
// dates
'start_date' => \FireflyIII\Support\Binder\Date::class,

View File

@ -47,7 +47,7 @@ Route::group(
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'attachments', 'as' => 'api.v1.attachments.'],
function () {
// Accounts API routes:
// Attachment API routes:
Route::get('', ['uses' => 'AttachmentController@index', 'as' => 'index']);
Route::post('', ['uses' => 'AttachmentController@store', 'as' => 'store']);
Route::get('{attachment}', ['uses' => 'AttachmentController@show', 'as' => 'show']);
@ -58,6 +58,21 @@ Route::group(
}
);
Route::group(
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'available_budgets', 'as' => 'api.v1.available_budgets.'],
function () {
// Available Budget API routes:
Route::get('', ['uses' => 'AvailableBudgetController@index', 'as' => 'index']);
Route::post('', ['uses' => 'AvailableBudgetController@store', 'as' => 'store']);
Route::get('{availableBudget}', ['uses' => 'AvailableBudgetController@show', 'as' => 'show']);
Route::get('{availableBudget}/download', ['uses' => 'AvailableBudgetController@download', 'as' => 'download']);
Route::post('{availableBudget}/upload', ['uses' => 'AvailableBudgetController@upload', 'as' => 'upload']);
Route::put('{availableBudget}', ['uses' => 'AvailableBudgetController@update', 'as' => 'update']);
Route::delete('{availableBudget}', ['uses' => 'AvailableBudgetController@delete', 'as' => 'delete']);
}
);
Route::group(
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'bills', 'as' => 'api.v1.bills.'], function () {