Merge branch 'release/4.7.5.1'

This commit is contained in:
James Cole
2018-07-14 07:05:31 +02:00
368 changed files with 5128 additions and 5721 deletions

View File

@@ -16,8 +16,8 @@ mkdir -p $FIREFLY_PATH/storage/upload
# make sure we own the volumes:
chown -R www-data:www-data -R $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload $FIREFLY_PATH/storage/logs $FIREFLY_PATH/storage/framework/cache
chmod -R 775 $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload $FIREFLY_PATH/storage/upload $FIREFLY_PATH/storage/logs $FIREFLY_PATH/storage/framework/cache
chown -R www-data:www-data -R $FIREFLY_PATH/storage
chmod -R 775 $FIREFLY_PATH/storage
# remove any lingering files that may break upgrades:
rm -f $FIREFLY_PATH/storage/logs/laravel.log

View File

@@ -1,4 +1,3 @@
# Ignore composer specific files and vendor folder
composer.phar
composer.lock
vendor

View File

@@ -1,3 +1,12 @@
# 4.7.5.1
- [Issue 1531](https://github.com/firefly-iii/firefly-iii/issues/1531), the database routine incorrectly reports empty categories.
- [Issue 1532](https://github.com/firefly-iii/firefly-iii/issues/1532), broken dropdown for autosuggest things.
- [Issue 1533](https://github.com/firefly-iii/firefly-iii/issues/1533), fix where the import could not import category names.
- [Issue 1538](https://github.com/firefly-iii/firefly-iii/issues/1538), fix a bug where Spectre would not work when ignoring rules.
- [Issue 1542](https://github.com/firefly-iii/firefly-iii/issues/1542), fix a bug where the importer was incapable of generating new currencies.
- [Issue 1541](https://github.com/firefly-iii/firefly-iii/issues/1541), no longer ignore composer.lock in Docker ignore.
- Bills are stored inactive.
# 4.7.5
- A new feature called "recurring transactions" that will make Firefly III automatically create transactions for you.
- New API end points for attachments, available budgets, budgets, budget limits, categories, configuration, currency exchange rates, journal links, link types, piggy banks, preferences, recurring transactions, rules, rule groups and tags.

View File

@@ -16,7 +16,7 @@ const pkgdef :Spk.PackageDefinition = (
manifest = (
appTitle = (defaultText = "Firefly III"),
appVersion = 14,
appMarketingVersion = (defaultText = "4.7.5"),
appMarketingVersion = (defaultText = "4.7.5.1"),
actions = [
# Define your "new document" handlers here.

View File

@@ -35,7 +35,7 @@ use League\Fractal\Serializer\JsonApiSerializer;
/**
* Returns basic information about this installation.
*
* Class AboutController
* Class AboutController.
*/
class AboutController extends Controller
{

View File

@@ -1,5 +1,4 @@
<?php
/**
* AccountController.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@@ -41,13 +40,15 @@ use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/**
* Class AccountController
* Class AccountController.
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class AccountController extends Controller
{
/** @var CurrencyRepositoryInterface */
/** @var CurrencyRepositoryInterface The currency repository */
private $currencyRepository;
/** @var AccountRepositoryInterface */
/** @var AccountRepositoryInterface The account repository */
private $repository;
/**
@@ -125,6 +126,8 @@ class AccountController extends Controller
}
/**
* Show single instance.
*
* @param Request $request
* @param Account $account
*
@@ -146,6 +149,8 @@ class AccountController extends Controller
}
/**
* Store a new instance.
*
* @param AccountRequest $request
*
* @return \Illuminate\Http\JsonResponse
@@ -197,52 +202,25 @@ class AccountController extends Controller
}
/**
* All the available types.
*
* @param string $type
*
* @return array
*/
private function mapTypes(string $type): array
{
$types = [
'all' => [
AccountType::DEFAULT,
AccountType::CASH,
AccountType::ASSET,
AccountType::EXPENSE,
AccountType::REVENUE,
AccountType::INITIAL_BALANCE,
AccountType::BENEFICIARY,
AccountType::IMPORT,
AccountType::RECONCILIATION,
AccountType::LOAN,
],
'asset' => [
AccountType::DEFAULT,
AccountType::ASSET,
],
'cash' => [
AccountType::CASH,
],
'expense' => [
AccountType::EXPENSE,
AccountType::BENEFICIARY,
],
'revenue' => [
AccountType::REVENUE,
],
'special' => [
AccountType::CASH,
AccountType::INITIAL_BALANCE,
AccountType::IMPORT,
AccountType::RECONCILIATION,
AccountType::LOAN,
],
'hidden' => [
AccountType::INITIAL_BALANCE,
AccountType::IMPORT,
AccountType::RECONCILIATION,
AccountType::LOAN,
],
$types = [
'all' => [AccountType::DEFAULT, AccountType::CASH, AccountType::ASSET, AccountType::EXPENSE, AccountType::REVENUE,
AccountType::INITIAL_BALANCE, AccountType::BENEFICIARY, AccountType::IMPORT, AccountType::RECONCILIATION,
AccountType::LOAN,],
'asset' => [AccountType::DEFAULT, AccountType::ASSET,],
'cash' => [AccountType::CASH,],
'expense' => [AccountType::EXPENSE, AccountType::BENEFICIARY,],
'revenue' => [AccountType::REVENUE,],
'special' => [AccountType::CASH, AccountType::INITIAL_BALANCE, AccountType::IMPORT, AccountType::RECONCILIATION,
AccountType::LOAN,],
'hidden' => [AccountType::INITIAL_BALANCE, AccountType::IMPORT, AccountType::RECONCILIATION, AccountType::LOAN,],
AccountType::DEFAULT => [AccountType::DEFAULT],
AccountType::CASH => [AccountType::CASH],
AccountType::ASSET => [AccountType::ASSET],
@@ -254,10 +232,11 @@ class AccountController extends Controller
AccountType::RECONCILIATION => [AccountType::RECONCILIATION],
AccountType::LOAN => [AccountType::LOAN],
];
$return = $types['all'];
if (isset($types[$type])) {
return $types[$type];
$return = $types[$type];
}
return $types['all']; // @codeCoverageIgnore
return $return; // @codeCoverageIgnore
}
}

View File

@@ -41,11 +41,13 @@ use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/**
* Class AttachmentController
* Class AttachmentController.
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class AttachmentController extends Controller
{
/** @var AttachmentRepositoryInterface */
/** @var AttachmentRepositoryInterface The attachment repository */
private $repository;
/**
@@ -81,6 +83,8 @@ class AttachmentController extends Controller
}
/**
* Download an attachment.
*
* @param Attachment $attachment
*
* @return LaravelResponse
@@ -88,7 +92,7 @@ class AttachmentController extends Controller
*/
public function download(Attachment $attachment): LaravelResponse
{
if ($attachment->uploaded === false) {
if (false === $attachment->uploaded) {
throw new FireflyException('No file has been uploaded for this attachment (yet).');
}
if ($this->repository->exists($attachment)) {
@@ -211,6 +215,8 @@ class AttachmentController extends Controller
}
/**
* Upload an attachment.
*
* @param Request $request
* @param Attachment $attachment
*

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\AvailableBudgetRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
@@ -39,13 +40,15 @@ use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/**
* Class AvailableBudgetController
* Class AvailableBudgetController.
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class AvailableBudgetController extends Controller
{
/** @var CurrencyRepositoryInterface */
/** @var CurrencyRepositoryInterface The currency repository */
private $currencyRepository;
/** @var BudgetRepositoryInterface */
/** @var BudgetRepositoryInterface The budget repository */
private $repository;
/**
@@ -144,11 +147,15 @@ class AvailableBudgetController extends Controller
* @param AvailableBudgetRequest $request
*
* @return JsonResponse
* @throws FireflyException
*/
public function store(AvailableBudgetRequest $request): JsonResponse
{
$data = $request->getAll();
$currency = $this->currencyRepository->findNull($data['transaction_currency_id']);
$data = $request->getAll();
$currency = $this->currencyRepository->findNull($data['transaction_currency_id']);
if (null === $currency) {
throw new FireflyException('Could not find the indicated currency.');
}
$availableBudget = $this->repository->setAvailableBudget($currency, $data['start_date'], $data['end_date'], $data['amount']);
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';

View File

@@ -29,6 +29,7 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Bill;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Transformers\BillTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
@@ -39,11 +40,11 @@ use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/**
* Class BillController
* Class BillController.
*/
class BillController extends Controller
{
/** @var BillRepositoryInterface */
/** @var BillRepositoryInterface The bill repository */
private $repository;
/**
@@ -54,9 +55,12 @@ class BillController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $admin */
$admin = auth()->user();
/** @var BillRepositoryInterface repository */
$this->repository = app(BillRepositoryInterface::class);
$this->repository->setUser(auth()->user());
$this->repository->setUser($admin);
return $next($request);
}
@@ -103,6 +107,8 @@ class BillController extends Controller
/**
* Show the specified bill.
*
* @param Request $request
* @param Bill $bill
*
@@ -124,6 +130,8 @@ class BillController extends Controller
}
/**
* Store a bill.
*
* @param BillRequest $request
*
* @return JsonResponse
@@ -147,6 +155,8 @@ class BillController extends Controller
/**
* Update a bill.
*
* @param BillRequest $request
* @param Bill $bill
*

View File

@@ -28,6 +28,7 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Budget;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Transformers\BudgetTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
@@ -38,11 +39,13 @@ use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/**
* Class BudgetController
* Class BudgetController.
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class BudgetController extends Controller
{
/** @var BudgetRepositoryInterface */
/** @var BudgetRepositoryInterface The budget repository */
private $repository;
/**
@@ -53,9 +56,12 @@ class BudgetController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $admin */
$admin = auth()->user();
/** @var BudgetRepositoryInterface repository */
$this->repository = app(BudgetRepositoryInterface::class);
$this->repository->setUser(auth()->user());
$this->repository->setUser($admin);
return $next($request);
}
@@ -111,6 +117,8 @@ class BudgetController extends Controller
/**
* Show a budget.
*
* @param Request $request
* @param Budget $budget
*
@@ -132,6 +140,8 @@ class BudgetController extends Controller
}
/**
* Store a budget.
*
* @param BudgetRequest $request
*
* @return JsonResponse
@@ -154,6 +164,8 @@ class BudgetController extends Controller
/**
* Update a budget.
*
* @param BudgetRequest $request
* @param Budget $budget
*

View File

@@ -24,8 +24,6 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use Carbon\Carbon;
use Exception;
use FireflyIII\Api\V1\Requests\AvailableBudgetRequest;
use FireflyIII\Api\V1\Requests\BudgetLimitRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\BudgetLimit;
@@ -43,16 +41,15 @@ use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
use Log;
use Throwable;
/**
* Class BudgetLimitController
* Class BudgetLimitController.
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class BudgetLimitController extends Controller
{
///** @var CurrencyRepositoryInterface */
//private $currencyRepository;
/** @var BudgetRepositoryInterface */
/** @var BudgetRepositoryInterface The budget repository */
private $repository;
/**
@@ -66,7 +63,6 @@ class BudgetLimitController extends Controller
/** @var User $user */
$user = auth()->user();
$this->repository = app(BudgetRepositoryInterface::class);
//$this->currencyRepository = app(CurrencyRepositoryInterface::class);
$this->repository->setUser($user);
return $next($request);
@@ -97,40 +93,30 @@ class BudgetLimitController extends Controller
*/
public function index(Request $request): JsonResponse
{
// create some objects:
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
// read budget from request
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$start = null;
$end = null;
$budgetId = (int)($request->get('budget_id') ?? 0);
$budget = null;
if ($budgetId > 0) {
$budget = $this->repository->findNull($budgetId);
}
// read start date from request
$start = null;
$budget = $this->repository->findNull($budgetId);
$this->parameters->set('budget_id', $budgetId);
try {
$start = Carbon::createFromFormat('Y-m-d', $request->get('start'));
$this->parameters->set('start', $start->format('Y-m-d'));
} catch (InvalidArgumentException $e) {
Log::debug(sprintf('Could not parse start date "%s": %s', $request->get('start'), $e->getMessage()));
Log::debug(sprintf('Invalid date: %s', $e->getMessage()));
}
// read end date from request
$end = null;
try {
$end = Carbon::createFromFormat('Y-m-d', $request->get('end'));
$this->parameters->set('end', $end->format('Y-m-d'));
} catch (InvalidArgumentException $e) {
Log::debug(sprintf('Could not parse end date "%s": %s', $request->get('end'), $e->getMessage()));
Log::debug(sprintf('Invalid date: %s', $e->getMessage()));
}
$this->parameters->set('budget_id', $budgetId);
// types to get, page size:
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
// get list of budget limits. Count it and split it.
$collection = new Collection;
if (null === $budget) {
$collection = $this->repository->getAllBudgetLimits($start, $end);
@@ -141,12 +127,9 @@ class BudgetLimitController extends Controller
$count = $collection->count();
$budgetLimits = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// make paginator:
$paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page'));
$paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.budget_limits.index') . $this->buildParams());
// present to user.
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new FractalCollection($budgetLimits, new BudgetLimitTransformer($this->parameters), 'budget_limits');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
@@ -206,8 +189,8 @@ class BudgetLimitController extends Controller
/**
* Update the specified resource in storage.
*
* @param AvailableBudgetRequest $request
* @param BudgetLimit $budgetLimit
* @param BudgetLimitRequest $request
* @param BudgetLimit $budgetLimit
*
* @return JsonResponse
*/

View File

@@ -28,6 +28,7 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Category;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Transformers\CategoryTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
@@ -38,11 +39,13 @@ use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/**
* Class CategoryController
* Class CategoryController.
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class CategoryController extends Controller
{
/** @var CategoryRepositoryInterface */
/** @var CategoryRepositoryInterface The category repository */
private $repository;
/**
@@ -53,9 +56,12 @@ class CategoryController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $admin */
$admin = auth()->user();
/** @var CategoryRepositoryInterface repository */
$this->repository = app(CategoryRepositoryInterface::class);
$this->repository->setUser(auth()->user());
$this->repository->setUser($admin);
return $next($request);
}
@@ -111,6 +117,8 @@ class CategoryController extends Controller
/**
* Show the category.
*
* @param Request $request
* @param Category $category
*
@@ -132,6 +140,8 @@ class CategoryController extends Controller
}
/**
* Store new category.
*
* @param CategoryRequest $request
*
* @return JsonResponse
@@ -154,6 +164,8 @@ class CategoryController extends Controller
/**
* Update the category.
*
* @param CategoryRequest $request
* @param Category $category
*

View File

@@ -25,39 +25,66 @@ namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Configuration;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
/**
* Class ConfigurationController
* Class ConfigurationController.
*/
class ConfigurationController extends Controller
{
/** @var UserRepositoryInterface The user repository */
private $repository;
/**
* @throws FireflyException
* BudgetController constructor.
*/
public function index()
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @noinspection UnusedConstructorDependenciesInspection */
$this->repository = app(UserRepositoryInterface::class);
/** @var User $admin */
$admin = auth()->user();
if (!$this->repository->hasRole($admin, 'owner')) {
throw new FireflyException('No access to method.'); // @codeCoverageIgnore
}
return $next($request);
}
);
}
/**
* Show all configuration.
*
* @return JsonResponse
*/
public function index(): JsonResponse
{
if (!auth()->user()->hasRole('owner')) {
throw new FireflyException('No access to method.'); // @codeCoverageIgnore
}
$configData = $this->getConfigData();
return response()->json(['data' => $configData], 200)->header('Content-Type', 'application/vnd.api+json');
}
/**
* Update the configuration.
*
* @param Request $request
*
* @return JsonResponse
* @throws FireflyException
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function update(Request $request): JsonResponse
{
if (!auth()->user()->hasRole('owner')) {
throw new FireflyException('No access to method.'); // @codeCoverageIgnore
}
$name = $request->get('name');
$value = $request->get('value');
$valid = ['is_demo_site', 'permission_update_check', 'single_user_mode'];
@@ -68,7 +95,7 @@ class ConfigurationController extends Controller
switch ($name) {
case 'is_demo_site':
case 'single_user_mode':
$configValue = $value === 'true';
$configValue = 'true' === $value;
break;
case 'permission_update_check':
$configValue = (int)$value >= -1 && (int)$value <= 1 ? (int)$value : -1;
@@ -81,7 +108,10 @@ class ConfigurationController extends Controller
}
/**
* Get all config values.
*
* @return array
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
private function getConfigData(): array
{

View File

@@ -37,12 +37,13 @@ use Symfony\Component\HttpFoundation\ParameterBag;
* Class Controller.
*
* @codeCoverageIgnore
* @SuppressWarnings(PHPMD.NumberOfChildren)
*/
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
/** @var ParameterBag */
/** @var ParameterBag Parameters from the URI are stored here. */
protected $parameters;
/**
@@ -56,39 +57,42 @@ class Controller extends BaseController
}
/**
* Method to help build URI's.
*
* @return string
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
protected function buildParams(): string
{
$return = '?';
$params = [];
foreach ($this->parameters as $key => $value) {
if ($key === 'page') {
if ('page' === $key) {
continue;
}
if ($value instanceof Carbon) {
$params[$key] = $value->format('Y-m-d');
continue;
}
if (!$value instanceof Carbon) {
$params[$key] = $value;
}
$params[$key] = $value;
}
$return .= http_build_query($params);
if (\strlen($return) === 1) {
return '';
}
return $return;
}
/**
* Method to grab all parameters from the URI.
*
* @return ParameterBag
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
private function getParameters(): ParameterBag
{
$bag = new ParameterBag;
$page = (int)request()->get('page');
if ($page === 0) {
if (0 === $page) {
$page = 1;
}
$bag->set('page', $page);

View File

@@ -30,6 +30,7 @@ use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Transformers\CurrencyTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
@@ -40,29 +41,32 @@ use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/**
* Class CurrencyController
* Class CurrencyController.
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class CurrencyController extends Controller
{
/** @var CurrencyRepositoryInterface */
/** @var CurrencyRepositoryInterface The currency repository */
private $repository;
/** @var UserRepositoryInterface */
/** @var UserRepositoryInterface The user repository */
private $userRepository;
/**
* CurrencyRepository constructor.
*
* @throws FireflyException
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $admin */
$admin = auth()->user();
/** @var CurrencyRepositoryInterface repository */
$this->repository = app(CurrencyRepositoryInterface::class);
$this->userRepository = app(UserRepositoryInterface::class);
$this->repository->setUser(auth()->user());
$this->repository->setUser($admin);
return $next($request);
}
@@ -79,7 +83,10 @@ class CurrencyController extends Controller
*/
public function delete(TransactionCurrency $currency): JsonResponse
{
if (!$this->userRepository->hasRole(auth()->user(), 'owner')) {
/** @var User $admin */
$admin = auth()->user();
if (!$this->userRepository->hasRole($admin, 'owner')) {
// access denied:
throw new FireflyException('No access to method, user is not owner.'); // @codeCoverageIgnore
}
@@ -123,6 +130,8 @@ class CurrencyController extends Controller
/**
* Show a currency.
*
* @param Request $request
* @param TransactionCurrency $currency
*
@@ -146,6 +155,8 @@ class CurrencyController extends Controller
}
/**
* Store new currency.
*
* @param CurrencyRequest $request
*
* @return JsonResponse
@@ -155,11 +166,11 @@ class CurrencyController extends Controller
{
$currency = $this->repository->store($request->getAll());
if ($request->boolean('default') === true) {
app('preferences')->set('currencyPreference', $currency->code);
app('preferences')->mark();
}
if (null !== $currency) {
if (true === $request->boolean('default')) {
app('preferences')->set('currencyPreference', $currency->code);
app('preferences')->mark();
}
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
@@ -176,6 +187,8 @@ class CurrencyController extends Controller
/**
* Update a currency.
*
* @param CurrencyRequest $request
* @param TransactionCurrency $currency
*
@@ -186,7 +199,7 @@ class CurrencyController extends Controller
$data = $request->getAll();
$currency = $this->repository->update($currency, $data);
if ($request->boolean('default') === true) {
if (true === $request->boolean('default')) {
app('preferences')->set('currencyPreference', $currency->code);
app('preferences')->mark();
}

View File

@@ -28,20 +28,19 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Services\Currency\ExchangeRateInterface;
use FireflyIII\Transformers\CurrencyExchangeRateTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use InvalidArgumentException;
use League\Fractal\Manager;
use League\Fractal\Resource\Item;
use Log;
use League\Fractal\Serializer\JsonApiSerializer;
/**
*
* Class CurrencyExchangeRateController
*/
class CurrencyExchangeRateController extends Controller
{
/** @var CurrencyRepositoryInterface */
/** @var CurrencyRepositoryInterface The currency repository */
private $repository;
/**
@@ -52,8 +51,11 @@ class CurrencyExchangeRateController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $admin */
$admin = auth()->user();
$this->repository = app(CurrencyRepositoryInterface::class);
$this->repository->setUser(auth()->user());
$this->repository->setUser($admin);
return $next($request);
}
@@ -62,6 +64,8 @@ class CurrencyExchangeRateController extends Controller
}
/**
* Show an exchange rate.
*
* @param Request $request
*
* @return JsonResponse
@@ -72,8 +76,8 @@ class CurrencyExchangeRateController extends Controller
// create some objects:
$manager = new Manager;
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// currencies
$fromCurrency = $this->repository->findByCodeNull($request->get('from') ?? 'EUR');
$toCurrency = $this->repository->findByCodeNull($request->get('to') ?? 'USD');
@@ -84,27 +88,19 @@ class CurrencyExchangeRateController extends Controller
throw new FireflyException('Unknown destination currency.');
}
$dateObj = new Carbon;
try {
$dateObj = Carbon::createFromFormat('Y-m-d', $request->get('date') ?? date('Y-m-d'));
} catch (InvalidArgumentException $e) {
Log::debug($e->getMessage());
}
$dateObj = Carbon::createFromFormat('Y-m-d', $request->get('date') ?? date('Y-m-d'));
$this->parameters->set('from', $fromCurrency->code);
$this->parameters->set('to', $toCurrency->code);
$this->parameters->set('date', $dateObj->format('Y-m-d'));
// get the exchange rate.
$rate = $this->repository->getExchangeRate($fromCurrency, $toCurrency, $dateObj);
if (null === $rate) {
/** @var User $admin */
$admin = auth()->user();
// create service:
/** @var ExchangeRateInterface $service */
$service = app(ExchangeRateInterface::class);
$service->setUser(auth()->user());
// get rate:
$service->setUser($admin);
$rate = $service->getRate($fromCurrency, $toCurrency, $dateObj);
}

View File

@@ -39,13 +39,21 @@ use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/**
* Class JournalLinkController.
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class JournalLinkController extends Controller
{
/** @var JournalRepositoryInterface */
/** @var JournalRepositoryInterface The journal repository */
private $journalRepository;
/** @var LinkTypeRepositoryInterface */
/** @var LinkTypeRepositoryInterface The link type repository */
private $repository;
/**
* JournalLinkController constructor.
*/
public function __construct()
{
parent::__construct();
@@ -175,6 +183,8 @@ class JournalLinkController extends Controller
}
/**
* Update object.
*
* @param JournalLinkRequest $request
* @param TransactionJournalLink $journalLink
*

View File

@@ -40,17 +40,21 @@ use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/**
* Class LinkTypeController.
*
* Class LinkTypeController
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class LinkTypeController extends Controller
{
/** @var LinkTypeRepositoryInterface */
/** @var LinkTypeRepositoryInterface The link type repository */
private $repository;
/** @var UserRepositoryInterface */
/** @var UserRepositoryInterface The user repository */
private $userRepository;
/**
* LinkTypeController constructor.
*/
public function __construct()
{
parent::__construct();
@@ -77,7 +81,7 @@ class LinkTypeController extends Controller
*/
public function delete(LinkType $linkType): JsonResponse
{
if ($linkType->editable === false) {
if (false === $linkType->editable) {
throw new FireflyException(sprintf('You cannot delete this link type (#%d, "%s")', $linkType->id, $linkType->name));
}
$this->repository->destroy($linkType, null);
@@ -151,7 +155,10 @@ class LinkTypeController extends Controller
*/
public function store(LinkTypeRequest $request): JsonResponse
{
if (!$this->userRepository->hasRole(auth()->user(), 'owner')) {
/** @var User $admin */
$admin = auth()->user();
if (!$this->userRepository->hasRole($admin, 'owner')) {
throw new FireflyException('You need the "owner"-role to do this.');
}
$data = $request->getAll();
@@ -168,6 +175,8 @@ class LinkTypeController extends Controller
}
/**
* Update object.
*
* @param LinkTypeRequest $request
* @param LinkType $linkType
*
@@ -176,10 +185,14 @@ class LinkTypeController extends Controller
*/
public function update(LinkTypeRequest $request, LinkType $linkType): JsonResponse
{
if ($linkType->editable === false) {
if (false === $linkType->editable) {
throw new FireflyException(sprintf('You cannot edit this link type (#%d, "%s")', $linkType->id, $linkType->name));
}
if (!$this->userRepository->hasRole(auth()->user(), 'owner')) {
/** @var User $admin */
$admin = auth()->user();
if (!$this->userRepository->hasRole($admin, 'owner')) {
throw new FireflyException('You need the "owner"-role to do this.');
}

View File

@@ -39,24 +39,30 @@ use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/**
* TODO order up and down.
* Class PiggyBankController
* Class PiggyBankController.
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class PiggyBankController extends Controller
{
/** @var PiggyBankRepositoryInterface */
/** @var PiggyBankRepositoryInterface The piggy bank repository */
private $repository;
/**
* PiggyBankController constructor.
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
/** @var User $admin */
$admin = auth()->user();
$this->repository = app(PiggyBankRepositoryInterface::class);
$this->repository->setUser($admin);
return $next($request);
}
@@ -160,6 +166,8 @@ class PiggyBankController extends Controller
}
/**
* Update piggy bank.
*
* @param PiggyBankRequest $request
* @param PiggyBank $piggyBank
*

View File

@@ -42,20 +42,6 @@ use Preferences;
*/
class PreferenceController extends Controller
{
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
// todo add local repositories.
return $next($request);
}
);
}
/**
* List all of them.
*
@@ -116,10 +102,13 @@ class PreferenceController extends Controller
}
/**
* Update a preference.
*
* @param PreferenceRequest $request
* @param Preference $preference
*
* @return JsonResponse
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function update(PreferenceRequest $request, Preference $preference): JsonResponse
{
@@ -138,7 +127,7 @@ class PreferenceController extends Controller
break;
case 'customFiscalYear':
case 'twoFactorAuthEnabled':
$newValue = (int)$data['data'] === 1;
$newValue = 1 === (int)$data['data'];
break;
}
$result = Preferences::set($preference->name, $newValue);

View File

@@ -38,14 +38,16 @@ use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/**
*
* Class RecurrenceController
*/
class RecurrenceController extends Controller
{
/** @var RecurringRepositoryInterface */
/** @var RecurringRepositoryInterface The recurring transaction repository */
private $repository;
/**
* RecurrenceController constructor.
*/
public function __construct()
{
parent::__construct();
@@ -155,6 +157,8 @@ class RecurrenceController extends Controller
}
/**
* Update single recurrence.
*
* @param RecurrenceRequest $request
* @param Recurrence $recurrence
*
@@ -163,9 +167,6 @@ class RecurrenceController extends Controller
public function update(RecurrenceRequest $request, Recurrence $recurrence): JsonResponse
{
$data = $request->getAll();
//
$category = $this->repository->update($recurrence, $data);
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';

View File

@@ -42,9 +42,12 @@ use League\Fractal\Serializer\JsonApiSerializer;
*/
class RuleController extends Controller
{
/** @var RuleRepositoryInterface */
/** @var RuleRepositoryInterface The rule repository */
private $ruleRepository;
/**
* RuleController constructor.
*/
public function __construct()
{
parent::__construct();
@@ -136,7 +139,7 @@ class RuleController extends Controller
/**
* Store new object.
*
* @param Request $request
* @param RuleRequest $request
*
* @return JsonResponse
*/
@@ -153,6 +156,8 @@ class RuleController extends Controller
}
/**
* Update a rule.
*
* @param RuleRequest $request
* @param Rule $rule
*
@@ -160,9 +165,9 @@ class RuleController extends Controller
*/
public function update(RuleRequest $request, Rule $rule): JsonResponse
{
$rule = $this->ruleRepository->update($rule, $request->getAll());
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$rule = $this->ruleRepository->update($rule, $request->getAll());
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($rule, new RuleTransformer($this->parameters), 'rules');

View File

@@ -38,11 +38,17 @@ use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
/**
* Class RuleGroupController
*/
class RuleGroupController extends Controller
{
/** @var RuleGroupRepositoryInterface */
/** @var RuleGroupRepositoryInterface The rule group repository */
private $ruleGroupRepository;
/**
* RuleGroupController constructor.
*/
public function __construct()
{
parent::__construct();
@@ -62,7 +68,7 @@ class RuleGroupController extends Controller
/**
* Delete the resource.
*
* @param string $object
* @param RuleGroup $ruleGroup
*
* @return JsonResponse
*/
@@ -151,8 +157,10 @@ class RuleGroupController extends Controller
}
/**
* @param Request $request
* @param string $object
* Update a rule group.
*
* @param RuleGroupRequest $request
* @param RuleGroup $ruleGroup
*
* @return JsonResponse
*/

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\TransactionRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Helpers\Filter\NegativeAmountFilter;
@@ -33,36 +34,39 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Transformers\TransactionTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Serializer\JsonApiSerializer;
use Log;
/**
* Class TransactionController
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class TransactionController extends Controller
{
/** @var JournalRepositoryInterface */
/** @var JournalRepositoryInterface The journal repository */
private $repository;
/**
* TransactionController constructor.
*
* @throws \FireflyIII\Exceptions\FireflyException
*/
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
/** @var User $admin */
$admin = auth()->user();
/** @var JournalRepositoryInterface repository */
$this->repository = app(JournalRepositoryInterface::class);
$this->repository->setUser(auth()->user());
$this->repository->setUser($admin);
return $next($request);
}
@@ -74,9 +78,9 @@ class TransactionController extends Controller
*
* @param \FireflyIII\Models\Transaction $transaction
*
* @return \Illuminate\Http\Response
* @return JsonResponse
*/
public function delete(Transaction $transaction)
public function delete(Transaction $transaction): JsonResponse
{
$journal = $transaction->transactionJournal;
$this->repository->destroy($journal);
@@ -85,33 +89,32 @@ class TransactionController extends Controller
}
/**
* Show all transactions.
*
* @param Request $request
*
* @return \Illuminate\Http\JsonResponse
* @return JsonResponse
*/
public function index(Request $request)
public function index(Request $request): JsonResponse
{
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
// read type from URI
$type = $request->get('type') ?? 'default';
$type = $request->get('type') ?? 'default';
$this->parameters->set('type', $type);
// types to get, page size:
$types = $this->mapTypes($this->parameters->get('type'));
$types = $this->mapTypes($this->parameters->get('type'));
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// collect transactions using the journal collector
/** @var User $admin */
$admin = auth()->user();
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setUser(auth()->user());
$collector->setUser($admin);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
$collector->setAllAssetAccounts();
// remove internal transfer filter:
if (\in_array(TransactionType::TRANSFER, $types)) {
if (\in_array(TransactionType::TRANSFER, $types, true)) {
$collector->removeFilter(InternalTransferFilter::class);
}
@@ -124,7 +127,6 @@ class TransactionController extends Controller
$paginator->setPath(route('api.v1.transactions.index') . $this->buildParams());
$transactions = $paginator->getCollection();
$resource = new FractalCollection($transactions, new TransactionTransformer($this->parameters), 'transactions');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
@@ -133,13 +135,15 @@ class TransactionController extends Controller
/**
* Show a single transaction.
*
* @param Request $request
* @param Transaction $transaction
* @param string $include
*
* @return \Illuminate\Http\JsonResponse
* @return JsonResponse
*/
public function show(Request $request, Transaction $transaction, string $include = null)
public function show(Request $request, Transaction $transaction, string $include = null): JsonResponse
{
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
@@ -173,13 +177,16 @@ class TransactionController extends Controller
}
/**
* Store a new transaction.
*
* @param TransactionRequest $request
*
* @param JournalRepositoryInterface $repository
*
* @return \Illuminate\Http\JsonResponse
* @throws FireflyException
* @return JsonResponse
*/
public function store(TransactionRequest $request, JournalRepositoryInterface $repository)
public function store(TransactionRequest $request, JournalRepositoryInterface $repository): JsonResponse
{
$data = $request->getAll();
$data['user'] = auth()->user()->id;
@@ -217,23 +224,21 @@ class TransactionController extends Controller
/**
* Update a transaction.
*
* @param TransactionRequest $request
* @param JournalRepositoryInterface $repository
* @param Transaction $transaction
*
* @return \Illuminate\Http\JsonResponse
* @return JsonResponse
*/
public function update(TransactionRequest $request, JournalRepositoryInterface $repository, Transaction $transaction)
public function update(TransactionRequest $request, JournalRepositoryInterface $repository, Transaction $transaction): JsonResponse
{
$data = $request->getAll();
$data['user'] = auth()->user()->id;
Log::debug('Inside transaction update');
$journal = $repository->update($transaction->transactionJournal, $data);
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$journal = $repository->update($transaction->transactionJournal, $data);
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// add include parameter:
@@ -265,72 +270,38 @@ class TransactionController extends Controller
}
/**
* All the types you can request.
*
* @param string $type
*
* @return array
*/
private function mapTypes(string $type): array
{
$types = [
'all' => [
TransactionType::WITHDRAWAL,
TransactionType::DEPOSIT,
TransactionType::TRANSFER,
TransactionType::OPENING_BALANCE,
TransactionType::RECONCILIATION,
],
'withdrawal' => [
TransactionType::WITHDRAWAL,
],
'withdrawals' => [
TransactionType::WITHDRAWAL,
],
'expense' => [
TransactionType::WITHDRAWAL,
],
'income' => [
TransactionType::DEPOSIT,
],
'deposit' => [
TransactionType::DEPOSIT,
],
'deposits' => [
TransactionType::DEPOSIT,
],
'transfer' => [
TransactionType::TRANSFER,
],
'transfers' => [
TransactionType::TRANSFER,
],
'opening_balance' => [
TransactionType::OPENING_BALANCE,
],
'reconciliation' => [
TransactionType::RECONCILIATION,
],
'reconciliations' => [
TransactionType::RECONCILIATION,
],
'special' => [
TransactionType::OPENING_BALANCE,
TransactionType::RECONCILIATION,
],
'specials' => [
TransactionType::OPENING_BALANCE,
TransactionType::RECONCILIATION,
],
'default' => [
TransactionType::WITHDRAWAL,
TransactionType::DEPOSIT,
TransactionType::TRANSFER,
],
$types = [
'all' => [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::OPENING_BALANCE,
TransactionType::RECONCILIATION,],
'withdrawal' => [TransactionType::WITHDRAWAL,],
'withdrawals' => [TransactionType::WITHDRAWAL,],
'expense' => [TransactionType::WITHDRAWAL,],
'income' => [TransactionType::DEPOSIT,],
'deposit' => [TransactionType::DEPOSIT,],
'deposits' => [TransactionType::DEPOSIT,],
'transfer' => [TransactionType::TRANSFER,],
'transfers' => [TransactionType::TRANSFER,],
'opening_balance' => [TransactionType::OPENING_BALANCE,],
'reconciliation' => [TransactionType::RECONCILIATION,],
'reconciliations' => [TransactionType::RECONCILIATION,],
'special' => [TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,],
'specials' => [TransactionType::OPENING_BALANCE, TransactionType::RECONCILIATION,],
'default' => [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER,],
];
$return = $types['default'];
if (isset($types[$type])) {
return $types[$type];
$return = $types[$type];
}
return $types['default']; // @codeCoverageIgnore
return $return;
}
}

View File

@@ -29,6 +29,7 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Transformers\UserTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use League\Fractal\Manager;
@@ -39,18 +40,18 @@ use League\Fractal\Serializer\JsonApiSerializer;
/**
* Class UserController
* Class UserController.
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class UserController extends Controller
{
/** @var UserRepositoryInterface */
/** @var UserRepositoryInterface The user repository */
private $repository;
/**
* UserController constructor.
*
* @throws \FireflyIII\Exceptions\FireflyException
*/
public function __construct()
{
@@ -70,12 +71,14 @@ class UserController extends Controller
*
* @param \FireflyIII\User $user
*
* @return \Illuminate\Http\Response
* @return JsonResponse
* @throws FireflyException
*/
public function delete(User $user)
public function delete(User $user): JsonResponse
{
if (auth()->user()->hasRole('owner')) {
/** @var User $admin */
$admin = auth()->user();
if ($this->repository->hasRole($admin, 'owner')) {
$this->repository->destroy($user);
return response()->json([], 204);
@@ -88,9 +91,9 @@ class UserController extends Controller
*
* @param Request $request
*
* @return \Illuminate\Http\JsonResponse
* @return JsonResponse
*/
public function index(Request $request)
public function index(Request $request): JsonResponse
{
// user preferences
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
@@ -117,12 +120,14 @@ class UserController extends Controller
}
/**
* Show a single user.
*
* @param Request $request
* @param User $user
*
* @return \Illuminate\Http\JsonResponse
* @return JsonResponse
*/
public function show(Request $request, User $user)
public function show(Request $request, User $user): JsonResponse
{
// make manager
$manager = new Manager();
@@ -140,11 +145,13 @@ class UserController extends Controller
}
/**
* Store a new user.
*
* @param UserRequest $request
*
* @return \Illuminate\Http\JsonResponse
* @return JsonResponse
*/
public function store(UserRequest $request)
public function store(UserRequest $request): JsonResponse
{
$data = $request->getAll();
$user = $this->repository->store($data);
@@ -165,12 +172,14 @@ class UserController extends Controller
}
/**
* Update a user.
*
* @param UserRequest $request
* @param User $user
*
* @return \Illuminate\Http\JsonResponse
* @return JsonResponse
*/
public function update(UserRequest $request, User $user)
public function update(UserRequest $request, User $user): JsonResponse
{
$data = $request->getAll();
$user = $this->repository->update($user, $data);

View File

@@ -23,7 +23,6 @@
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
/**
* Class AccountRequest
*/
@@ -31,6 +30,8 @@ class AccountRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
@@ -40,6 +41,8 @@ class AccountRequest extends Request
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
@@ -67,6 +70,8 @@ class AccountRequest extends Request
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array

View File

@@ -26,7 +26,6 @@ namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Models\Bill;
use FireflyIII\Models\ImportJob;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Rules\IsBase64;
use FireflyIII\Rules\IsValidAttachmentModel;
/**
@@ -35,6 +34,8 @@ use FireflyIII\Rules\IsValidAttachmentModel;
class AttachmentRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
@@ -44,6 +45,8 @@ class AttachmentRequest extends Request
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
@@ -58,6 +61,8 @@ class AttachmentRequest extends Request
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array

View File

@@ -29,6 +29,8 @@ namespace FireflyIII\Api\V1\Requests;
class AvailableBudgetRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
@@ -38,6 +40,8 @@ class AvailableBudgetRequest extends Request
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
@@ -51,6 +55,9 @@ class AvailableBudgetRequest extends Request
}
/**
* TODO must also accept currency code.
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array

View File

@@ -33,6 +33,8 @@ class BillRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
@@ -42,6 +44,8 @@ class BillRequest extends Request
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
@@ -64,6 +68,8 @@ class BillRequest extends Request
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array

View File

@@ -30,6 +30,8 @@ namespace FireflyIII\Api\V1\Requests;
class BudgetLimitRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
@@ -39,6 +41,8 @@ class BudgetLimitRequest extends Request
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
@@ -52,6 +56,8 @@ class BudgetLimitRequest extends Request
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array

View File

@@ -31,6 +31,8 @@ use FireflyIII\Models\Budget;
class BudgetRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
@@ -40,6 +42,8 @@ class BudgetRequest extends Request
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
@@ -52,6 +56,8 @@ class BudgetRequest extends Request
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array

View File

@@ -31,6 +31,8 @@ use FireflyIII\Models\Category;
class CategoryRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
@@ -40,6 +42,8 @@ class CategoryRequest extends Request
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
@@ -51,6 +55,8 @@ class CategoryRequest extends Request
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array

View File

@@ -30,6 +30,8 @@ namespace FireflyIII\Api\V1\Requests;
class CurrencyRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
@@ -39,6 +41,8 @@ class CurrencyRequest extends Request
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
@@ -53,6 +57,8 @@ class CurrencyRequest extends Request
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array
@@ -62,7 +68,7 @@ class CurrencyRequest extends Request
'code' => 'required|between:3,3|unique:transaction_currencies,code',
'symbol' => 'required|between:1,5|unique:transaction_currencies,symbol',
'decimal_places' => 'required|between:0,20|numeric|min:0|max:20',
'default' => 'in:true,false',
'default' => 'boolean',
];
switch ($this->method()) {

View File

@@ -31,6 +31,8 @@ namespace FireflyIII\Api\V1\Requests;
class JournalLinkRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
@@ -40,6 +42,8 @@ class JournalLinkRequest extends Request
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
@@ -53,6 +57,11 @@ class JournalLinkRequest extends Request
}
/**
* TODO include link-type name as optional parameter.
* TODO be consistent and remove notes from this object.
*
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array

View File

@@ -33,6 +33,8 @@ use Illuminate\Validation\Rule;
class LinkTypeRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
@@ -42,6 +44,8 @@ class LinkTypeRequest extends Request
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
@@ -56,6 +60,8 @@ class LinkTypeRequest extends Request
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array

View File

@@ -33,6 +33,8 @@ use FireflyIII\Rules\IsAssetAccountId;
class PiggyBankRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
@@ -42,6 +44,8 @@ class PiggyBankRequest extends Request
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
@@ -58,6 +62,8 @@ class PiggyBankRequest extends Request
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array

View File

@@ -32,6 +32,8 @@ class PreferenceRequest extends Request
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
@@ -40,6 +42,11 @@ class PreferenceRequest extends Request
return auth()->check();
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
{
return [
@@ -47,6 +54,11 @@ class PreferenceRequest extends Request
];
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array
{
return [

View File

@@ -24,20 +24,21 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Rules\BelongsUser;
use FireflyIII\Validation\RecurrenceValidation;
use FireflyIII\Validation\TransactionValidation;
use Illuminate\Validation\Validator;
use InvalidArgumentException;
use Log;
/**
* Class RecurrenceRequest
*/
class RecurrenceRequest extends Request
{
use RecurrenceValidation, TransactionValidation;
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
@@ -47,6 +48,8 @@ class RecurrenceRequest extends Request
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
@@ -67,55 +70,16 @@ class RecurrenceRequest extends Request
'piggy_bank_name' => $this->string('piggy_bank_name'),
'tags' => explode(',', $this->string('tags')),
],
'transactions' => [],
'repetitions' => [],
'transactions' => $this->getTransactionData(),
'repetitions' => $this->getRepetitionData(),
];
// repetition data:
/** @var array $repetitions */
$repetitions = $this->get('repetitions');
/** @var array $repetition */
foreach ($repetitions as $repetition) {
$return['repetitions'][] = [
'type' => $repetition['type'],
'moment' => $repetition['moment'],
'skip' => (int)$repetition['skip'],
'weekend' => (int)$repetition['weekend'],
];
}
// transaction data:
/** @var array $transactions */
$transactions = $this->get('transactions');
/** @var array $transaction */
foreach ($transactions as $transaction) {
$return['transactions'][] = [
'amount' => $transaction['amount'],
'currency_id' => isset($transaction['currency_id']) ? (int)$transaction['currency_id'] : null,
'currency_code' => $transaction['currency_code'] ?? null,
'foreign_amount' => $transaction['foreign_amount'] ?? null,
'foreign_currency_id' => isset($transaction['foreign_currency_id']) ? (int)$transaction['foreign_currency_id'] : null,
'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null,
'budget_id' => isset($transaction['budget_id']) ? (int)$transaction['budget_id'] : null,
'budget_name' => $transaction['budget_name'] ?? null,
'category_id' => isset($transaction['category_id']) ? (int)$transaction['category_id'] : null,
'category_name' => $transaction['category_name'] ?? null,
'source_id' => isset($transaction['source_id']) ? (int)$transaction['source_id'] : null,
'source_name' => isset($transaction['source_name']) ? (string)$transaction['source_name'] : null,
'destination_id' => isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null,
'destination_name' => isset($transaction['destination_name']) ? (string)$transaction['destination_name'] : null,
'description' => $transaction['description'],
];
}
return $return;
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array
@@ -132,18 +96,12 @@ class RecurrenceRequest extends Request
'nr_of_repetitions' => 'numeric|between:1,31',
'apply_rules' => 'required|boolean',
'active' => 'required|boolean',
// rules for meta values:
'tags' => 'between:1,64000',
'piggy_bank_id' => 'numeric',
// rules for repetitions.
'repetitions.*.type' => 'required|in:daily,weekly,ndom,monthly,yearly',
'repetitions.*.moment' => 'between:0,10',
'repetitions.*.skip' => 'required|numeric|between:0,31',
'repetitions.*.weekend' => 'required|numeric|min:1|max:4',
// rules for transactions.
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|required_without:transactions.*.currency_code',
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code|required_without:transactions.*.currency_id',
'transactions.*.foreign_amount' => 'numeric|more:0',
@@ -171,312 +129,76 @@ class RecurrenceRequest extends Request
{
$validator->after(
function (Validator $validator) {
$this->atLeastOneTransaction($validator);
$this->atLeastOneRepetition($validator);
$this->validRepeatsUntil($validator);
$this->validRepetitionMoment($validator);
$this->foreignCurrencyInformation($validator);
$this->validateOneTransaction($validator);
$this->validateOneRepetition($validator);
$this->validateRecurrenceRepetition($validator);
$this->validateRepetitionMoment($validator);
$this->validateForeignCurrencyInformation($validator);
$this->validateAccountInformation($validator);
}
);
}
/**
* Throws an error when this asset account is invalid.
* Returns the repetition data as it is found in the submitted data.
*
* @noinspection MoreThanThreeArgumentsInspection
*
* @param Validator $validator
* @param int|null $accountId
* @param null|string $accountName
* @param string $idField
* @param string $nameField
*
* @return null|Account
* @return array
*/
protected function assetAccountExists(Validator $validator, ?int $accountId, ?string $accountName, string $idField, string $nameField): ?Account
private function getRepetitionData(): array
{
$accountId = (int)$accountId;
$accountName = (string)$accountName;
// both empty? hard exit.
if ($accountId < 1 && '' === $accountName) {
$validator->errors()->add($idField, trans('validation.filled', ['attribute' => $idField]));
return null;
}
// ID belongs to user and is asset account:
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$repository->setUser(auth()->user());
$set = $repository->getAccountsById([$accountId]);
Log::debug(sprintf('Count of accounts found by ID %d is: %d', $accountId, $set->count()));
if ($set->count() === 1) {
/** @var Account $first */
$first = $set->first();
if ($first->accountType->type !== AccountType::ASSET) {
$validator->errors()->add($idField, trans('validation.belongs_user'));
return null;
}
// we ignore the account name at this point.
return $first;
$return = [];
// repetition data:
/** @var array $repetitions */
$repetitions = $this->get('repetitions');
/** @var array $repetition */
foreach ($repetitions as $repetition) {
$return[] = [
'type' => $repetition['type'],
'moment' => $repetition['moment'],
'skip' => (int)$repetition['skip'],
'weekend' => (int)$repetition['weekend'],
];
}
$account = $repository->findByName($accountName, [AccountType::ASSET]);
if (null === $account) {
$validator->errors()->add($nameField, trans('validation.belongs_user'));
return null;
}
return $account;
return $return;
}
/**
* Adds an error to the validator when there are no repetitions in the array of data.
* Returns the transaction data as it is found in the submitted data. It's a complex method according to code
* standards but it just has a lot of ??-statements because of the fields that may or may not exist.
*
* @param Validator $validator
* @return array
* @SuppressWarnings(PHPMD.NPathComplexity)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
protected function atLeastOneRepetition(Validator $validator): void
private function getTransactionData(): array
{
$data = $validator->getData();
$repetitions = $data['repetitions'] ?? [];
// need at least one transaction
if (\count($repetitions) === 0) {
$validator->errors()->add('description', trans('validation.at_least_one_repetition'));
}
}
/**
* Adds an error to the validator when there are no transactions in the array of data.
*
* @param Validator $validator
*/
protected function atLeastOneTransaction(Validator $validator): void
{
$data = $validator->getData();
$transactions = $data['transactions'] ?? [];
// need at least one transaction
if (\count($transactions) === 0) {
$validator->errors()->add('description', trans('validation.at_least_one_transaction'));
}
}
/**
* TODO can be made a rule?
* If the transactions contain foreign amounts, there must also be foreign currency information.
*
* @param Validator $validator
*/
protected function foreignCurrencyInformation(Validator $validator): void
{
$data = $validator->getData();
$transactions = $data['transactions'] ?? [];
foreach ($transactions as $index => $transaction) {
// must have currency info.
if (isset($transaction['foreign_amount'])
&& !(isset($transaction['foreign_currency_id'])
|| isset($transaction['foreign_currency_code']))) {
$validator->errors()->add(
'transactions.' . $index . '.foreign_amount',
trans('validation.require_currency_info')
);
}
}
}
/**
* Throws an error when the given opposing account (of type $type) is invalid.
* Empty data is allowed, system will default to cash.
*
* @noinspection MoreThanThreeArgumentsInspection
*
* @param Validator $validator
* @param string $type
* @param int|null $accountId
* @param null|string $accountName
* @param string $idField
*
* @return null|Account
*/
protected function opposingAccountExists(Validator $validator, string $type, ?int $accountId, ?string $accountName, string $idField): ?Account
{
$accountId = (int)$accountId;
$accountName = (string)$accountName;
// both empty? done!
if ($accountId < 1 && \strlen($accountName) === 0) {
return null;
}
if ($accountId !== 0) {
// ID belongs to user and is $type account:
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$repository->setUser(auth()->user());
$set = $repository->getAccountsById([$accountId]);
if ($set->count() === 1) {
/** @var Account $first */
$first = $set->first();
if ($first->accountType->type !== $type) {
$validator->errors()->add($idField, trans('validation.belongs_user'));
return null;
}
// we ignore the account name at this point.
return $first;
}
$return = [];
// transaction data:
/** @var array $transactions */
$transactions = $this->get('transactions');
/** @var array $transaction */
foreach ($transactions as $transaction) {
$return[] = [
'amount' => $transaction['amount'],
'currency_id' => isset($transaction['currency_id']) ? (int)$transaction['currency_id'] : null,
'currency_code' => $transaction['currency_code'] ?? null,
'foreign_amount' => $transaction['foreign_amount'] ?? null,
'foreign_currency_id' => isset($transaction['foreign_currency_id']) ? (int)$transaction['foreign_currency_id'] : null,
'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null,
'budget_id' => isset($transaction['budget_id']) ? (int)$transaction['budget_id'] : null,
'budget_name' => $transaction['budget_name'] ?? null,
'category_id' => isset($transaction['category_id']) ? (int)$transaction['category_id'] : null,
'category_name' => $transaction['category_name'] ?? null,
'source_id' => isset($transaction['source_id']) ? (int)$transaction['source_id'] : null,
'source_name' => isset($transaction['source_name']) ? (string)$transaction['source_name'] : null,
'destination_id' => isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null,
'destination_name' => isset($transaction['destination_name']) ? (string)$transaction['destination_name'] : null,
'description' => $transaction['description'],
];
}
// not having an opposing account by this name is NOT a problem.
return null;
}
/**
* TODO can be a rule?
*
* Validates the given account information. Switches on given transaction type.
*
* @param Validator $validator
*/
protected function validateAccountInformation(Validator $validator): void
{
$data = $validator->getData();
$transactions = $data['transactions'] ?? [];
$idField = 'description';
$transactionType = $data['type'] ?? 'false';
foreach ($transactions as $index => $transaction) {
$sourceId = isset($transaction['source_id']) ? (int)$transaction['source_id'] : null;
$sourceName = $transaction['source_name'] ?? null;
$destinationId = isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null;
$destinationName = $transaction['destination_name'] ?? null;
$sourceAccount = null;
$destinationAccount = null;
switch ($transactionType) {
case 'withdrawal':
$idField = 'transactions.' . $index . '.source_id';
$nameField = 'transactions.' . $index . '.source_name';
$sourceAccount = $this->assetAccountExists($validator, $sourceId, $sourceName, $idField, $nameField);
$idField = 'transactions.' . $index . '.destination_id';
$destinationAccount = $this->opposingAccountExists($validator, AccountType::EXPENSE, $destinationId, $destinationName, $idField);
break;
case 'deposit':
$idField = 'transactions.' . $index . '.source_id';
$sourceAccount = $this->opposingAccountExists($validator, AccountType::REVENUE, $sourceId, $sourceName, $idField);
$idField = 'transactions.' . $index . '.destination_id';
$nameField = 'transactions.' . $index . '.destination_name';
$destinationAccount = $this->assetAccountExists($validator, $destinationId, $destinationName, $idField, $nameField);
break;
case 'transfer':
$idField = 'transactions.' . $index . '.source_id';
$nameField = 'transactions.' . $index . '.source_name';
$sourceAccount = $this->assetAccountExists($validator, $sourceId, $sourceName, $idField, $nameField);
$idField = 'transactions.' . $index . '.destination_id';
$nameField = 'transactions.' . $index . '.destination_name';
$destinationAccount = $this->assetAccountExists($validator, $destinationId, $destinationName, $idField, $nameField);
break;
default:
$validator->errors()->add($idField, trans('validation.invalid_account_info'));
return;
}
// add some errors in case of same account submitted:
if (null !== $sourceAccount && null !== $destinationAccount && $sourceAccount->id === $destinationAccount->id) {
$validator->errors()->add($idField, trans('validation.source_equals_destination'));
}
}
}
/**
* @param Validator $validator
*/
private function validRepeatsUntil(Validator $validator): void
{
$data = $validator->getData();
$repetitions = $data['nr_of_repetitions'] ?? null;
$repeatUntil = $data['repeat_until'] ?? null;
if (null !== $repetitions && null !== $repeatUntil) {
// expect a date OR count:
$validator->errors()->add('repeat_until', trans('validation.require_repeat_until'));
$validator->errors()->add('nr_of_repetitions', trans('validation.require_repeat_until'));
return;
}
}
/**
* TODO merge this in a rule somehow.
*
* @param Validator $validator
*/
private function validRepetitionMoment(Validator $validator): void
{
$data = $validator->getData();
$repetitions = $data['repetitions'] ?? [];
/**
* @var int $index
* @var array $repetition
*/
foreach ($repetitions as $index => $repetition) {
switch ($repetition['type']) {
default:
$validator->errors()->add(sprintf('repetitions.%d.type', $index), trans('validation.valid_recurrence_rep_type'));
return;
case 'daily':
if ('' !== (string)$repetition['moment']) {
$validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment'));
}
return;
case 'monthly':
$dayOfMonth = (int)$repetition['moment'];
if ($dayOfMonth < 1 || $dayOfMonth > 31) {
$validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment'));
}
return;
case 'ndom':
$parameters = explode(',', $repetition['moment']);
if (\count($parameters) !== 2) {
$validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment'));
return;
}
$nthDay = (int)($parameters[0] ?? 0.0);
$dayOfWeek = (int)($parameters[1] ?? 0.0);
if ($nthDay < 1 || $nthDay > 5) {
$validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment'));
return;
}
if ($dayOfWeek < 1 || $dayOfWeek > 7) {
$validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment'));
return;
}
return;
case 'weekly':
$dayOfWeek = (int)$repetition['moment'];
if ($dayOfWeek < 1 || $dayOfWeek > 7) {
$validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment'));
return;
}
break;
case 'yearly':
try {
Carbon::createFromFormat('Y-m-d', $repetition['moment']);
} catch (InvalidArgumentException $e) {
$validator->errors()->add(sprintf('repetitions.%d.moment', $index), trans('validation.valid_recurrence_rep_moment'));
return;
}
}
}
return $return;
}
}

View File

@@ -28,6 +28,10 @@ use FireflyIII\Http\Requests\Request as FireflyIIIRequest;
/**
* Class Request.
*
* Technically speaking this class does not have to be extended like this but who knows what the future brings.
*
* @SuppressWarnings(PHPMD.NumberOfChildren)
*/
class Request extends FireflyIIIRequest
{

View File

@@ -33,6 +33,8 @@ use FireflyIII\Models\RuleGroup;
class RuleGroupRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
@@ -42,6 +44,8 @@ class RuleGroupRequest extends Request
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
@@ -54,6 +58,8 @@ class RuleGroupRequest extends Request
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array

View File

@@ -32,6 +32,8 @@ use Illuminate\Validation\Validator;
class RuleRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
@@ -41,6 +43,8 @@ class RuleRequest extends Request
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
@@ -62,14 +66,14 @@ class RuleRequest extends Request
$data['rule-triggers'][] = [
'name' => $trigger['name'],
'value' => $trigger['value'],
'stop-processing' => (int)($trigger['stop-processing'] ?? 0) === 1,
'stop-processing' => 1 === (int)($trigger['stop-processing'] ?? 0),
];
}
foreach ($this->get('rule-actions') as $action) {
$data['rule-actions'][] = [
'name' => $action['name'],
'value' => $action['value'],
'stop-processing' => (int)($action['stop-processing'] ?? 0) === 1,
'stop-processing' => 1 === (int)($action['stop-processing'] ?? 0),
];
}
@@ -77,6 +81,8 @@ class RuleRequest extends Request
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array
@@ -134,7 +140,7 @@ class RuleRequest extends Request
$data = $validator->getData();
$repetitions = $data['rule-actions'] ?? [];
// need at least one transaction
if (\count($repetitions) === 0) {
if (0 === \count($repetitions)) {
$validator->errors()->add('title', trans('validation.at_least_one_action'));
}
}
@@ -149,7 +155,7 @@ class RuleRequest extends Request
$data = $validator->getData();
$repetitions = $data['rule-triggers'] ?? [];
// need at least one transaction
if (\count($repetitions) === 0) {
if (0 === \count($repetitions)) {
$validator->errors()->add('title', trans('validation.at_least_one_trigger'));
}
}

View File

@@ -24,12 +24,8 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Rules\BelongsUser;
use FireflyIII\Validation\TransactionValidation;
use Illuminate\Validation\Validator;
@@ -38,7 +34,11 @@ use Illuminate\Validation\Validator;
*/
class TransactionRequest extends Request
{
use TransactionValidation;
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
@@ -48,12 +48,15 @@ class TransactionRequest extends Request
}
/**
* Get all data. Is pretty complex because of all the ??-statements.
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
* @return array
*/
public function getAll(): array
{
$data = [
// basic fields for journal:
'type' => $this->string('type'),
'date' => $this->date('date'),
'description' => $this->string('description'),
@@ -62,8 +65,6 @@ class TransactionRequest extends Request
'bill_id' => $this->integer('bill_id'),
'bill_name' => $this->string('bill_name'),
'tags' => explode(',', $this->string('tags')),
// then, custom fields for journal
'interest_date' => $this->date('interest_date'),
'book_date' => $this->date('book_date'),
'process_date' => $this->date('process_date'),
@@ -72,39 +73,17 @@ class TransactionRequest extends Request
'invoice_date' => $this->date('invoice_date'),
'internal_reference' => $this->string('internal_reference'),
'notes' => $this->string('notes'),
// then, transactions (see below).
'transactions' => [],
'transactions' => $this->getTransactionData(),
];
foreach ($this->get('transactions') as $index => $transaction) {
$array = [
'description' => $transaction['description'] ?? null,
'amount' => $transaction['amount'],
'currency_id' => isset($transaction['currency_id']) ? (int)$transaction['currency_id'] : null,
'currency_code' => $transaction['currency_code'] ?? null,
'foreign_amount' => $transaction['foreign_amount'] ?? null,
'foreign_currency_id' => isset($transaction['foreign_currency_id']) ? (int)$transaction['foreign_currency_id'] : null,
'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null,
'budget_id' => isset($transaction['budget_id']) ? (int)$transaction['budget_id'] : null,
'budget_name' => $transaction['budget_name'] ?? null,
'category_id' => isset($transaction['category_id']) ? (int)$transaction['category_id'] : null,
'category_name' => $transaction['category_name'] ?? null,
'source_id' => isset($transaction['source_id']) ? (int)$transaction['source_id'] : null,
'source_name' => isset($transaction['source_name']) ? (string)$transaction['source_name'] : null,
'destination_id' => isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null,
'destination_name' => isset($transaction['destination_name']) ? (string)$transaction['destination_name'] : null,
'reconciled' => $transaction['reconciled'] ?? false,
'identifier' => $index,
];
$data['transactions'][] = $array;
}
return $data;
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function rules(): array
{
@@ -149,13 +128,8 @@ class TransactionRequest extends Request
'transactions.*.destination_name' => 'between:1,255|nullable',
];
switch ($this->method()) {
default:
break;
case 'PUT':
case 'PATCH':
unset($rules['type'], $rules['piggy_bank_id'], $rules['piggy_bank_name']);
break;
if ('PUT' === $this->method()) {
unset($rules['type'], $rules['piggy_bank_id'], $rules['piggy_bank_name']);
}
return $rules;
@@ -174,11 +148,11 @@ class TransactionRequest extends Request
{
$validator->after(
function (Validator $validator) {
$this->atLeastOneTransaction($validator);
$this->checkValidDescriptions($validator);
$this->equalToJournalDescription($validator);
$this->emptySplitDescriptions($validator);
$this->foreignCurrencyInformation($validator);
$this->validateOneTransaction($validator);
$this->validateDescriptions($validator);
$this->validateJournalDescription($validator);
$this->validateSplitDescriptions($validator);
$this->validateForeignCurrencyInformation($validator);
$this->validateAccountInformation($validator);
$this->validateSplitAccounts($validator);
}
@@ -186,335 +160,37 @@ class TransactionRequest extends Request
}
/**
* Throws an error when this asset account is invalid.
* Get transaction data.
*
* @noinspection MoreThanThreeArgumentsInspection
*
* @param Validator $validator
* @param int|null $accountId
* @param null|string $accountName
* @param string $idField
* @param string $nameField
*
* @return null|Account
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
* @return array
*/
protected function assetAccountExists(Validator $validator, ?int $accountId, ?string $accountName, string $idField, string $nameField): ?Account
private function getTransactionData(): array
{
$accountId = (int)$accountId;
$accountName = (string)$accountName;
// both empty? hard exit.
if ($accountId < 1 && \strlen($accountName) === 0) {
$validator->errors()->add($idField, trans('validation.filled', ['attribute' => $idField]));
return null;
}
// ID belongs to user and is asset account:
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$repository->setUser(auth()->user());
$set = $repository->getAccountsById([$accountId]);
if ($set->count() === 1) {
/** @var Account $first */
$first = $set->first();
if ($first->accountType->type !== AccountType::ASSET) {
$validator->errors()->add($idField, trans('validation.belongs_user'));
return null;
}
// we ignore the account name at this point.
return $first;
$return = [];
foreach ($this->get('transactions') as $index => $transaction) {
$return[] = [
'description' => $transaction['description'] ?? null,
'amount' => $transaction['amount'],
'currency_id' => isset($transaction['currency_id']) ? (int)$transaction['currency_id'] : null,
'currency_code' => $transaction['currency_code'] ?? null,
'foreign_amount' => $transaction['foreign_amount'] ?? null,
'foreign_currency_id' => isset($transaction['foreign_currency_id']) ? (int)$transaction['foreign_currency_id'] : null,
'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null,
'budget_id' => isset($transaction['budget_id']) ? (int)$transaction['budget_id'] : null,
'budget_name' => $transaction['budget_name'] ?? null,
'category_id' => isset($transaction['category_id']) ? (int)$transaction['category_id'] : null,
'category_name' => $transaction['category_name'] ?? null,
'source_id' => isset($transaction['source_id']) ? (int)$transaction['source_id'] : null,
'source_name' => isset($transaction['source_name']) ? (string)$transaction['source_name'] : null,
'destination_id' => isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null,
'destination_name' => isset($transaction['destination_name']) ? (string)$transaction['destination_name'] : null,
'reconciled' => $transaction['reconciled'] ?? false,
'identifier' => $index,
];
}
$account = $repository->findByName($accountName, [AccountType::ASSET]);
if (null === $account) {
$validator->errors()->add($nameField, trans('validation.belongs_user'));
return null;
}
return $account;
return $return;
}
/**
* Adds an error to the validator when there are no transactions in the array of data.
*
* @param Validator $validator
*/
protected function atLeastOneTransaction(Validator $validator): void
{
$data = $validator->getData();
$transactions = $data['transactions'] ?? [];
// need at least one transaction
if (\count($transactions) === 0) {
$validator->errors()->add('description', trans('validation.at_least_one_transaction'));
}
}
/**
* Adds an error to the "description" field when the user has submitted no descriptions and no
* journal description.
*
* @param Validator $validator
*/
protected function checkValidDescriptions(Validator $validator): void
{
$data = $validator->getData();
$transactions = $data['transactions'] ?? [];
$journalDescription = (string)($data['description'] ?? '');
$validDescriptions = 0;
foreach ($transactions as $index => $transaction) {
if (\strlen((string)($transaction['description'] ?? '')) > 0) {
$validDescriptions++;
}
}
// no valid descriptions and empty journal description? error.
if ($validDescriptions === 0 && \strlen($journalDescription) === 0) {
$validator->errors()->add('description', trans('validation.filled', ['attribute' => trans('validation.attributes.description')]));
}
}
/**
* Adds an error to the validator when the user submits a split transaction (more than 1 transactions)
* but does not give them a description.
*
* @param Validator $validator
*/
protected function emptySplitDescriptions(Validator $validator): void
{
$data = $validator->getData();
$transactions = $data['transactions'] ?? [];
foreach ($transactions as $index => $transaction) {
$description = (string)($transaction['description'] ?? '');
// filled description is mandatory for split transactions.
if (\count($transactions) > 1 && \strlen($description) === 0) {
$validator->errors()->add(
'transactions.' . $index . '.description',
trans('validation.filled', ['attribute' => trans('validation.attributes.transaction_description')])
);
}
}
}
/**
* Adds an error to the validator when any transaction descriptions are equal to the journal description.
*
* @param Validator $validator
*/
protected function equalToJournalDescription(Validator $validator): void
{
$data = $validator->getData();
$transactions = $data['transactions'] ?? [];
$journalDescription = (string)($data['description'] ?? '');
foreach ($transactions as $index => $transaction) {
$description = (string)($transaction['description'] ?? '');
// description cannot be equal to journal description.
if ($description === $journalDescription) {
$validator->errors()->add('transactions.' . $index . '.description', trans('validation.equal_description'));
}
}
}
/**
* TODO can be made a rule?
*
* If the transactions contain foreign amounts, there must also be foreign currency information.
*
* @param Validator $validator
*/
protected function foreignCurrencyInformation(Validator $validator): void
{
$data = $validator->getData();
$transactions = $data['transactions'] ?? [];
foreach ($transactions as $index => $transaction) {
// must have currency info.
if (isset($transaction['foreign_amount'])
&& !(isset($transaction['foreign_currency_id'])
|| isset($transaction['foreign_currency_code']))) {
$validator->errors()->add(
'transactions.' . $index . '.foreign_amount',
trans('validation.require_currency_info')
);
}
}
}
/**
* Throws an error when the given opposing account (of type $type) is invalid.
* Empty data is allowed, system will default to cash.
*
* @noinspection MoreThanThreeArgumentsInspection
*
* @param Validator $validator
* @param string $type
* @param int|null $accountId
* @param null|string $accountName
* @param string $idField
*
* @return null|Account
*/
protected function opposingAccountExists(Validator $validator, string $type, ?int $accountId, ?string $accountName, string $idField): ?Account
{
$accountId = (int)$accountId;
$accountName = (string)$accountName;
// both empty? done!
if ($accountId < 1 && \strlen($accountName) === 0) {
return null;
}
if ($accountId !== 0) {
// ID belongs to user and is $type account:
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$repository->setUser(auth()->user());
$set = $repository->getAccountsById([$accountId]);
if ($set->count() === 1) {
/** @var Account $first */
$first = $set->first();
if ($first->accountType->type !== $type) {
$validator->errors()->add($idField, trans('validation.belongs_user'));
return null;
}
// we ignore the account name at this point.
return $first;
}
}
// not having an opposing account by this name is NOT a problem.
return null;
}
/**
* Validates the given account information. Switches on given transaction type.
*
* @param Validator $validator
*
* @throws FireflyException
*/
protected function validateAccountInformation(Validator $validator): void
{
$data = $validator->getData();
$transactions = $data['transactions'] ?? [];
if (!isset($data['type'])) {
// the journal may exist in the request:
/** @var Transaction $transaction */
$transaction = $this->route()->parameter('transaction');
if (null === $transaction) {
return; // @codeCoverageIgnore
}
$data['type'] = strtolower($transaction->transactionJournal->transactionType->type);
}
foreach ($transactions as $index => $transaction) {
$sourceId = isset($transaction['source_id']) ? (int)$transaction['source_id'] : null;
$sourceName = $transaction['source_name'] ?? null;
$destinationId = isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null;
$destinationName = $transaction['destination_name'] ?? null;
$sourceAccount = null;
$destinationAccount = null;
switch ($data['type']) {
case 'withdrawal':
$idField = 'transactions.' . $index . '.source_id';
$nameField = 'transactions.' . $index . '.source_name';
$sourceAccount = $this->assetAccountExists($validator, $sourceId, $sourceName, $idField, $nameField);
$idField = 'transactions.' . $index . '.destination_id';
$destinationAccount = $this->opposingAccountExists($validator, AccountType::EXPENSE, $destinationId, $destinationName, $idField);
break;
case 'deposit':
$idField = 'transactions.' . $index . '.source_id';
$sourceAccount = $this->opposingAccountExists($validator, AccountType::REVENUE, $sourceId, $sourceName, $idField);
$idField = 'transactions.' . $index . '.destination_id';
$nameField = 'transactions.' . $index . '.destination_name';
$destinationAccount = $this->assetAccountExists($validator, $destinationId, $destinationName, $idField, $nameField);
break;
case 'transfer':
$idField = 'transactions.' . $index . '.source_id';
$nameField = 'transactions.' . $index . '.source_name';
$sourceAccount = $this->assetAccountExists($validator, $sourceId, $sourceName, $idField, $nameField);
$idField = 'transactions.' . $index . '.destination_id';
$nameField = 'transactions.' . $index . '.destination_name';
$destinationAccount = $this->assetAccountExists($validator, $destinationId, $destinationName, $idField, $nameField);
break;
default:
// @codeCoverageIgnoreStart
throw new FireflyException(
sprintf('The validator cannot handle transaction type "%s" in validateAccountInformation().', $data['type'])
);
// @codeCoverageIgnoreEnd
}
// add some errors in case of same account submitted:
if (null !== $sourceAccount && null !== $destinationAccount && $sourceAccount->id === $destinationAccount->id) {
$validator->errors()->add($idField, trans('validation.source_equals_destination'));
}
}
}
/**
* @param Validator $validator
*
* @throws FireflyException
*/
protected function validateSplitAccounts(Validator $validator): void
{
$data = $validator->getData();
$count = isset($data['transactions']) ? \count($data['transactions']) : 0;
if ($count < 2) {
return;
}
// this is pretty much impossible:
// @codeCoverageIgnoreStart
if (!isset($data['type'])) {
// the journal may exist in the request:
/** @var Transaction $transaction */
$transaction = $this->route()->parameter('transaction');
if (null === $transaction) {
return;
}
$data['type'] = strtolower($transaction->transactionJournal->transactionType->type);
}
// @codeCoverageIgnoreEnd
// collect all source ID's and destination ID's, if present:
$sources = [];
$destinations = [];
foreach ($data['transactions'] as $transaction) {
$sources[] = isset($transaction['source_id']) ? (int)$transaction['source_id'] : 0;
$destinations[] = isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : 0;
}
$destinations = array_unique($destinations);
$sources = array_unique($sources);
// switch on type:
switch ($data['type']) {
case 'withdrawal':
if (\count($sources) > 1) {
$validator->errors()->add('transactions.0.source_id', trans('validation.all_accounts_equal'));
}
break;
case 'deposit':
if (\count($destinations) > 1) {
$validator->errors()->add('transactions.0.destination_id', trans('validation.all_accounts_equal'));
}
break;
case 'transfer':
if (\count($sources) > 1 || \count($destinations) > 1) {
$validator->errors()->add('transactions.0.source_id', trans('validation.all_accounts_equal'));
$validator->errors()->add('transactions.0.destination_id', trans('validation.all_accounts_equal'));
}
break;
default:
// @codeCoverageIgnoreStart
throw new FireflyException(
sprintf('The validator cannot handle transaction type "%s" in validateSplitAccounts().', $data['type'])
);
// @codeCoverageIgnoreEnd
}
}
}

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User;
@@ -33,24 +34,32 @@ use FireflyIII\User;
class UserRequest extends Request
{
/**
* Authorize logged in users.
*
* @return bool
*/
public function authorize(): bool
{
$result = false;
// Only allow authenticated users
if (!auth()->check()) {
return false; // @codeCoverageIgnore
}
/** @var User $user */
$user = auth()->user();
if (!$user->hasRole('owner')) {
return false; // @codeCoverageIgnore
if (auth()->check()) {
/** @var User $user */
$user = auth()->user();
/** @var UserRepositoryInterface $repository */
$repository = app(UserRepositoryInterface::class);
if ($repository->hasRole($user, 'owner')) {
$result = true; // @codeCoverageIgnore
}
}
return true;
return $result;
}
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array
@@ -65,6 +74,8 @@ class UserRequest extends Request
}
/**
* The rules that the incoming request must be matched against.
*
* @return array
*/
public function rules(): array

View File

@@ -1,5 +1,4 @@
<?php
/**
* CreateExport.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@@ -20,6 +19,8 @@
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection MultipleReturnStatementsInspection */
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
@@ -42,6 +43,7 @@ use Storage;
class CreateExport extends Command
{
use VerifiesAccessToken;
/**
* The console command description.
*
@@ -64,9 +66,11 @@ class CreateExport extends Command
/**
* Execute the console command.
*
* @return mixed
* @return int
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function handle()
public function handle(): int
{
if (!$this->verifyAccessToken()) {
$this->error('Invalid access token.');
@@ -86,6 +90,9 @@ class CreateExport extends Command
// set user
$user = $userRepository->findNull((int)$this->option('user'));
if (null === $user) {
return 1;
}
$jobRepository->setUser($user);
$journalRepository->setUser($user);
$accountRepository->setUser($user);

View File

@@ -1,5 +1,4 @@
<?php
/**
* CreateImport.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@@ -20,6 +19,8 @@
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection MultipleReturnStatementsInspection */
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
@@ -81,10 +82,15 @@ class CreateImport extends Command
$configuration = (string)$this->argument('configuration');
$user = $userRepository->findNull((int)$this->option('user'));
$cwd = getcwd();
$type = strtolower((string)$this->option('type'));
$provider = strtolower((string)$this->option('provider'));
$configurationData = [];
if (null === $user) {
$this->errorLine('User is NULL.');
return 1;
}
if (!$this->validArguments()) {
$this->errorLine('Invalid arguments.');
@@ -182,7 +188,7 @@ class CreateImport extends Command
}
$count++;
}
if ($importJob->status === 'provider_finished') {
if ('provider_finished' === $importJob->status) {
$this->infoLine('Import has finished. Please wait for storage of data.');
// set job to be storing data:
$jobRepository->setStatus($importJob, 'storing_data');
@@ -274,19 +280,19 @@ class CreateImport extends Command
return false;
}
if ($provider === 'file' && !\in_array($type, $validTypes, true)) {
if ('file' === $provider && !\in_array($type, $validTypes, true)) {
$this->errorLine(sprintf('Cannot import file of type "%s"', $type));
return false;
}
if ($provider === 'file' && !file_exists($file)) {
if ('file' === $provider && !file_exists($file)) {
$this->errorLine(sprintf('Firefly III cannot find file "%s" (working directory: "%s").', $file, $cwd));
return false;
}
if ($provider === 'file' && !file_exists($configuration)) {
if ('file' === $provider && !file_exists($configuration)) {
$this->errorLine(sprintf('Firefly III cannot find configuration file "%s" (working directory: "%s").', $configuration, $cwd));
return false;

View File

@@ -1,5 +1,4 @@
<?php
/**
* DecryptAttachment.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@@ -20,6 +19,8 @@
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection MultipleReturnStatementsInspection */
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
@@ -51,11 +52,12 @@ class DecryptAttachment extends Command
/**
* Execute the console command.
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five its fine.
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*
* @return int
*/
public function handle()
public function handle(): int
{
/** @var AttachmentRepositoryInterface $repository */
$repository = app(AttachmentRepositoryInterface::class);
@@ -67,28 +69,28 @@ class DecryptAttachment extends Command
$this->error(sprintf('No attachment with id #%d', $attachmentId));
Log::error(sprintf('DecryptAttachment: No attachment with id #%d', $attachmentId));
return;
return 1;
}
if ($attachmentName !== $attachment->filename) {
$this->error('File name does not match.');
Log::error('DecryptAttachment: File name does not match.');
return;
return 1;
}
if (!is_dir($storagePath)) {
$this->error(sprintf('Path "%s" is not a directory.', $storagePath));
Log::error(sprintf('DecryptAttachment: Path "%s" is not a directory.', $storagePath));
return;
return 1;
}
if (!is_writable($storagePath)) {
$this->error(sprintf('Path "%s" is not writable.', $storagePath));
Log::error(sprintf('DecryptAttachment: Path "%s" is not writable.', $storagePath));
return;
return 1;
}
$fullPath = $storagePath . DIRECTORY_SEPARATOR . $attachment->filename;
@@ -98,9 +100,10 @@ class DecryptAttachment extends Command
if (false === $result) {
$this->error('Could not write to file.');
return;
return 1;
}
$this->info(sprintf('%d bytes written. Exiting now..', $result));
return 0;
}
}

View File

@@ -1,5 +1,4 @@
<?php
/**
* EncryptFile.php
* Copyright (c) 2018 thegrumpydictator@gmail.com

View File

@@ -1,5 +1,4 @@
<?php
/**
* Import.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@@ -20,6 +19,9 @@
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection MultipleReturnStatementsInspection */
/** @noinspection PhpDynamicAsStaticMethodCallInspection */
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
@@ -27,8 +29,8 @@ namespace FireflyIII\Console\Commands;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Import\Routine\RoutineInterface;
use FireflyIII\Models\ImportJob;
use FireflyIII\Models\Tag;
use Illuminate\Console\Command;
use Illuminate\Support\MessageBag;
use Log;
/**
@@ -52,10 +54,12 @@ class Import extends Command
/**
* Run the import routine.
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*
* @throws FireflyException
*/
public function handle()
public function handle(): void
{
Log::debug('Start start-import command');
$jobKey = $this->argument('key');
@@ -83,21 +87,30 @@ class Import extends Command
/** @var RoutineInterface $routine */
$routine = app($className);
$routine->setJob($job);
$routine->setImportJob($job);
$routine->run();
/** @var MessageBag $error */
foreach ($routine->getErrors() as $index => $error) {
/**
* @var int $index
* @var string $error
*/
foreach ($job->errors as $index => $error) {
$this->errorLine(sprintf('Error importing line #%d: %s', $index, $error));
}
$this->infoLine(
sprintf('The import has finished. %d transactions have been imported out of %d records.', $routine->getJournals()->count(), $routine->getLines())
);
/** @var Tag $tag */
$tag = $job->tag()->first();
$count = 0;
if (null === $tag) {
$count = $tag->transactionJournals()->count();
}
$this->infoLine(sprintf('The import has finished. %d transactions have been imported.', $count));
}
/**
* Displays an error.
*
* @param string $message
* @param array|null $data
*/
@@ -109,6 +122,8 @@ class Import extends Command
}
/**
* Displays an informational message.
*
* @param string $message
* @param array $data
*/

View File

@@ -1,5 +1,4 @@
<?php
/**
* ScanAttachments.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@@ -20,6 +19,8 @@
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection PhpDynamicAsStaticMethodCallInspection */
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
@@ -53,7 +54,7 @@ class ScanAttachments extends Command
/**
* Execute the console command.
*/
public function handle()
public function handle(): void
{
$attachments = Attachment::get();
$disk = Storage::disk('upload');
@@ -63,13 +64,13 @@ class ScanAttachments extends Command
try {
$content = $disk->get($fileName);
} catch (FileNotFoundException $e) {
$this->error(sprintf('Could not find data for attachment #%d', $attachment->id));
$this->error(sprintf('Could not find data for attachment #%d: %s', $attachment->id, $e->getMessage()));
continue;
}
try {
$decrypted = Crypt::decrypt($content);
} catch (DecryptException $e) {
$this->error(sprintf('Could not decrypt data of attachment #%d', $attachment->id));
$this->error(sprintf('Could not decrypt data of attachment #%d: %s', $attachment->id, $e->getMessage()));
continue;
}
$tmpfname = tempnam(sys_get_temp_dir(), 'FireflyIII');

View File

@@ -20,6 +20,10 @@
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection MultipleReturnStatementsInspection */
/** @noinspection PhpStaticAsDynamicMethodCallInspection */
/** @noinspection PhpDynamicAsStaticMethodCallInspection */
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
@@ -42,7 +46,9 @@ use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionJournalMeta;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\User;
use Illuminate\Console\Command;
use Illuminate\Database\QueryException;
@@ -50,14 +56,14 @@ use Illuminate\Support\Collection;
use Log;
use Preferences;
use Schema;
use UnexpectedValueException;
/**
* Class UpgradeDatabase.
*
* Upgrade user database.
*
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects) // it just touches a lot of things.
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class UpgradeDatabase extends Command
{
@@ -77,7 +83,7 @@ class UpgradeDatabase extends Command
/**
* Execute the console command.
*/
public function handle()
public function handle(): void
{
$this->setTransactionIdentifier();
$this->updateAccountCurrencies();
@@ -93,7 +99,14 @@ class UpgradeDatabase extends Command
$this->info('Firefly III database is up to date.');
}
public function migrateBillsToRules()
/**
* Since it is one routine these warnings make sense and should be supressed.
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @SuppressWarnings(PHPMD.NPathComplexity)
*/
public function migrateBillsToRules(): void
{
foreach (User::get() as $user) {
/** @var Preference $lang */
@@ -101,12 +114,20 @@ class UpgradeDatabase extends Command
$groupName = (string)trans('firefly.rulegroup_for_bills_title', [], $lang->data);
$ruleGroup = $user->ruleGroups()->where('title', $groupName)->first();
$currencyPreference = Preferences::getForUser($user, 'currencyPreference', config('firefly.default_currency', 'EUR'));
$currency = TransactionCurrency::where('code', $currencyPreference->data)->first();
if (null === $currencyPreference) {
$this->error('User has no currency preference. Impossible.');
return;
}
$currency = TransactionCurrency::where('code', $currencyPreference->data)->first();
if (null === $currency) {
$this->line('Fall back to default currency in migrateBillsToRules().');
$currency = app('amount')->getDefaultCurrency();
}
if ($ruleGroup === null) {
if (null === $ruleGroup) {
$array = RuleGroup::get(['order'])->pluck('order')->toArray();
$order = \count($array) > 0 ? max($array) + 1 : 1;
$ruleGroup = RuleGroup::create(
@@ -126,7 +147,7 @@ class UpgradeDatabase extends Command
$collection = $user->bills()->get();
/** @var Bill $bill */
foreach ($collection as $bill) {
if ($bill->match !== 'MIGRATED_TO_RULES') {
if ('MIGRATED_TO_RULES' !== $bill->match) {
$rule = Rule::create(
[
'user_id' => $user->id,
@@ -260,23 +281,29 @@ class UpgradeDatabase extends Command
/**
* Each (asset) account must have a reference to a preferred currency. If the account does not have one, it's forced upon the account.
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's seven but it can't really be helped.
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function updateAccountCurrencies(): void
{
$accounts = Account::leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->whereIn('account_types.type', [AccountType::DEFAULT, AccountType::ASSET])->get(['accounts.*']);
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$accounts->each(
function (Account $account) {
function (Account $account) use ($repository) {
$repository->setUser($account->user);
// get users preference, fall back to system pref.
$defaultCurrencyCode = Preferences::getForUser($account->user, 'currencyPreference', config('firefly.default_currency', 'EUR'))->data;
$defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first();
$accountCurrency = (int)$account->getMeta('currency_id');
$accountCurrency = (int)$repository->getMetaValue($account, 'currency_id');
$openingBalance = $account->getOpeningBalance();
$obCurrency = (int)$openingBalance->transaction_currency_id;
if (null === $defaultCurrency) {
throw new UnexpectedValueException('The default currency is NULL, and this is more or less impossible.');
}
// both 0? set to default currency:
if (0 === $accountCurrency && 0 === $obCurrency) {
AccountMeta::where('account_id', $account->id)->where('name', 'currency_id')->forceDelete();
@@ -317,19 +344,22 @@ class UpgradeDatabase extends Command
* Both source and destination must match the respective currency preference of the related asset account.
* So FF3 must verify all transactions.
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function updateOtherCurrencies(): void
{
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
$set = TransactionJournal
/** @var AccountRepositoryInterface $accountRepos */
$accountRepos = app(AccountRepositoryInterface::class);
$set = TransactionJournal
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->whereIn('transaction_types.type', [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE])
->get(['transaction_journals.*']);
$set->each(
function (TransactionJournal $journal) use ($repository) {
function (TransactionJournal $journal) use ($repository, $accountRepos) {
// get the transaction with the asset account in it:
/** @var Transaction $transaction */
$transaction = $journal->transactions()
@@ -339,9 +369,13 @@ class UpgradeDatabase extends Command
if (null === $transaction) {
return;
}
$accountRepos->setUser($journal->user);
/** @var Account $account */
$account = $transaction->account;
$currency = $repository->find((int)$account->getMeta('currency_id'));
$account = $transaction->account;
$currency = $repository->findNull((int)$accountRepos->getMetaValue($account, 'currency_id'));
if (null === $currency) {
return;
}
$transactions = $journal->transactions()->get();
$transactions->each(
function (Transaction $transaction) use ($currency) {
@@ -377,7 +411,7 @@ class UpgradeDatabase extends Command
* Both source and destination must match the respective currency preference. So FF3 must verify ALL
* transactions.
*/
public function updateTransferCurrencies()
public function updateTransferCurrencies(): void
{
$set = TransactionJournal
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
@@ -449,6 +483,7 @@ class UpgradeDatabase extends Command
*/
private function migrateNotes(): void
{
/** @noinspection PhpUndefinedMethodInspection */
$set = TransactionJournalMeta::whereName('notes')->get();
/** @var TransactionJournalMeta $meta */
foreach ($set as $meta) {
@@ -479,8 +514,15 @@ class UpgradeDatabase extends Command
{
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
$currency = $repository->find((int)$transaction->account->getMeta('currency_id'));
$journal = $transaction->transactionJournal;
/** @var AccountRepositoryInterface $accountRepos */
$accountRepos = app(AccountRepositoryInterface::class);
$accountRepos->setUser($transaction->account->user);
$currency = $repository->findNull((int)$accountRepos->getMetaValue($transaction->account, 'currency_id'));
$journal = $transaction->transactionJournal;
if (null === $currency) {
return;
}
if (!((int)$currency->id === (int)$journal->transaction_currency_id)) {
$this->line(
@@ -549,11 +591,11 @@ class UpgradeDatabase extends Command
*
* The transaction that is sent to this function MUST be the source transaction (amount negative).
*
* Method is long and complex bit I'm taking it for granted.
* Method is long and complex but I'll allow it. https://imgur.com/gallery/dVDJiez
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @SuppressWarnings(PHPMD.NPathComplexity)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*
* @param Transaction $transaction
*/
@@ -561,7 +603,14 @@ class UpgradeDatabase extends Command
{
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
$currency = $repository->findNull((int)$transaction->account->getMeta('currency_id'));
/** @var AccountRepositoryInterface $accountRepos */
$accountRepos = app(AccountRepositoryInterface::class);
/** @var JournalRepositoryInterface $journalRepos */
$journalRepos = app(JournalRepositoryInterface::class);
$accountRepos->setUser($transaction->account->user);
$journalRepos->setUser($transaction->account->user);
$currency = $repository->findNull((int)$accountRepos->getMetaValue($transaction->account, 'currency_id'));
if (null === $currency) {
Log::error(sprintf('Account #%d ("%s") must have currency preference but has none.', $transaction->account->id, $transaction->account->name));
@@ -597,7 +646,7 @@ class UpgradeDatabase extends Command
$journal = $transaction->transactionJournal;
/** @var Transaction $opposing */
$opposing = $journal->transactions()->where('amount', '>', 0)->where('identifier', $transaction->identifier)->first();
$opposingCurrency = $repository->findNull((int)$opposing->account->getMeta('currency_id'));
$opposingCurrency = $repository->findNull((int)$accountRepos->getMetaValue($opposing->account, 'currency_id'));
if (null === $opposingCurrency) {
Log::error(sprintf('Account #%d ("%s") must have currency preference but has none.', $opposing->account->id, $opposing->account->name));
@@ -653,7 +702,7 @@ class UpgradeDatabase extends Command
// when both are zero, try to grab it from journal:
if (null === $opposing->foreign_amount && null === $transaction->foreign_amount) {
$foreignAmount = $journal->getMeta('foreign_amount');
$foreignAmount = $journalRepos->getMetaField($journal, 'foreign_amount');
if (null === $foreignAmount) {
Log::debug(sprintf('Journal #%d has missing foreign currency data, forced to do 1:1 conversion :(.', $transaction->transaction_journal_id));
$transaction->foreign_amount = bcmul((string)$transaction->amount, '-1');

View File

@@ -1,5 +1,4 @@
<?php
/**
* UpgradeFireflyInstructions.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@@ -47,7 +46,7 @@ class UpgradeFireflyInstructions extends Command
/**
* Execute the console command.
*/
public function handle()
public function handle(): void
{
if ('update' === $this->argument('task')) {
$this->updateInstructions();
@@ -62,7 +61,7 @@ class UpgradeFireflyInstructions extends Command
*
* @param string $text
*/
private function boxed(string $text)
private function boxed(string $text): void
{
$parts = explode("\n", wordwrap($text));
foreach ($parts as $string) {
@@ -75,7 +74,7 @@ class UpgradeFireflyInstructions extends Command
*
* @param string $text
*/
private function boxedInfo(string $text)
private function boxedInfo(string $text): void
{
$parts = explode("\n", wordwrap($text));
foreach ($parts as $string) {
@@ -86,7 +85,7 @@ class UpgradeFireflyInstructions extends Command
/**
* Render instructions.
*/
private function installInstructions()
private function installInstructions(): void
{
/** @var string $version */
$version = config('firefly.version');
@@ -94,8 +93,7 @@ class UpgradeFireflyInstructions extends Command
$text = '';
foreach (array_keys($config) as $compare) {
// if string starts with:
$len = \strlen($compare);
if (substr($version, 0, $len) === $compare) {
if (0 === strpos($version, $compare)) {
$text = $config[$compare];
}
}
@@ -120,7 +118,7 @@ class UpgradeFireflyInstructions extends Command
/**
* Show a line.
*/
private function showLine()
private function showLine(): void
{
$line = '+';
for ($i = 0; $i < 78; ++$i) {
@@ -133,7 +131,7 @@ class UpgradeFireflyInstructions extends Command
/**
* Render upgrade instructions.
*/
private function updateInstructions()
private function updateInstructions(): void
{
/** @var string $version */
$version = config('firefly.version');
@@ -141,8 +139,7 @@ class UpgradeFireflyInstructions extends Command
$text = '';
foreach (array_keys($config) as $compare) {
// if string starts with:
$len = \strlen($compare);
if (substr($version, 0, $len) === $compare) {
if (0 === strpos($version, $compare)) {
$text = $config[$compare];
}
}

View File

@@ -1,5 +1,4 @@
<?php
/**
* UseEncryption.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@@ -48,12 +47,12 @@ class UseEncryption extends Command
/**
* Execute the console command.
*/
public function handle()
public function handle(): void
{
if (config('firefly.encryption') === true) {
if (true === config('firefly.encryption')) {
$this->info('Firefly III configuration calls for encrypted data.');
}
if (config('firefly.encryption') === false) {
if (false === config('firefly.encryption')) {
$this->info('Firefly III configuration calls for unencrypted data.');
}
$this->handleObjects('Account', 'name', 'encrypted');
@@ -72,18 +71,21 @@ class UseEncryption extends Command
* @param string $field
* @param string $indicator
*/
public function handleObjects(string $class, string $field, string $indicator)
public function handleObjects(string $class, string $field, string $indicator): void
{
$fqn = sprintf('FireflyIII\Models\%s', $class);
$encrypt = config('firefly.encryption') === true ? 0 : 1;
$set = $fqn::where($indicator, $encrypt)->get();
$encrypt = true === config('firefly.encryption') ? 0 : 1;
/** @noinspection PhpUndefinedMethodInspection */
$set = $fqn::where($indicator, $encrypt)->get();
foreach ($set as $entry) {
$newName = $entry->$field;
$entry->$field = $newName;
/** @noinspection PhpUndefinedMethodInspection */
$entry->save();
}
/** @noinspection PhpUndefinedMethodInspection */
$this->line(sprintf('Updated %d %s.', $set->count(), strtolower(Str::plural($class))));
}
}

View File

@@ -1,5 +1,4 @@
<?php
/**
* VerifiesAccessToken.php
* Copyright (c) 2018 thegrumpydictator@gmail.com

View File

@@ -1,5 +1,4 @@
<?php
/**
* VerifyDatabase.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@@ -20,6 +19,8 @@
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection PhpDynamicAsStaticMethodCallInspection */
declare(strict_types=1);
namespace FireflyIII\Console\Commands;
@@ -29,6 +30,7 @@ use DB;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\LinkType;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\Transaction;
@@ -39,6 +41,7 @@ use FireflyIII\User;
use Illuminate\Console\Command;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Database\Eloquent\Builder;
use Log;
use Preferences;
use Schema;
use stdClass;
@@ -46,6 +49,7 @@ use stdClass;
/**
* Class VerifyDatabase.
*
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class VerifyDatabase extends Command
@@ -66,15 +70,15 @@ class VerifyDatabase extends Command
/**
* Execute the console command.
*/
public function handle()
public function handle(): void
{
// if table does not exist, return false
if (!Schema::hasTable('users')) {
return;
}
$this->reportObject('budget');
$this->reportObject('category');
$this->reportEmptyBudgets();
$this->reportEmptyCategories();
$this->reportObject('tag');
$this->reportAccounts();
$this->reportBudgetLimits();
@@ -145,9 +149,10 @@ class VerifyDatabase extends Command
}
/**
* Fix the situation where the matching transactions
* of a journal somehow have non-matching categories
* or budgets
* Fix the situation where the matching transactions of a journal somehow have non-matching categories or budgets.
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
private function fixBadMeta(): void
{
@@ -208,6 +213,12 @@ class VerifyDatabase extends Command
}
}
/**
* Makes sure amounts are stored correctly.
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
private function fixDoubleAmounts(): void
{
$count = 0;
@@ -257,7 +268,7 @@ class VerifyDatabase extends Command
}
/**
*
* Removes bills from journals that should not have bills.
*/
private function removeBills(): void
{
@@ -305,7 +316,7 @@ class VerifyDatabase extends Command
/**
* Reports on accounts with no transactions.
*/
private function reportAccounts()
private function reportAccounts(): void
{
$set = Account::leftJoin('transactions', 'transactions.account_id', '=', 'accounts.id')
->leftJoin('users', 'accounts.user_id', '=', 'users.id')
@@ -378,6 +389,82 @@ class VerifyDatabase extends Command
}
}
/**
* Report on budgets with no transactions or journals.
*/
private function reportEmptyBudgets(): void
{
$set = Budget::leftJoin('budget_transaction_journal', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
->leftJoin('users', 'budgets.user_id', '=', 'users.id')
->distinct()
->whereNull('budget_transaction_journal.budget_id')
->whereNull('budgets.deleted_at')
->get(['budgets.id', 'budgets.name', 'budgets.user_id', 'users.email']);
/** @var stdClass $entry */
foreach ($set as $entry) {
$objName = $entry->name;
try {
$objName = Crypt::decrypt($objName);
} catch (DecryptException $e) {
// it probably was not encrypted.
Log::debug(sprintf('Not a problem: %s', $e->getMessage()));
}
// also count the transactions:
$countTransactions = DB::table('budget_transaction')->where('budget_id', $entry->id)->count();
if (0 === $countTransactions) {
$line = sprintf(
'User #%d (%s) has budget #%d ("%s") which has no transactions.',
$entry->user_id,
$entry->email,
$entry->id,
$objName
);
$this->line($line);
}
}
}
/**
* Report on categories with no transactions or journals.
*/
private function reportEmptyCategories(): void
{
$set = Category::leftJoin('category_transaction_journal', 'categories.id', '=', 'category_transaction_journal.category_id')
->leftJoin('users', 'categories.user_id', '=', 'users.id')
->distinct()
->whereNull('category_transaction_journal.category_id')
->whereNull('categories.deleted_at')
->get(['categories.id', 'categories.name', 'categories.user_id', 'users.email']);
/** @var stdClass $entry */
foreach ($set as $entry) {
$objName = $entry->name;
try {
$objName = Crypt::decrypt($objName);
} catch (DecryptException $e) {
// it probably was not encrypted.
Log::debug(sprintf('Not a problem: %s', $e->getMessage()));
}
// also count the transactions:
$countTransactions = DB::table('category_transaction')->where('category_id', $entry->id)->count();
if (0 === $countTransactions) {
$line = sprintf(
'User #%d (%s) has category #%d ("%s") which has no transactions.',
$entry->user_id,
$entry->email,
$entry->id,
$objName
);
$this->line($line);
}
}
}
/**
* Report on journals with bad account types linked to them.
*/
@@ -483,12 +570,13 @@ class VerifyDatabase extends Command
$plural = str_plural($name);
$class = sprintf('FireflyIII\Models\%s', ucfirst($name));
$field = 'tag' === $name ? 'tag' : 'name';
$set = $class::leftJoin($name . '_transaction_journal', $plural . '.id', '=', $name . '_transaction_journal.' . $name . '_id')
->leftJoin('users', $plural . '.user_id', '=', 'users.id')
->distinct()
->whereNull($name . '_transaction_journal.' . $name . '_id')
->whereNull($plural . '.deleted_at')
->get([$plural . '.id', $plural . '.' . $field . ' as name', $plural . '.user_id', 'users.email']);
/** @noinspection PhpUndefinedMethodInspection */
$set = $class::leftJoin($name . '_transaction_journal', $plural . '.id', '=', $name . '_transaction_journal.' . $name . '_id')
->leftJoin('users', $plural . '.user_id', '=', 'users.id')
->distinct()
->whereNull($name . '_transaction_journal.' . $name . '_id')
->whereNull($plural . '.deleted_at')
->get([$plural . '.id', $plural . '.' . $field . ' as name', $plural . '.user_id', 'users.email']);
/** @var stdClass $entry */
foreach ($set as $entry) {
@@ -497,6 +585,7 @@ class VerifyDatabase extends Command
$objName = Crypt::decrypt($objName);
} catch (DecryptException $e) {
// it probably was not encrypted.
Log::debug(sprintf('Not a problem: %s', $e->getMessage()));
}
$line = sprintf(
@@ -524,7 +613,8 @@ class VerifyDatabase extends Command
$sum = (string)$user->transactions()->sum('amount');
if (0 !== bccomp($sum, '0')) {
$this->error('Error: Transactions for user #' . $user->id . ' (' . $user->email . ') are off by ' . $sum . '!');
} else {
}
if (0 === bccomp($sum, '0')) {
$this->info(sprintf('Amount integrity OK for user #%d', $user->id));
}
}
@@ -554,7 +644,7 @@ class VerifyDatabase extends Command
/**
* Report on transfers that have budgets.
*/
private function reportTransfersBudgets()
private function reportTransfersBudgets(): void
{
$set = TransactionJournal::distinct()
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')

View File

@@ -34,15 +34,6 @@ use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
*/
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands
= [
];
/**
* Register the commands for the application.
*/
@@ -50,6 +41,7 @@ class Kernel extends ConsoleKernel
{
$this->load(__DIR__ . '/Commands');
/** @noinspection PhpIncludeInspection */
require base_path('routes/console.php');
}

View File

@@ -35,13 +35,9 @@ class AdminRequestedTestMessage extends Event
{
use SerializesModels;
/**
* @var string
*/
/** @var string The users IP address */
public $ipAddress;
/**
* @var User
*/
/** @var User The user */
public $user;
/**

View File

@@ -34,13 +34,9 @@ class RegisteredUser extends Event
{
use SerializesModels;
/**
* @var string
*/
/** @var string The users IP address */
public $ipAddress;
/**
* @var User
*/
/** @var User The user */
public $user;
/**

View File

@@ -34,18 +34,12 @@ class RequestedNewPassword extends Event
{
use SerializesModels;
/**
* @var string
*/
/** @var string The users IP address */
public $ipAddress;
/**
* @var string
*/
public $token;
/**
* @var User
*/
/** @var User The user */
public $user;
/** @var string The token */
public $token;
/**
* Create a new event instance. This event is triggered when a users tries to reset his or her password.

View File

@@ -1,4 +1,23 @@
<?php
/**
* RequestedReportOnJournals.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/>.
*/
namespace FireflyIII\Events;
@@ -16,9 +35,9 @@ class RequestedReportOnJournals
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/** @var Collection */
/** @var Collection The journals to report on. */
public $journals;
/** @var int */
/** @var int The ID of the user. */
public $userId;
/**

View File

@@ -35,9 +35,7 @@ class RequestedVersionCheckStatus extends Event
{
use SerializesModels;
/**
* @var User
*/
/** @var User The user */
public $user;
/**

View File

@@ -28,16 +28,17 @@ use FireflyIII\Models\TransactionJournal;
use Illuminate\Queue\SerializesModels;
/**
* @codeCoverageIgnore
* Class StoredTransactionJournal.
*
* @codeCoverageIgnore
*/
class StoredTransactionJournal extends Event
{
use SerializesModels;
/** @var TransactionJournal */
/** @var TransactionJournal The journal that was stored. */
public $journal;
/** @var int */
/** @var int The piggy bank ID. */
public $piggyBankId;
/**

View File

@@ -37,7 +37,7 @@ class UpdatedTransactionJournal extends Event
{
use SerializesModels;
/** @var TransactionJournal */
/** @var TransactionJournal The journal. */
public $journal;
/**

View File

@@ -34,13 +34,13 @@ class UserChangedEmail extends Event
{
use SerializesModels;
/** @var string */
/** @var string The user's IP address */
public $ipAddress;
/** @var string */
/** @var string The user's new email address */
public $newEmail;
/** @var string */
/** @var string The user's old email address */
public $oldEmail;
/** @var User */
/** @var User The user itself */
public $user;
/**

View File

@@ -20,6 +20,8 @@
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection MultipleReturnStatementsInspection */
declare(strict_types=1);
namespace FireflyIII\Exceptions;
@@ -39,32 +41,16 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
*/
class Handler extends ExceptionHandler
{
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* @var array
*/
protected $dontFlash
= [
'password',
'password_confirmation',
];
/**
* A list of the exception types that are not reported.
*
* @var array
*/
protected $dontReport
= [
];
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @param Request $request
* @param Exception $exception
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*
* @return \Illuminate\Http\Response
* @return mixed
*/
public function render($request, Exception $exception)
{
@@ -130,18 +116,10 @@ class Handler extends ExceptionHandler
{
$doMailError = env('SEND_ERROR_MESSAGE', true);
if (
// if the user wants us to mail:
$doMailError === true
&& (
// and if is one of these error instances
$exception instanceof FireflyException
|| $exception instanceof ErrorException
|| $exception instanceof OAuthServerException
)
) {
// then, send email
// if the user wants us to mail:
if (true === $doMailError
// and if is one of these error instances
&& ($exception instanceof FireflyException || $exception instanceof ErrorException || $exception instanceof OAuthServerException)) {
$userData = [
'id' => 0,
'email' => 'unknown@example.com',

View File

@@ -1,5 +1,4 @@
<?php
/**
* AttachmentCollector.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@@ -29,6 +28,7 @@ use Crypt;
use FireflyIII\Models\Attachment;
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Support\Collection;
use Log;
use Storage;
@@ -38,15 +38,15 @@ use Storage;
*/
class AttachmentCollector extends BasicCollector implements CollectorInterface
{
/** @var Carbon */
/** @var Carbon The end date of the range. */
private $end;
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
/** @var \Illuminate\Contracts\Filesystem\Filesystem File system */
private $exportDisk;
/** @var AttachmentRepositoryInterface */
/** @var AttachmentRepositoryInterface Attachment repository */
private $repository;
/** @var Carbon */
/** @var Carbon Start date of range */
private $start;
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
/** @var \Illuminate\Contracts\Filesystem\Filesystem Disk with uploads on it */
private $uploadDisk;
/**
@@ -64,6 +64,8 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
}
/**
* Run the routine.
*
* @return bool
*/
public function run(): bool
@@ -80,6 +82,8 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
}
/**
* Set the start and end date.
*
* @param Carbon $start
* @param Carbon $end
*/
@@ -89,7 +93,10 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
$this->end = $end;
}
/** @noinspection MultipleReturnStatementsInspection */
/**
* Export attachments.
*
* @param Attachment $attachment
*
* @return bool
@@ -101,13 +108,13 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
if ($this->uploadDisk->exists($file)) {
try {
$decrypted = Crypt::decrypt($this->uploadDisk->get($file));
} catch (DecryptException $e) {
} catch (FileNotFoundException|DecryptException $e) {
Log::error('Catchable error: could not decrypt attachment #' . $attachment->id . ' because: ' . $e->getMessage());
return false;
}
}
if ($decrypted === false) {
if (false === $decrypted) {
return false;
}
$exportFile = $this->exportFileName($attachment);
@@ -130,6 +137,8 @@ class AttachmentCollector extends BasicCollector implements CollectorInterface
}
/**
* Get the attachments.
*
* @return Collection
*/
private function getAttachments(): Collection

View File

@@ -33,11 +33,11 @@ use Illuminate\Support\Collection;
*/
class BasicCollector
{
/** @var ExportJob */
/** @var ExportJob The job to export. */
protected $job;
/** @var User */
/** @var User The user */
protected $user;
/** @var Collection */
/** @var Collection All the entries. */
private $entries;
/**
@@ -49,6 +49,8 @@ class BasicCollector
}
/**
* Get all entries.
*
* @return Collection
*/
public function getEntries(): Collection
@@ -57,26 +59,32 @@ class BasicCollector
}
/**
* Set entries.
*
* @param Collection $entries
*/
public function setEntries(Collection $entries)
public function setEntries(Collection $entries): void
{
$this->entries = $entries;
}
/**
* Set export job.
*
* @param ExportJob $job
*/
public function setJob(ExportJob $job)
public function setJob(ExportJob $job): void
{
$this->job = $job;
$this->user = $job->user;
}
/**
* Set user.
*
* @param User $user
*/
public function setUser(User $user)
public function setUser(User $user): void
{
$this->user = $user;
}

View File

@@ -33,21 +33,29 @@ use Illuminate\Support\Collection;
interface CollectorInterface
{
/**
* Get entries.
*
* @return Collection
*/
public function getEntries(): Collection;
/**
* Run the collector.
*
* @return bool
*/
public function run(): bool;
/**
* Set entries.
*
* @param Collection $entries
*/
public function setEntries(Collection $entries);
/**
* Set export job.
*
* @param ExportJob $job
*
* @return mixed

View File

@@ -1,5 +1,4 @@
<?php
/**
* UploadCollector.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@@ -84,7 +83,10 @@ class UploadCollector extends BasicCollector implements CollectorInterface
return true;
}
/** @noinspection MultipleReturnStatementsInspection */
/**
* Process new file uploads.
*
* @param string $key
*
* @return bool

View File

@@ -46,137 +46,65 @@ use FireflyIII\Models\Transaction;
*/
final class Entry
{
// @formatter:off
/**
* @var int
*/
/** @var int ID of the journal */
public $journal_id;
/**
* @var int
*/
/** @var int ID of the transaction */
public $transaction_id = 0;
/**
* @var string
*/
/** @var string The date. */
public $date;
/**
* @var string
*/
/** @var string The description */
public $description;
/**
* @var string
*/
/** @var string The currency code. */
public $currency_code;
/**
* @var string
*/
/** @var string The amount. */
public $amount;
/**
* @var string
*/
/** @var string The foreign currency code */
public $foreign_currency_code = '';
/**
* @var string
*/
/** @var string Foreign amount */
public $foreign_amount = '0';
/**
* @var string
*/
/** @var string Transaction type */
public $transaction_type;
/**
* @var string
*/
/** @var string Asset account ID */
public $asset_account_id;
/**
* @var string
*/
/** @var string Asset account name */
public $asset_account_name;
/**
* @var string
*/
/** @var string Asset account IBAN */
public $asset_account_iban;
/**
* @var string
*/
/** @var string Asset account BIC */
public $asset_account_bic;
/**
* @var string
*/
/** @var string Asset account number */
public $asset_account_number;
/**
* @var string
*/
/** @var string Asset account currency code */
public $asset_currency_code;
/**
* @var string
*/
/** @var string Opposing account ID */
public $opposing_account_id;
/**
* @var string
*/
/** @var string Opposing account name */
public $opposing_account_name;
/**
* @var string
*/
/** @var string Opposing account IBAN */
public $opposing_account_iban;
/**
* @var string
*/
/** @var string Opposing account BIC */
public $opposing_account_bic;
/**
* @var string
*/
/** @var string Opposing account number */
public $opposing_account_number;
/**
* @var string
*/
/** @var string Opposing account code */
public $opposing_currency_code;
/**
* @var string
*/
/** @var string Budget ID */
public $budget_id;
/**
* @var string
*/
/** @var string Budget name */
public $budget_name;
/**
* @var string
*/
/** @var string Category ID */
public $category_id;
/**
* @var string
*/
/** @var string Category name */
public $category_name;
/**
* @var string
*/
/** @var string Bill ID */
public $bill_id;
/**
* @var string
*/
/** @var string Bill name */
public $bill_name;
/**
* @var string
*/
/** @var string Notes */
public $notes;
/**
* @var string
*/
/** @var string Tags */
public $tags;
// @formatter:on
/**
* Entry constructor.
*/

View File

@@ -1,5 +1,4 @@
<?php
/**
* ExpandedProcessor.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@@ -20,6 +19,8 @@
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection PhpDynamicAsStaticMethodCallInspection */
declare(strict_types=1);
namespace FireflyIII\Export;
@@ -50,23 +51,23 @@ use ZipArchive;
*/
class ExpandedProcessor implements ProcessorInterface
{
/** @var Collection */
/** @var Collection All accounts */
public $accounts;
/** @var string */
/** @var string The export format*/
public $exportFormat;
/** @var bool */
/** @var bool Should include attachments */
public $includeAttachments;
/** @var bool */
/** @var bool Should include old uploads */
public $includeOldUploads;
/** @var ExportJob */
/** @var ExportJob The export job itself */
public $job;
/** @var array */
/** @var array The settings*/
public $settings;
/** @var Collection */
/** @var Collection The entries to export. */
private $exportEntries;
/** @var Collection */
/** @var Collection The files to export */
private $files;
/** @var Collection */
/** @var Collection The journals. */
private $journals;
/**
@@ -80,6 +81,8 @@ class ExpandedProcessor implements ProcessorInterface
}
/**
* Collect all attachments
*
* @return bool
*/
public function collectAttachments(): bool
@@ -143,6 +146,8 @@ class ExpandedProcessor implements ProcessorInterface
}
/**
* Get old oploads.
*
* @return bool
*/
public function collectOldUploads(): bool
@@ -158,6 +163,8 @@ class ExpandedProcessor implements ProcessorInterface
}
/**
* Convert journals to export objects.
*
* @return bool
*/
public function convertJournals(): bool
@@ -173,6 +180,8 @@ class ExpandedProcessor implements ProcessorInterface
}
/**
* Create a ZIP file.
*
* @return bool
*
* @throws FireflyException
@@ -204,6 +213,8 @@ class ExpandedProcessor implements ProcessorInterface
}
/**
* Export the journals.
*
* @return bool
*/
public function exportJournals(): bool
@@ -219,6 +230,8 @@ class ExpandedProcessor implements ProcessorInterface
}
/**
* Get files.
*
* @return Collection
*/
public function getFiles(): Collection
@@ -231,7 +244,7 @@ class ExpandedProcessor implements ProcessorInterface
*
* @param array $settings
*/
public function setSettings(array $settings)
public function setSettings(array $settings): void
{
// save settings
$this->settings = $settings;
@@ -243,9 +256,9 @@ class ExpandedProcessor implements ProcessorInterface
}
/**
*
* Delete files.
*/
private function deleteFiles()
private function deleteFiles():void
{
$disk = Storage::disk('export');
foreach ($this->getFiles() as $file) {
@@ -254,6 +267,8 @@ class ExpandedProcessor implements ProcessorInterface
}
/**
* Get currencies.
*
* @param array $array
*
* @return array

View File

@@ -32,9 +32,9 @@ use Illuminate\Support\Collection;
*/
class BasicExporter
{
/** @var ExportJob */
/** @var ExportJob The export job */
protected $job;
/** @var Collection */
/** @var Collection The entries */
private $entries;
/**
@@ -46,6 +46,8 @@ class BasicExporter
}
/**
* Get all entries.
*
* @return Collection
*/
public function getEntries(): Collection
@@ -54,17 +56,21 @@ class BasicExporter
}
/**
* Set all entries.
*
* @param Collection $entries
*/
public function setEntries(Collection $entries)
public function setEntries(Collection $entries): void
{
$this->entries = $entries;
}
/**
* Set the job.
*
* @param ExportJob $job
*/
public function setJob(ExportJob $job)
public function setJob(ExportJob $job): void
{
$this->job = $job;
}

View File

@@ -33,10 +33,12 @@ use Storage;
*/
class CsvExporter extends BasicExporter implements ExporterInterface
{
/** @var string */
/** @var string Filename */
private $fileName;
/**
* Get file name.
*
* @return string
*/
public function getFileName(): string
@@ -45,6 +47,8 @@ class CsvExporter extends BasicExporter implements ExporterInterface
}
/**
* Run collector.
*
* @return bool
*
*/
@@ -83,6 +87,9 @@ class CsvExporter extends BasicExporter implements ExporterInterface
return true;
}
/**
* Make a temp file.
*/
private function tempFile()
{
$this->fileName = $this->job->key . '-records.csv';

View File

@@ -33,26 +33,36 @@ use Illuminate\Support\Collection;
interface ExporterInterface
{
/**
* Get entries.
*
* @return Collection
*/
public function getEntries(): Collection;
/**
* Get file name.
*
* @return string
*/
public function getFileName(): string;
/**
* Run exporter.
*
* @return bool
*/
public function run(): bool;
/**
* Set entries.
*
* @param Collection $entries
*/
public function setEntries(Collection $entries);
/**
* Set job.
*
* @param ExportJob $job
*/
public function setJob(ExportJob $job);

View File

@@ -37,41 +37,57 @@ interface ProcessorInterface
public function __construct();
/**
* Collect all attachments.
*
* @return bool
*/
public function collectAttachments(): bool;
/**
* Collect all journals.
*
* @return bool
*/
public function collectJournals(): bool;
/**
* Collect old uploads.
*
* @return bool
*/
public function collectOldUploads(): bool;
/**
* Convert all journals.
*
* @return bool
*/
public function convertJournals(): bool;
/**
* Create a zip file.
*
* @return bool
*/
public function createZipFile(): bool;
/**
* Export journals.
*
* @return bool
*/
public function exportJournals(): bool;
/**
* Get all files.
*
* @return Collection
*/
public function getFiles(): Collection;
/**
* Set the settings.
*
* @param array $settings
*/
public function setSettings(array $settings);

View File

@@ -20,10 +20,14 @@
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection PhpDynamicAsStaticMethodCallInspection */
/** @noinspection PhpUndefinedMethodInspection */
declare(strict_types=1);
namespace FireflyIII\Factory;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Services\Internal\Support\AccountServiceTrait;
@@ -44,56 +48,63 @@ class AccountFactory
* @param array $data
*
* @return Account
* @throws FireflyException
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function create(array $data): Account
{
$type = $this->getAccountType($data['account_type_id'], $data['accountType']);
$type = $this->getAccountType($data['account_type_id'], $data['accountType']);
if (null === $type) {
throw new FireflyException(
sprintf('AccountFactory::create() was unable to find account type #%d ("%s").', $data['account_type_id'], $data['accountType'])
);
}
$data['iban'] = $this->filterIban($data['iban']);
// account may exist already:
$existingAccount = $this->find($data['name'], $type->type);
if (null !== $existingAccount) {
return $existingAccount;
$return = $this->find($data['name'], $type->type);
if (null === $return) {
// create it:
$databaseData
= [
'user_id' => $this->user->id,
'account_type_id' => $type->id,
'name' => $data['name'],
'virtual_balance' => $data['virtualBalance'] ?? '0',
'active' => true === $data['active'],
'iban' => $data['iban'],
];
// remove virtual balance when not an asset account:
if ($type->type !== AccountType::ASSET) {
$databaseData['virtual_balance'] = '0';
}
// fix virtual balance when it's empty
if ('' === $databaseData['virtual_balance']) {
$databaseData['virtual_balance'] = '0';
}
$return = Account::create($databaseData);
$this->updateMetaData($return, $data);
if ($type->type === AccountType::ASSET) {
if ($this->validIBData($data)) {
$this->updateIB($return, $data);
}
if (!$this->validIBData($data)) {
$this->deleteIB($return);
}
}
$this->updateNote($return, $data['notes'] ?? '');
}
// create it:
$databaseData
= [
'user_id' => $this->user->id,
'account_type_id' => $type->id,
'name' => $data['name'],
'virtual_balance' => $data['virtualBalance'] ?? '0',
'active' => true === $data['active'],
'iban' => $data['iban'],
];
// remove virtual balance when not an asset account:
if ($type->type !== AccountType::ASSET) {
$databaseData['virtual_balance'] = '0';
}
// fix virtual balance when it's empty
if ($databaseData['virtual_balance'] === '') {
$databaseData['virtual_balance'] = '0';
}
$newAccount = Account::create($databaseData);
$this->updateMetaData($newAccount, $data);
if ($this->validIBData($data) && $type->type === AccountType::ASSET) {
$this->updateIB($newAccount, $data);
}
if (!$this->validIBData($data) && $type->type === AccountType::ASSET) {
$this->deleteIB($newAccount);
}
// update note:
if (isset($data['notes'])) {
$this->updateNote($newAccount, $data['notes']);
}
return $newAccount;
return $return;
}
/**
@@ -106,15 +117,16 @@ class AccountFactory
{
$type = AccountType::whereType($accountType)->first();
$accounts = $this->user->accounts()->where('account_type_id', $type->id)->get(['accounts.*']);
$return = null;
/** @var Account $object */
foreach ($accounts as $object) {
if ($object->name === $accountName) {
return $object;
$return = $object;
break;
}
}
return null;
return $return;
}
/**
@@ -122,30 +134,35 @@ class AccountFactory
* @param string $accountType
*
* @return Account
* @throws FireflyException
*/
public function findOrCreate(string $accountName, string $accountType): Account
{
$type = AccountType::whereType($accountType)->first();
$accounts = $this->user->accounts()->where('account_type_id', $type->id)->get(['accounts.*']);
$return = null;
/** @var Account $object */
foreach ($accounts as $object) {
if ($object->name === $accountName) {
return $object;
$return = $object;
break;
}
}
if (null === $return) {
$return = $this->create(
[
'user_id' => $this->user->id,
'name' => $accountName,
'account_type_id' => $type->id,
'accountType' => null,
'virtualBalance' => '0',
'iban' => null,
'active' => true,
]
);
}
return $this->create(
[
'user_id' => $this->user->id,
'name' => $accountName,
'account_type_id' => $type->id,
'accountType' => null,
'virtualBalance' => '0',
'iban' => null,
'active' => true,
]
);
return $return;
}
/**
@@ -161,18 +178,23 @@ class AccountFactory
* @param null|string $accountType
*
* @return AccountType|null
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
protected function getAccountType(?int $accountTypeId, ?string $accountType): ?AccountType
{
$accountTypeId = (int)$accountTypeId;
$result = null;
if ($accountTypeId > 0) {
return AccountType::find($accountTypeId);
$result = AccountType::find($accountTypeId);
}
$type = config('firefly.accountTypeByIdentifier.' . (string)$accountType);
$result = AccountType::whereType($type)->first();
if (null === $result && null !== $accountType) {
// try as full name:
$result = AccountType::whereType($accountType)->first();
if (null === $result) {
/** @var string $type */
$type = (string)config('firefly.accountTypeByIdentifier.' . (string)$accountType);
$result = AccountType::whereType($type)->first();
if (null === $result && null !== $accountType) {
// try as full name:
$result = AccountType::whereType($accountType)->first();
}
}
return $result;

View File

@@ -81,25 +81,19 @@ class BillFactory
{
$billId = (int)$billId;
$billName = (string)$billName;
$bill = null;
// first find by ID:
if ($billId > 0) {
/** @var Bill $bill */
$bill = $this->user->bills()->find($billId);
if (null !== $bill) {
return $bill;
}
}
// then find by name:
if (\strlen($billName) > 0) {
if (null === $bill && \strlen($billName) > 0) {
$bill = $this->findByName($billName);
if (null !== $bill) {
return $bill;
}
}
return null;
return $bill;
}
@@ -112,22 +106,24 @@ class BillFactory
{
/** @var Collection $collection */
$collection = $this->user->bills()->get();
$return = null;
/** @var Bill $bill */
foreach ($collection as $bill) {
Log::debug(sprintf('"%s" vs. "%s"', $bill->name, $name));
if ($bill->name === $name) {
return $bill;
$return = $bill;
break;
}
}
Log::debug(sprintf('Bill::Find by name returns NULL based on "%s"', $name));
Log::debug(sprintf('Bill::find("%s") by name returns null? %s', $name, var_export($return, true)));
return null;
return $return;
}
/**
* @param User $user
*/
public function setUser(User $user)
public function setUser(User $user): void
{
$this->user = $user;
}

View File

@@ -1,5 +1,4 @@
<?php
/**
* BudgetFactory.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@@ -19,7 +18,7 @@
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection MultipleReturnStatementsInspection */
declare(strict_types=1);
@@ -30,7 +29,7 @@ use FireflyIII\User;
use Illuminate\Support\Collection;
/**
* Class BudgetFactory
* Class BudgetFactory.
*/
class BudgetFactory
{
@@ -43,13 +42,14 @@ class BudgetFactory
* @param null|string $budgetName
*
* @return Budget|null
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function find(?int $budgetId, ?string $budgetName): ?Budget
{
$budgetId = (int)$budgetId;
$budgetName = (string)$budgetName;
if (\strlen($budgetName) === 0 && $budgetId === 0) {
if (0 === $budgetId && '' === $budgetName) {
return null;
}
@@ -62,7 +62,7 @@ class BudgetFactory
}
}
if (\strlen($budgetName) > 0) {
if ('' !== $budgetName) {
$budget = $this->findByName($budgetName);
if (null !== $budget) {
return $budget;
@@ -94,7 +94,7 @@ class BudgetFactory
/**
* @param User $user
*/
public function setUser(User $user)
public function setUser(User $user): void
{
$this->user = $user;
}

View File

@@ -1,5 +1,4 @@
<?php
/**
* CategoryFactory.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@@ -19,7 +18,7 @@
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection MultipleReturnStatementsInspection */
declare(strict_types=1);
namespace FireflyIII\Factory;
@@ -64,6 +63,7 @@ class CategoryFactory
* @param null|string $categoryName
*
* @return Category|null
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function findOrCreate(?int $categoryId, ?string $categoryName): ?Category
{
@@ -72,7 +72,7 @@ class CategoryFactory
Log::debug(sprintf('Going to find category with ID %d and name "%s"', $categoryId, $categoryName));
if ('' === $categoryName && $categoryId === 0) {
if ('' === $categoryName && 0 === $categoryId) {
return null;
}
// first by ID:
@@ -104,7 +104,7 @@ class CategoryFactory
/**
* @param User $user
*/
public function setUser(User $user)
public function setUser(User $user): void
{
$this->user = $user;
}

View File

@@ -1,5 +1,4 @@
<?php
/**
* PiggyBankEventFactory.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@@ -19,7 +18,7 @@
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection MultipleReturnStatementsInspection */
declare(strict_types=1);
namespace FireflyIII\Factory;
@@ -43,6 +42,7 @@ class PiggyBankEventFactory
* @param PiggyBank|null $piggyBank
*
* @return PiggyBankEvent|null
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function create(TransactionJournal $journal, ?PiggyBank $piggyBank): ?PiggyBankEvent
{
@@ -51,7 +51,6 @@ class PiggyBankEventFactory
return null;
}
// is a transfer?
if (!(TransactionType::TRANSFER === $journal->transactionType->type)) {
Log::info(sprintf('Will not connect %s #%d to a piggy bank.', $journal->transactionType->type, $journal->id));
@@ -62,7 +61,6 @@ class PiggyBankEventFactory
$piggyRepos = app(PiggyBankRepositoryInterface::class);
$piggyRepos->setUser($journal->user);
// repetition exists?
$repetition = $piggyRepos->getRepetition($piggyBank);
if (null === $repetition) {
Log::error(sprintf('No piggy bank repetition on %s!', $journal->date->format('Y-m-d')));
@@ -70,7 +68,6 @@ class PiggyBankEventFactory
return null;
}
// get the amount
$amount = $piggyRepos->getExactAmount($piggyBank, $repetition, $journal);
if (0 === bccomp($amount, '0')) {
Log::debug('Amount is zero, will not create event.');
@@ -78,10 +75,8 @@ class PiggyBankEventFactory
return null;
}
// update amount
$piggyRepos->addAmountToRepetition($repetition, $amount);
$event = $piggyRepos->createEventWithJournal($piggyBank, $amount, $journal);
Log::debug(sprintf('Created piggy bank event #%d', $event->id));
return $event;

View File

@@ -1,5 +1,4 @@
<?php
/**
* PiggyBankFactory.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@@ -19,7 +18,7 @@
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection MultipleReturnStatementsInspection */
declare(strict_types=1);
namespace FireflyIII\Factory;
@@ -41,12 +40,13 @@ class PiggyBankFactory
* @param null|string $piggyBankName
*
* @return PiggyBank|null
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function find(?int $piggyBankId, ?string $piggyBankName): ?PiggyBank
{
$piggyBankId = (int)$piggyBankId;
$piggyBankName = (string)$piggyBankName;
if (\strlen($piggyBankName) === 0 && $piggyBankId === 0) {
if ('' === $piggyBankName && 0 === $piggyBankId) {
return null;
}
// first find by ID:
@@ -92,7 +92,7 @@ class PiggyBankFactory
/**
* @param User $user
*/
public function setUser(User $user)
public function setUser(User $user): void
{
$this->user = $user;

View File

@@ -18,12 +18,14 @@
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection MultipleReturnStatementsInspection */
declare(strict_types=1);
namespace FireflyIII\Factory;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Recurrence;
use FireflyIII\Services\Internal\Support\RecurringTransactionTrait;
@@ -56,6 +58,9 @@ class RecurrenceFactory
return null;
}
/** @var Carbon $firstDate */
$firstDate = $data['recurrence']['first_date'];
$repetitions = (int)$data['recurrence']['repetitions'];
$recurrence = new Recurrence(
[
@@ -63,7 +68,7 @@ class RecurrenceFactory
'transaction_type_id' => $type->id,
'title' => $data['recurrence']['title'],
'description' => $data['recurrence']['description'],
'first_date' => $data['recurrence']['first_date']->format('Y-m-d'),
'first_date' => $firstDate->format('Y-m-d'),
'repeat_until' => $repetitions > 0 ? null : $data['recurrence']['repeat_until'],
'latest_date' => null,
'repetitions' => $data['recurrence']['repetitions'],

View File

@@ -1,5 +1,4 @@
<?php
/**
* TagFactory.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@@ -19,6 +18,7 @@
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection MultipleReturnStatementsInspection */
declare(strict_types=1);

View File

@@ -1,5 +1,4 @@
<?php
/**
* TransactionCurrencyFactory.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@@ -20,6 +19,10 @@
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection PhpDynamicAsStaticMethodCallInspection */
/** @noinspection PhpUndefinedMethodInspection */
/** @noinspection MultipleReturnStatementsInspection */
declare(strict_types=1);
namespace FireflyIII\Factory;
@@ -63,13 +66,14 @@ class TransactionCurrencyFactory
* @param null|string $currencyCode
*
* @return TransactionCurrency|null
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function find(?int $currencyId, ?string $currencyCode): ?TransactionCurrency
{
$currencyCode = (string)$currencyCode;
$currencyId = (int)$currencyId;
if ('' === $currencyCode && $currencyId === 0) {
if ('' === $currencyCode && 0 === $currencyId) {
Log::warning('Cannot find anything on empty currency code and empty currency ID!');
return null;

View File

@@ -50,6 +50,7 @@ class TransactionFactory
*
* @return Transaction
* @throws FireflyException
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function create(array $data): ?Transaction
{
@@ -89,37 +90,41 @@ class TransactionFactory
*
* @return Collection
* @throws FireflyException
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function createPair(TransactionJournal $journal, array $data): Collection
{
Log::debug('Start of TransactionFactory::createPair()');
Log::debug('Start of TransactionFactory::createPair()', $data);
// all this data is the same for both transactions:
$currency = $this->findCurrency($data['currency_id'], $data['currency_code']);
$description = $journal->description === $data['description'] ? null : $data['description'];
// type of source account depends on journal type:
$sourceType = $this->accountType($journal, 'source');
Log::debug(sprintf('Expect source account to be of type %s', $sourceType));
$sourceAccount = $this->findAccount($sourceType, $data['source_id'], $data['source_name']);
// same for destination account:
// type of source account and destination account depends on journal type:
$sourceType = $this->accountType($journal, 'source');
$destinationType = $this->accountType($journal, 'destination');
Log::debug(sprintf('Expect source destination to be of type %s', $destinationType));
Log::debug(sprintf('Expect source account to be of type "%s"', $sourceType));
Log::debug(sprintf('Expect source destination to be of type "%s"', $destinationType));
// find source and destination account:
$sourceAccount = $this->findAccount($sourceType, $data['source_id'], $data['source_name']);
$destinationAccount = $this->findAccount($destinationType, $data['destination_id'], $data['destination_name']);
if (null === $sourceAccount || null === $destinationAccount) {
throw new FireflyException('Could not determine source or destination account.');
}
Log::debug(sprintf('Source type is "%s", destination type is "%s"', $sourceAccount->accountType->type, $destinationAccount->accountType->type));
// throw big fat error when source type === dest type
if ($sourceAccount->accountType->type === $destinationAccount->accountType->type
&& ($journal->transactionType->type !== TransactionType::TRANSFER
&& $journal->transactionType->type !== TransactionType::RECONCILIATION)
) {
// throw big fat error when source type === dest type and it's not a transfer or reconciliation.
if ($sourceAccount->accountType->type === $destinationAccount->accountType->type && $journal->transactionType->type !== TransactionType::TRANSFER) {
throw new FireflyException(sprintf('Source and destination account cannot be both of the type "%s"', $destinationAccount->accountType->type));
}
if ($sourceAccount->accountType->type !== AccountType::ASSET && $destinationAccount->accountType->type !== AccountType::ASSET) {
throw new FireflyException('At least one of the accounts must be an asset account.');
}
// first make a "negative" (source) transaction based on the data in the array.
$source = $this->create(
[
'description' => $description,
@@ -132,8 +137,7 @@ class TransactionFactory
'identifier' => $data['identifier'],
]
);
// then make a "positive" transaction based on the data in the array.
$dest = $this->create(
$dest = $this->create(
[
'description' => $description,
'amount' => app('steam')->positive((string)$data['amount']),
@@ -145,6 +149,9 @@ class TransactionFactory
'identifier' => $data['identifier'],
]
);
if (null === $source || null === $dest) {
throw new FireflyException('Could not create transactions.');
}
// set foreign currency
$foreign = $this->findCurrency($data['foreign_currency_id'], $data['foreign_currency_code']);
@@ -178,7 +185,7 @@ class TransactionFactory
/**
* @param User $user
*/
public function setUser(User $user)
public function setUser(User $user): void
{
$this->user = $user;
}

View File

@@ -37,14 +37,18 @@ use Log;
class TransactionJournalFactory
{
use JournalServiceTrait, TransactionTypeTrait;
/** @var User */
/** @var User The user */
private $user;
/**
* Store a new transaction journal.
*
* @param array $data
*
* @return TransactionJournal
* @throws FireflyException
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function create(array $data): TransactionJournal
{
@@ -124,6 +128,8 @@ class TransactionJournalFactory
}
/**
* Link a piggy bank to this journal.
*
* @param TransactionJournal $journal
* @param array $data
*/

View File

@@ -1,5 +1,4 @@
<?php
/**
* TransactionJournalMetaFactory.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
@@ -19,6 +18,7 @@
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection MultipleReturnStatementsInspection */
declare(strict_types=1);
@@ -38,6 +38,8 @@ class TransactionJournalMetaFactory
* @param array $data
*
* @return TransactionJournalMeta|null
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function updateOrCreate(array $data): ?TransactionJournalMeta
{
@@ -57,7 +59,7 @@ class TransactionJournalMetaFactory
if ($data['data'] instanceof Carbon) {
$value = $data['data']->toW3cString();
}
if ((string)$value === '') {
if ('' === (string)$value) {
// don't store blank strings.
if (null !== $entry) {
try {

View File

@@ -19,6 +19,7 @@
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection PhpUndefinedMethodInspection */
declare(strict_types=1);

View File

@@ -23,7 +23,6 @@ declare(strict_types=1);
namespace FireflyIII\Generator\Chart\Basic;
use FireflyIII\Support\ChartColour;
use Steam;
/**
* Class ChartJsGenerator.
@@ -90,7 +89,7 @@ class ChartJsGenerator implements GeneratorInterface
if (isset($set['currency_symbol'])) {
$currentSet['currency_symbol'] = $set['currency_symbol'];
}
if(isset($set['backgroundColor'])) {
if (isset($set['backgroundColor'])) {
$currentSet['backgroundColor'] = $set['backgroundColor'];
}
$chartData['datasets'][] = $currentSet;
@@ -130,7 +129,7 @@ class ChartJsGenerator implements GeneratorInterface
$index = 0;
foreach ($data as $key => $value) {
// make larger than 0
$chartData['datasets'][0]['data'][] = (float)Steam::positive($value);
$chartData['datasets'][0]['data'][] = (float)app('steam')->positive($value);
$chartData['datasets'][0]['backgroundColor'][] = ChartColour::getColour($index);
$chartData['labels'][] = $key;
++$index;

View File

@@ -31,17 +31,20 @@ use Illuminate\Support\Collection;
*/
class MonthReportGenerator implements ReportGeneratorInterface
{
/** @var Collection */
/** @var Collection The accounts involved in the report. */
private $accounts;
/** @var Carbon */
/** @var Carbon The end date */
private $end;
/** @var Collection */
/** @var Collection The expense accounts. */
private $expense;
/** @var Carbon */
/** @var Carbon The start date. */
private $start;
/**
* Generate the report.
*
* @return string
* @throws \Throwable
*/
public function generate(): string
{
@@ -57,6 +60,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
}
/**
* Set accounts.
*
* @param Collection $accounts
*
* @return ReportGeneratorInterface
@@ -69,6 +74,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
}
/**
* Set budgets.
*
* @param Collection $budgets
*
* @return ReportGeneratorInterface
@@ -79,6 +86,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
}
/**
* Set categories.
*
* @param Collection $categories
*
* @return ReportGeneratorInterface
@@ -89,6 +98,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
}
/**
* Set end date.
*
* @param Carbon $date
*
* @return ReportGeneratorInterface
@@ -101,6 +112,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
}
/**
* Set expense collection.
*
* @param Collection $expense
*
* @return ReportGeneratorInterface
@@ -113,6 +126,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
}
/**
* Set start date.
*
* @param Carbon $date
*
* @return ReportGeneratorInterface
@@ -125,6 +140,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
}
/**
* Set collection of tags.
*
* @param Collection $tags
*
* @return ReportGeneratorInterface
@@ -135,6 +152,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
}
/**
* Return the preferred period.
*
* @return string
*/
protected function preferredPeriod(): string

View File

@@ -27,9 +27,9 @@ namespace FireflyIII\Generator\Report\Account;
*/
class MultiYearReportGenerator extends MonthReportGenerator
{
// Doesn't do anything different.
/**
* Returns the preferred period.
*
* @return string
*/
protected function preferredPeriod(): string

View File

@@ -27,9 +27,9 @@ namespace FireflyIII\Generator\Report\Account;
*/
class YearReportGenerator extends MonthReportGenerator
{
// Doesn't do anything different.
/**
* Returns the preferred period.
*
* @return string
*/
protected function preferredPeriod(): string

View File

@@ -18,33 +18,40 @@
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection PhpUndefinedMethodInspection */
declare(strict_types=1);
namespace FireflyIII\Generator\Report\Audit;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Generator\Report\ReportGeneratorInterface;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use Illuminate\Support\Collection;
use Steam;
/**
* Class MonthReportGenerator.
*/
class MonthReportGenerator implements ReportGeneratorInterface
{
/** @var Collection */
/** @var Collection The accounts used. */
private $accounts;
/** @var Carbon */
/** @var Carbon End date of the report. */
private $end;
/** @var Carbon */
/** @var Carbon Start date of the report. */
private $start;
/**
* Generates the report.
*
* @return string
* @throws \Throwable
*/
public function generate(): string
{
@@ -77,6 +84,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
}
/**
* Account collection setter.
*
* @param Collection $accounts
*
* @return ReportGeneratorInterface
@@ -89,6 +98,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
}
/**
* Budget collection setter.
*
* @param Collection $budgets
*
* @return ReportGeneratorInterface
@@ -99,6 +110,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
}
/**
* Category collection setter.
*
* @param Collection $categories
*
* @return ReportGeneratorInterface
@@ -109,6 +122,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
}
/**
* End date setter.
*
* @param Carbon $date
*
* @return ReportGeneratorInterface
@@ -121,6 +136,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
}
/**
* Expenses collection setter.
*
* @param Collection $expense
*
* @return ReportGeneratorInterface
@@ -131,6 +148,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
}
/**
* Start date collection setter.
*
* @param Carbon $date
*
* @return ReportGeneratorInterface
@@ -143,6 +162,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
}
/**
* Tags collection setter.
*
* @param Collection $tags
*
* @return ReportGeneratorInterface
@@ -153,26 +174,37 @@ class MonthReportGenerator implements ReportGeneratorInterface
}
/**
* Get the audit report.
*
* @param Account $account
* @param Carbon $date
*
* @return array
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength) // not that long
* @throws FireflyException
*/
private function getAuditReport(Account $account, Carbon $date): array
{
/** @var CurrencyRepositoryInterface $currencyRepos */
$currencyRepos = app(CurrencyRepositoryInterface::class);
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$accountRepository->setUser($account->user);
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setRange($this->start, $this->end);
$journals = $collector->getJournals();
$journals = $journals->reverse();
$dayBeforeBalance = Steam::balance($account, $date);
$dayBeforeBalance = app('steam')->balance($account, $date);
$startBalance = $dayBeforeBalance;
$currency = $currencyRepos->find((int)$account->getMeta('currency_id'));
$currency = $currencyRepos->findNull((int)$accountRepository->getMetaValue($account, 'currency_id'));
if (null === $currency) {
throw new FireflyException('Unexpected NULL value in account currency preference.');
}
/** @var Transaction $transaction */
foreach ($journals as $transaction) {
@@ -183,17 +215,16 @@ class MonthReportGenerator implements ReportGeneratorInterface
$transactionAmount = $transaction->transaction_foreign_amount;
}
$newBalance = bcadd($startBalance, $transactionAmount);
$transaction->after = $newBalance;
$startBalance = $newBalance;
$transaction->currency = $currency;
$newBalance = bcadd($startBalance, $transactionAmount);
$transaction->after = $newBalance;
$startBalance = $newBalance;
}
$return = [
'journals' => $journals->reverse(),
'exists' => $journals->count() > 0,
'end' => $this->end->formatLocalized((string)trans('config.month_and_day')),
'endBalance' => Steam::balance($account, $this->end),
'endBalance' => app('steam')->balance($account, $this->end),
'dayBefore' => $date->formatLocalized((string)trans('config.month_and_day')),
'dayBeforeBalance' => $dayBeforeBalance,
];

View File

@@ -27,5 +27,4 @@ namespace FireflyIII\Generator\Report\Audit;
*/
class MultiYearReportGenerator extends MonthReportGenerator
{
// Doesn't do anything different.
}

View File

@@ -27,5 +27,4 @@ namespace FireflyIII\Generator\Report\Audit;
*/
class YearReportGenerator extends MonthReportGenerator
{
// Doesn't do anything different.
}

View File

@@ -18,6 +18,8 @@
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection MultipleReturnStatementsInspection */
/** @noinspection PhpUndefinedMethodInspection */
declare(strict_types=1);
namespace FireflyIII\Generator\Report\Budget;
@@ -39,17 +41,15 @@ use Log;
*/
class MonthReportGenerator extends Support implements ReportGeneratorInterface
{
/** @var Collection */
/** @var Collection The accounts in the report. */
private $accounts;
/** @var Collection */
/** @var Collection The budgets in the report. */
private $budgets;
/** @var Carbon */
/** @var Carbon The end date. */
private $end;
/** @var Collection */
/** @var Collection The expenses in the report. */
private $expenses;
/** @var Collection */
private $income;
/** @var Carbon */
/** @var Carbon The start date. */
private $start;
/**
@@ -57,12 +57,14 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
*/
public function __construct()
{
$this->income = new Collection;
$this->expenses = new Collection;
}
/**
* Generates the report.
*
* @return string
* @throws \Throwable
*/
public function generate(): string
{
@@ -83,6 +85,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
}
/**
* Set the involved accounts.
*
* @param Collection $accounts
*
* @return ReportGeneratorInterface
@@ -95,6 +99,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
}
/**
* Set the involved budgets.
*
* @param Collection $budgets
*
* @return ReportGeneratorInterface
@@ -107,6 +113,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
}
/**
* Unused category setter.
*
* @param Collection $categories
*
* @return ReportGeneratorInterface
@@ -117,6 +125,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
}
/**
* Set the end date of the report.
*
* @param Carbon $date
*
* @return ReportGeneratorInterface
@@ -129,6 +139,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
}
/**
* Unused expense setter.
*
* @param Collection $expense
*
* @return ReportGeneratorInterface
@@ -139,6 +151,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
}
/**
* Set the start date of the report.
*
* @param Carbon $date
*
* @return ReportGeneratorInterface
@@ -151,6 +165,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
}
/**
* Unused tags setter.
*
* @param Collection $tags
*
* @return ReportGeneratorInterface
@@ -161,6 +177,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
}
/**
* Get the expenses.
*
* @return Collection
*/
protected function getExpenses(): Collection
@@ -188,6 +206,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
}
/**
* Summarize a collection by its budget.
*
* @param Collection $collection
*
* @return array

View File

@@ -27,5 +27,4 @@ namespace FireflyIII\Generator\Report\Budget;
*/
class MultiYearReportGenerator extends MonthReportGenerator
{
// Doesn't do anything different.
}

View File

@@ -27,5 +27,4 @@ namespace FireflyIII\Generator\Report\Budget;
*/
class YearReportGenerator extends MonthReportGenerator
{
// Doesn't do anything different.
}

View File

@@ -18,6 +18,8 @@
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
/** @noinspection MultipleReturnStatementsInspection */
/** @noinspection PhpUndefinedMethodInspection */
declare(strict_types=1);
namespace FireflyIII\Generator\Report\Category;
@@ -40,17 +42,17 @@ use Log;
*/
class MonthReportGenerator extends Support implements ReportGeneratorInterface
{
/** @var Collection */
/** @var Collection The included accounts */
private $accounts;
/** @var Collection */
/** @var Collection The included categories */
private $categories;
/** @var Carbon */
/** @var Carbon The end date */
private $end;
/** @var Collection */
/** @var Collection The expenses */
private $expenses;
/** @var Collection */
/** @var Collection The income in the report. */
private $income;
/** @var Carbon */
/** @var Carbon The start date. */
private $start;
/**
@@ -63,7 +65,10 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
}
/**
* Generates the report.
*
* @return string
* @throws \Throwable
*/
public function generate(): string
{
@@ -101,6 +106,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
}
/**
* Set the involved accounts.
*
* @param Collection $accounts
*
* @return ReportGeneratorInterface
@@ -113,6 +120,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
}
/**
* Empty budget setter.
*
* @param Collection $budgets
*
* @return ReportGeneratorInterface
@@ -123,6 +132,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
}
/**
* Set the categories involved in this report.
*
* @param Collection $categories
*
* @return ReportGeneratorInterface
@@ -135,6 +146,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
}
/**
* Set the end date for this report.
*
* @param Carbon $date
*
* @return ReportGeneratorInterface
@@ -147,6 +160,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
}
/**
* Set the expenses involved in this report.
*
* @param Collection $expense
*
* @return ReportGeneratorInterface
@@ -157,6 +172,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
}
/**
* Set the start date for this report.
*
* @param Carbon $date
*
* @return ReportGeneratorInterface
@@ -169,6 +186,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
}
/**
* Unused tag setter.
*
* @param Collection $tags
*
* @return ReportGeneratorInterface
@@ -179,6 +198,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
}
/**
* Get the expenses for this report.
*
* @return Collection
*/
protected function getExpenses(): Collection
@@ -206,6 +227,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
}
/**
* Get the income for this report.
*
* @return Collection
*/
protected function getIncome(): Collection
@@ -230,6 +253,8 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
}
/**
* Summarize the category.
*
* @param Collection $collection
*
* @return array

View File

@@ -27,5 +27,4 @@ namespace FireflyIII\Generator\Report\Category;
*/
class MultiYearReportGenerator extends MonthReportGenerator
{
// Doesn't do anything different.
}

View File

@@ -27,5 +27,4 @@ namespace FireflyIII\Generator\Report\Category;
*/
class YearReportGenerator extends MonthReportGenerator
{
// Doesn't do anything different.
}

Some files were not shown because too many files have changed in this diff Show More