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: # 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 chown -R www-data:www-data -R $FIREFLY_PATH/storage
chmod -R 775 $FIREFLY_PATH/storage/export $FIREFLY_PATH/storage/upload $FIREFLY_PATH/storage/upload $FIREFLY_PATH/storage/logs $FIREFLY_PATH/storage/framework/cache chmod -R 775 $FIREFLY_PATH/storage
# remove any lingering files that may break upgrades: # remove any lingering files that may break upgrades:
rm -f $FIREFLY_PATH/storage/logs/laravel.log rm -f $FIREFLY_PATH/storage/logs/laravel.log

View File

@@ -1,4 +1,3 @@
# Ignore composer specific files and vendor folder # Ignore composer specific files and vendor folder
composer.phar composer.phar
composer.lock
vendor 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 # 4.7.5
- A new feature called "recurring transactions" that will make Firefly III automatically create transactions for you. - 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. - 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 = ( manifest = (
appTitle = (defaultText = "Firefly III"), appTitle = (defaultText = "Firefly III"),
appVersion = 14, appVersion = 14,
appMarketingVersion = (defaultText = "4.7.5"), appMarketingVersion = (defaultText = "4.7.5.1"),
actions = [ actions = [
# Define your "new document" handlers here. # Define your "new document" handlers here.

View File

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

View File

@@ -1,5 +1,4 @@
<?php <?php
/** /**
* AccountController.php * AccountController.php
* Copyright (c) 2018 thegrumpydictator@gmail.com * Copyright (c) 2018 thegrumpydictator@gmail.com
@@ -41,13 +40,15 @@ use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer; use League\Fractal\Serializer\JsonApiSerializer;
/** /**
* Class AccountController * Class AccountController.
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/ */
class AccountController extends Controller class AccountController extends Controller
{ {
/** @var CurrencyRepositoryInterface */ /** @var CurrencyRepositoryInterface The currency repository */
private $currencyRepository; private $currencyRepository;
/** @var AccountRepositoryInterface */ /** @var AccountRepositoryInterface The account repository */
private $repository; private $repository;
/** /**
@@ -125,6 +126,8 @@ class AccountController extends Controller
} }
/** /**
* Show single instance.
*
* @param Request $request * @param Request $request
* @param Account $account * @param Account $account
* *
@@ -146,6 +149,8 @@ class AccountController extends Controller
} }
/** /**
* Store a new instance.
*
* @param AccountRequest $request * @param AccountRequest $request
* *
* @return \Illuminate\Http\JsonResponse * @return \Illuminate\Http\JsonResponse
@@ -197,52 +202,25 @@ class AccountController extends Controller
} }
/** /**
* All the available types.
*
* @param string $type * @param string $type
* *
* @return array * @return array
*/ */
private function mapTypes(string $type): array private function mapTypes(string $type): array
{ {
$types = [ $types = [
'all' => [ 'all' => [AccountType::DEFAULT, AccountType::CASH, AccountType::ASSET, AccountType::EXPENSE, AccountType::REVENUE,
AccountType::DEFAULT, AccountType::INITIAL_BALANCE, AccountType::BENEFICIARY, AccountType::IMPORT, AccountType::RECONCILIATION,
AccountType::CASH, AccountType::LOAN,],
AccountType::ASSET, 'asset' => [AccountType::DEFAULT, AccountType::ASSET,],
AccountType::EXPENSE, 'cash' => [AccountType::CASH,],
AccountType::REVENUE, 'expense' => [AccountType::EXPENSE, AccountType::BENEFICIARY,],
AccountType::INITIAL_BALANCE, 'revenue' => [AccountType::REVENUE,],
AccountType::BENEFICIARY, 'special' => [AccountType::CASH, AccountType::INITIAL_BALANCE, AccountType::IMPORT, AccountType::RECONCILIATION,
AccountType::IMPORT, AccountType::LOAN,],
AccountType::RECONCILIATION, 'hidden' => [AccountType::INITIAL_BALANCE, AccountType::IMPORT, AccountType::RECONCILIATION, AccountType::LOAN,],
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::DEFAULT => [AccountType::DEFAULT],
AccountType::CASH => [AccountType::CASH], AccountType::CASH => [AccountType::CASH],
AccountType::ASSET => [AccountType::ASSET], AccountType::ASSET => [AccountType::ASSET],
@@ -254,10 +232,11 @@ class AccountController extends Controller
AccountType::RECONCILIATION => [AccountType::RECONCILIATION], AccountType::RECONCILIATION => [AccountType::RECONCILIATION],
AccountType::LOAN => [AccountType::LOAN], AccountType::LOAN => [AccountType::LOAN],
]; ];
$return = $types['all'];
if (isset($types[$type])) { 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; use League\Fractal\Serializer\JsonApiSerializer;
/** /**
* Class AttachmentController * Class AttachmentController.
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/ */
class AttachmentController extends Controller class AttachmentController extends Controller
{ {
/** @var AttachmentRepositoryInterface */ /** @var AttachmentRepositoryInterface The attachment repository */
private $repository; private $repository;
/** /**
@@ -81,6 +83,8 @@ class AttachmentController extends Controller
} }
/** /**
* Download an attachment.
*
* @param Attachment $attachment * @param Attachment $attachment
* *
* @return LaravelResponse * @return LaravelResponse
@@ -88,7 +92,7 @@ class AttachmentController extends Controller
*/ */
public function download(Attachment $attachment): LaravelResponse 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).'); throw new FireflyException('No file has been uploaded for this attachment (yet).');
} }
if ($this->repository->exists($attachment)) { if ($this->repository->exists($attachment)) {
@@ -211,6 +215,8 @@ class AttachmentController extends Controller
} }
/** /**
* Upload an attachment.
*
* @param Request $request * @param Request $request
* @param Attachment $attachment * @param Attachment $attachment
* *

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -40,17 +40,21 @@ use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer; use League\Fractal\Serializer\JsonApiSerializer;
/** /**
* Class LinkTypeController.
* *
* Class LinkTypeController * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/ */
class LinkTypeController extends Controller class LinkTypeController extends Controller
{ {
/** @var LinkTypeRepositoryInterface */ /** @var LinkTypeRepositoryInterface The link type repository */
private $repository; private $repository;
/** @var UserRepositoryInterface */ /** @var UserRepositoryInterface The user repository */
private $userRepository; private $userRepository;
/**
* LinkTypeController constructor.
*/
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
@@ -77,7 +81,7 @@ class LinkTypeController extends Controller
*/ */
public function delete(LinkType $linkType): JsonResponse 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)); throw new FireflyException(sprintf('You cannot delete this link type (#%d, "%s")', $linkType->id, $linkType->name));
} }
$this->repository->destroy($linkType, null); $this->repository->destroy($linkType, null);
@@ -151,7 +155,10 @@ class LinkTypeController extends Controller
*/ */
public function store(LinkTypeRequest $request): JsonResponse 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.'); throw new FireflyException('You need the "owner"-role to do this.');
} }
$data = $request->getAll(); $data = $request->getAll();
@@ -168,6 +175,8 @@ class LinkTypeController extends Controller
} }
/** /**
* Update object.
*
* @param LinkTypeRequest $request * @param LinkTypeRequest $request
* @param LinkType $linkType * @param LinkType $linkType
* *
@@ -176,10 +185,14 @@ class LinkTypeController extends Controller
*/ */
public function update(LinkTypeRequest $request, LinkType $linkType): JsonResponse 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)); 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.'); 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; use League\Fractal\Serializer\JsonApiSerializer;
/** /**
* TODO order up and down. * Class PiggyBankController.
* Class PiggyBankController *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/ */
class PiggyBankController extends Controller class PiggyBankController extends Controller
{ {
/** @var PiggyBankRepositoryInterface */ /** @var PiggyBankRepositoryInterface The piggy bank repository */
private $repository; private $repository;
/**
* PiggyBankController constructor.
*/
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
$this->middleware( $this->middleware(
function ($request, $next) { function ($request, $next) {
/** @var User $user */ /** @var User $admin */
$user = auth()->user(); $admin = auth()->user();
$this->repository = app(PiggyBankRepositoryInterface::class); $this->repository = app(PiggyBankRepositoryInterface::class);
$this->repository->setUser($admin);
return $next($request); return $next($request);
} }
@@ -160,6 +166,8 @@ class PiggyBankController extends Controller
} }
/** /**
* Update piggy bank.
*
* @param PiggyBankRequest $request * @param PiggyBankRequest $request
* @param PiggyBank $piggyBank * @param PiggyBank $piggyBank
* *

View File

@@ -42,20 +42,6 @@ use Preferences;
*/ */
class PreferenceController extends Controller 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. * List all of them.
* *
@@ -116,10 +102,13 @@ class PreferenceController extends Controller
} }
/** /**
* Update a preference.
*
* @param PreferenceRequest $request * @param PreferenceRequest $request
* @param Preference $preference * @param Preference $preference
* *
* @return JsonResponse * @return JsonResponse
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/ */
public function update(PreferenceRequest $request, Preference $preference): JsonResponse public function update(PreferenceRequest $request, Preference $preference): JsonResponse
{ {
@@ -138,7 +127,7 @@ class PreferenceController extends Controller
break; break;
case 'customFiscalYear': case 'customFiscalYear':
case 'twoFactorAuthEnabled': case 'twoFactorAuthEnabled':
$newValue = (int)$data['data'] === 1; $newValue = 1 === (int)$data['data'];
break; break;
} }
$result = Preferences::set($preference->name, $newValue); $result = Preferences::set($preference->name, $newValue);

View File

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

View File

@@ -42,9 +42,12 @@ use League\Fractal\Serializer\JsonApiSerializer;
*/ */
class RuleController extends Controller class RuleController extends Controller
{ {
/** @var RuleRepositoryInterface */ /** @var RuleRepositoryInterface The rule repository */
private $ruleRepository; private $ruleRepository;
/**
* RuleController constructor.
*/
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
@@ -136,7 +139,7 @@ class RuleController extends Controller
/** /**
* Store new object. * Store new object.
* *
* @param Request $request * @param RuleRequest $request
* *
* @return JsonResponse * @return JsonResponse
*/ */
@@ -153,6 +156,8 @@ class RuleController extends Controller
} }
/** /**
* Update a rule.
*
* @param RuleRequest $request * @param RuleRequest $request
* @param Rule $rule * @param Rule $rule
* *
@@ -160,9 +165,9 @@ class RuleController extends Controller
*/ */
public function update(RuleRequest $request, Rule $rule): JsonResponse public function update(RuleRequest $request, Rule $rule): JsonResponse
{ {
$rule = $this->ruleRepository->update($rule, $request->getAll()); $rule = $this->ruleRepository->update($rule, $request->getAll());
$manager = new Manager(); $manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl)); $manager->setSerializer(new JsonApiSerializer($baseUrl));
$resource = new Item($rule, new RuleTransformer($this->parameters), 'rules'); $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; use League\Fractal\Serializer\JsonApiSerializer;
/**
* Class RuleGroupController
*/
class RuleGroupController extends Controller class RuleGroupController extends Controller
{ {
/** @var RuleGroupRepositoryInterface */ /** @var RuleGroupRepositoryInterface The rule group repository */
private $ruleGroupRepository; private $ruleGroupRepository;
/**
* RuleGroupController constructor.
*/
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
@@ -62,7 +68,7 @@ class RuleGroupController extends Controller
/** /**
* Delete the resource. * Delete the resource.
* *
* @param string $object * @param RuleGroup $ruleGroup
* *
* @return JsonResponse * @return JsonResponse
*/ */
@@ -151,8 +157,10 @@ class RuleGroupController extends Controller
} }
/** /**
* @param Request $request * Update a rule group.
* @param string $object *
* @param RuleGroupRequest $request
* @param RuleGroup $ruleGroup
* *
* @return JsonResponse * @return JsonResponse
*/ */

View File

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

View File

@@ -23,7 +23,6 @@
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests; namespace FireflyIII\Api\V1\Requests;
/** /**
* Class AccountRequest * Class AccountRequest
*/ */
@@ -31,6 +30,8 @@ class AccountRequest extends Request
{ {
/** /**
* Authorize logged in users.
*
* @return bool * @return bool
*/ */
public function authorize(): bool public function authorize(): bool
@@ -40,6 +41,8 @@ class AccountRequest extends Request
} }
/** /**
* Get all data from the request.
*
* @return array * @return array
*/ */
public function getAll(): 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 * @return array
*/ */
public function rules(): array public function rules(): array

View File

@@ -26,7 +26,6 @@ namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use FireflyIII\Models\ImportJob; use FireflyIII\Models\ImportJob;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Rules\IsBase64;
use FireflyIII\Rules\IsValidAttachmentModel; use FireflyIII\Rules\IsValidAttachmentModel;
/** /**
@@ -35,6 +34,8 @@ use FireflyIII\Rules\IsValidAttachmentModel;
class AttachmentRequest extends Request class AttachmentRequest extends Request
{ {
/** /**
* Authorize logged in users.
*
* @return bool * @return bool
*/ */
public function authorize(): bool public function authorize(): bool
@@ -44,6 +45,8 @@ class AttachmentRequest extends Request
} }
/** /**
* Get all data from the request.
*
* @return array * @return array
*/ */
public function getAll(): 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 * @return array
*/ */
public function rules(): array public function rules(): array

