Can now create auto budget over API

This commit is contained in:
James Cole 2020-03-14 07:30:55 +01:00
parent 309633069c
commit d1d11ae717
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
15 changed files with 183 additions and 47 deletions

View File

@ -25,7 +25,8 @@ namespace FireflyIII\Api\V1\Controllers;
use Exception; use Exception;
use FireflyIII\Api\V1\Requests\BudgetLimitRequest; use FireflyIII\Api\V1\Requests\BudgetLimitRequest;
use FireflyIII\Api\V1\Requests\BudgetRequest; use FireflyIII\Api\V1\Requests\BudgetStoreRequest;
use FireflyIII\Api\V1\Requests\BudgetUpdateRequest;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
@ -179,13 +180,13 @@ class BudgetController extends Controller
/** /**
* Store a budget. * Store a budget.
* *
* @param BudgetRequest $request * @param BudgetStoreRequest $request
* *
* @return JsonResponse * @return JsonResponse
* @throws FireflyException * @throws FireflyException
* *
*/ */
public function store(BudgetRequest $request): JsonResponse public function store(BudgetStoreRequest $request): JsonResponse
{ {
$budget = $this->repository->store($request->getAll()); $budget = $this->repository->store($request->getAll());
$manager = $this->getManager(); $manager = $this->getManager();
@ -291,12 +292,12 @@ class BudgetController extends Controller
/** /**
* Update a budget. * Update a budget.
* *
* @param BudgetRequest $request * @param BudgetUpdateRequest $request
* @param Budget $budget * @param Budget $budget
* *
* @return JsonResponse * @return JsonResponse
*/ */
public function update(BudgetRequest $request, Budget $budget): JsonResponse public function update(BudgetUpdateRequest $request, Budget $budget): JsonResponse
{ {
$data = $request->getAll(); $data = $request->getAll();
$budget = $this->repository->update($budget, $data); $budget = $this->repository->update($budget, $data);

View File

@ -85,7 +85,7 @@ class AvailableBudgetController extends Controller
} }
$left = bcadd($availableBudget->amount, (string)$spent); $left = bcadd($availableBudget->amount, (string)$spent);
// left less than zero? Set to zero. // left less than zero? Set to zero.
if (bccomp($left, '0') === -1) { if (-1 === bccomp($left, '0')) {
$left = '0'; $left = '0';
} }

View File

@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
/** /**
* AccountController.php * AccountController.php
* Copyright (c) 2019 james@firefly-iii.org * Copyright (c) 2019 james@firefly-iii.org

View File

@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
/** /**
* TransactionController.php * TransactionController.php
* Copyright (c) 2019 james@firefly-iii.org * Copyright (c) 2019 james@firefly-iii.org
@ -23,8 +24,6 @@ namespace FireflyIII\Api\V1\Controllers\Search;
use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Support\Search\SearchInterface;
use FireflyIII\Support\Search\TransactionSearch;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
@ -35,13 +34,13 @@ use Illuminate\Http\Response;
class TransactionController extends Controller class TransactionController extends Controller
{ {
/** @var string */ /** @var string */
const SEARCH_ALL = 'all'; public const SEARCH_ALL = 'all';
/** @var string */ /** @var string */
const SEARCH_DESCRIPTION = 'description'; public const SEARCH_DESCRIPTION = 'description';
/** @var string */ /** @var string */
const SEARCH_NOTES = 'notes'; public const SEARCH_NOTES = 'notes';
/** @var string */ /** @var string */
const SEARCH_ACCOUNTS = 'accounts'; public const SEARCH_ACCOUNTS = 'accounts';
/** @var array */ /** @var array */
private $validFields; private $validFields;
@ -59,7 +58,7 @@ class TransactionController extends Controller
/** /**
* @param Request $request * @param Request $request
* *
* @return JsonResponse|Response * @return void
*/ */
public function search(Request $request) public function search(Request $request)
{ {

View File

@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
/** /**
* TransferController.php * TransferController.php
* Copyright (c) 2019 james@firefly-iii.org * Copyright (c) 2019 james@firefly-iii.org
@ -41,10 +42,9 @@ use League\Fractal\Resource\Collection as FractalCollection;
class TransferController extends Controller class TransferController extends Controller
{ {
/** /**
* @param Request $request * @param TransferRequest $request
* *
* @return JsonResponse|Response * @return JsonResponse|Response
* @throws FireflyException
*/ */
public function search(TransferRequest $request) public function search(TransferRequest $request)
{ {

View File

@ -42,7 +42,6 @@ use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Support\Collection;
/** /**
* Class SummaryController * Class SummaryController

View File

@ -0,0 +1,106 @@
<?php
/**
* BudgetStoreRequest.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Rules\IsBoolean;
use Illuminate\Validation\Validator;
/**
* Class BudgetStoreRequest
*
* @codeCoverageIgnore
*/
class BudgetStoreRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
{
// Only allow authenticated users
return auth()->check();
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
{
$active = true;
if (null !== $this->get('active')) {
$active = $this->boolean('active');
}
return [
'name' => $this->string('name'),
'active' => $active,
'order' => 0,
'auto_budget_type' => $this->string('auto_budget_type'),
'transaction_currency_id' => $this->integer('auto_budget_currency_id'),
'transaction_currency_code' => $this->string('auto_budget_currency_code'),
'auto_budget_amount' => $this->string('auto_budget_amount'),
'auto_budget_period' => $this->string('auto_budget_period'),
];
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array
{
return [
'name' => 'required|between:1,100|uniqueObjectForUser:budgets,name',
'active' => [new IsBoolean],
'auto_budget_type' => 'in:reset,rollover',
'auto_budget_currency_id' => 'exists:transaction_currencies,id',
'auto_budget_currency_code' => 'exists:transaction_currencies,code',
'auto_budget_amount' => 'min:0|max:1000000000',
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
];
}
/**
* Configure the validator instance with special rules for after the basic validation rules.
*
* @param Validator $validator
*
* @return void
*/
public function withValidator(Validator $validator): void
{
$validator->after(
function (Validator $validator) {
// validate all account info
$this->validateAutoBudgetAmount($validator);
}
);
}
}

View File

@ -1,6 +1,6 @@
<?php <?php
/** /**
* BudgetRequest.php * BudgetUpdateRequest.php
* Copyright (c) 2019 james@firefly-iii.org * Copyright (c) 2019 james@firefly-iii.org
* *
* This file is part of Firefly III (https://github.com/firefly-iii). * This file is part of Firefly III (https://github.com/firefly-iii).
@ -23,16 +23,14 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests; namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Models\Budget;
use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsBoolean;
/** /**
* Class BudgetRequest * Class BudgetUpdateRequest
* *
* @codeCoverageIgnore * @codeCoverageIgnore
* TODO AFTER 4.8,0: split this into two request classes.
*/ */
class BudgetRequest extends Request class BudgetUpdateRequest extends Request
{ {
/** /**
* Authorize logged in users. * Authorize logged in users.
@ -71,21 +69,11 @@ class BudgetRequest extends Request
*/ */
public function rules(): array public function rules(): array
{ {
$rules = [ $budget = $this->route()->parameter('budget');
'name' => 'required|between:1,100|uniqueObjectForUser:budgets,name',
return [
'name' => sprintf('required|between:1,100|uniqueObjectForUser:budgets,name,%d', $budget->id),
'active' => [new IsBoolean], 'active' => [new IsBoolean],
]; ];
switch ($this->method()) {
default:
break;
case 'PUT':
case 'PATCH':
/** @var Budget $budget */
$budget = $this->route()->parameter('budget');
$rules['name'] = sprintf('required|between:1,100|uniqueObjectForUser:budgets,name,%d', $budget->id);
break;
}
return $rules;
} }
} }

View File

@ -34,4 +34,5 @@ use FireflyIII\Http\Requests\Request as FireflyIIIRequest;
*/ */
class Request extends FireflyIIIRequest class Request extends FireflyIIIRequest
{ {
} }

View File

@ -1,4 +1,5 @@
<?php <?php
declare(strict_types=1);
/** /**
* TransferRequest.php * TransferRequest.php
* Copyright (c) 2019 james@firefly-iii.org * Copyright (c) 2019 james@firefly-iii.org

View File

@ -50,7 +50,7 @@ class BudgetFormStoreRequest extends Request
return [ return [
'name' => $this->string('name'), 'name' => $this->string('name'),
'active' => $this->boolean('active'), 'active' => $this->boolean('active'),
'auto_budget_option' => $this->integer('auto_budget_option'), 'auto_budget_type' => $this->integer('auto_budget_type'),
'transaction_currency_id' => $this->integer('transaction_currency_id'), 'transaction_currency_id' => $this->integer('transaction_currency_id'),
'auto_budget_amount' => $this->string('auto_budget_amount'), 'auto_budget_amount' => $this->string('auto_budget_amount'),
'auto_budget_period' => $this->string('auto_budget_period'), 'auto_budget_period' => $this->string('auto_budget_period'),

View File

@ -387,17 +387,27 @@ class Request extends FormRequest
return $data; return $data;
} }
/** /**
* @param Validator $validator * @param Validator $validator
*/ */
protected function validateAutoBudgetAmount(Validator $validator): void protected function validateAutoBudgetAmount(Validator $validator): void
{ {
$data = $validator->getData(); $data = $validator->getData();
$option = (int)$data['auto_budget_option']; $type = $data['auto_budget_type'] ?? '';
$amount = $data['auto_budget_amount'] ?? ''; $amount = $data['auto_budget_amount'] ?? '';
switch ($option) { $period = (string)($data['auto_budget_period'] ?? '');
$currencyId = $data['auto_budget_currency_id'] ?? '';
$currencyCode = $data['auto_budget_currency_code'] ?? '';
if (is_numeric($type)) {
$type = (int)$type;
}
switch ($type) {
case AutoBudget::AUTO_BUDGET_RESET: case AutoBudget::AUTO_BUDGET_RESET:
case AutoBudget::AUTO_BUDGET_ROLLOVER: case AutoBudget::AUTO_BUDGET_ROLLOVER:
case 'reset':
case 'rollover':
// basic float check: // basic float check:
if ('' === $amount) { if ('' === $amount) {
$validator->errors()->add('auto_budget_amount', (string)trans('validation.amount_required_for_auto_budget')); $validator->errors()->add('auto_budget_amount', (string)trans('validation.amount_required_for_auto_budget'));
@ -405,8 +415,16 @@ class Request extends FormRequest
if (1 !== bccomp((string)$amount, '0')) { if (1 !== bccomp((string)$amount, '0')) {
$validator->errors()->add('auto_budget_amount', (string)trans('validation.auto_budget_amount_positive')); $validator->errors()->add('auto_budget_amount', (string)trans('validation.auto_budget_amount_positive'));
} }
if ('' === $period) {
$validator->errors()->add('auto_budget_period', (string)trans('validation.auto_budget_period_mandatory'));
}
if('' === $currencyCode && '' === $currencyId) {
$validator->errors()->add('auto_budget_amount', (string)trans('validation.require_currency_info'));
}
break; break;
} }
} }
} }

View File

@ -25,13 +25,14 @@ namespace FireflyIII\Repositories\Budget;
use Carbon\Carbon; use Carbon\Carbon;
use DB; use DB;
use Exception; use Exception;
use FireflyIII\Models\AutoBudget;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\AutoBudget;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\RecurrenceTransactionMeta; use FireflyIII\Models\RecurrenceTransactionMeta;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
use FireflyIII\Models\RuleTrigger; use FireflyIII\Models\RuleTrigger;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Services\Internal\Destroy\BudgetDestroyService; use FireflyIII\Services\Internal\Destroy\BudgetDestroyService;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Database\QueryException; use Illuminate\Database\QueryException;
@ -290,17 +291,37 @@ class BudgetRepository implements BudgetRepositoryInterface
} }
// try to create associated auto budget: // try to create associated auto budget:
$option = $data['auto_budget_option'] ?? 0; $type = $data['auto_budget_type'] ?? 0;
if (0 === $option) { if (0 === $type) {
return $newBudget; return $newBudget;
} }
if ('reset' === $type) {
$type = AutoBudget::AUTO_BUDGET_RESET;
}
if ('rollover' === $type) {
$type = AutoBudget::AUTO_BUDGET_ROLLOVER;
}
$repos = app(CurrencyRepositoryInterface::class);
$currencyId = (int)($data['transaction_currency_id'] ?? 0);
$currencyCode = (string)($data['transaction_currency_code'] ?? '');
$currency = $repos->findNull($currencyId);
if(null === $currency) {
$currency = $repos->findByCodeNull($currencyCode);
}
if(null === $currency) {
$currency = app('amount')->getDefaultCurrencyByUser($this->user);
}
$autoBudget = new AutoBudget; $autoBudget = new AutoBudget;
$autoBudget->budget()->associate($newBudget); $autoBudget->budget()->associate($newBudget);
$autoBudget->transaction_currency_id = $data['transaction_currency_id'] ?? 1; $autoBudget->transaction_currency_id = $currency->id;
$autoBudget->auto_budget_type = $option; $autoBudget->auto_budget_type = $type;
$autoBudget->amount = $data['auto_budget_amount'] ?? '1'; $autoBudget->amount = $data['auto_budget_amount'] ?? '1';
$autoBudget->period = $data['auto_budget_period'] ?? 'monthly'; $autoBudget->period = $data['auto_budget_period'] ?? 'monthly';
$autoBudget->save(); $autoBudget->save();
return $newBudget; return $newBudget;
} }

View File

@ -98,11 +98,11 @@ class BudgetTransformer extends AbstractTransformer
'updated_at' => $budget->updated_at->toAtomString(), 'updated_at' => $budget->updated_at->toAtomString(),
'active' => $budget->active, 'active' => $budget->active,
'name' => $budget->name, 'name' => $budget->name,
'auto_budget_type' => $abType,
'auto_budget_period' => $abPeriod,
'auto_budget_currency_id' => $abCurrencyId, 'auto_budget_currency_id' => $abCurrencyId,
'auto_budget_currency_code' => $abCurrencyCode, 'auto_budget_currency_code' => $abCurrencyCode,
'auto_budget_type' => $abType,
'auto_budget_amount' => $abAmount, 'auto_budget_amount' => $abAmount,
'auto_budget_period' => $abPeriod,
'spent' => $spent, 'spent' => $spent,
'links' => [ 'links' => [
[ [

View File

@ -204,4 +204,5 @@ return [
'amount_required_for_auto_budget' => 'The amount is required.', 'amount_required_for_auto_budget' => 'The amount is required.',
'auto_budget_amount_positive' => 'The amount must be more than zero.', 'auto_budget_amount_positive' => 'The amount must be more than zero.',
'auto_budget_period_mandatory' => 'The auto budget period is a mandatory field.',
]; ];