From 12a84572e20da93ba9d236bafc4b334f65c22054 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 29 Jun 2018 06:43:44 +0200 Subject: [PATCH] First code for the piggy bank API. --- .../V1/Controllers/PiggyBankController.php | 52 ++++++++--- app/Api/V1/Requests/PiggyBankRequest.php | 90 +++++++++++++++++++ app/Models/PiggyBank.php | 15 ++-- app/Models/PiggyBankRepetition.php | 2 + app/Providers/EventServiceProvider.php | 2 +- .../PiggyBank/PiggyBankRepository.php | 29 ++++-- app/Rules/IsAssetAccountId.php | 46 ++++++++++ app/Transformers/PiggyBankTransformer.php | 6 +- 8 files changed, 214 insertions(+), 28 deletions(-) create mode 100644 app/Api/V1/Requests/PiggyBankRequest.php create mode 100644 app/Rules/IsAssetAccountId.php diff --git a/app/Api/V1/Controllers/PiggyBankController.php b/app/Api/V1/Controllers/PiggyBankController.php index 01eb9608dc..080aa676f2 100644 --- a/app/Api/V1/Controllers/PiggyBankController.php +++ b/app/Api/V1/Controllers/PiggyBankController.php @@ -23,8 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers; +use FireflyIII\Api\V1\Requests\PiggyBankRequest; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\PiggyBank; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; -use FireflyIII\Transformers\BudgetTransformer; use FireflyIII\Transformers\PiggyBankTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; @@ -33,8 +35,13 @@ 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; +/** + * TODO order up and down. + * Class PiggyBankController + */ class PiggyBankController extends Controller { @@ -59,13 +66,13 @@ class PiggyBankController extends Controller /** * Delete the resource. * - * @param string $object + * @param PiggyBank $piggyBank * * @return JsonResponse */ - public function delete(string $object): JsonResponse + public function delete(PiggyBank $piggyBank): JsonResponse { - // todo delete object. + $this->repository->destroy($piggyBank); return response()->json([], 204); } @@ -89,7 +96,7 @@ class PiggyBankController extends Controller // get list of budgets. Count it and split it. $collection = $this->repository->getPiggyBanks(); $count = $collection->count(); - $piggyBanks= $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); + $piggyBanks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); // make paginator: $paginator = new LengthAwarePaginator($piggyBanks, $count, $pageSize, $this->parameters->get('page')); @@ -107,27 +114,48 @@ class PiggyBankController extends Controller /** * List single resource. * - * @param Request $request - * @param string $object + * @param Request $request + * @param PiggyBank $piggyBank * * @return JsonResponse */ - public function show(Request $request, string $object): JsonResponse + public function show(Request $request, PiggyBank $piggyBank): JsonResponse { - // todo implement me. + $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($piggyBank, new PiggyBankTransformer($this->parameters), 'piggy_banks'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); } /** * Store new object. * - * @param Request $request + * @param PiggyBankRequest $request * * @return JsonResponse + * @throws FireflyException */ - public function store(Request $request): JsonResponse + public function store(PiggyBankRequest $request): JsonResponse { - // todo replace code and replace request object. + $piggyBank = $this->repository->store($request->getAll()); + if (null !== $piggyBank) { + $manager = new Manager(); + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($piggyBank, new PiggyBankTransformer($this->parameters), 'piggy_banks'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + throw new FireflyException('Could not store new piggy bank.'); // @codeCoverageIgnore } diff --git a/app/Api/V1/Requests/PiggyBankRequest.php b/app/Api/V1/Requests/PiggyBankRequest.php new file mode 100644 index 0000000000..15a41bda01 --- /dev/null +++ b/app/Api/V1/Requests/PiggyBankRequest.php @@ -0,0 +1,90 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Requests; + +use FireflyIII\Models\PiggyBank; +use FireflyIII\Rules\IsAssetAccountId; + +/** + * + * Class PiggyBankRequest + */ +class PiggyBankRequest extends Request +{ + /** + * @return bool + */ + public function authorize(): bool + { + // Only allow authenticated users + return auth()->check(); + } + + /** + * @return array + */ + public function getAll(): array + { + return [ + 'name' => $this->string('name'), + 'account_id' => $this->integer('account_id'), + 'targetamount' => $this->string('target_amount'), + 'current_amount' => $this->string('current_amount'), + 'start_date' => $this->date('start_date'), + 'target_date' => $this->date('target_date'), + 'note' => $this->string('notes'), + ]; + } + + /** + * @return array + */ + public function rules(): array + { + $rules = [ + 'name' => 'required|between:1,255|uniquePiggyBankForUser', + 'account_id' => ['required', 'belongsToUser:accounts', new IsAssetAccountId], + 'target_amount' => 'required|numeric|more:0', + 'current_amount' => 'numeric|more:0|lte:target_amount', + 'start_date' => 'date|nullable', + 'target_date' => 'date|nullable', + 'notes' => 'max:65000', + ]; + + switch ($this->method()) { + default: + break; + case 'PUT': + case 'PATCH': + /** @var PiggyBank $piggyBank */ + $piggyBank = $this->route()->parameter('piggyBank'); + $rules['name'] = 'required|between:1,255|uniquePiggyBankForUser:' . $piggyBank->id; + break; + } + + + return $rules; + } + +} \ No newline at end of file diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index 82721a243a..e6cc263d3e 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -33,12 +33,17 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class PiggyBank. * - * @property Carbon $targetdate - * @property Carbon $startdate - * @property string $targetamount - * @property int $id - * @property string $name + * @property Carbon $targetdate + * @property Carbon $startdate + * @property string $targetamount + * @property int $id + * @property string $name * @property Account $account + * @property Carbon $updated_at + * @property Carbon $created_at + * @property int $order + * @property bool $active + * @property int $account_id * */ class PiggyBank extends Model diff --git a/app/Models/PiggyBankRepetition.php b/app/Models/PiggyBankRepetition.php index 42494f1f9e..470d9b7a11 100644 --- a/app/Models/PiggyBankRepetition.php +++ b/app/Models/PiggyBankRepetition.php @@ -30,6 +30,8 @@ use Illuminate\Database\Eloquent\Model; * Class PiggyBankRepetition. * * @property string $currentamount + * @property Carbon $startdate + * @property Carbon $targetdate */ class PiggyBankRepetition extends Model { diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index ec5891f6f3..2ffb23d88b 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -117,7 +117,7 @@ class EventServiceProvider extends ServiceProvider */ protected function registerCreateEvents(): void { - // move this routine to a filter + // todo move this routine to a filter // in case of repeated piggy banks and/or other problems. PiggyBank::created( function (PiggyBank $piggyBank) { diff --git a/app/Repositories/PiggyBank/PiggyBankRepository.php b/app/Repositories/PiggyBank/PiggyBankRepository.php index 99ef2b7f28..f2043a11a6 100644 --- a/app/Repositories/PiggyBank/PiggyBankRepository.php +++ b/app/Repositories/PiggyBank/PiggyBankRepository.php @@ -49,7 +49,10 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface */ public function addAmount(PiggyBank $piggyBank, string $amount): bool { - $repetition = $piggyBank->currentRelevantRep(); + $repetition = $this->getRepetition($piggyBank); + if (null === $repetition) { + return false; + } $currentAmount = $repetition->currentamount ?? '0'; $repetition->currentamount = bcadd($currentAmount, $amount); $repetition->save(); @@ -99,7 +102,11 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface */ public function canRemoveAmount(PiggyBank $piggyBank, string $amount): bool { - $savedSoFar = $piggyBank->currentRelevantRep()->currentamount; + $repetition = $this->getRepetition($piggyBank); + if (null === $repetition) { + return false; + } + $savedSoFar = $repetition->currentamount; return bccomp($amount, $savedSoFar) <= 0; } @@ -171,6 +178,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface /** * @param int $piggyBankid * + * @deprecated * @return PiggyBank */ public function find(int $piggyBankid): PiggyBank @@ -268,7 +276,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface Log::debug(sprintf('Will add/remove %f to piggy bank #%d ("%s")', $amount, $piggyBank->id, $piggyBank->name)); // if piggy account matches source account, the amount is positive - if (\in_array($piggyBank->account_id, $sources)) { + if (\in_array($piggyBank->account_id, $sources, true)) { $amount = bcmul($amount, '-1'); Log::debug(sprintf('Account #%d is the source, so will remove amount from piggy bank.', $piggyBank->account_id)); } @@ -438,8 +446,8 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface /** * set id of piggy bank. * - * @param int $piggyBankId - * @param int $order + * @param PiggyBank $piggyBank + * @param int $order * * @return bool */ @@ -454,7 +462,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface /** * @param User $user */ - public function setUser(User $user) + public function setUser(User $user): void { $this->user = $user; } @@ -472,6 +480,13 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface $this->updateNote($piggyBank, $data['note']); + // repetition is auto created. + $repetition = $this->getRepetition($piggyBank); + if (null !== $repetition && isset($data['current_amount'])) { + $repetition->currentamount = $data['current_amount']; + $repetition->save(); + } + return $piggyBank; } @@ -495,7 +510,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface // if the piggy bank is now smaller than the current relevant rep, // remove money from the rep. - $repetition = $piggyBank->currentRelevantRep(); + $repetition = $this->getRepetition($piggyBank); if ($repetition->currentamount > $piggyBank->targetamount) { $diff = bcsub($piggyBank->targetamount, $repetition->currentamount); $this->createEvent($piggyBank, $diff); diff --git a/app/Rules/IsAssetAccountId.php b/app/Rules/IsAssetAccountId.php new file mode 100644 index 0000000000..7ffa717037 --- /dev/null +++ b/app/Rules/IsAssetAccountId.php @@ -0,0 +1,46 @@ +find($accountId); + if (null === $account) { + return false; + } + if ($account->accountType->type !== AccountType::ASSET && $account->accountType->type !== AccountType::DEFAULT) { + return false; + } + + return true; + } +} diff --git a/app/Transformers/PiggyBankTransformer.php b/app/Transformers/PiggyBankTransformer.php index 5afe13a6ca..0816fb0f63 100644 --- a/app/Transformers/PiggyBankTransformer.php +++ b/app/Transformers/PiggyBankTransformer.php @@ -166,9 +166,9 @@ class PiggyBankTransformer extends TransformerAbstract 'percentage' => $percentage, 'current_amount' => $currentAmount, 'left_to_save' => round($leftToSave, $decimalPlaces), - 'save_per_month' => $piggyRepos->getSuggestedMonthlyAmount($piggyBank), - 'startdate' => $startDate, - 'targetdate' => $targetDate, + 'save_per_month' => round($piggyRepos->getSuggestedMonthlyAmount($piggyBank), $decimalPlaces), + 'start_date' => $startDate, + 'target_date' => $targetDate, 'order' => (int)$piggyBank->order, 'active' => (int)$piggyBank->active === 1, 'notes' => null,