View File

@@ -29,6 +29,8 @@ namespace FireflyIII\Api\V1\Requests;
class AvailableBudgetRequest extends Request class AvailableBudgetRequest extends Request
{ {
/** /**
* Authorize logged in users.
*
* @return bool * @return bool
*/ */
public function authorize(): bool public function authorize(): bool
@@ -38,6 +40,8 @@ class AvailableBudgetRequest extends Request
} }
/** /**
* Get all data from the request.
*
* @return array * @return array
*/ */
public function getAll(): 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 * @return array
*/ */
public function rules(): array public function rules(): array

View File

@@ -33,6 +33,8 @@ class BillRequest extends Request
{ {
/** /**
* Authorize logged in users.
*
* @return bool * @return bool
*/ */
public function authorize(): bool public function authorize(): bool
@@ -42,6 +44,8 @@ class BillRequest extends Request
} }
/** /**
* Get all data from the request.
*
* @return array * @return array
*/ */
public function getAll(): 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 * @return array
*/ */
public function rules(): array public function rules(): array

View File

@@ -30,6 +30,8 @@ namespace FireflyIII\Api\V1\Requests;
class BudgetLimitRequest extends Request class BudgetLimitRequest extends Request
{ {
/** /**
* Authorize logged in users.
*
* @return bool * @return bool
*/ */
public function authorize(): bool public function authorize(): bool
@@ -39,6 +41,8 @@ class BudgetLimitRequest extends Request
} }
/** /**
* Get all data from the request.
*
* @return array * @return array
*/ */
public function getAll(): 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 * @return array
*/ */
public function rules(): array public function rules(): array

View File

@@ -31,6 +31,8 @@ use FireflyIII\Models\Budget;
class BudgetRequest extends Request class BudgetRequest extends Request
{ {
/** /**
* Authorize logged in users.
*
* @return bool * @return bool
*/ */
public function authorize(): bool public function authorize(): bool
@@ -40,6 +42,8 @@ class BudgetRequest extends Request
} }
/** /**
* Get all data from the request.
*
* @return array * @return array
*/ */
public function getAll(): 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 * @return array
*/ */
public function rules(): array public function rules(): array

View File

@@ -31,6 +31,8 @@ use FireflyIII\Models\Category;
class CategoryRequest extends Request class CategoryRequest extends Request
{ {
/** /**
* Authorize logged in users.
*
* @return bool * @return bool
*/ */
public function authorize(): bool public function authorize(): bool
@@ -40,6 +42,8 @@ class CategoryRequest extends Request
} }
/** /**
* Get all data from the request.
*
* @return array * @return array
*/ */
public function getAll(): 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 * @return array
*/ */
public function rules(): array public function rules(): array

View File

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

View File

@@ -31,6 +31,8 @@ namespace FireflyIII\Api\V1\Requests;
class JournalLinkRequest extends Request class JournalLinkRequest extends Request
{ {
/** /**
* Authorize logged in users.
*
* @return bool * @return bool
*/ */
public function authorize(): bool public function authorize(): bool
@@ -40,6 +42,8 @@ class JournalLinkRequest extends Request
} }
/** /**
* Get all data from the request.
*
* @return array * @return array
*/ */
public function getAll(): 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 * @return array
*/ */
public function rules(): array public function rules(): array

View File

@@ -33,6 +33,8 @@ use Illuminate\Validation\Rule;
class LinkTypeRequest extends Request class LinkTypeRequest extends Request
{ {
/** /**
* Authorize logged in users.
*
* @return bool * @return bool
*/ */
public function authorize(): bool public function authorize(): bool
@@ -42,6 +44,8 @@ class LinkTypeRequest extends Request
} }
/** /**
* Get all data from the request.
*
* @return array * @return array
*/ */
public function getAll(): 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 * @return array
*/ */
public function rules(): array public function rules(): array

View File

@@ -33,6 +33,8 @@ use FireflyIII\Rules\IsAssetAccountId;
class PiggyBankRequest extends Request class PiggyBankRequest extends Request
{ {
/** /**
* Authorize logged in users.
*
* @return bool * @return bool
*/ */
public function authorize(): bool public function authorize(): bool
@@ -42,6 +44,8 @@ class PiggyBankRequest extends Request
} }
/** /**
* Get all data from the request.
*
* @return array * @return array
*/ */
public function getAll(): 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 * @return array
*/ */
public function rules(): array public function rules(): array

View File

@@ -32,6 +32,8 @@ class PreferenceRequest extends Request
/** /**
* Authorize logged in users.
*
* @return bool * @return bool
*/ */
public function authorize(): bool public function authorize(): bool
@@ -40,6 +42,11 @@ class PreferenceRequest extends Request
return auth()->check(); return auth()->check();
} }
/**
* Get all data from the request.
*
* @return array
*/
public function getAll(): array public function getAll(): array
{ {
return [ 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 public function rules(): array
{ {
return [ return [

View File

@@ -24,20 +24,21 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests; namespace FireflyIII\Api\V1\Requests;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Rules\BelongsUser; use FireflyIII\Rules\BelongsUser;
use FireflyIII\Validation\RecurrenceValidation;
use FireflyIII\Validation\TransactionValidation;
use Illuminate\Validation\Validator; use Illuminate\Validation\Validator;
use InvalidArgumentException;
use Log;
/** /**
* Class RecurrenceRequest * Class RecurrenceRequest
*/ */
class RecurrenceRequest extends Request class RecurrenceRequest extends Request
{ {
use RecurrenceValidation, TransactionValidation;
/** /**
* Authorize logged in users.
*
* @return bool * @return bool
*/ */
public function authorize(): bool public function authorize(): bool
@@ -47,6 +48,8 @@ class RecurrenceRequest extends Request
} }
/** /**
* Get all data from the request.
*
* @return array * @return array
*/ */
public function getAll(): array public function getAll(): array
@@ -67,55 +70,16 @@ class RecurrenceRequest extends Request
'piggy_bank_name' => $this->string('piggy_bank_name'), 'piggy_bank_name' => $this->string('piggy_bank_name'),
'tags' => explode(',', $this->string('tags')), 'tags' => explode(',', $this->string('tags')),
], ],
'transactions' => [], 'transactions' => $this->getTransactionData(),
'repetitions' => [], '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; return $return;
} }
/** /**
* The rules that the incoming request must be matched against.
*
* @return array * @return array
*/ */
public function rules(): array public function rules(): array
@@ -132,18 +96,12 @@ class RecurrenceRequest extends Request
'nr_of_repetitions' => 'numeric|between:1,31', 'nr_of_repetitions' => 'numeric|between:1,31',
'apply_rules' => 'required|boolean', 'apply_rules' => 'required|boolean',
'active' => 'required|boolean', 'active' => 'required|boolean',
// rules for meta values:
'tags' => 'between:1,64000', 'tags' => 'between:1,64000',
'piggy_bank_id' => 'numeric', 'piggy_bank_id' => 'numeric',
// rules for repetitions.
'repetitions.*.type' => 'required|in:daily,weekly,ndom,monthly,yearly', 'repetitions.*.type' => 'required|in:daily,weekly,ndom,monthly,yearly',
'repetitions.*.moment' => 'between:0,10', 'repetitions.*.moment' => 'between:0,10',
'repetitions.*.skip' => 'required|numeric|between:0,31', 'repetitions.*.skip' => 'required|numeric|between:0,31',
'repetitions.*.weekend' => 'required|numeric|min:1|max:4', '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_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.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code|required_without:transactions.*.currency_id',
'transactions.*.foreign_amount' => 'numeric|more:0', 'transactions.*.foreign_amount' => 'numeric|more:0',
@@ -171,312 +129,76 @@ class RecurrenceRequest extends Request
{ {
$validator->after( $validator->after(
function (Validator $validator) { function (Validator $validator) {
$this->atLeastOneTransaction($validator); $this->validateOneTransaction($validator);
$this->atLeastOneRepetition($validator); $this->validateOneRepetition($validator);
$this->validRepeatsUntil($validator); $this->validateRecurrenceRepetition($validator);
$this->validRepetitionMoment($validator); $this->validateRepetitionMoment($validator);
$this->foreignCurrencyInformation($validator); $this->validateForeignCurrencyInformation($validator);
$this->validateAccountInformation($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 * @return array
*
* @param Validator $validator
* @param int|null $accountId
* @param null|string $accountName
* @param string $idField
* @param string $nameField
*
* @return null|Account
*/ */
protected function assetAccountExists(Validator $validator, ?int $accountId, ?string $accountName, string $idField, string $nameField): ?Account private function getRepetitionData(): array
{ {
$accountId = (int)$accountId; $return = [];
$accountName = (string)$accountName; // repetition data:
// both empty? hard exit. /** @var array $repetitions */
if ($accountId < 1 && '' === $accountName) { $repetitions = $this->get('repetitions');
$validator->errors()->add($idField, trans('validation.filled', ['attribute' => $idField])); /** @var array $repetition */
foreach ($repetitions as $repetition) {
return null; $return[] = [
} 'type' => $repetition['type'],
// ID belongs to user and is asset account: 'moment' => $repetition['moment'],
/** @var AccountRepositoryInterface $repository */ 'skip' => (int)$repetition['skip'],
$repository = app(AccountRepositoryInterface::class); 'weekend' => (int)$repetition['weekend'],
$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;
} }
$account = $repository->findByName($accountName, [AccountType::ASSET]); return $return;
if (null === $account) {
$validator->errors()->add($nameField, trans('validation.belongs_user'));
return null;
}
return $account;
} }
/** /**
* 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(); $return = [];
$repetitions = $data['repetitions'] ?? []; // transaction data:
// need at least one transaction /** @var array $transactions */
if (\count($repetitions) === 0) { $transactions = $this->get('transactions');
$validator->errors()->add('description', trans('validation.at_least_one_repetition')); /** @var array $transaction */
} foreach ($transactions as $transaction) {
} $return[] = [
'amount' => $transaction['amount'],
/** 'currency_id' => isset($transaction['currency_id']) ? (int)$transaction['currency_id'] : null,
* Adds an error to the validator when there are no transactions in the array of data. 'currency_code' => $transaction['currency_code'] ?? null,
* 'foreign_amount' => $transaction['foreign_amount'] ?? null,
* @param Validator $validator 'foreign_currency_id' => isset($transaction['foreign_currency_id']) ? (int)$transaction['foreign_currency_id'] : null,
*/ 'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null,
protected function atLeastOneTransaction(Validator $validator): void 'budget_id' => isset($transaction['budget_id']) ? (int)$transaction['budget_id'] : null,
{ 'budget_name' => $transaction['budget_name'] ?? null,
$data = $validator->getData(); 'category_id' => isset($transaction['category_id']) ? (int)$transaction['category_id'] : null,
$transactions = $data['transactions'] ?? []; 'category_name' => $transaction['category_name'] ?? null,
// need at least one transaction 'source_id' => isset($transaction['source_id']) ? (int)$transaction['source_id'] : null,
if (\count($transactions) === 0) { 'source_name' => isset($transaction['source_name']) ? (string)$transaction['source_name'] : null,
$validator->errors()->add('description', trans('validation.at_least_one_transaction')); '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'],
];
/**
* 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 $return;
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;
}
}
}
} }
} }

View File

@@ -28,6 +28,10 @@ use FireflyIII\Http\Requests\Request as FireflyIIIRequest;
/** /**
* Class Request. * 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 class Request extends FireflyIIIRequest
{ {

View File

@@ -33,6 +33,8 @@ use FireflyIII\Models\RuleGroup;
class RuleGroupRequest extends Request class RuleGroupRequest extends Request
{ {
/** /**
* Authorize logged in users.
*
* @return bool * @return bool
*/ */
public function authorize(): bool public function authorize(): bool
@@ -42,6 +44,8 @@ class RuleGroupRequest extends Request
} }
/** /**
* Get all data from the request.
*
* @return array * @return array
*/ */
public function getAll(): 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 * @return array
*/ */
public function rules(): array public function rules(): array

View File

@@ -32,6 +32,8 @@ use Illuminate\Validation\Validator;
class RuleRequest extends Request class RuleRequest extends Request
{ {
/** /**
* Authorize logged in users.
*
* @return bool * @return bool
*/ */
public function authorize(): bool public function authorize(): bool
@@ -41,6 +43,8 @@ class RuleRequest extends Request
} }
/** /**
* Get all data from the request.
*
* @return array * @return array
*/ */
public function getAll(): array public function getAll(): array
@@ -62,14 +66,14 @@ class RuleRequest extends Request
$data['rule-triggers'][] = [ $data['rule-triggers'][] = [
'name' => $trigger['name'], 'name' => $trigger['name'],
'value' => $trigger['value'], '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) { foreach ($this->get('rule-actions') as $action) {
$data['rule-actions'][] = [ $data['rule-actions'][] = [
'name' => $action['name'], 'name' => $action['name'],
'value' => $action['value'], '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 * @return array
*/ */
public function rules(): array public function rules(): array
@@ -134,7 +140,7 @@ class RuleRequest extends Request
$data = $validator->getData(); $data = $validator->getData();
$repetitions = $data['rule-actions'] ?? []; $repetitions = $data['rule-actions'] ?? [];
// need at least one transaction // need at least one transaction
if (\count($repetitions) === 0) { if (0 === \count($repetitions)) {
$validator->errors()->add('title', trans('validation.at_least_one_action')); $validator->errors()->add('title', trans('validation.at_least_one_action'));
} }
} }
@@ -149,7 +155,7 @@ class RuleRequest extends Request
$data = $validator->getData(); $data = $validator->getData();
$repetitions = $data['rule-triggers'] ?? []; $repetitions = $data['rule-triggers'] ?? [];
// need at least one transaction // need at least one transaction
if (\count($repetitions) === 0) { if (0 === \count($repetitions)) {
$validator->errors()->add('title', trans('validation.at_least_one_trigger')); $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; 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\Rules\BelongsUser;
use FireflyIII\Validation\TransactionValidation;
use Illuminate\Validation\Validator; use Illuminate\Validation\Validator;
@@ -38,7 +34,11 @@ use Illuminate\Validation\Validator;
*/ */
class TransactionRequest extends Request class TransactionRequest extends Request
{ {
use TransactionValidation;
/** /**
* Authorize logged in users.
*
* @return bool * @return bool
*/ */
public function authorize(): 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 * @return array
*/ */
public function getAll(): array public function getAll(): array
{ {
$data = [ $data = [
// basic fields for journal:
'type' => $this->string('type'), 'type' => $this->string('type'),
'date' => $this->date('date'), 'date' => $this->date('date'),
'description' => $this->string('description'), 'description' => $this->string('description'),
@@ -62,8 +65,6 @@ class TransactionRequest extends Request
'bill_id' => $this->integer('bill_id'), 'bill_id' => $this->integer('bill_id'),
'bill_name' => $this->string('bill_name'), 'bill_name' => $this->string('bill_name'),
'tags' => explode(',', $this->string('tags')), 'tags' => explode(',', $this->string('tags')),
// then, custom fields for journal
'interest_date' => $this->date('interest_date'), 'interest_date' => $this->date('interest_date'),
'book_date' => $this->date('book_date'), 'book_date' => $this->date('book_date'),
'process_date' => $this->date('process_date'), 'process_date' => $this->date('process_date'),
@@ -72,39 +73,17 @@ class TransactionRequest extends Request
'invoice_date' => $this->date('invoice_date'), 'invoice_date' => $this->date('invoice_date'),
'internal_reference' => $this->string('internal_reference'), 'internal_reference' => $this->string('internal_reference'),
'notes' => $this->string('notes'), 'notes' => $this->string('notes'),
'transactions' => $this->getTransactionData(),
// then, transactions (see below).
'transactions' => [],
]; ];
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; return $data;
} }
/** /**
* The rules that the incoming request must be matched against.
*
* @return array * @return array
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/ */
public function rules(): array public function rules(): array
{ {
@@ -149,13 +128,8 @@ class TransactionRequest extends Request
'transactions.*.destination_name' => 'between:1,255|nullable', 'transactions.*.destination_name' => 'between:1,255|nullable',
]; ];
switch ($this->method()) { if ('PUT' === $this->method()) {
default: unset($rules['type'], $rules['piggy_bank_id'], $rules['piggy_bank_name']);
break;
case 'PUT':
case 'PATCH':
unset($rules['type'], $rules['piggy_bank_id'], $rules['piggy_bank_name']);
break;
} }
return $rules; return $rules;
@@ -174,11 +148,11 @@ class TransactionRequest extends Request
{ {
$validator->after( $validator->after(
function (Validator $validator) { function (Validator $validator) {
$this->atLeastOneTransaction($validator); $this->validateOneTransaction($validator);
$this->checkValidDescriptions($validator); $this->validateDescriptions($validator);
$this->equalToJournalDescription($validator); $this->validateJournalDescription($validator);
$this->emptySplitDescriptions($validator); $this->validateSplitDescriptions($validator);
$this->foreignCurrencyInformation($validator); $this->validateForeignCurrencyInformation($validator);
$this->validateAccountInformation($validator); $this->validateAccountInformation($validator);
$this->validateSplitAccounts($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 * @SuppressWarnings(PHPMD.CyclomaticComplexity)
* * @SuppressWarnings(PHPMD.NPathComplexity)
* @param Validator $validator * @return array
* @param int|null $accountId
* @param null|string $accountName
* @param string $idField
* @param string $nameField
*
* @return null|Account
*/ */
protected function assetAccountExists(Validator $validator, ?int $accountId, ?string $accountName, string $idField, string $nameField): ?Account private function getTransactionData(): array
{ {
$return = [];
$accountId = (int)$accountId; foreach ($this->get('transactions') as $index => $transaction) {
$accountName = (string)$accountName; $return[] = [
// both empty? hard exit. 'description' => $transaction['description'] ?? null,
if ($accountId < 1 && \strlen($accountName) === 0) { 'amount' => $transaction['amount'],
$validator->errors()->add($idField, trans('validation.filled', ['attribute' => $idField])); 'currency_id' => isset($transaction['currency_id']) ? (int)$transaction['currency_id'] : null,
'currency_code' => $transaction['currency_code'] ?? null,
return null; 'foreign_amount' => $transaction['foreign_amount'] ?? null,
} 'foreign_currency_id' => isset($transaction['foreign_currency_id']) ? (int)$transaction['foreign_currency_id'] : null,
// ID belongs to user and is asset account: 'foreign_currency_code' => $transaction['foreign_currency_code'] ?? null,
/** @var AccountRepositoryInterface $repository */ 'budget_id' => isset($transaction['budget_id']) ? (int)$transaction['budget_id'] : null,
$repository = app(AccountRepositoryInterface::class); 'budget_name' => $transaction['budget_name'] ?? null,
$repository->setUser(auth()->user()); 'category_id' => isset($transaction['category_id']) ? (int)$transaction['category_id'] : null,
$set = $repository->getAccountsById([$accountId]); 'category_name' => $transaction['category_name'] ?? null,
if ($set->count() === 1) { 'source_id' => isset($transaction['source_id']) ? (int)$transaction['source_id'] : null,
/** @var Account $first */ 'source_name' => isset($transaction['source_name']) ? (string)$transaction['source_name'] : null,
$first = $set->first(); 'destination_id' => isset($transaction['destination_id']) ? (int)$transaction['destination_id'] : null,
if ($first->accountType->type !== AccountType::ASSET) { 'destination_name' => isset($transaction['destination_name']) ? (string)$transaction['destination_name'] : null,
$validator->errors()->add($idField, trans('validation.belongs_user')); 'reconciled' => $transaction['reconciled'] ?? false,
'identifier' => $index,
return null; ];
}
// we ignore the account name at this point.
return $first;
} }
$account = $repository->findByName($accountName, [AccountType::ASSET]); return $return;
if (null === $account) {
$validator->errors()->add($nameField, trans('validation.belongs_user'));
return null;
}
return $account;
} }
/**
* 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; namespace FireflyIII\Api\V1\Requests;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
@@ -33,24 +34,32 @@ use FireflyIII\User;
class UserRequest extends Request class UserRequest extends Request
{ {
/** /**
* Authorize logged in users.
*
* @return bool * @return bool
*/ */
public function authorize(): bool public function authorize(): bool
{ {
$result = false;
// Only allow authenticated users // Only allow authenticated users
if (!auth()->check()) { if (auth()->check()) {
return false; // @codeCoverageIgnore /** @var User $user */
} $user = auth()->user();
/** @var User $user */
$user = auth()->user(); /** @var UserRepositoryInterface $repository */
if (!$user->hasRole('owner')) { $repository = app(UserRepositoryInterface::class);
return false; // @codeCoverageIgnore
if ($repository->hasRole($user, 'owner')) {
$result = true; // @codeCoverageIgnore
}
} }
return true; return $result;
} }
/** /**
* Get all data from the request.
*
* @return array * @return array
*/ */
public function getAll(): 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 * @return array
*/ */
public function rules(): array public function rules(): array

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,4 @@
<?php <?php
/** /**
* ScanAttachments.php * ScanAttachments.php
* Copyright (c) 2018 thegrumpydictator@gmail.com * Copyright (c) 2018 thegrumpydictator@gmail.com
@@ -20,6 +19,8 @@
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>. * along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @noinspection PhpDynamicAsStaticMethodCallInspection */
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\Console\Commands; namespace FireflyIII\Console\Commands;
@@ -53,7 +54,7 @@ class ScanAttachments extends Command
/** /**
* Execute the console command. * Execute the console command.
*/ */
public function handle() public function handle(): void
{ {
$attachments = Attachment::get(); $attachments = Attachment::get();
$disk = Storage::disk('upload'); $disk = Storage::disk('upload');
@@ -63,13 +64,13 @@ class ScanAttachments extends Command
try { try {
$content = $disk->get($fileName); $content = $disk->get($fileName);
} catch (FileNotFoundException $e) { } 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; continue;
} }
try { try {
$decrypted = Crypt::decrypt($content); $decrypted = Crypt::decrypt($content);
} catch (DecryptException $e) { } 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; continue;
} }
$tmpfname = tempnam(sys_get_temp_dir(), 'FireflyIII'); $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/>. * along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @noinspection MultipleReturnStatementsInspection */
/** @noinspection PhpStaticAsDynamicMethodCallInspection */
/** @noinspection PhpDynamicAsStaticMethodCallInspection */
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\Console\Commands; namespace FireflyIII\Console\Commands;
@@ -42,7 +46,9 @@ use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionJournalMeta; use FireflyIII\Models\TransactionJournalMeta;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Database\QueryException; use Illuminate\Database\QueryException;
@@ -50,14 +56,14 @@ use Illuminate\Support\Collection;
use Log; use Log;
use Preferences; use Preferences;
use Schema; use Schema;
use UnexpectedValueException;
/** /**
* Class UpgradeDatabase. * Class UpgradeDatabase.
* *
* Upgrade user database. * Upgrade user database.
* * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
* * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects) // it just touches a lot of things.
*/ */
class UpgradeDatabase extends Command class UpgradeDatabase extends Command
{ {
@@ -77,7 +83,7 @@ class UpgradeDatabase extends Command
/** /**
* Execute the console command. * Execute the console command.
*/ */
public function handle() public function handle(): void
{ {
$this->setTransactionIdentifier(); $this->setTransactionIdentifier();
$this->updateAccountCurrencies(); $this->updateAccountCurrencies();
@@ -93,7 +99,14 @@ class UpgradeDatabase extends Command
$this->info('Firefly III database is up to date.'); $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) { foreach (User::get() as $user) {
/** @var Preference $lang */ /** @var Preference $lang */
@@ -101,12 +114,20 @@ class UpgradeDatabase extends Command
$groupName = (string)trans('firefly.rulegroup_for_bills_title', [], $lang->data); $groupName = (string)trans('firefly.rulegroup_for_bills_title', [], $lang->data);
$ruleGroup = $user->ruleGroups()->where('title', $groupName)->first(); $ruleGroup = $user->ruleGroups()->where('title', $groupName)->first();
$currencyPreference = Preferences::getForUser($user, 'currencyPreference', config('firefly.default_currency', 'EUR')); $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) { if (null === $currency) {
$this->line('Fall back to default currency in migrateBillsToRules().');
$currency = app('amount')->getDefaultCurrency(); $currency = app('amount')->getDefaultCurrency();
} }
if ($ruleGroup === null) { if (null === $ruleGroup) {
$array = RuleGroup::get(['order'])->pluck('order')->toArray(); $array = RuleGroup::get(['order'])->pluck('order')->toArray();
$order = \count($array) > 0 ? max($array) + 1 : 1; $order = \count($array) > 0 ? max($array) + 1 : 1;
$ruleGroup = RuleGroup::create( $ruleGroup = RuleGroup::create(
@@ -126,7 +147,7 @@ class UpgradeDatabase extends Command
$collection = $user->bills()->get(); $collection = $user->bills()->get();
/** @var Bill $bill */ /** @var Bill $bill */
foreach ($collection as $bill) { foreach ($collection as $bill) {
if ($bill->match !== 'MIGRATED_TO_RULES') { if ('MIGRATED_TO_RULES' !== $bill->match) {
$rule = Rule::create( $rule = Rule::create(
[ [
'user_id' => $user->id, '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. * 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) * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/ */
public function updateAccountCurrencies(): void public function updateAccountCurrencies(): void
{ {
$accounts = Account::leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') $accounts = Account::leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->whereIn('account_types.type', [AccountType::DEFAULT, AccountType::ASSET])->get(['accounts.*']); ->whereIn('account_types.type', [AccountType::DEFAULT, AccountType::ASSET])->get(['accounts.*']);
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$accounts->each( $accounts->each(
function (Account $account) { function (Account $account) use ($repository) {
$repository->setUser($account->user);
// get users preference, fall back to system pref. // get users preference, fall back to system pref.
$defaultCurrencyCode = Preferences::getForUser($account->user, 'currencyPreference', config('firefly.default_currency', 'EUR'))->data; $defaultCurrencyCode = Preferences::getForUser($account->user, 'currencyPreference', config('firefly.default_currency', 'EUR'))->data;
$defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first(); $defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first();
$accountCurrency = (int)$account->getMeta('currency_id'); $accountCurrency = (int)$repository->getMetaValue($account, 'currency_id');
$openingBalance = $account->getOpeningBalance(); $openingBalance = $account->getOpeningBalance();
$obCurrency = (int)$openingBalance->transaction_currency_id; $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: // both 0? set to default currency:
if (0 === $accountCurrency && 0 === $obCurrency) { if (0 === $accountCurrency && 0 === $obCurrency) {
AccountMeta::where('account_id', $account->id)->where('name', 'currency_id')->forceDelete(); 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. * Both source and destination must match the respective currency preference of the related asset account.
* So FF3 must verify all transactions. * So FF3 must verify all transactions.
* *
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/ */
public function updateOtherCurrencies(): void public function updateOtherCurrencies(): void
{ {
/** @var CurrencyRepositoryInterface $repository */ /** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class); $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') ::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->whereIn('transaction_types.type', [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE]) ->whereIn('transaction_types.type', [TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::OPENING_BALANCE])
->get(['transaction_journals.*']); ->get(['transaction_journals.*']);
$set->each( $set->each(
function (TransactionJournal $journal) use ($repository) { function (TransactionJournal $journal) use ($repository, $accountRepos) {
// get the transaction with the asset account in it: // get the transaction with the asset account in it:
/** @var Transaction $transaction */ /** @var Transaction $transaction */
$transaction = $journal->transactions() $transaction = $journal->transactions()
@@ -339,9 +369,13 @@ class UpgradeDatabase extends Command
if (null === $transaction) { if (null === $transaction) {
return; return;
} }
$accountRepos->setUser($journal->user);
/** @var Account $account */ /** @var Account $account */
$account = $transaction->account; $account = $transaction->account;
$currency = $repository->find((int)$account->getMeta('currency_id')); $currency = $repository->findNull((int)$accountRepos->getMetaValue($account, 'currency_id'));
if (null === $currency) {
return;
}
$transactions = $journal->transactions()->get(); $transactions = $journal->transactions()->get();
$transactions->each( $transactions->each(
function (Transaction $transaction) use ($currency) { 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 * Both source and destination must match the respective currency preference. So FF3 must verify ALL
* transactions. * transactions.
*/ */
public function updateTransferCurrencies() public function updateTransferCurrencies(): void
{ {
$set = TransactionJournal $set = TransactionJournal
::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') ::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
@@ -449,6 +483,7 @@ class UpgradeDatabase extends Command
*/ */
private function migrateNotes(): void private function migrateNotes(): void
{ {
/** @noinspection PhpUndefinedMethodInspection */
$set = TransactionJournalMeta::whereName('notes')->get(); $set = TransactionJournalMeta::whereName('notes')->get();
/** @var TransactionJournalMeta $meta */ /** @var TransactionJournalMeta $meta */
foreach ($set as $meta) { foreach ($set as $meta) {
@@ -479,8 +514,15 @@ class UpgradeDatabase extends Command
{ {
/** @var CurrencyRepositoryInterface $repository */ /** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class); $repository = app(CurrencyRepositoryInterface::class);
$currency = $repository->find((int)$transaction->account->getMeta('currency_id')); /** @var AccountRepositoryInterface $accountRepos */
$journal = $transaction->transactionJournal; $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)) { if (!((int)$currency->id === (int)$journal->transaction_currency_id)) {
$this->line( $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). * 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.ExcessiveMethodLength)
* @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.NPathComplexity)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* *
* @param Transaction $transaction * @param Transaction $transaction
*/ */
@@ -561,7 +603,14 @@ class UpgradeDatabase extends Command
{ {
/** @var CurrencyRepositoryInterface $repository */ /** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class); $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) { if (null === $currency) {
Log::error(sprintf('Account #%d ("%s") must have currency preference but has none.', $transaction->account->id, $transaction->account->name)); 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; $journal = $transaction->transactionJournal;
/** @var Transaction $opposing */ /** @var Transaction $opposing */
$opposing = $journal->transactions()->where('amount', '>', 0)->where('identifier', $transaction->identifier)->first(); $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) { if (null === $opposingCurrency) {
Log::error(sprintf('Account #%d ("%s") must have currency preference but has none.', $opposing->account->id, $opposing->account->name)); 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: // when both are zero, try to grab it from journal:
if (null === $opposing->foreign_amount && null === $transaction->foreign_amount) { if (null === $opposing->foreign_amount && null === $transaction->foreign_amount) {
$foreignAmount = $journal->getMeta('foreign_amount'); $foreignAmount = $journalRepos->getMetaField($journal, 'foreign_amount');
if (null === $foreignAmount) { if (null === $foreignAmount) {
Log::debug(sprintf('Journal #%d has missing foreign currency data, forced to do 1:1 conversion :(.', $transaction->transaction_journal_id)); 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'); $transaction->foreign_amount = bcmul((string)$transaction->amount, '-1');

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -34,18 +34,12 @@ class RequestedNewPassword extends Event
{ {
use SerializesModels; use SerializesModels;
/** /** @var string The users IP address */
* @var string
*/
public $ipAddress; public $ipAddress;
/** /** @var User The user */
* @var string
*/
public $token;
/**
* @var User
*/
public $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. * 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 <?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; namespace FireflyIII\Events;
@@ -16,9 +35,9 @@ class RequestedReportOnJournals
{ {
use Dispatchable, InteractsWithSockets, SerializesModels; use Dispatchable, InteractsWithSockets, SerializesModels;
/** @var Collection */ /** @var Collection The journals to report on. */
public $journals; public $journals;
/** @var int */ /** @var int The ID of the user. */
public $userId; public $userId;
/** /**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -37,41 +37,57 @@ interface ProcessorInterface
public function __construct(); public function __construct();
/** /**
* Collect all attachments.
*
* @return bool * @return bool
*/ */
public function collectAttachments(): bool; public function collectAttachments(): bool;
/** /**
* Collect all journals.
*
* @return bool * @return bool
*/ */
public function collectJournals(): bool; public function collectJournals(): bool;
/** /**
* Collect old uploads.
*
* @return bool * @return bool
*/ */
public function collectOldUploads(): bool; public function collectOldUploads(): bool;
/** /**
* Convert all journals.
*
* @return bool * @return bool
*/ */
public function convertJournals(): bool; public function convertJournals(): bool;
/** /**
* Create a zip file.
*
* @return bool * @return bool
*/ */
public function createZipFile(): bool; public function createZipFile(): bool;
/** /**
* Export journals.
*
* @return bool * @return bool
*/ */
public function exportJournals(): bool; public function exportJournals(): bool;
/** /**
* Get all files.
*
* @return Collection * @return Collection
*/ */
public function getFiles(): Collection; public function getFiles(): Collection;
/** /**
* Set the settings.
*
* @param array $settings * @param array $settings
*/ */
public function setSettings(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/>. * along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @noinspection PhpDynamicAsStaticMethodCallInspection */
/** @noinspection PhpUndefinedMethodInspection */
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\Factory; namespace FireflyIII\Factory;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Services\Internal\Support\AccountServiceTrait; use FireflyIII\Services\Internal\Support\AccountServiceTrait;
@@ -44,56 +48,63 @@ class AccountFactory
* @param array $data * @param array $data
* *
* @return Account * @return Account
* @throws FireflyException
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/ */
public function create(array $data): Account 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']); $data['iban'] = $this->filterIban($data['iban']);
// account may exist already: // account may exist already:
$existingAccount = $this->find($data['name'], $type->type); $return = $this->find($data['name'], $type->type);
if (null !== $existingAccount) {
return $existingAccount;
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'] ?? '');
} }
return $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';
}
$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;
} }
/** /**
@@ -106,15 +117,16 @@ class AccountFactory
{ {
$type = AccountType::whereType($accountType)->first(); $type = AccountType::whereType($accountType)->first();
$accounts = $this->user->accounts()->where('account_type_id', $type->id)->get(['accounts.*']); $accounts = $this->user->accounts()->where('account_type_id', $type->id)->get(['accounts.*']);
$return = null;
/** @var Account $object */ /** @var Account $object */
foreach ($accounts as $object) { foreach ($accounts as $object) {
if ($object->name === $accountName) { if ($object->name === $accountName) {
return $object; $return = $object;
break;
} }
} }
return null; return $return;
} }
/** /**
@@ -122,30 +134,35 @@ class AccountFactory
* @param string $accountType * @param string $accountType
* *
* @return Account * @return Account
* @throws FireflyException
*/ */
public function findOrCreate(string $accountName, string $accountType): Account public function findOrCreate(string $accountName, string $accountType): Account
{ {
$type = AccountType::whereType($accountType)->first(); $type = AccountType::whereType($accountType)->first();
$accounts = $this->user->accounts()->where('account_type_id', $type->id)->get(['accounts.*']); $accounts = $this->user->accounts()->where('account_type_id', $type->id)->get(['accounts.*']);
$return = null;
/** @var Account $object */ /** @var Account $object */
foreach ($accounts as $object) { foreach ($accounts as $object) {
if ($object->name === $accountName) { 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( return $return;
[
'user_id' => $this->user->id,
'name' => $accountName,
'account_type_id' => $type->id,
'accountType' => null,
'virtualBalance' => '0',
'iban' => null,
'active' => true,
]
);
} }
/** /**
@@ -161,18 +178,23 @@ class AccountFactory
* @param null|string $accountType * @param null|string $accountType
* *
* @return AccountType|null * @return AccountType|null
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/ */
protected function getAccountType(?int $accountTypeId, ?string $accountType): ?AccountType protected function getAccountType(?int $accountTypeId, ?string $accountType): ?AccountType
{ {
$accountTypeId = (int)$accountTypeId; $accountTypeId = (int)$accountTypeId;
$result = null;
if ($accountTypeId > 0) { if ($accountTypeId > 0) {
return AccountType::find($accountTypeId); $result = AccountType::find($accountTypeId);
} }
$type = config('firefly.accountTypeByIdentifier.' . (string)$accountType); if (null === $result) {
$result = AccountType::whereType($type)->first(); /** @var string $type */
if (null === $result && null !== $accountType) { $type = (string)config('firefly.accountTypeByIdentifier.' . (string)$accountType);
// try as full name: $result = AccountType::whereType($type)->first();
$result = AccountType::whereType($accountType)->first(); if (null === $result && null !== $accountType) {
// try as full name:
$result = AccountType::whereType($accountType)->first();
}
} }
return $result; return $result;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -27,5 +27,4 @@ namespace FireflyIII\Generator\Report\Category;
*/ */
class YearReportGenerator extends MonthReportGenerator 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