Expand test coverage and improve transaction management code.

This commit is contained in:
James Cole
2019-07-01 20:22:35 +02:00
parent 94acb50a6f
commit 5bbe1eab7c
63 changed files with 1251 additions and 812 deletions

View File

@@ -153,7 +153,7 @@ class BudgetReportController extends Controller
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
if ($cache->has()) { if ($cache->has()) {
//return response()->json($cache->get()); // @codeCoverageIgnore return response()->json($cache->get()); // @codeCoverageIgnore
} }
$format = app('navigation')->preferredCarbonLocalizedFormat($start, $end); $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
$function = app('navigation')->preferredEndOfPeriod($start, $end); $function = app('navigation')->preferredEndOfPeriod($start, $end);

View File

@@ -30,8 +30,10 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Repositories\TransactionType\TransactionTypeRepositoryInterface;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Log; use Log;
@@ -54,7 +56,7 @@ class AutoCompleteController extends Controller
public function accounts(Request $request): JsonResponse public function accounts(Request $request): JsonResponse
{ {
$accountTypes = explode(',', $request->get('types') ?? ''); $accountTypes = explode(',', $request->get('types') ?? '');
$search = $request->get('query'); $search = $request->get('search');
/** @var AccountRepositoryInterface $repository */ /** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class); $repository = app(AccountRepositoryInterface::class);
@@ -92,15 +94,45 @@ class AutoCompleteController extends Controller
} }
/** /**
* Searches in the titles of all transaction journals.
* The result is limited to the top 15 unique results.
*
* @param Request $request
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore
*/ */
public function budgets(): JsonResponse public function allJournals(Request $request): JsonResponse
{ {
$search = (string)$request->get('search');
/** @var JournalRepositoryInterface $repository */
$repository = app(JournalRepositoryInterface::class);
$result = $repository->searchJournalDescriptions($search);
// limit and unique
$filtered = $result->unique('description');
$limited = $filtered->slice(0, 15);
$array = $limited->toArray();
foreach ($array as $index => $item) {
// give another key for consistency
$array[$index]['name'] = $item['description'];
}
return response()->json($array);
}
/**
* @param Request $request
* @return JsonResponse
*/
public function budgets(Request $request): JsonResponse
{
$search = (string)$request->get('search');
/** @var BudgetRepositoryInterface $repository */ /** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class); $repository = app(BudgetRepositoryInterface::class);
$result = $repository->searchBudget($search);
return response()->json($repository->getActiveBudgets()->toArray()); return response()->json($result->toArray());
} }
/** /**
@@ -111,7 +143,7 @@ class AutoCompleteController extends Controller
*/ */
public function categories(Request $request): JsonResponse public function categories(Request $request): JsonResponse
{ {
$query = (string)$request->get('query'); $query = (string)$request->get('search');
/** @var CategoryRepositoryInterface $repository */ /** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class); $repository = app(CategoryRepositoryInterface::class);
$result = $repository->searchCategory($query); $result = $repository->searchCategory($query);
@@ -119,6 +151,44 @@ class AutoCompleteController extends Controller
return response()->json($result->toArray()); return response()->json($result->toArray());
} }
/**
* @param Request $request
* @return JsonResponse
*/
public function currencyNames(Request $request): JsonResponse
{
$query = (string)$request->get('search');
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
$result = $repository->searchCurrency($query)->toArray();
foreach ($result as $index => $item) {
$result[$index]['name'] = sprintf('%s (%s)', $item['name'], $item['code']);
}
return response()->json($result);
}
/**
* @param Request $request
*
* @return JsonResponse
* @codeCoverageIgnore
*/
public function transactionTypes(Request $request): JsonResponse
{
$query = (string)$request->get('search');
/** @var TransactionTypeRepositoryInterface $repository */
$repository = app(TransactionTypeRepositoryInterface::class);
$array = $repository->searchTypes($query)->toArray();
foreach ($array as $index => $item) {
// different key for consistency.
$array[$index]['name'] = $item['type'];
}
return response()->json($array);
}
/** /**
* @return JsonResponse * @return JsonResponse
* @codeCoverageIgnore * @codeCoverageIgnore
@@ -164,13 +234,18 @@ class AutoCompleteController extends Controller
*/ */
public function tags(Request $request): JsonResponse public function tags(Request $request): JsonResponse
{ {
$query = (string)$request->get('query'); $search = (string)$request->get('search');
/** @var TagRepositoryInterface $repository */ /** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class); $repository = app(TagRepositoryInterface::class);
$result = $repository->searchTags($query); $result = $repository->searchTags($search);
$array = $result->toArray();
foreach ($array as $index => $item) {
// rename field for consistency.
$array[$index]['name'] = $item['tag'];
}
return response()->json($result->toArray()); return response()->json($array);
} }
} }

View File

@@ -46,6 +46,7 @@ class CreateController extends Controller
/** /**
* CreateController constructor. * CreateController constructor.
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {

View File

@@ -39,6 +39,7 @@ class DeleteController extends Controller
/** /**
* DeleteController constructor. * DeleteController constructor.
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -79,8 +80,8 @@ class DeleteController extends Controller
* Destroy the recurring transaction. * Destroy the recurring transaction.
* *
* @param RecurringRepositoryInterface $repository * @param RecurringRepositoryInterface $repository
* @param Request $request * @param Request $request
* @param Recurrence $recurrence * @param Recurrence $recurrence
* *
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/ */

View File

@@ -47,6 +47,7 @@ class EditController extends Controller
/** /**
* EditController constructor. * EditController constructor.
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -70,7 +71,7 @@ class EditController extends Controller
/** /**
* Edit a recurring transaction. * Edit a recurring transaction.
* *
* @param Request $request * @param Request $request
* @param Recurrence $recurrence * @param Recurrence $recurrence
* *
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
@@ -92,7 +93,7 @@ class EditController extends Controller
$repetition = $recurrence->recurrenceRepetitions()->first(); $repetition = $recurrence->recurrenceRepetitions()->first();
$currentRepType = $repetition->repetition_type; $currentRepType = $repetition->repetition_type;
if ('' !== $repetition->repetition_moment) { if ('' !== $repetition->repetition_moment) {
$currentRepType .= ',' . $repetition->repetition_moment; $currentRepType .= ',' . $repetition->repetition_moment; // @codeCoverageIgnore
} }
// put previous url in session if not redirect from store (not "return_to_edit"). // put previous url in session if not redirect from store (not "return_to_edit").
@@ -123,9 +124,12 @@ class EditController extends Controller
$hasOldInput = null !== $request->old('_token'); $hasOldInput = null !== $request->old('_token');
$preFilled = [ $preFilled = [
'transaction_type' => strtolower($recurrence->transactionType->type), 'transaction_type' => strtolower($recurrence->transactionType->type),
'active' => $hasOldInput ? (bool)$request->old('active') : $recurrence->active, 'active' => $hasOldInput ? (bool)$request->old('active') : $recurrence->active,
'apply_rules' => $hasOldInput ? (bool)$request->old('apply_rules') : $recurrence->apply_rules, 'apply_rules' => $hasOldInput ? (bool)$request->old('apply_rules') : $recurrence->apply_rules,
'deposit_source_id' => $array['transactions'][0]['source_id'],
'withdrawal_destination_id' => $array['transactions'][0]['destination_id'],
]; ];
return view( return view(
@@ -138,7 +142,7 @@ class EditController extends Controller
* Update the recurring transaction. * Update the recurring transaction.
* *
* @param RecurrenceFormRequest $request * @param RecurrenceFormRequest $request
* @param Recurrence $recurrence * @param Recurrence $recurrence
* *
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @throws \FireflyIII\Exceptions\FireflyException * @throws \FireflyIII\Exceptions\FireflyException

View File

@@ -48,6 +48,7 @@ class IndexController extends Controller
/** /**
* IndexController constructor. * IndexController constructor.
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -121,8 +122,8 @@ class IndexController extends Controller
$transformer = app(RecurrenceTransformer::class); $transformer = app(RecurrenceTransformer::class);
$transformer->setParameters(new ParameterBag); $transformer->setParameters(new ParameterBag);
$array = $transformer->transform($recurrence); $array = $transformer->transform($recurrence);
$transactions = $this->recurring->getTransactions($recurrence); $groups = $this->recurring->getTransactions($recurrence);
// transform dates back to Carbon objects: // transform dates back to Carbon objects:
foreach ($array['recurrence_repetitions'] as $index => $repetition) { foreach ($array['recurrence_repetitions'] as $index => $repetition) {
@@ -133,7 +134,7 @@ class IndexController extends Controller
$subTitle = (string)trans('firefly.overview_for_recurrence', ['title' => $recurrence->title]); $subTitle = (string)trans('firefly.overview_for_recurrence', ['title' => $recurrence->title]);
return view('recurring.show', compact('recurrence', 'subTitle', 'array', 'transactions')); return view('recurring.show', compact('recurrence', 'subTitle', 'array', 'groups'));
} }
} }

View File

@@ -47,6 +47,7 @@ class ExpenseController extends Controller
/** /**
* Constructor for ExpenseController * Constructor for ExpenseController
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -252,11 +253,11 @@ class ExpenseController extends Controller
$cache = new CacheProperties; $cache = new CacheProperties;
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty('expense-budget'); $cache->addProperty('top-expense');
$cache->addProperty($accounts->pluck('id')->toArray()); $cache->addProperty($accounts->pluck('id')->toArray());
$cache->addProperty($expense->pluck('id')->toArray()); $cache->addProperty($expense->pluck('id')->toArray());
if ($cache->has()) { if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore //return $cache->get(); // @codeCoverageIgnore
} }
$combined = $this->combineAccounts($expense); $combined = $this->combineAccounts($expense);
$all = new Collection; $all = new Collection;
@@ -268,11 +269,11 @@ class ExpenseController extends Controller
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($accounts); $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($accounts);
$collector->setAccounts($all); $collector->setAccounts($all)->withAccountInformation();
$set = $collector->getExtractedJournals(); $sorted = $collector->getExtractedJournals();
usort($set, function ($a, $b) { usort($sorted, function ($a, $b) {
return $a['amount'] <=> $b['amount']; return $a['amount'] <=> $b['amount']; // @codeCoverageIgnore
}); });
try { try {
@@ -304,11 +305,11 @@ class ExpenseController extends Controller
$cache = new CacheProperties; $cache = new CacheProperties;
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty('expense-budget'); $cache->addProperty('top-income');
$cache->addProperty($accounts->pluck('id')->toArray()); $cache->addProperty($accounts->pluck('id')->toArray());
$cache->addProperty($expense->pluck('id')->toArray()); $cache->addProperty($expense->pluck('id')->toArray());
if ($cache->has()) { if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore //return $cache->get(); // @codeCoverageIgnore
} }
$combined = $this->combineAccounts($expense); $combined = $this->combineAccounts($expense);
$all = new Collection; $all = new Collection;
@@ -321,11 +322,15 @@ class ExpenseController extends Controller
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$total = $accounts->merge($all); $total = $accounts->merge($all);
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($total); $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($total)->withAccountInformation();
$journals = $collector->getExtractedJournals(); $sorted = $collector->getExtractedJournals();
usort($journals, function ($a, $b) { foreach (array_keys($sorted) as $key) {
return $a['amount'] <=> $b['amount']; $sorted[$key]['amount'] = bcmul($sorted[$key]['amount'], '-1');
}
usort($sorted, function ($a, $b) {
return $a['amount'] <=> $b['amount']; // @codeCoverageIgnore
}); });
try { try {

View File

@@ -41,6 +41,7 @@ class OperationsController extends Controller
/** /**
* OperationsController constructor. * OperationsController constructor.
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {

View File

@@ -45,6 +45,7 @@ class CreateController extends Controller
/** /**
* RuleController constructor. * RuleController constructor.
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {

View File

@@ -39,6 +39,7 @@ class DeleteController extends Controller
/** /**
* RuleController constructor. * RuleController constructor.
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {

View File

@@ -45,6 +45,7 @@ class EditController extends Controller
/** /**
* RuleController constructor. * RuleController constructor.
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {

View File

@@ -45,6 +45,7 @@ class IndexController extends Controller
/** /**
* RuleController constructor. * RuleController constructor.
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {

View File

@@ -76,7 +76,7 @@ class SelectController extends Controller
* Execute the given rule on a set of existing transactions. * Execute the given rule on a set of existing transactions.
* *
* @param SelectTransactionsRequest $request * @param SelectTransactionsRequest $request
* @param Rule $rule * @param Rule $rule
* *
* @return RedirectResponse * @return RedirectResponse
*/ */
@@ -181,11 +181,12 @@ class SelectController extends Controller
// Return json response // Return json response
$view = 'ERROR, see logs.'; $view = 'ERROR, see logs.';
try { try {
$view = view('list.journals-tiny', ['transactions' => $matchingTransactions])->render(); $view = view('list.journals-array-tiny', ['journals' => $matchingTransactions])->render();
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
} catch (Throwable $exception) { } catch (Throwable $exception) {
Log::error(sprintf('Could not render view in testTriggers(): %s', $exception->getMessage())); Log::error(sprintf('Could not render view in testTriggers(): %s', $exception->getMessage()));
Log::error($exception->getTraceAsString()); Log::error($exception->getTraceAsString());
$view = sprintf('Could not render list.journals-tiny: %s', $exception->getMessage());
} }
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
@@ -236,17 +237,17 @@ class SelectController extends Controller
// Warn the user if only a subset of transactions is returned // Warn the user if only a subset of transactions is returned
$warning = ''; $warning = '';
if ($matchingTransactions->count() === $limit) { if (count($matchingTransactions) === $limit) {
$warning = (string)trans('firefly.warning_transaction_subset', ['max_num_transactions' => $limit]); // @codeCoverageIgnore $warning = (string)trans('firefly.warning_transaction_subset', ['max_num_transactions' => $limit]); // @codeCoverageIgnore
} }
if (0 === $matchingTransactions->count()) { if (0 === count($matchingTransactions)) {
$warning = (string)trans('firefly.warning_no_matching_transactions', ['num_transactions' => $range]); // @codeCoverageIgnore $warning = (string)trans('firefly.warning_no_matching_transactions', ['num_transactions' => $range]); // @codeCoverageIgnore
} }
// Return json response // Return json response
$view = 'ERROR, see logs.'; $view = 'ERROR, see logs.';
try { try {
$view = view('list.journals-tiny', ['transactions' => $matchingTransactions])->render(); $view = view('list.journals-array-tiny', ['journals' => $matchingTransactions])->render();
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
} catch (Throwable $exception) { } catch (Throwable $exception) {
Log::error(sprintf('Could not render view in testTriggersByRule(): %s', $exception->getMessage())); Log::error(sprintf('Could not render view in testTriggersByRule(): %s', $exception->getMessage()));

View File

@@ -36,7 +36,31 @@ use Illuminate\Http\Request;
*/ */
class IndexController extends Controller class IndexController extends Controller
{ {
use PeriodOverview; use PeriodOverview;
/** @var JournalRepositoryInterface */
private $repository;
/**
* IndexController constructor.
* @codeCoverageIgnore
*/
public function __construct()
{
parent::__construct();
// translations:
$this->middleware(
function ($request, $next) {
app('view')->share('mainTitleIcon', 'fa-credit-card');
app('view')->share('title', (string)trans('firefly.accounts'));
$this->repository = app(JournalRepositoryInterface::class);
return $next($request);
}
);
}
/** /**
* Index for a range of transactions. * Index for a range of transactions.
@@ -54,6 +78,7 @@ class IndexController extends Controller
$types = config('firefly.transactionTypesByType.' . $objectType); $types = config('firefly.transactionTypesByType.' . $objectType);
$page = (int)$request->get('page'); $page = (int)$request->get('page');
$pageSize = (int)app('preferences')->get('listPageSize', 50)->data; $pageSize = (int)app('preferences')->get('listPageSize', 50)->data;
$pageSize =3;
if (null === $start) { if (null === $start) {
$start = session('start'); $start = session('start');
$end = session('end'); $end = session('end');
@@ -62,16 +87,16 @@ class IndexController extends Controller
$end = session('end'); $end = session('end');
} }
if ($end < $start) { [$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
[$start, $end] = [$end, $start]; $path = route('transactions.index', [$objectType, $start->format('Y-m-d'), $end->format('Y-m-d')]);
}
$path = route('transactions.index', [$objectType, $start->format('Y-m-d'), $end->format('Y-m-d')]);
$startStr = $start->formatLocalized($this->monthAndDayFormat); $startStr = $start->formatLocalized($this->monthAndDayFormat);
$endStr = $end->formatLocalized($this->monthAndDayFormat); $endStr = $end->formatLocalized($this->monthAndDayFormat);
$subTitle = (string)trans(sprintf('firefly.title_%s_between', $objectType), ['start' => $startStr, 'end' => $endStr]); $subTitle = (string)trans(sprintf('firefly.title_%s_between', $objectType), ['start' => $startStr, 'end' => $endStr]);
$periods = $this->getTransactionPeriodOverview($objectType, $end);
$firstJournal = $this->repository->firstNull();
$startPeriod = null === $firstJournal ? new Carbon : $firstJournal->date;
$endPeriod = clone $end;
$periods = $this->getTransactionPeriodOverview($objectType, $startPeriod, $endPeriod);
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);

View File

@@ -53,10 +53,10 @@ class StartFireflySession extends StartSession
&& 'GET' === $request->method() && 'GET' === $request->method()
&& !$request->ajax()) { && !$request->ajax()) {
$session->setPreviousUrl($uri); $session->setPreviousUrl($uri);
Log::debug(sprintf('Will set previous URL to %s', $uri)); //Log::debug(sprintf('Will set previous URL to %s', $uri));
return; return;
} }
Log::debug(sprintf('Will NOT set previous URL to %s', $uri)); //Log::debug(sprintf('Will NOT set previous URL to %s', $uri));
} }
} }

View File

@@ -29,6 +29,9 @@ use FireflyIII\Models\Recurrence;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Rules\ValidRecurrenceRepetitionType; use FireflyIII\Rules\ValidRecurrenceRepetitionType;
use FireflyIII\Rules\ValidRecurrenceRepetitionValue; use FireflyIII\Rules\ValidRecurrenceRepetitionValue;
use FireflyIII\Validation\AccountValidator;
use Illuminate\Validation\Validator;
use Log;
/** /**
* Class RecurrenceFormRequest * Class RecurrenceFormRequest
@@ -136,6 +139,86 @@ class RecurrenceFormRequest extends Request
return $return; return $return;
} }
/**
* Configure the validator instance with special rules for after the basic validation rules.
*
* @param Validator $validator
*
* @return void
*/
public function withValidator(Validator $validator): void
{
$validator->after(
function (Validator $validator) {
// validate all account info
$this->validateAccountInformation($validator);
}
);
}
/**
* Validates the given account information. Switches on given transaction type.
*
* @param Validator $validator
* @throws FireflyException
*/
public function validateAccountInformation(Validator $validator): void
{
Log::debug('Now in validateAccountInformation()');
/** @var AccountValidator $accountValidator */
$accountValidator = app(AccountValidator::class);
$data = $validator->getData();
$transactionType = $data['transaction_type'] ?? 'invalid';
$accountValidator->setTransactionType($transactionType);
// default values:
$sourceId = null;
$destinationId = null;
switch ($this->string('transaction_type')) {
default:
throw new FireflyException(sprintf('Cannot handle transaction type "%s"', $this->string('transaction_type'))); // @codeCoverageIgnore
case 'withdrawal':
$sourceId = (int)$data['source_id'];
$destinationId = (int)$data['withdrawal_destination_id'];
break;
case 'deposit':
$sourceId = (int)$data['deposit_source_id'];
$destinationId = (int)$data['destination_id'];
break;
case 'transfer':
$sourceId = (int)$data['source_id'];
$destinationId = (int)$data['destination_id'];
break;
}
// validate source account.
$validSource = $accountValidator->validateSource($sourceId, null);
// do something with result:
if (false === $validSource) {
$message = (string)trans('validation.generic_invalid_source');
$validator->errors()->add('source_id', $message);
$validator->errors()->add('deposit_source_id', $message);
return;
}
// validate destination account
$validDestination = $accountValidator->validateDestination($destinationId, null);
// do something with result:
if (false === $validDestination) {
$message = (string)trans('validation.generic_invalid_destination');
$validator->errors()->add('destination_id', $message);
$validator->errors()->add('withdrawal_destination_id', $message);
return;
}
}
/** /**
* The rules for this request. * The rules for this request.
* *

View File

@@ -647,9 +647,13 @@ class BudgetRepository implements BudgetRepositoryInterface
*/ */
public function searchBudget(string $query): Collection public function searchBudget(string $query): Collection
{ {
$query = sprintf('%%%s%%', $query);
return $this->user->budgets()->where('name', 'LIKE', $query)->get(); $search = $this->user->budgets();
if ('' !== $query) {
$search->where('name', 'LIKE', sprintf('%%%s%%', $query));
}
return $search->get();
} }
/** /**

View File

@@ -111,22 +111,6 @@ class CategoryRepository implements CategoryRepositoryInterface
return $collector->getExtractedJournals(); return $collector->getExtractedJournals();
} }
/**
* @param array $journals
* @return string
*/
private function sumJournals(array $journals): string
{
$sum = '0';
/** @var array $journal */
foreach ($journals as $journal) {
$amount = (string)$journal['amount'];
$sum = bcadd($sum, $amount);
}
return $sum;
}
/** /**
* A very cryptic method name that means: * A very cryptic method name that means:
* *
@@ -156,7 +140,7 @@ class CategoryRepository implements CategoryRepositoryInterface
$currencyId = (int)$journal['currency_id']; $currencyId = (int)$journal['currency_id'];
if (!isset($return[$currencyId])) { if (!isset($return[$currencyId])) {
$return[$currencyId] = [ $return[$currencyId] = [
'earned' => '0', 'earned' => '0',
'currency_id' => $currencyId, 'currency_id' => $currencyId,
'currency_symbol' => $journal['currency_symbol'], 'currency_symbol' => $journal['currency_symbol'],
'currency_code' => $journal['currency_code'], 'currency_code' => $journal['currency_code'],
@@ -264,8 +248,6 @@ class CategoryRepository implements CategoryRepositoryInterface
return $result; return $result;
} }
/** @noinspection MoreThanThreeArgumentsInspection */
/** /**
* Find a category or return NULL * Find a category or return NULL
* *
@@ -278,6 +260,8 @@ class CategoryRepository implements CategoryRepositoryInterface
return $this->user->categories()->find($categoryId); return $this->user->categories()->find($categoryId);
} }
/** @noinspection MoreThanThreeArgumentsInspection */
/** /**
* Find a category. * Find a category.
* *
@@ -297,8 +281,6 @@ class CategoryRepository implements CategoryRepositoryInterface
return null; return null;
} }
/** @noinspection MoreThanThreeArgumentsInspection */
/** /**
* @param array $data * @param array $data
* *
@@ -313,6 +295,8 @@ class CategoryRepository implements CategoryRepositoryInterface
return $factory->findOrCreate(null, $data['name']); return $factory->findOrCreate(null, $data['name']);
} }
/** @noinspection MoreThanThreeArgumentsInspection */
/** /**
* @param Category $category * @param Category $category
* *
@@ -341,47 +325,6 @@ class CategoryRepository implements CategoryRepositoryInterface
return $firstJournalDate; return $firstJournalDate;
} }
/**
* @param Category $category
*
* @return Carbon|null
*/
private function getFirstJournalDate(Category $category): ?Carbon
{
$query = $category->transactionJournals()->orderBy('date', 'ASC');
$result = $query->first(['transaction_journals.*']);
if (null !== $result) {
return $result->date;
}
return null;
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* @param Category $category
*
* @return Carbon|null
*/
private function getFirstTransactionDate(Category $category): ?Carbon
{
// check transactions:
$query = $category->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->orderBy('transaction_journals.date', 'ASC');
$lastTransaction = $query->first(['transaction_journals.*']);
if (null !== $lastTransaction) {
return new Carbon($lastTransaction->date);
}
return null;
}
/** @noinspection MoreThanThreeArgumentsInspection */
/** /**
* Get all categories with ID's. * Get all categories with ID's.
* *
@@ -394,8 +337,6 @@ class CategoryRepository implements CategoryRepositoryInterface
return $this->user->categories()->whereIn('id', $categoryIds)->get(); return $this->user->categories()->whereIn('id', $categoryIds)->get();
} }
/** @noinspection MoreThanThreeArgumentsInspection */
/** /**
* @param Category $category * @param Category $category
* @param Collection $accounts * @param Collection $accounts
@@ -426,55 +367,7 @@ class CategoryRepository implements CategoryRepositoryInterface
return $lastJournalDate; return $lastJournalDate;
} }
/** /** @noinspection MoreThanThreeArgumentsInspection */
* @param Category $category
* @param Collection $accounts
*
* @return Carbon|null
*/
private function getLastJournalDate(Category $category, Collection $accounts): ?Carbon
{
$query = $category->transactionJournals()->orderBy('date', 'DESC');
if ($accounts->count() > 0) {
$query->leftJoin('transactions as t', 't.transaction_journal_id', '=', 'transaction_journals.id');
$query->whereIn('t.account_id', $accounts->pluck('id')->toArray());
}
$result = $query->first(['transaction_journals.*']);
if (null !== $result) {
return $result->date;
}
return null;
}
/**
* @param Category $category
* @param Collection $accounts
*
* @return Carbon|null
* @throws \Exception
*/
private function getLastTransactionDate(Category $category, Collection $accounts): ?Carbon
{
// check transactions:
$query = $category->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->orderBy('transaction_journals.date', 'DESC');
if ($accounts->count() > 0) {
// filter journals:
$query->whereIn('transactions.account_id', $accounts->pluck('id')->toArray());
}
$lastTransaction = $query->first(['transaction_journals.*']);
if (null !== $lastTransaction) {
return new Carbon($lastTransaction->date);
}
return null;
}
/** /**
* @param Collection $categories * @param Collection $categories
@@ -518,6 +411,8 @@ class CategoryRepository implements CategoryRepositoryInterface
return $data; return $data;
} }
/** @noinspection MoreThanThreeArgumentsInspection */
/** /**
* @param Collection $accounts * @param Collection $accounts
* @param Carbon $start * @param Carbon $start
@@ -555,6 +450,8 @@ class CategoryRepository implements CategoryRepositoryInterface
return $result; return $result;
} }
/** @noinspection MoreThanThreeArgumentsInspection */
/** /**
* @param Collection $categories * @param Collection $categories
* @param Collection $accounts * @param Collection $accounts
@@ -644,9 +541,12 @@ class CategoryRepository implements CategoryRepositoryInterface
*/ */
public function searchCategory(string $query): Collection public function searchCategory(string $query): Collection
{ {
$query = sprintf('%%%s%%', $query); $search = $this->user->categories();
if ('' !== $query) {
$search->where('name', 'LIKE', sprintf('%%%s%%', $query));
}
return $this->user->categories()->where('name', 'LIKE', $query)->get(); return $search->get();
} }
/** /**
@@ -831,4 +731,107 @@ class CategoryRepository implements CategoryRepositoryInterface
return $service->update($category, $data); return $service->update($category, $data);
} }
/**
* @param array $journals
* @return string
*/
private function sumJournals(array $journals): string
{
$sum = '0';
/** @var array $journal */
foreach ($journals as $journal) {
$amount = (string)$journal['amount'];
$sum = bcadd($sum, $amount);
}
return $sum;
}
/**
* @param Category $category
*
* @return Carbon|null
*/
private function getFirstJournalDate(Category $category): ?Carbon
{
$query = $category->transactionJournals()->orderBy('date', 'ASC');
$result = $query->first(['transaction_journals.*']);
if (null !== $result) {
return $result->date;
}
return null;
}
/**
* @param Category $category
*
* @return Carbon|null
*/
private function getFirstTransactionDate(Category $category): ?Carbon
{
// check transactions:
$query = $category->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->orderBy('transaction_journals.date', 'ASC');
$lastTransaction = $query->first(['transaction_journals.*']);
if (null !== $lastTransaction) {
return new Carbon($lastTransaction->date);
}
return null;
}
/**
* @param Category $category
* @param Collection $accounts
*
* @return Carbon|null
*/
private function getLastJournalDate(Category $category, Collection $accounts): ?Carbon
{
$query = $category->transactionJournals()->orderBy('date', 'DESC');
if ($accounts->count() > 0) {
$query->leftJoin('transactions as t', 't.transaction_journal_id', '=', 'transaction_journals.id');
$query->whereIn('t.account_id', $accounts->pluck('id')->toArray());
}
$result = $query->first(['transaction_journals.*']);
if (null !== $result) {
return $result->date;
}
return null;
}
/**
* @param Category $category
* @param Collection $accounts
*
* @return Carbon|null
* @throws \Exception
*/
private function getLastTransactionDate(Category $category, Collection $accounts): ?Carbon
{
// check transactions:
$query = $category->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->orderBy('transaction_journals.date', 'DESC');
if ($accounts->count() > 0) {
// filter journals:
$query->whereIn('transactions.account_id', $accounts->pluck('id')->toArray());
}
$lastTransaction = $query->first(['transaction_journals.*']);
if (null !== $lastTransaction) {
return new Carbon($lastTransaction->date);
}
return null;
}
} }

View File

@@ -258,7 +258,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
/** /**
* Find by object, ID or code. Returns user default or system default. * Find by object, ID or code. Returns user default or system default.
* *
* @param int|null $currencyId * @param int|null $currencyId
* @param string|null $currencyCode * @param string|null $currencyCode
* *
* @return TransactionCurrency|null * @return TransactionCurrency|null
@@ -288,7 +288,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
/** /**
* Find by object, ID or code. Returns NULL if nothing found. * Find by object, ID or code. Returns NULL if nothing found.
* *
* @param int|null $currencyId * @param int|null $currencyId
* @param string|null $currencyCode * @param string|null $currencyCode
* *
* @return TransactionCurrency|null * @return TransactionCurrency|null
@@ -369,7 +369,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
* *
* @param TransactionCurrency $fromCurrency * @param TransactionCurrency $fromCurrency
* @param TransactionCurrency $toCurrency * @param TransactionCurrency $toCurrency
* @param Carbon $date * @param Carbon $date
* *
* @return CurrencyExchangeRate|null * @return CurrencyExchangeRate|null
*/ */
@@ -438,7 +438,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
/** /**
* @param TransactionCurrency $currency * @param TransactionCurrency $currency
* @param array $data * @param array $data
* *
* @return TransactionCurrency * @return TransactionCurrency
*/ */
@@ -449,4 +449,18 @@ class CurrencyRepository implements CurrencyRepositoryInterface
return $service->update($currency, $data); return $service->update($currency, $data);
} }
/**
* @param string $search
* @return Collection
*/
public function searchCurrency(string $search): Collection
{
$query = TransactionCurrency::where('enabled', 1);
if ('' !== $search) {
$query->where('name', 'LIKE', sprintf('%%%s%%', $search));
}
return $query->get();
}
} }

View File

@@ -34,6 +34,12 @@ use Illuminate\Support\Collection;
*/ */
interface CurrencyRepositoryInterface interface CurrencyRepositoryInterface
{ {
/**
* @param string $search
* @return Collection
*/
public function searchCurrency(string $search): Collection;
/** /**
* @param TransactionCurrency $currency * @param TransactionCurrency $currency
* *

View File

@@ -54,6 +54,8 @@ use stdClass;
*/ */
class JournalRepository implements JournalRepositoryInterface class JournalRepository implements JournalRepositoryInterface
{ {
/** @var User */ /** @var User */
private $user; private $user;
@@ -67,7 +69,25 @@ class JournalRepository implements JournalRepositoryInterface
} }
} }
/**
* Search in journal descriptions.
*
* @param string $search
* @return Collection
*/
public function searchJournalDescriptions(string $search): Collection
{
$query = $this->user->transactionJournals()
->orderBy('date', 'DESC');
if ('' !== $query) {
$query->where('description', 'LIKE', sprintf('%%%s%%', $search));
}
return $query->get();
}
/** @noinspection MoreThanThreeArgumentsInspection */ /** @noinspection MoreThanThreeArgumentsInspection */
/** /**
* @param TransactionJournal $journal * @param TransactionJournal $journal
* @param TransactionType $type * @param TransactionType $type

View File

@@ -41,6 +41,14 @@ use Illuminate\Support\MessageBag;
interface JournalRepositoryInterface interface JournalRepositoryInterface
{ {
/**
* Search in journal descriptions.
*
* @param string $search
* @return Collection
*/
public function searchJournalDescriptions(string $search): Collection;
/** /**
* Get all transaction journals with a specific type, regardless of user. * Get all transaction journals with a specific type, regardless of user.
* *

View File

@@ -303,13 +303,11 @@ class RecurringRepository implements RecurringRepositoryInterface
} }
/** /**
* TODO check usage and verify it still works.
*
* @param Recurrence $recurrence * @param Recurrence $recurrence
* *
* @return Collection * @return Collection
*/ */
public function getTransactions(Recurrence $recurrence): array public function getTransactions(Recurrence $recurrence): Collection
{ {
$journalMeta = TransactionJournalMeta $journalMeta = TransactionJournalMeta
::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id') ::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id')
@@ -319,9 +317,17 @@ class RecurringRepository implements RecurringRepositoryInterface
->where('data', json_encode((string)$recurrence->id)) ->where('data', json_encode((string)$recurrence->id))
->get()->pluck('transaction_journal_id')->toArray(); ->get()->pluck('transaction_journal_id')->toArray();
$search = []; $search = [];
foreach ($journalMeta as $journalId) { foreach ($journalMeta as $journalId) {
$search[] = (int)$journalId; $search[] = (int)$journalId;
} }
if (0 === count($search)) {
return [];
}
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
@@ -330,7 +336,7 @@ class RecurringRepository implements RecurringRepositoryInterface
// filter on specific journals. // filter on specific journals.
$collector->setJournalIds($search); $collector->setJournalIds($search);
return $collector->getExtractedJournals(); return $collector->getGroups();
} }
/** /**

View File

@@ -141,9 +141,9 @@ interface RecurringRepositoryInterface
/** /**
* @param Recurrence $recurrence * @param Recurrence $recurrence
* *
* @return array * @return Collection
*/ */
public function getTransactions(Recurrence $recurrence): array; public function getTransactions(Recurrence $recurrence): Collection;
/** /**
* Calculate the next X iterations starting on the date given in $date. * Calculate the next X iterations starting on the date given in $date.

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\TransactionType; namespace FireflyIII\Repositories\TransactionType;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use Illuminate\Support\Collection;
use Log; use Log;
/** /**
@@ -46,7 +47,7 @@ class TransactionTypeRepository implements TransactionTypeRepositoryInterface
/** /**
* @param TransactionType|null $type * @param TransactionType|null $type
* @param string|null $typeString * @param string|null $typeString
* *
* @return TransactionType * @return TransactionType
*/ */
@@ -67,4 +68,17 @@ class TransactionTypeRepository implements TransactionTypeRepositoryInterface
return $search; return $search;
} }
/**
* @param string $query
* @return Collection
*/
public function searchTypes(string $query): Collection
{
if ('' === $query) {
return TransactionType::get();
}
return TransactionType::where('type', 'LIKE', sprintf('%%%s%%', $query))->get();
}
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\TransactionType; namespace FireflyIII\Repositories\TransactionType;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use Illuminate\Support\Collection;
/** /**
* Interface TransactionTypeRepositoryInterface * Interface TransactionTypeRepositoryInterface
@@ -44,4 +45,10 @@ interface TransactionTypeRepositoryInterface
* @return TransactionType|null * @return TransactionType|null
*/ */
public function findByType(string $type): ?TransactionType; public function findByType(string $type): ?TransactionType;
/**
* @param string $query
* @return Collection
*/
public function searchTypes(string $query): Collection;
} }

View File

@@ -36,7 +36,7 @@ class AccountList implements BinderInterface
/** /**
* @param string $value * @param string $value
* @param Route $route * @param Route $route
* *
* @return Collection * @return Collection
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
@@ -44,25 +44,29 @@ class AccountList implements BinderInterface
*/ */
public static function routeBinder(string $value, Route $route): Collection public static function routeBinder(string $value, Route $route): Collection
{ {
Log::debug(sprintf('Now in AccountList::routeBinder("%s")', $value));
if (auth()->check()) { if (auth()->check()) {
Log::debug('User is logged in.');
$collection = new Collection; $collection = new Collection;
if ('allAssetAccounts' === $value) { if ('allAssetAccounts' === $value) {
/** @var \Illuminate\Support\Collection $collection */ /** @var Collection $collection */
$collection = auth()->user()->accounts() $collection = auth()->user()->accounts()
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->where('account_types.type', AccountType::ASSET) ->where('account_types.type', AccountType::ASSET)
->orderBy('accounts.name', 'ASC') ->orderBy('accounts.name', 'ASC')
->get(['accounts.*']); ->get(['accounts.*']);
Log::debug(sprintf('Collection length is %d', $collection->count()));
} }
if ('allAssetAccounts' !== $value) { if ('allAssetAccounts' !== $value) {
$incoming = array_map('\intval', explode(',', $value)); $incoming = array_map('\intval', explode(',', $value));
$list = array_merge(array_unique($incoming), [0]); $list = array_merge(array_unique($incoming), [0]);
/** @var \Illuminate\Support\Collection $collection */ /** @var Collection $collection */
$collection = auth()->user()->accounts() $collection = auth()->user()->accounts()
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->whereIn('accounts.id', $list) ->whereIn('accounts.id', $list)
->orderBy('accounts.name', 'ASC') ->orderBy('accounts.name', 'ASC')
->get(['accounts.*']); ->get(['accounts.*']);
Log::debug(sprintf('Collection length is %d', $collection->count()));
} }
if ($collection->count() > 0) { if ($collection->count() > 0) {

View File

@@ -25,6 +25,7 @@ namespace FireflyIII\Support\Binder;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
use Illuminate\Routing\Route; use Illuminate\Routing\Route;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/** /**
@@ -34,7 +35,7 @@ class BudgetList implements BinderInterface
{ {
/** /**
* @param string $value * @param string $value
* @param Route $route * @param Route $route
* *
* @return Collection * @return Collection
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
@@ -42,27 +43,36 @@ class BudgetList implements BinderInterface
*/ */
public static function routeBinder(string $value, Route $route): Collection public static function routeBinder(string $value, Route $route): Collection
{ {
Log::debug(sprintf('Now in BudgetList::routeBinder("%s")', $value));
if (auth()->check()) { if (auth()->check()) {
$list = array_unique(array_map('\intval', explode(',', $value))); $list = array_unique(array_map('\intval', explode(',', $value)));
Log::debug('List is now', $list);
if (0 === count($list)) { if (0 === count($list)) {
Log::warning('List count is zero, return 404.');
throw new NotFoundHttpException; // @codeCoverageIgnore throw new NotFoundHttpException; // @codeCoverageIgnore
} }
/** @var \Illuminate\Support\Collection $collection */ /** @var Collection $collection */
$collection = auth()->user()->budgets() $collection = auth()->user()->budgets()
->where('active', 1) ->where('active', 1)
->whereIn('id', $list) ->whereIn('id', $list)
->get(); ->get();
Log::debug(sprintf('Found %d active budgets', $collection->count()), $list);
// add empty budget if applicable. // add empty budget if applicable.
if (in_array(0, $list, true)) { if (in_array(0, $list, true)) {
Log::debug('Add empty budget because $list contains 0.');
$collection->push(new Budget); $collection->push(new Budget);
} }
if ($collection->count() > 0) { if ($collection->count() > 0) {
Log::debug(sprintf('List length is > 0 (%d), so return it.', $collection->count()));
return $collection; return $collection;
} }
Log::debug('List length is zero, fall back to 404.');
} }
Log::debug('Final fallback to 404.');
throw new NotFoundHttpException; throw new NotFoundHttpException;
} }
} }

View File

@@ -102,10 +102,7 @@ class ExpandedForm
{ {
// make repositories // make repositories
/** @var AccountRepositoryInterface $repository */ /** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class); $repository = app(AccountRepositoryInterface::class);
/** @var CurrencyRepositoryInterface $currencyRepos */
$currencyRepos = app(CurrencyRepositoryInterface::class);
$accountList = $repository->getActiveAccountsByType( $accountList = $repository->getActiveAccountsByType(
[AccountType::ASSET, AccountType::DEFAULT, AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN,] [AccountType::ASSET, AccountType::DEFAULT, AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN,]
); );
@@ -113,12 +110,15 @@ class ExpandedForm
$defaultCurrency = app('amount')->getDefaultCurrency(); $defaultCurrency = app('amount')->getDefaultCurrency();
$grouped = []; $grouped = [];
// group accounts: // group accounts:
/** @var Account $account */ /** @var Account $account */
foreach ($accountList as $account) { foreach ($accountList as $account) {
$balance = app('steam')->balance($account, new Carbon); $balance = app('steam')->balance($account, new Carbon);
$currencyId = (int)$repository->getMetaValue($account, 'currency_id');
$currency = $currencyRepos->findNull($currencyId); $currency = $repository->getAccountCurrency($account) ?? $defaultCurrency;
$role = $repository->getMetaValue($account, 'account_role');
$role = $repository->getMetaValue($account, 'account_role');
if ('' === $role && !in_array($account->accountType->type, $liabilityTypes, true)) { if ('' === $role && !in_array($account->accountType->type, $liabilityTypes, true)) {
$role = 'no_account_type'; // @codeCoverageIgnore $role = 'no_account_type'; // @codeCoverageIgnore
} }
@@ -126,15 +126,11 @@ class ExpandedForm
if (in_array($account->accountType->type, $liabilityTypes, true)) { if (in_array($account->accountType->type, $liabilityTypes, true)) {
$role = 'l_' . $account->accountType->type; // @codeCoverageIgnore $role = 'l_' . $account->accountType->type; // @codeCoverageIgnore
} }
if (null === $currency) {
$currency = $defaultCurrency;
}
$key = (string)trans('firefly.opt_group_' . $role); $key = (string)trans('firefly.opt_group_' . $role);
$grouped[$key][$account->id] = $account->name . ' (' . app('amount')->formatAnything($currency, $balance, false) . ')'; $grouped[$key][$account->id] = $account->name . ' (' . app('amount')->formatAnything($currency, $balance, false) . ')';
} }
return $this->select($name, $grouped, $value, $options); return $this->select($name, $grouped, $value, $options);
} }
@@ -152,8 +148,6 @@ class ExpandedForm
// make repositories // make repositories
/** @var AccountRepositoryInterface $repository */ /** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class); $repository = app(AccountRepositoryInterface::class);
/** @var CurrencyRepositoryInterface $currencyRepos */
$currencyRepos = app(CurrencyRepositoryInterface::class);
$accountList = $repository->getActiveAccountsByType( $accountList = $repository->getActiveAccountsByType(
[ [
@@ -176,10 +170,9 @@ class ExpandedForm
// group accounts: // group accounts:
/** @var Account $account */ /** @var Account $account */
foreach ($accountList as $account) { foreach ($accountList as $account) {
$balance = app('steam')->balance($account, new Carbon); $balance = app('steam')->balance($account, new Carbon);
$currencyId = (int)$repository->getMetaValue($account, 'currency_id'); $currency = $repository->getAccountCurrency($account) ?? $defaultCurrency;
$currency = $currencyRepos->findNull($currencyId); $role = (string)$repository->getMetaValue($account, 'account_role');
$role = (string)$repository->getMetaValue($account, 'account_role');
if ('' === $role && !in_array($account->accountType->type, $liabilityTypes, true)) { if ('' === $role && !in_array($account->accountType->type, $liabilityTypes, true)) {
$role = 'no_account_type'; // @codeCoverageIgnore $role = 'no_account_type'; // @codeCoverageIgnore
} }
@@ -217,8 +210,6 @@ class ExpandedForm
// make repositories // make repositories
/** @var AccountRepositoryInterface $repository */ /** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class); $repository = app(AccountRepositoryInterface::class);
/** @var CurrencyRepositoryInterface $currencyRepos */
$currencyRepos = app(CurrencyRepositoryInterface::class);
$accountList = $repository->getActiveAccountsByType( $accountList = $repository->getActiveAccountsByType(
[ [
@@ -241,10 +232,9 @@ class ExpandedForm
// group accounts: // group accounts:
/** @var Account $account */ /** @var Account $account */
foreach ($accountList as $account) { foreach ($accountList as $account) {
$balance = app('steam')->balance($account, new Carbon); $balance = app('steam')->balance($account, new Carbon);
$currencyId = (int)$repository->getMetaValue($account, 'currency_id'); $currency = $repository->getAccountCurrency($account) ?? $defaultCurrency;
$currency = $currencyRepos->findNull($currencyId); $role = (string)$repository->getMetaValue($account, 'account_role');
$role = (string)$repository->getMetaValue($account, 'account_role');
if ('' === $role && !in_array($account->accountType->type, $liabilityTypes, true)) { if ('' === $role && !in_array($account->accountType->type, $liabilityTypes, true)) {
$role = 'no_account_type'; // @codeCoverageIgnore $role = 'no_account_type'; // @codeCoverageIgnore
} }
@@ -257,10 +247,6 @@ class ExpandedForm
$role = 'l_' . $account->accountType->type; // @codeCoverageIgnore $role = 'l_' . $account->accountType->type; // @codeCoverageIgnore
} }
if (null === $currency) {
$currency = $defaultCurrency;
}
$key = (string)trans('firefly.opt_group_' . $role); $key = (string)trans('firefly.opt_group_' . $role);
$grouped[$key][$account->id] = $account->name . ' (' . app('amount')->formatAnything($currency, $balance, false) . ')'; $grouped[$key][$account->id] = $account->name . ' (' . app('amount')->formatAnything($currency, $balance, false) . ')';
} }

View File

@@ -30,7 +30,6 @@ use FireflyIII\Models\Category;
use FireflyIII\Models\Tag; use FireflyIII\Models\Tag;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@@ -81,10 +80,7 @@ trait PeriodOverview
protected function getAccountPeriodOverview(Account $account, Carbon $start, Carbon $end): array protected function getAccountPeriodOverview(Account $account, Carbon $start, Carbon $end): array
{ {
$range = app('preferences')->get('viewRange', '1M')->data; $range = app('preferences')->get('viewRange', '1M')->data;
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
if ($end < $start) {
[$start, $end] = [$end, $start]; // @codeCoverageIgnore
}
// properties for cache // properties for cache
$cache = new CacheProperties; $cache = new CacheProperties;
@@ -159,10 +155,7 @@ trait PeriodOverview
protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array
{ {
$range = app('preferences')->get('viewRange', '1M')->data; $range = app('preferences')->get('viewRange', '1M')->data;
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
if ($end < $start) {
[$start, $end] = [$end, $start]; // @codeCoverageIgnore
}
// properties for entries with their amounts. // properties for entries with their amounts.
$cache = new CacheProperties(); $cache = new CacheProperties();
@@ -173,7 +166,7 @@ trait PeriodOverview
$cache->addProperty($category->id); $cache->addProperty($category->id);
if ($cache->has()) { if ($cache->has()) {
//return $cache->get(); // @codeCoverageIgnore return $cache->get(); // @codeCoverageIgnore
} }
/** @var array $dates */ /** @var array $dates */
$dates = app('navigation')->blockPeriods($start, $end, $range); $dates = app('navigation')->blockPeriods($start, $end, $range);
@@ -240,9 +233,7 @@ trait PeriodOverview
{ {
$range = app('preferences')->get('viewRange', '1M')->data; $range = app('preferences')->get('viewRange', '1M')->data;
if ($end < $start) { [$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
[$start, $end] = [$end, $start]; // @codeCoverageIgnore
}
$cache = new CacheProperties; $cache = new CacheProperties;
$cache->addProperty($start); $cache->addProperty($start);
@@ -250,7 +241,7 @@ trait PeriodOverview
$cache->addProperty('no-budget-period-entries'); $cache->addProperty('no-budget-period-entries');
if ($cache->has()) { if ($cache->has()) {
//return $cache->get(); // @codeCoverageIgnore return $cache->get(); // @codeCoverageIgnore
} }
/** @var array $dates */ /** @var array $dates */
@@ -284,6 +275,8 @@ trait PeriodOverview
} }
/** /**
* TODO fix date.
*
* Show period overview for no category view. * Show period overview for no category view.
* *
* @param Carbon $theDate * @param Carbon $theDate
@@ -309,7 +302,7 @@ trait PeriodOverview
$cache->addProperty('no-category-period-entries'); $cache->addProperty('no-category-period-entries');
if ($cache->has()) { if ($cache->has()) {
//return $cache->get(); // @codeCoverageIgnore return $cache->get(); // @codeCoverageIgnore
} }
$dates = app('navigation')->blockPeriods($start, $end, $range); $dates = app('navigation')->blockPeriods($start, $end, $range);
@@ -379,11 +372,7 @@ trait PeriodOverview
/** @var Carbon $end */ /** @var Carbon $end */
$start = clone $date; $start = clone $date;
$end = $repository->firstUseDate($tag) ?? new Carbon; $end = $repository->firstUseDate($tag) ?? new Carbon;
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
if ($end < $start) {
[$start, $end] = [$end, $start]; // @codeCoverageIgnore
}
// properties for entries with their amounts. // properties for entries with their amounts.
$cache = new CacheProperties; $cache = new CacheProperties;
@@ -431,63 +420,60 @@ trait PeriodOverview
* @param string $transactionType * @param string $transactionType
* @param Carbon $endDate * @param Carbon $endDate
* *
* @return Collection * @return array
*/ */
protected function getTransactionPeriodOverview(string $transactionType, Carbon $endDate): Collection protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array
{ {
die('not yet complete'); $range = app('preferences')->get('viewRange', '1M')->data;
/** @var JournalRepositoryInterface $repository */ $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType));
$repository = app(JournalRepositoryInterface::class); [$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
$range = app('preferences')->get('viewRange', '1M')->data;
$endJournal = $repository->firstNull();
$end = null === $endJournal ? new Carbon : $endJournal->date;
$start = clone $endDate;
$types = config('firefly.transactionTypesByType.' . $transactionType);
if ($end < $start) { // properties for cache
[$start, $end] = [$end, $start]; // @codeCoverageIgnore $cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('transactions-period-entries');
$cache->addProperty($transactionType);
if ($cache->has()) {
// return $cache->get(); // @codeCoverageIgnore
} }
/** @var array $dates */ /** @var array $dates */
$dates = app('navigation')->blockPeriods($start, $end, $range); $dates = app('navigation')->blockPeriods($start, $end, $range);
$entries = new Collection; $entries = [];
// collect all journals in this period (regardless of type)
$collector = app(GroupCollectorInterface::class);
$collector->setTypes($types)->setRange($start, $end);
$genericSet = $collector->getExtractedJournals();
foreach ($dates as $currentDate) { foreach ($dates as $currentDate) {
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setTypes($types)->setRange($currentDate['start'], $currentDate['end']);
$journals = $collector->getExtractedJournals();
$amounts = $this->getJournalsSum($journals);
$spent = []; $spent = [];
$earned = []; $earned = [];
$transferred = []; $transferred = [];
$title = app('navigation')->periodShow($currentDate['end'], $currentDate['period']);
// set to correct array // set to correct array
if ('expenses' === $transactionType || 'withdrawal' === $transactionType) { if ('expenses' === $transactionType || 'withdrawal' === $transactionType) {
$spent = $amounts; $spent = $this->filterJournalsByDate($genericSet, $currentDate['start'], $currentDate['end']);
} }
if ('revenue' === $transactionType || 'deposit' === $transactionType) { if ('revenue' === $transactionType || 'deposit' === $transactionType) {
$earned = $amounts; $earned = $this->filterJournalsByDate($genericSet, $currentDate['start'], $currentDate['end']);
} }
if ('transfer' === $transactionType || 'transfers' === $transactionType) { if ('transfer' === $transactionType || 'transfers' === $transactionType) {
$transferred = $amounts; $transferred = $this->filterJournalsByDate($genericSet, $currentDate['start'], $currentDate['end']);
} }
$title = app('navigation')->periodShow($currentDate['end'], $currentDate['period']); $entries[] =
$entries->push(
[ [
'transactions' => $amounts['count'], 'title' => $title,
'title' => $title, 'route' =>
'spent' => $spent, route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]),
'earned' => $earned, 'total_transactions' => count($spent) + count($earned) + count($transferred),
'transferred' => $transferred, 'spent' => $this->groupByCurrency($spent),
'route' => route( 'earned' => $this->groupByCurrency($earned),
'transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] 'transferred' => $this->groupByCurrency($transferred),
), ];
]
);
} }
return $entries; return $entries;

View File

@@ -178,7 +178,7 @@ trait RequestInformation
// both must be array and either must be > 0 // both must be array and either must be > 0
if (count($intro) > 0 || count($specialIntro) > 0) { if (count($intro) > 0 || count($specialIntro) > 0) {
$shownDemo = app('preferences')->get($key, false)->data; $shownDemo = app('preferences')->get($key, false)->data;
Log::debug(sprintf('Check if user has already seen intro with key "%s". Result is %s', $key, var_export($shownDemo, true))); //Log::debug(sprintf('Check if user has already seen intro with key "%s". Result is %s', $key, var_export($shownDemo, true)));
} }
if (!is_bool($shownDemo)) { if (!is_bool($shownDemo)) {
$shownDemo = true; $shownDemo = true;

View File

@@ -50,7 +50,7 @@ trait UserNavigation
*/ */
protected function getPreviousUri(string $identifier): string protected function getPreviousUri(string $identifier): string
{ {
Log::debug(sprintf('Trying to retrieve URL stored under "%s"', $identifier)); // Log::debug(sprintf('Trying to retrieve URL stored under "%s"', $identifier));
// "forbidden" words for specific identifiers: // "forbidden" words for specific identifiers:
// if these are in the previous URI, don't refer back there. // if these are in the previous URI, don't refer back there.
$array = [ $array = [
@@ -67,24 +67,24 @@ trait UserNavigation
'transactions.mass-delete.uri' => '/transactions/show/', 'transactions.mass-delete.uri' => '/transactions/show/',
]; ];
$forbidden = $array[$identifier] ?? '/show/'; $forbidden = $array[$identifier] ?? '/show/';
Log::debug(sprintf('The forbidden word for %s is "%s"', $identifier, $forbidden)); //Log::debug(sprintf('The forbidden word for %s is "%s"', $identifier, $forbidden));
$uri = (string)session($identifier); $uri = (string)session($identifier);
Log::debug(sprintf('The URI is %s', $uri)); //Log::debug(sprintf('The URI is %s', $uri));
if ( if (
!(false === strpos($identifier, 'delete')) !(false === strpos($identifier, 'delete'))
&& !(false === strpos($uri, $forbidden))) { && !(false === strpos($uri, $forbidden))) {
$uri = $this->redirectUri; $uri = $this->redirectUri;
Log::debug(sprintf('URI is now %s (identifier contains "delete")', $uri)); //Log::debug(sprintf('URI is now %s (identifier contains "delete")', $uri));
} }
if (!(false === strpos($uri, 'jscript'))) { if (!(false === strpos($uri, 'jscript'))) {
$uri = $this->redirectUri; // @codeCoverageIgnore $uri = $this->redirectUri; // @codeCoverageIgnore
Log::debug(sprintf('URI is now %s (uri contains jscript)', $uri)); //Log::debug(sprintf('URI is now %s (uri contains jscript)', $uri));
} }
// more debug notes: // more debug notes:
Log::debug(sprintf('strpos($identifier, "delete"): %s', var_export(strpos($identifier, 'delete'), true))); //Log::debug(sprintf('strpos($identifier, "delete"): %s', var_export(strpos($identifier, 'delete'), true)));
Log::debug(sprintf('strpos($uri, $forbidden): %s', var_export(strpos($uri, $forbidden), true))); //Log::debug(sprintf('strpos($uri, $forbidden): %s', var_export(strpos($uri, $forbidden), true)));
return $uri; return $uri;
} }
@@ -154,10 +154,10 @@ trait UserNavigation
if (null === $errors || (null !== $errors && 0 === $errors->count())) { if (null === $errors || (null !== $errors && 0 === $errors->count())) {
$url = app('url')->previous(); $url = app('url')->previous();
session()->put($identifier, $url); session()->put($identifier, $url);
Log::debug(sprintf('Will put previous URI in cache under key %s: %s', $identifier, $url)); //Log::debug(sprintf('Will put previous URI in cache under key %s: %s', $identifier, $url));
return; return;
} }
Log::debug(sprintf('The users session contains errors somehow so we will not remember the URI!: %s', var_export($errors, true))); //Log::debug(sprintf('The users session contains errors somehow so we will not remember the URI!: %s', var_export($errors, true)));
} }
} }

View File

@@ -265,10 +265,10 @@ class TransactionMatcher
// - all transactions have been fetched from the database // - all transactions have been fetched from the database
// - the maximum number of transactions to return has been found // - the maximum number of transactions to return has been found
// - the maximum number of transactions to search in have been searched // - the maximum number of transactions to search in have been searched
$pageSize = min($this->searchLimit, min($this->triggeredLimit, 50)); $pageSize = min($this->searchLimit, min($this->triggeredLimit, 50));
$processed = 0; $processed = 0;
$page = 1; $page = 1;
$result = []; $totalResult = [];
Log::debug(sprintf('Search limit is %d, triggered limit is %d, so page size is %d', $this->searchLimit, $this->triggeredLimit, $pageSize)); Log::debug(sprintf('Search limit is %d, triggered limit is %d, so page size is %d', $this->searchLimit, $this->triggeredLimit, $pageSize));
@@ -322,8 +322,8 @@ class TransactionMatcher
Log::debug(sprintf('Found %d journals that match.', count($filtered))); Log::debug(sprintf('Found %d journals that match.', count($filtered)));
// merge: // merge:
$result = $result + $filtered; $totalResult = $totalResult + $filtered;
Log::debug(sprintf('Total count is now %d', count($result))); Log::debug(sprintf('Total count is now %d', count($totalResult)));
// Update counters // Update counters
++$page; ++$page;
@@ -333,7 +333,7 @@ class TransactionMatcher
// Check for conditions to finish the loop // Check for conditions to finish the loop
$reachedEndOfList = count($journals) < 1; $reachedEndOfList = count($journals) < 1;
$foundEnough = count($result) >= $this->triggeredLimit; $foundEnough = count($totalResult) >= $this->triggeredLimit;
$searchedEnough = ($processed >= $this->searchLimit); $searchedEnough = ($processed >= $this->searchLimit);
Log::debug(sprintf('reachedEndOfList: %s', var_export($reachedEndOfList, true))); Log::debug(sprintf('reachedEndOfList: %s', var_export($reachedEndOfList, true)));
@@ -342,6 +342,6 @@ class TransactionMatcher
} while (!$reachedEndOfList && !$foundEnough && !$searchedEnough); } while (!$reachedEndOfList && !$foundEnough && !$searchedEnough);
Log::debug('End of do-loop'); Log::debug('End of do-loop');
return $result; return $totalResult;
} }
} }

View File

@@ -70,8 +70,15 @@ final class CurrencyIs extends AbstractTrigger implements TriggerInterface
{ {
/** @var CurrencyRepositoryInterface $repository */ /** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class); $repository = app(CurrencyRepositoryInterface::class);
$currency = $repository->findByNameNull($this->triggerValue);
$hit = true; // if currency name contains " ("
if (0 === strpos($this->triggerValue, ' (')) {
$parts = explode(' (', $this->triggerValue);
$this->triggerValue = $parts[0];
}
$currency = $repository->findByNameNull($this->triggerValue);
$hit = true;
if (null !== $currency) { if (null !== $currency) {
/** @var Transaction $transaction */ /** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) { foreach ($journal->transactions as $transaction) {

View File

@@ -41,7 +41,7 @@ trait TransactionValidation
*/ */
public function validateAccountInformation(Validator $validator): void public function validateAccountInformation(Validator $validator): void
{ {
Log::debug('Now in validateAccountInformation()'); //Log::debug('Now in validateAccountInformation()');
$data = $validator->getData(); $data = $validator->getData();
$transactionType = $data['type'] ?? 'invalid'; $transactionType = $data['type'] ?? 'invalid';

View File

@@ -19,6 +19,10 @@
*/ */
.no-margin-pagination {padding-bottom:0;padding-top:0;}
.no-margin-pagination ul.pagination {margin:0 !important;}
input.ti-new-tag-input { input.ti-new-tag-input {
font-size: 14px !important; font-size: 14px !important;
line-height: 1.42857143; line-height: 1.42857143;

View File

@@ -189,13 +189,13 @@ function updateFormFields() {
// show source account ID: // show source account ID:
$('#source_id_holder').show(); $('#source_id_holder').show();
// show destination name:
// $('#destination_name_holder').show(); // old one
$('#withdrawal_destination_id_holder').show();
// hide destination ID: // hide destination ID:
$('#destination_id_holder').hide(); $('#destination_id_holder').hide();
// show destination name:
//$('#destination_name_holder').show(); // old one
$('#withdrawal_destination_id_holder').show();
// show budget // show budget
$('#budget_id_holder').show(); $('#budget_id_holder').show();
@@ -212,7 +212,6 @@ function updateFormFields() {
// $('#destination_name_holder').hide(); // old one // $('#destination_name_holder').hide(); // old one
$('#withdrawal_destination_id_holder').hide(); $('#withdrawal_destination_id_holder').hide();
$('#destination_id_holder').show(); $('#destination_id_holder').show();
$('#budget_id_holder').hide(); $('#budget_id_holder').hide();
$('#piggy_bank_id_holder').hide(); $('#piggy_bank_id_holder').hide();

View File

@@ -184,13 +184,15 @@ function updateFormFields() {
if (transactionType === 'withdrawal') { if (transactionType === 'withdrawal') {
// hide source account name: // hide source account name:
$('#source_name_holder').hide(); // $('#source_name_holder').hide(); // no longer used
$('#deposit_source_id_holder').hide();
// show source account ID: // show source account ID:
$('#source_id_holder').show(); $('#source_id_holder').show();
// show destination name: // show destination name:
$('#destination_name_holder').show(); // $('#destination_name_holder').show(); // no longer used.
$('#withdrawal_destination_id_holder').show();
// hide destination ID: // hide destination ID:
$('#destination_id_holder').hide(); $('#destination_id_holder').hide();
@@ -203,18 +205,28 @@ function updateFormFields() {
} }
if (transactionType === 'deposit') { if (transactionType === 'deposit') {
$('#source_name_holder').show(); // $('#source_name_holder').show(); // no longer used
$('#deposit_source_id_holder').show();
$('#source_id_holder').hide(); $('#source_id_holder').hide();
$('#destination_name_holder').hide();
// $('#destination_name_holder').hide(); // no longer used
$('#withdrawal_destination_id_holder').hide();
$('#destination_id_holder').show(); $('#destination_id_holder').show();
$('#budget_id_holder').hide(); $('#budget_id_holder').hide();
$('#piggy_bank_id_holder').hide(); $('#piggy_bank_id_holder').hide();
} }
if (transactionType === 'transfer') { if (transactionType === 'transfer') {
$('#source_name_holder').hide(); // $('#source_name_holder').hide(); // no longer used
$('#deposit_source_id_holder').hide();
$('#source_id_holder').show(); $('#source_id_holder').show();
$('#destination_name_holder').hide();
// $('#destination_name_holder').hide(); // no longer used
$('#withdrawal_destination_id_holder').show();
$('#destination_id_holder').show(); $('#destination_id_holder').show();
$('#budget_id_holder').hide(); $('#budget_id_holder').hide();
$('#piggy_bank_id_holder').show(); $('#piggy_bank_id_holder').show();

View File

@@ -307,7 +307,7 @@ function updateTriggerInput(selectList) {
case 'to_account_is': case 'to_account_is':
case 'to_account_contains': case 'to_account_contains':
console.log('Select list value is ' + selectList.val() + ', so input needs auto complete.'); console.log('Select list value is ' + selectList.val() + ', so input needs auto complete.');
createAutoComplete(inputResult, 'json/all-accounts'); createAutoComplete(inputResult, 'json/accounts');
break; break;
case 'tag_is': case 'tag_is':
console.log('Select list value is ' + selectList.val() + ', so input needs auto complete.'); console.log('Select list value is ' + selectList.val() + ', so input needs auto complete.');
@@ -377,8 +377,8 @@ function createAutoComplete(input, URI) {
prefetch: { prefetch: {
url: URI + '?uid=' + uid, url: URI + '?uid=' + uid,
filter: function (list) { filter: function (list) {
return $.map(list, function (name) { return $.map(list, function (item) {
return {name: name}; return {name: item.name};
}); });
} }
}, },
@@ -386,8 +386,8 @@ function createAutoComplete(input, URI) {
url: URI + '?search=%QUERY&uid=' + uid, url: URI + '?search=%QUERY&uid=' + uid,
wildcard: '%QUERY', wildcard: '%QUERY',
filter: function (list) { filter: function (list) {
return $.map(list, function (name) { return $.map(list, function (item) {
return {name: name}; return {name: item.name};
}); });
} }
} }

View File

@@ -189,4 +189,7 @@ return [
'ob_source_need_data' => 'Need to get a valid source account ID and/or valid source account name to continue.', 'ob_source_need_data' => 'Need to get a valid source account ID and/or valid source account name to continue.',
'ob_dest_need_data' => 'Need to get a valid destination account ID and/or valid destination account name to continue.', 'ob_dest_need_data' => 'Need to get a valid destination account ID and/or valid destination account name to continue.',
'ob_dest_bad_data' => 'Could not find a valid destination account when searching for ID ":id" or name ":name".', 'ob_dest_bad_data' => 'Could not find a valid destination account when searching for ID ":id" or name ":name".',
'generic_invalid_source' => 'You can\'t use this account as the source account.',
'generic_invalid_destination' => 'You can\'t use this account as the destination account.',
]; ];

View File

@@ -5,9 +5,12 @@ TODO: hide and show columns
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
<th> <td colspan="7" class="no-margin-pagination">{{ groups.render|raw }}</td>
{{ groups.render|raw }} <td colspan="1">
</th> <div class="pull-right">
<input id="list_ALL" value="1" name="all" type="checkbox" class="mass-select-all form-check-inline"/>
</div>
</td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -16,7 +19,7 @@ TODO: hide and show columns
<tr> <tr>
<td colspan="2" style="border-top:1px #aaa solid;"> <td colspan="2" style="border-top:1px #aaa solid;">
<small><strong> <small><strong>
<a href="{{ route('transactions.edit', [group.id]) }}" title="{{ group.title }}">{{ group.title }}</a> <a href="{{ route('transactions.show', [group.id]) }}" title="{{ group.title }}">{{ group.title }}</a>
</strong></small> </strong></small>
</td> </td>
<td colspan="2" style="border-top:1px #aaa solid;"> <td colspan="2" style="border-top:1px #aaa solid;">
@@ -25,7 +28,7 @@ TODO: hide and show columns
{% endfor %} {% endfor %}
</td> </td>
<td colspan="2" style="border-top:1px #aaa solid;">&nbsp;</td> <td colspan="2" style="border-top:1px #aaa solid;">&nbsp;</td>
<td style="border-top:1px #aaa solid;"> <td style="border-top:1px #aaa solid;" colspan="2">
<div class="btn-group btn-group-xs"> <div class="btn-group btn-group-xs">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ 'actions'|_ }} <span class="caret"></span></button> {{ 'actions'|_ }} <span class="caret"></span></button>
@@ -41,52 +44,54 @@ TODO: hide and show columns
{% for index, transaction in group.transactions %} {% for index, transaction in group.transactions %}
{% set style="" %} {% set style="" %}
{% if group.transactions|length == loop.index and group.count > 1 %} {% if group.transactions|length == loop.index and group.count > 1 %}
{% set style="style='border-bottom:1px #aaa solid;'" %} {% set style="border-bottom:1px #aaa solid;" %}
{% endif %} {% endif %}
<tr> <tr>
<td {{ style|raw }}> <td style=" {{ style|raw }}">
{% if journal.transaction_type_type == 'Withdrawal' %} {% if transaction.transaction_type_type == 'Withdrawal' %}
<i class="fa fa-long-arrow-left fa-fw" title="{{ trans('firefly.Withdrawal') }}"></i> <i class="fa fa-long-arrow-left fa-fw" title="{{ trans('firefly.Withdrawal') }}"></i>
{% endif %} {% endif %}
{% if journal.transaction_type_type == 'Deposit' %} {% if transaction.transaction_type_type == 'Deposit' %}
<i class="fa fa-long-arrow-right fa-fw" title="{{ trans('firefly.Deposit') }}"></i> <i class="fa fa-long-arrow-right fa-fw" title="{{ trans('firefly.Deposit') }}"></i>
{% endif %} {% endif %}
{% if journal.transaction_type_type == 'Transfer' %} {% if transaction.transaction_type_type == 'Transfer' %}
<i class="fa fa-exchange fa-fw" title="{{ trans('firefly.Deposit') }}"></i> <i class="fa fa-exchange fa-fw" title="{{ trans('firefly.Deposit') }}"></i>
{% endif %} {% endif %}
{% if journal.transaction_type_type == 'Reconciliation' %} {% if transaction.transaction_type_type == 'Reconciliation' %}
<i class="fa-fw fa fa-calculator" title="{{ trans('firefly.reconciliation_transaction') }}"></i> <i class="fa-fw fa fa-calculator" title="{{ trans('firefly.reconciliation_transaction') }}"></i>
{% endif %} {% endif %}
{% if journal.transaction_type_type == 'Opening balance' %} {% if transaction.transaction_type_type == 'Opening balance' %}
<i class="fa-fw fa fa-star-o" title="{{ trans('firefly.Opening balance') }}"></i> <i class="fa-fw fa fa-star-o" title="{{ trans('firefly.Opening balance') }}"></i>
{% endif %} {% endif %}
</td> </td>
<td {{ style|raw }}> <td style=" {{ style|raw }}">
{% if transaction.reconciled %} {% if transaction.reconciled %}
<i class="fa fa-check"></i> <i class="fa fa-check"></i>
{% endif %} {% endif %}
<a href="{{ route('transactions.show', [group.id]) }}" title="{{ transaction.description }}">{{ transaction.description }}</a> <a href="{{ route('transactions.show', [group.id]) }}" title="{{ transaction.description }}">{{ transaction.description }}</a>
</td> </td>
<td {{ style|raw }}> <td style=" {{ style|raw }}">
{{ formatAmountBySymbol(transaction.amount, transaction.currency_symbol, transaction.currency_symbol_decimal_places) }} {{ formatAmountBySymbol(transaction.amount, transaction.currency_symbol, transaction.currency_symbol_decimal_places) }}
{% if null != transaction.foreign_amount %} {% if null != transaction.foreign_amount %}
({{ formatAmountBySymbol(transaction.foreign_amount, transaction.foreign_currency_symbol, transaction.foreign_currency_symbol_decimal_places) }}) ({{ formatAmountBySymbol(transaction.foreign_amount, transaction.foreign_currency_symbol, transaction.foreign_currency_symbol_decimal_places) }})
{% endif %} {% endif %}
</td> </td>
<td {{ style|raw }}>{{ transaction.date.formatLocalized(monthAndDayFormat) }}</td> <td style=" {{ style|raw }}">
<td {{ style|raw }}> {{ transaction.date.formatLocalized(monthAndDayFormat) }}
</td>
<td style=" {{ style|raw }}">
<a href="{{ route('accounts.show', [transaction.source_account_id]) }}" <a href="{{ route('accounts.show', [transaction.source_account_id]) }}"
title="{{ transaction.source_account_iban|default(transaction.source_account_name) }}">{{ transaction.source_account_name }}</a> title="{{ transaction.source_account_iban|default(transaction.source_account_name) }}">{{ transaction.source_account_name }}</a>
</td> </td>
<td {{ style|raw }}> <td style=" {{ style|raw }}">
<a href="{{ route('accounts.show', [transaction.destination_account_id]) }}" <a href="{{ route('accounts.show', [transaction.destination_account_id]) }}"
title="{{ transaction.destination_account_iban|default(transaction.destination_account_name) }}">{{ transaction.destination_account_name }}</a> title="{{ transaction.destination_account_iban|default(transaction.destination_account_name) }}">{{ transaction.destination_account_name }}</a>
</td> </td>
<td {{ style|raw }}> <td style=" {{ style|raw }}">
{% if group.count == 1 %} {% if group.count == 1 %}
<div class="btn-group btn-group-xs"> <div class="btn-group btn-group-xs">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@@ -99,10 +104,35 @@ TODO: hide and show columns
</div> </div>
{% endif %} {% endif %}
</td> </td>
<td style="{{ style|raw }}">
<div class="pull-right">
<input id="list_{{ transaction.transaction_journal_id }}" value="1" name="journals[{{ transaction.transaction_journal_id }}]"
type="checkbox" class="mass-select form-check-inline" data-value="{{ transaction.transaction_journal_id }}"/>
</div>
</td>
</tr> </tr>
{% endfor %} {% endfor %}
{% endfor %} {% endfor %}
</tbody> </tbody>
<tfoot>
<tr>
<td colspan="8">
<div class="pull-right">
<!-- Single button -->
<div class="btn-group">
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ 'actions'|_ }} <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="#">Edit individually (x)</a></li>
<li><a href="#">Bulk edit (x)</a></li>
<li><a href="#">Delete (x)</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
</ul>
</div>
</div>
</td>
</tr>
</tfoot>
</table> </table>

View File

@@ -0,0 +1,10 @@
<div class="list-group">
{% for journal in journals %}
<a class="list-group-item" href="{{ route('transactions.show', [journal.transaction_group_id]) }}">
{{ journal.description }}
<span class="pull-right small">
{{ journalArrayAmount(journal) }}
</span>
</a>
{% endfor %}
</div>

View File

@@ -91,13 +91,19 @@
{{ ExpandedForm.longAccountList('source_id', array.transactions[0].source_id, {label: trans('form.asset_source_account')}) }} {{ ExpandedForm.longAccountList('source_id', array.transactions[0].source_id, {label: trans('form.asset_source_account')}) }}
{# source account name for deposits: #} {# source account name for deposits: #}
{{ ExpandedForm.text('source_name', array.transactions[0].source_name, {label: trans('form.revenue_account')}) }} {#{{ ExpandedForm.text('source_name', array.transactions[0].source_name, {label: trans('form.revenue_account')}) }}#}
{# NEW for deposits, a drop down with revenue accounts, loan debt cash and mortgage #}
{{ ExpandedForm.activeDepositDestinations('deposit_source_id', preFilled.deposit_source_id, {label: trans('form.deposit_source_id')}) }}
{# destination if deposit or transfer: #} {# destination if deposit or transfer: #}
{{ ExpandedForm.longAccountList('destination_id', array.transactions[0].destination_id, {label: trans('form.asset_destination_account')} ) }} {{ ExpandedForm.longAccountList('destination_id', array.transactions[0].destination_id, {label: trans('form.asset_destination_account')} ) }}
{# destination account name for withdrawals #} {# destination account name for withdrawals #}
{{ ExpandedForm.text('destination_name', array.transactions[0].destination_name, {label: trans('form.expense_account')}) }} {# {{ ExpandedForm.text('destination_name', array.transactions[0].destination_name, {label: trans('form.expense_account')}) }}#}
{# NEW for withdrawals, also a drop down with expense accounts, loans, debts, mortgages or (cash). #}
{{ ExpandedForm.activeWithdrawalDestinations('withdrawal_destination_id', preFilled.withdrawal_destination_id, {label: trans('form.withdrawal_destination_id')}) }}
</div> </div>
</div> </div>
</div> </div>

View File

@@ -198,7 +198,7 @@
</h3> </h3>
</div> </div>
<div class="box-body"> <div class="box-body">
{% include 'list.transactions' with {showBudgets:true, showCategories:true} %} {% include 'list.groups' %}
</div> </div>
</div> </div>
</div> </div>

View File

@@ -31,7 +31,7 @@
{{ '0'|formatAmount }} {{ '0'|formatAmount }}
{% else %} {% else %}
{% for income in entry.earned.per_currency %} {% for income in entry.earned.per_currency %}
{{ formatAmountBySymbol(income.sum, income.currency.symbol, income.currency.dp) }}<br/> {{ formatAmountBySymbol(income.sum * -1, income.currency.symbol, income.currency.dp) }}<br/>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
</td> </td>

View File

@@ -23,7 +23,7 @@
{{ '0'|formatAmount }} {{ '0'|formatAmount }}
{% endif %} {% endif %}
{% for income in amounts.earned.per_currency %} {% for income in amounts.earned.per_currency %}
{{ formatAmountBySymbol(income.sum, income.currency.symbol, income.currency.dp) }}<br/> {{ formatAmountBySymbol(income.sum * -1, income.currency.symbol, income.currency.dp) }}<br/>
{% endfor %} {% endfor %}
</td> </td>
</tr> </tr>

View File

@@ -10,15 +10,21 @@
<tbody> <tbody>
{% for transaction in sorted %} {% for transaction in sorted %}
<tr> <tr>
<td data-value="{{ transaction.opposing_account_name }}"> <td data-value="{{ transaction.destination_account_name }}">
<a href="{{ route('accounts.show',transaction.opposing_account_id) }}">{{ transaction.opposing_account_name }}</a> <a href="{{ route('accounts.show',transaction.destination_account_id) }}">{{ transaction.destination_account_name }}</a>
</td> </td>
<td data-value="{{ transaction.description }}">{{ transaction.description }}</td> <td data-value="{{ transaction.description }}">{{ transaction.description }}</td>
<td data-value="{{ transaction.date.format('Y-m-d') }}"> <td data-value="{{ transaction.date.format('Y-m-d') }}">
{{ transaction.date.formatLocalized(monthAndDayFormat) }} {{ transaction.date.formatLocalized(monthAndDayFormat) }}
</td><!-- TODO i dont think transactionAmount will work --> </td><!-- TODO i dont think transactionAmount will work -->
<td style="text-align: right;" data-value="{{ transaction.transaction_amount }}"><span <td style="text-align: right;" data-value="{{ transaction.amount}}"><span
style="margin-right:5px;">{{ transaction|transactionAmount }}</span></td> style="margin-right:5px;">
{{ formatAmountBySymbol(transaction.amount, transaction.currency_symbol, transaction.currency_symbol_decimal_places) }}
{% if null != transaction.foreign_amount %}
({{ formatAmountBySymbol(transaction.foreign_amount, transaction.foreign_currency_symbol, transaction.foreign_currency_symbol_decimal_places) }})
{% endif %}
</span></td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@@ -7,7 +7,7 @@
{% block content %} {% block content %}
{# upper show-all instruction #} {# upper show-all instruction #}
{% if periods.count > 0 %} {% if periods|length > 0 %}
<div class="row"> <div class="row">
<div class="col-lg-offset-10 col-lg-2 col-md-offset-10 col-md-2 col-sm-12 col-xs-12"> <div class="col-lg-offset-10 col-lg-2 col-md-offset-10 col-md-2 col-sm-12 col-xs-12">
<p class="small text-center"><a href="{{ route('transactions.index.all',[transactionType]) }}">{{ 'showEverything'|_ }}</a></p> <p class="small text-center"><a href="{{ route('transactions.index.all',[transactionType]) }}">{{ 'showEverything'|_ }}</a></p>
@@ -17,7 +17,7 @@
{# list with journals #} {# list with journals #}
<div class="row"> <div class="row">
<div class="{% if periods.count > 0 %}col-lg-10 col-md-10 col-sm-12{% else %}col-lg-12 col-md-12 col-sm-12{% endif %}"> <div class="{% if periods|length > 0 %}col-lg-10 col-md-10 col-sm-12{% else %}col-lg-12 col-md-12 col-sm-12{% endif %}">
<div class="box"> <div class="box">
<div class="box-header with-border"> <div class="box-header with-border">
<h3 class="box-title">{{ subTitle }}</h3> <h3 class="box-title">{{ subTitle }}</h3>
@@ -31,7 +31,7 @@
</div> </div>
<div class="box-footer"> <div class="box-footer">
{# links for other views #} {# links for other views #}
{% if periods.count > 0 %} {% if periods|length > 0 %}
<p> <p>
<i class="fa fa-calendar"></i> <i class="fa fa-calendar"></i>
<a href="{{ route('transactions.index.all', [transactionType]) }}">{{ 'show_all_no_filter'|_ }}</a> <a href="{{ route('transactions.index.all', [transactionType]) }}">{{ 'show_all_no_filter'|_ }}</a>
@@ -47,7 +47,7 @@
</div> </div>
{# boxes with info #} {# boxes with info #}
{% if periods.count > 0 %} {% if periods|length > 0 %}
<div class="col-lg-2 col-md-2 col-sm-12 col-xs-12"> <div class="col-lg-2 col-md-2 col-sm-12 col-xs-12">
{% include 'list.periods' %} {% include 'list.periods' %}
</div> </div>
@@ -56,7 +56,7 @@
</div> </div>
{# lower show-all instruction #} {# lower show-all instruction #}
{% if periods.count > 0 %} {% if periods|length > 0 %}
<div class="row"> <div class="row">
<div class="col-lg-offset-10 col-lg-2 col-md-offset-10 col-md-2 col-sm-12 col-xs-12"> <div class="col-lg-offset-10 col-lg-2 col-md-offset-10 col-md-2 col-sm-12 col-xs-12">
<p class="small text-center"><a href="{{ route('transactions.index.all',[transactionType]) }}">{{ 'showEverything'|_ }}</a></p> <p class="small text-center"><a href="{{ route('transactions.index.all',[transactionType]) }}">{{ 'showEverything'|_ }}</a></p>

View File

@@ -537,6 +537,8 @@ Route::group(
Route::get('currencies', ['uses' => 'Json\AutoCompleteController@currencies', 'as' => 'autocomplete.currencies']); Route::get('currencies', ['uses' => 'Json\AutoCompleteController@currencies', 'as' => 'autocomplete.currencies']);
Route::get('piggy-banks', ['uses' => 'Json\AutoCompleteController@piggyBanks', 'as' => 'autocomplete.piggy-banks']); Route::get('piggy-banks', ['uses' => 'Json\AutoCompleteController@piggyBanks', 'as' => 'autocomplete.piggy-banks']);
Route::get('tags', ['uses' => 'Json\AutoCompleteController@tags', 'as' => 'autocomplete.tags']); Route::get('tags', ['uses' => 'Json\AutoCompleteController@tags', 'as' => 'autocomplete.tags']);
Route::get('transaction-journals/all', ['uses' => 'Json\AutoCompleteController@allJournals', 'as' => 'autocomplete.all-journals']);
Route::get('currency-names', ['uses' => 'Json\AutoCompleteController@currencyNames', 'as' => 'autocomplete.currency-names']);

View File

@@ -133,6 +133,8 @@ class BudgetReportControllerTest extends TestCase
$limit3->budget_id = $budget->id; $limit3->budget_id = $budget->id;
$limit3->start_date = new Carbon('2012-01-01'); $limit3->start_date = new Carbon('2012-01-01');
$limit3->end_date = new Carbon('2012-01-31'); $limit3->end_date = new Carbon('2012-01-31');
$limit3->amount = '100';
$limit3->save();
$fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date); $fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date);

View File

@@ -24,8 +24,9 @@ declare(strict_types=1);
namespace Tests\Feature\Controllers\Recurring; namespace Tests\Feature\Controllers\Recurring;
use Amount;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
@@ -33,14 +34,18 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Validation\AccountValidator;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log; use Log;
use Mockery; use Mockery;
use Preferences;
use Steam;
use Tests\TestCase; use Tests\TestCase;
/** /**
* *
* Class CreateControllerTest * Class CreateControllerTest
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/ */
class CreateControllerTest extends TestCase class CreateControllerTest extends TestCase
{ {
@@ -58,76 +63,100 @@ class CreateControllerTest extends TestCase
*/ */
public function testCreate(): void public function testCreate(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0'); // mock repositories, even if not used.
$this->mock(RecurringRepositoryInterface::class);
$this->mock(CurrencyRepositoryInterface::class);
$this->mock(PiggyBankRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$budgetRepos = $this->mock(BudgetRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$euro = $this->getEuro();
$asset = $this->getRandomAsset();
$cash = $this->getRandomAsset();
$this->mockDefaultSession();
return;
$recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$budgetRepos = $this->mock(BudgetRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$piggyRepos = $this->mock(PiggyBankRepositoryInterface::class);
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true); $userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true);
$budgetRepos->shouldReceive('getActiveBudgets')->andReturn(new Collection)->once(); $budgetRepos->shouldReceive('getActiveBudgets')->andReturn(new Collection)->once();
\Amount::shouldReceive('getDefaultCurrency')->andReturn(TransactionCurrency::find(1));
// for view:
$accountRepos->shouldReceive('getActiveAccountsByType')->atLeast()->once()->andReturn(new Collection([$asset]));
Steam::shouldReceive('balance')->andReturn('100')->atLeast()->once();
$accountRepos->shouldReceive('getAccountCurrency')->atLeast()->once()->andReturn($euro);
$accountRepos->shouldReceive('getMetaValue')->atLeast()->once()->andReturnNull();
$accountRepos->shouldReceive('getCashAccount')->atLeast()->once()->andReturn($cash);
//Amount::shouldReceive('getDefaultCurrency')->andReturn($euro)->atLeast()->once();
Amount::shouldReceive('formatAnything')->atLeast()->once()->andReturn('100');
$this->be($this->user()); $this->be($this->user());
$response = $this->get(route('recurring.create')); $response = $this->get(route('recurring.create'));
$response->assertStatus(200); $response->assertStatus(200);
$response->assertSee('<ol class="breadcrumb">'); $response->assertSee('<ol class="breadcrumb">');
$response->assertSee('source_id_holder');
$response->assertSee('deposit_source_id');
$response->assertSee('withdrawal_destination_id');
} }
/** /**
* Stores a withdrawal. From Asset account to Expense account
*
* @covers \FireflyIII\Http\Controllers\Recurring\CreateController * @covers \FireflyIII\Http\Controllers\Recurring\CreateController
* @covers \FireflyIII\Http\Requests\RecurrenceFormRequest * @covers \FireflyIII\Http\Requests\RecurrenceFormRequest
*/ */
public function testStore(): void public function testStoreWithdrawalExpense(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0'); // mock repositories, even if not used.
$this->mock(BudgetRepositoryInterface::class);
return;
$recurringRepos = $this->mock(RecurringRepositoryInterface::class); $recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$budgetRepos = $this->mock(BudgetRepositoryInterface::class); $validator = $this->mock(AccountValidator::class);
$categoryRepos = $this->mock(CategoryRepositoryInterface::class); $source = $this->getRandomAsset();
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class); $destination = $this->getRandomExpense();
$recurringRepos = $this->mock(RecurringRepositoryInterface::class); $tomorrow = Carbon::now()->addDays(2);
$userRepos = $this->mock(UserRepositoryInterface::class); $recurrence = $this->user()->recurrences()->first();
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$piggyRepos = $this->mock(PiggyBankRepositoryInterface::class);
$tomorrow = Carbon::now()->addDays(2); $this->mockDefaultSession();
$recurrence = $this->user()->recurrences()->first(); Preferences::shouldReceive('mark')->atLeast()->once();
$data = [
'title' => 'hello' . $this->randomInt(), // validator:
'first_date' => $tomorrow->format('Y-m-d'), $validator->shouldReceive('setTransactionType')->withArgs(['withdrawal'])->atLeast()->once();
'repetition_type' => 'daily', $validator->shouldReceive('validateSource')->atLeast()->once()->andReturn(true);
'skip' => 0, $validator->shouldReceive('validateDestination')->atLeast()->once()->andReturn(true);
'recurring_description' => 'Some descr' . $this->randomInt(),
'active' => '1',
'apply_rules' => '1', $data = [
'foreign_amount' => '1', 'title' => sprintf('hello %d', $this->randomInt()),
'foreign_currency_id' => '2', 'first_date' => $tomorrow->format('Y-m-d'),
'repetition_type' => 'daily',
'skip' => 0,
'recurring_description' => sprintf('Some descr %d', $this->randomInt()),
'active' => '1',
'apply_rules' => '1',
'foreign_amount' => '1',
'foreign_currency_id' => '2',
// mandatory for transaction: // mandatory for transaction:
'transaction_description' => 'Some descr', 'transaction_description' => 'Some descr',
'transaction_type' => 'withdrawal', 'transaction_type' => 'withdrawal',
'transaction_currency_id' => '1', 'transaction_currency_id' => '1',
'amount' => '30', 'amount' => '30',
// mandatory account info: // mandatory account info:
'source_id' => '1', 'source_id' => $source->id,
'destination_name' => 'Some Expense', 'withdrawal_destination_id' => $destination->id,
// optional fields: // optional fields:
'budget_id' => '1', 'budget_id' => '1',
'category' => 'CategoryA', 'category' => 'CategoryA',
'tags' => 'A,B,C', 'tags' => 'A,B,C',
'create_another' => '1', 'create_another' => '1',
'repetition_end' => 'times', 'repetition_end' => 'times',
'repetitions' => 3, 'repetitions' => 3,
]; ];
$recurringRepos->shouldReceive('store')->andReturn($recurrence)->once(); $recurringRepos->shouldReceive('store')->andReturn($recurrence)->once();
@@ -139,23 +168,89 @@ class CreateControllerTest extends TestCase
} }
/** /**
* Stores a withdrawal. But throw error.
*
* @covers \FireflyIII\Http\Controllers\Recurring\CreateController * @covers \FireflyIII\Http\Controllers\Recurring\CreateController
* @covers \FireflyIII\Http\Requests\RecurrenceFormRequest * @covers \FireflyIII\Http\Requests\RecurrenceFormRequest
*/ */
public function testStoreDeposit(): void public function testStoreError(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0'); // mock repositories, even if not used.
$this->mock(BudgetRepositoryInterface::class);
return;
$recurringRepos = $this->mock(RecurringRepositoryInterface::class); $recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$budgetRepos = $this->mock(BudgetRepositoryInterface::class); $validator = $this->mock(AccountValidator::class);
$categoryRepos = $this->mock(CategoryRepositoryInterface::class); $source = $this->getRandomAsset();
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class); $destination = $this->getRandomExpense();
$recurringRepos = $this->mock(RecurringRepositoryInterface::class); $tomorrow = Carbon::now()->addDays(2);
$userRepos = $this->mock(UserRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$piggyRepos = $this->mock(PiggyBankRepositoryInterface::class);
$this->mockDefaultSession();
// validator:
$validator->shouldReceive('setTransactionType')->withArgs(['withdrawal'])->atLeast()->once();
$validator->shouldReceive('validateSource')->atLeast()->once()->andReturn(true);
$validator->shouldReceive('validateDestination')->atLeast()->once()->andReturn(true);
$data = [
'title' => sprintf('hello %d', $this->randomInt()),
'first_date' => $tomorrow->format('Y-m-d'),
'repetition_type' => 'daily',
'skip' => 0,
'recurring_description' => sprintf('Some descr %d', $this->randomInt()),
'active' => '1',
'apply_rules' => '1',
'foreign_amount' => '1',
'foreign_currency_id' => '2',
// mandatory for transaction:
'transaction_description' => 'Some descr',
'transaction_type' => 'withdrawal',
'transaction_currency_id' => '1',
'amount' => '30',
// mandatory account info:
'source_id' => $source->id,
'withdrawal_destination_id' => $destination->id,
// optional fields:
'budget_id' => '1',
'category' => 'CategoryA',
'tags' => 'A,B,C',
'create_another' => '1',
'repetition_end' => 'times',
'repetitions' => 3,
];
$recurringRepos->shouldReceive('store')->andThrow(new FireflyException('Some exception'));
$this->be($this->user());
$response = $this->post(route('recurring.store'), $data);
$response->assertStatus(302);
$response->assertSessionHas('error', 'Some exception');
}
/**
* Store a deposit from Revenue to Asset.
*
* @covers \FireflyIII\Http\Controllers\Recurring\CreateController
* @covers \FireflyIII\Http\Requests\RecurrenceFormRequest
*/
public function testStoreDepositRevenue(): void
{
$this->mock(BudgetRepositoryInterface::class);
$recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$validator = $this->mock(AccountValidator::class);
$source = $this->getRandomRevenue();
$destination = $this->getRandomAsset();
$this->mockDefaultSession();
Preferences::shouldReceive('mark')->atLeast()->once();
// validator:
$validator->shouldReceive('setTransactionType')->withArgs(['deposit'])->atLeast()->once();
$validator->shouldReceive('validateSource')->atLeast()->once()->andReturn(true);
$validator->shouldReceive('validateDestination')->atLeast()->once()->andReturn(true);
$tomorrow = Carbon::now()->addDays(2); $tomorrow = Carbon::now()->addDays(2);
$recurrence = $this->user()->recurrences()->first(); $recurrence = $this->user()->recurrences()->first();
@@ -177,10 +272,8 @@ class CreateControllerTest extends TestCase
'amount' => '30', 'amount' => '30',
// mandatory account info: // mandatory account info:
'source_id' => '2', 'deposit_source_id' => $source->id,
'source_name' => 'Some source', 'destination_id' => $destination->id,
'destination_id' => '1',
'destination_name' => 'Some Expense',
// optional fields: // optional fields:
'budget_id' => '1', 'budget_id' => '1',
@@ -200,53 +293,58 @@ class CreateControllerTest extends TestCase
} }
/** /**
* Store a withdrawal but it's monthly, not daily.
*
* @covers \FireflyIII\Http\Controllers\Recurring\CreateController * @covers \FireflyIII\Http\Controllers\Recurring\CreateController
* @covers \FireflyIII\Http\Requests\RecurrenceFormRequest * @covers \FireflyIII\Http\Requests\RecurrenceFormRequest
*/ */
public function testStoreMonthly(): void public function testStoreMonthly(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0'); $this->mock(BudgetRepositoryInterface::class);
return;
$recurringRepos = $this->mock(RecurringRepositoryInterface::class); $recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$budgetRepos = $this->mock(BudgetRepositoryInterface::class); $validator = $this->mock(AccountValidator::class);
$categoryRepos = $this->mock(CategoryRepositoryInterface::class); $source = $this->getRandomAsset();
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class); $destination = $this->getRandomExpense();
$recurringRepos = $this->mock(RecurringRepositoryInterface::class); $tomorrow = Carbon::now()->addDays(2);
$userRepos = $this->mock(UserRepositoryInterface::class); $recurrence = $this->user()->recurrences()->first();
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$piggyRepos = $this->mock(PiggyBankRepositoryInterface::class);
$tomorrow = Carbon::now()->addDays(2); $this->mockDefaultSession();
$recurrence = $this->user()->recurrences()->first(); Preferences::shouldReceive('mark')->atLeast()->once();
$data = [
'title' => 'hello' . $this->randomInt(), // validator:
'first_date' => $tomorrow->format('Y-m-d'), $validator->shouldReceive('setTransactionType')->withArgs(['withdrawal'])->atLeast()->once();
'repetition_type' => 'monthly,5', $validator->shouldReceive('validateSource')->atLeast()->once()->andReturn(true);
'skip' => 0, $validator->shouldReceive('validateDestination')->atLeast()->once()->andReturn(true);
'recurring_description' => 'Some descr' . $this->randomInt(),
'active' => '1', $data = [
'apply_rules' => '1', 'title' => sprintf('hello %d', $this->randomInt()),
'foreign_amount' => '1', 'first_date' => $tomorrow->format('Y-m-d'),
'foreign_currency_id' => '2', 'repetition_type' => 'monthly,5',
'skip' => 0,
'recurring_description' => sprintf('Some descr %d', $this->randomInt()),
'active' => '1',
'apply_rules' => '1',
'foreign_amount' => '1',
'foreign_currency_id' => '2',
// mandatory for transaction: // mandatory for transaction:
'transaction_description' => 'Some descr', 'transaction_description' => 'Some descr',
'transaction_type' => 'withdrawal', 'transaction_type' => 'withdrawal',
'transaction_currency_id' => '1', 'transaction_currency_id' => '1',
'amount' => '30', 'amount' => '30',
// mandatory account info: // mandatory account info:
'source_id' => '1', 'source_id' => $source->id,
'destination_name' => 'Some Expense', 'withdrawal_destination_id' => $destination->id,
// optional fields: // optional fields:
'budget_id' => '1', 'budget_id' => '1',
'category' => 'CategoryA', 'category' => 'CategoryA',
'tags' => 'A,B,C', 'tags' => 'A,B,C',
'create_another' => '1', 'create_another' => '1',
'repetition_end' => 'times', 'repetition_end' => 'times',
'repetitions' => 3, 'repetitions' => 3,
]; ];
$recurringRepos->shouldReceive('store')->andReturn($recurrence)->once(); $recurringRepos->shouldReceive('store')->andReturn($recurrence)->once();
@@ -258,53 +356,58 @@ class CreateControllerTest extends TestCase
} }
/** /**
* Store a withdrawal but use ndom.
*
* @covers \FireflyIII\Http\Controllers\Recurring\CreateController * @covers \FireflyIII\Http\Controllers\Recurring\CreateController
* @covers \FireflyIII\Http\Requests\RecurrenceFormRequest * @covers \FireflyIII\Http\Requests\RecurrenceFormRequest
*/ */
public function testStoreNdom(): void public function testStoreNdom(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0'); $this->mock(BudgetRepositoryInterface::class);
return;
$recurringRepos = $this->mock(RecurringRepositoryInterface::class); $recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$budgetRepos = $this->mock(BudgetRepositoryInterface::class); $validator = $this->mock(AccountValidator::class);
$categoryRepos = $this->mock(CategoryRepositoryInterface::class); $source = $this->getRandomAsset();
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class); $destination = $this->getRandomExpense();
$recurringRepos = $this->mock(RecurringRepositoryInterface::class); $tomorrow = Carbon::now()->addDays(2);
$userRepos = $this->mock(UserRepositoryInterface::class); $recurrence = $this->user()->recurrences()->first();
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$piggyRepos = $this->mock(PiggyBankRepositoryInterface::class);
$tomorrow = Carbon::now()->addDays(2); $this->mockDefaultSession();
$recurrence = $this->user()->recurrences()->first(); Preferences::shouldReceive('mark')->atLeast()->once();
$data = [
'title' => 'hello' . $this->randomInt(), // validator:
'first_date' => $tomorrow->format('Y-m-d'), $validator->shouldReceive('setTransactionType')->withArgs(['withdrawal'])->atLeast()->once();
'repetition_type' => 'ndom,3,5', $validator->shouldReceive('validateSource')->atLeast()->once()->andReturn(true);
'skip' => 0, $validator->shouldReceive('validateDestination')->atLeast()->once()->andReturn(true);
'recurring_description' => 'Some descr' . $this->randomInt(),
'active' => '1', $data = [
'apply_rules' => '1', 'title' => sprintf('hello %d', $this->randomInt()),
'foreign_amount' => '1', 'first_date' => $tomorrow->format('Y-m-d'),
'foreign_currency_id' => '2', 'repetition_type' => 'ndom,3,5',
'skip' => 0,
'recurring_description' => sprintf('Some descr %d', $this->randomInt()),
'active' => '1',
'apply_rules' => '1',
'foreign_amount' => '1',
'foreign_currency_id' => '2',
// mandatory for transaction: // mandatory for transaction:
'transaction_description' => 'Some descr', 'transaction_description' => 'Some descr',
'transaction_type' => 'withdrawal', 'transaction_type' => 'withdrawal',
'transaction_currency_id' => '1', 'transaction_currency_id' => '1',
'amount' => '30', 'amount' => '30',
// mandatory account info: // mandatory account info:
'source_id' => '1', 'source_id' => $source->id,
'destination_name' => 'Some Expense', 'withdrawal_destination_id' => $destination->id,
// optional fields: // optional fields:
'budget_id' => '1', 'budget_id' => '1',
'category' => 'CategoryA', 'category' => 'CategoryA',
'tags' => 'A,B,C', 'tags' => 'A,B,C',
'create_another' => '1', 'create_another' => '1',
'repetition_end' => 'times', 'repetition_end' => 'times',
'repetitions' => 3, 'repetitions' => 3,
]; ];
$recurringRepos->shouldReceive('store')->andReturn($recurrence)->once(); $recurringRepos->shouldReceive('store')->andReturn($recurrence)->once();
@@ -321,17 +424,20 @@ class CreateControllerTest extends TestCase
*/ */
public function testStoreTransfer(): void public function testStoreTransfer(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0'); $this->mock(BudgetRepositoryInterface::class);
return;
$recurringRepos = $this->mock(RecurringRepositoryInterface::class); $recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$budgetRepos = $this->mock(BudgetRepositoryInterface::class); $validator = $this->mock(AccountValidator::class);
$categoryRepos = $this->mock(CategoryRepositoryInterface::class); $source = $this->getRandomAsset();
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class); $destination = $this->getRandomAsset($source->id);
$recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $this->mockDefaultSession();
$accountRepos = $this->mock(AccountRepositoryInterface::class); Preferences::shouldReceive('mark')->atLeast()->once();
$piggyRepos = $this->mock(PiggyBankRepositoryInterface::class);
// validator:
$validator->shouldReceive('setTransactionType')->withArgs(['transfer'])->atLeast()->once();
$validator->shouldReceive('validateSource')->atLeast()->once()->andReturn(true);
$validator->shouldReceive('validateDestination')->atLeast()->once()->andReturn(true);
$tomorrow = Carbon::now()->addDays(2); $tomorrow = Carbon::now()->addDays(2);
@@ -354,10 +460,8 @@ class CreateControllerTest extends TestCase
'amount' => '30', 'amount' => '30',
// mandatory account info: // mandatory account info:
'source_id' => '2', 'source_id' => $source->id,
'source_name' => 'Some source', 'destination_id' => $destination->id,
'destination_id' => '1',
'destination_name' => 'Some Expense',
// optional fields: // optional fields:
'budget_id' => '1', 'budget_id' => '1',
@@ -382,49 +486,51 @@ class CreateControllerTest extends TestCase
*/ */
public function testStoreUntilDate(): void public function testStoreUntilDate(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0'); $this->mock(BudgetRepositoryInterface::class);
return;
$recurringRepos = $this->mock(RecurringRepositoryInterface::class); $recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$budgetRepos = $this->mock(BudgetRepositoryInterface::class); $validator = $this->mock(AccountValidator::class);
$categoryRepos = $this->mock(CategoryRepositoryInterface::class); $source = $this->getRandomAsset();
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class); $destination = $this->getRandomExpense();
$recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$piggyRepos = $this->mock(PiggyBankRepositoryInterface::class);
$this->mockDefaultSession();
Preferences::shouldReceive('mark')->atLeast()->once();
// validator:
$validator->shouldReceive('setTransactionType')->withArgs(['withdrawal'])->atLeast()->once();
$validator->shouldReceive('validateSource')->atLeast()->once()->andReturn(true);
$validator->shouldReceive('validateDestination')->atLeast()->once()->andReturn(true);
$tomorrow = Carbon::now()->addDays(2); $tomorrow = Carbon::now()->addDays(2);
$recurrence = $this->user()->recurrences()->first(); $recurrence = $this->user()->recurrences()->first();
$data = [ $data = [
'title' => 'hello' . $this->randomInt(), 'title' => sprintf('hello %d', $this->randomInt()),
'first_date' => $tomorrow->format('Y-m-d'), 'first_date' => $tomorrow->format('Y-m-d'),
'repetition_type' => 'daily', 'repetition_type' => 'daily',
'skip' => 0, 'skip' => 0,
'recurring_description' => 'Some descr' . $this->randomInt(), 'recurring_description' => sprintf('Some descr %d', $this->randomInt()),
'active' => '1', 'active' => '1',
'apply_rules' => '1', 'apply_rules' => '1',
'foreign_amount' => '1', 'foreign_amount' => '1',
'foreign_currency_id' => '2', 'foreign_currency_id' => '2',
// mandatory for transaction: // mandatory for transaction:
'transaction_description' => 'Some descr', 'transaction_description' => 'Some descr',
'transaction_type' => 'withdrawal', 'transaction_type' => 'withdrawal',
'transaction_currency_id' => '1', 'transaction_currency_id' => '1',
'amount' => '30', 'amount' => '30',
// mandatory account info: // mandatory account info:
'source_id' => '1', 'source_id' => $source->id,
'destination_name' => 'Some Expense', 'withdrawal_destination_id' => $destination->id,
// optional fields: // optional fields:
'budget_id' => '1', 'budget_id' => '1',
'category' => 'CategoryA', 'category' => 'CategoryA',
'tags' => 'A,B,C', 'tags' => 'A,B,C',
'create_another' => '1', 'create_another' => '1',
'repetition_end' => 'until_date', 'repetition_end' => 'until_date',
'repeat_until' => $tomorrow->format('Y-m-d'), 'repeat_until' => $tomorrow->format('Y-m-d'),
]; ];
$recurringRepos->shouldReceive('store')->andReturn($recurrence)->once(); $recurringRepos->shouldReceive('store')->andReturn($recurrence)->once();
@@ -441,48 +547,51 @@ class CreateControllerTest extends TestCase
*/ */
public function testStoreYearly(): void public function testStoreYearly(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0'); $this->mock(BudgetRepositoryInterface::class);
return;
$recurringRepos = $this->mock(RecurringRepositoryInterface::class); $recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$budgetRepos = $this->mock(BudgetRepositoryInterface::class); $validator = $this->mock(AccountValidator::class);
$categoryRepos = $this->mock(CategoryRepositoryInterface::class); $source = $this->getRandomAsset();
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class); $destination = $this->getRandomExpense();
$recurringRepos = $this->mock(RecurringRepositoryInterface::class); $tomorrow = Carbon::now()->addDays(2);
$userRepos = $this->mock(UserRepositoryInterface::class); $recurrence = $this->user()->recurrences()->first();
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$piggyRepos = $this->mock(PiggyBankRepositoryInterface::class);
$tomorrow = Carbon::now()->addDays(2); $this->mockDefaultSession();
$recurrence = $this->user()->recurrences()->first(); Preferences::shouldReceive('mark')->atLeast()->once();
$data = [
'title' => 'hello' . $this->randomInt(), // validator:
'first_date' => $tomorrow->format('Y-m-d'), $validator->shouldReceive('setTransactionType')->withArgs(['withdrawal'])->atLeast()->once();
'repetition_type' => 'yearly,2018-01-01', $validator->shouldReceive('validateSource')->atLeast()->once()->andReturn(true);
'skip' => 0, $validator->shouldReceive('validateDestination')->atLeast()->once()->andReturn(true);
'recurring_description' => 'Some descr' . $this->randomInt(),
'active' => '1', $data = [
'apply_rules' => '1', 'title' => sprintf('hello %d', $this->randomInt()),
'foreign_amount' => '1', 'first_date' => $tomorrow->format('Y-m-d'),
'foreign_currency_id' => '2', 'repetition_type' => 'yearly,2018-01-01',
'skip' => 0,
'recurring_description' => sprintf('Some descr %d', $this->randomInt()),
'active' => '1',
'apply_rules' => '1',
'foreign_amount' => '1',
'foreign_currency_id' => '2',
// mandatory for transaction: // mandatory for transaction:
'transaction_description' => 'Some descr', 'transaction_description' => 'Some descr',
'transaction_type' => 'withdrawal', 'transaction_type' => 'withdrawal',
'transaction_currency_id' => '1', 'transaction_currency_id' => '1',
'amount' => '30', 'amount' => '30',
// mandatory account info: // mandatory account info:
'source_id' => '1', 'source_id' => $source->id,
'destination_name' => 'Some Expense', 'withdrawal_destination_id' => $destination->id,
// optional fields: // optional fields:
'budget_id' => '1', 'budget_id' => '1',
'category' => 'CategoryA', 'category' => 'CategoryA',
'tags' => 'A,B,C', 'tags' => 'A,B,C',
'create_another' => '1', 'create_another' => '1',
'repetition_end' => 'times', 'repetition_end' => 'times',
'repetitions' => 3, 'repetitions' => 3,
]; ];
$recurringRepos->shouldReceive('store')->andReturn($recurrence)->once(); $recurringRepos->shouldReceive('store')->andReturn($recurrence)->once();

View File

@@ -28,6 +28,7 @@ use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log; use Log;
use Mockery; use Mockery;
use Preferences;
use Tests\TestCase; use Tests\TestCase;
/** /**
@@ -53,7 +54,9 @@ class DeleteControllerTest extends TestCase
$recurringRepos = $this->mock(RecurringRepositoryInterface::class); $recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$recurringRepos->shouldReceive('getTransactions')->andReturn(new Collection())->once(); $this->mockDefaultSession();
$recurringRepos->shouldReceive('getTransactions')->andReturn(new Collection)->once();
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true); $userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true);
$this->be($this->user()); $this->be($this->user());
@@ -70,6 +73,9 @@ class DeleteControllerTest extends TestCase
$recurringRepos = $this->mock(RecurringRepositoryInterface::class); $recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$this->mockDefaultSession();
Preferences::shouldReceive('mark')->atLeast()->once();
$recurringRepos->shouldReceive('destroy')->once(); $recurringRepos->shouldReceive('destroy')->once();

View File

@@ -23,19 +23,21 @@ declare(strict_types=1);
namespace Tests\Feature\Controllers\Recurring; namespace Tests\Feature\Controllers\Recurring;
use Amount;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Factory\CategoryFactory;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Transformers\RecurrenceTransformer; use FireflyIII\Transformers\RecurrenceTransformer;
use FireflyIII\Validation\AccountValidator;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log; use Log;
use Mockery; use Mockery;
use Preferences;
use Steam;
use Tests\TestCase; use Tests\TestCase;
/** /**
@@ -58,23 +60,44 @@ class EditControllerTest extends TestCase
*/ */
public function testEdit(): void public function testEdit(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0'); $this->mock(CurrencyRepositoryInterface::class);
$this->mock(PiggyBankRepositoryInterface::class);
return; $recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$recurringRepos = $this->mock(RecurringRepositoryInterface::class); $budgetRepos = $this->mock(BudgetRepositoryInterface::class);
$budgetRepos = $this->mock(BudgetRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $accountRepos = $this->mock(AccountRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class); $transformer = $this->mock(RecurrenceTransformer::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class); $asset = $this->getRandomAsset();
$categoryFactory = $this->mock(CategoryFactory::class); $euro = $this->getEuro();
$piggyRepos = $this->mock(PiggyBankRepositoryInterface::class); $cash = $this->getRandomAsset();
$transformer = $this->mock(RecurrenceTransformer::class); $this->mockDefaultSession();
$transformed = [
'transactions' => [
[
'source_id' => 1,
'destination_id' => 1,
],
],
];
// for view:
$accountRepos->shouldReceive('getActiveAccountsByType')->atLeast()->once()->andReturn(new Collection([$asset]));
Steam::shouldReceive('balance')->andReturn('100')->atLeast()->once();
$accountRepos->shouldReceive('getAccountCurrency')->atLeast()->once()->andReturn($euro);
$accountRepos->shouldReceive('getMetaValue')->atLeast()->once()->andReturnNull();
$accountRepos->shouldReceive('getCashAccount')->atLeast()->once()->andReturn($cash);
//Amount::shouldReceive('getDefaultCurrency')->andReturn($euro)->atLeast()->once();
Amount::shouldReceive('formatAnything')->atLeast()->once()->andReturn('100');
// transform recurrence.
$transformer->shouldReceive('setParameters')->atLeast()->once(); $transformer->shouldReceive('setParameters')->atLeast()->once();
$transformer->shouldReceive('transform')->atLeast()->once(); $transformer->shouldReceive('transform')->atLeast()->once()->andReturn($transformed);
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true); $userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true);
// get stuff from recurrence.
$recurringRepos->shouldReceive('setUser'); $recurringRepos->shouldReceive('setUser');
$recurringRepos->shouldReceive('getNoteText')->andReturn('Note!'); $recurringRepos->shouldReceive('getNoteText')->andReturn('Note!');
$recurringRepos->shouldReceive('repetitionDescription')->andReturn('dunno'); $recurringRepos->shouldReceive('repetitionDescription')->andReturn('dunno');
@@ -90,6 +113,8 @@ class EditControllerTest extends TestCase
$response = $this->get(route('recurring.edit', [1])); $response = $this->get(route('recurring.edit', [1]));
$response->assertStatus(200); $response->assertStatus(200);
$response->assertSee('<ol class="breadcrumb">'); $response->assertSee('<ol class="breadcrumb">');
$response->assertSee('deposit_source_id');
$response->assertSee('withdrawal_destination_id');
} }
/** /**
@@ -98,51 +123,52 @@ class EditControllerTest extends TestCase
*/ */
public function testUpdate(): void public function testUpdate(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0'); $this->mock(BudgetRepositoryInterface::class);
$recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$validator = $this->mock(AccountValidator::class);
$expense = $this->getRandomExpense();
return; $this->mockDefaultSession();
$recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$budgetRepos = $this->mock(BudgetRepositoryInterface::class);
$categoryRepos = $this->mock(CategoryRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$categoryFactory = $this->mock(CategoryFactory::class);
$piggyRepos = $this->mock(PiggyBankRepositoryInterface::class);
$transformer = $this->mock(RecurrenceTransformer::class);
$recurringRepos->shouldReceive('update')->once(); $recurringRepos->shouldReceive('update')->once();
// validator:
$validator->shouldReceive('setTransactionType')->withArgs(['withdrawal'])->atLeast()->once();
$validator->shouldReceive('validateSource')->atLeast()->once()->andReturn(true);
$validator->shouldReceive('validateDestination')->atLeast()->once()->andReturn(true);
Preferences::shouldReceive('mark')->once();
$tomorrow = Carbon::now()->addDays(2); $tomorrow = Carbon::now()->addDays(2);
$recurrence = $this->user()->recurrences()->first(); $recurrence = $this->user()->recurrences()->first();
$data = [ $data = [
'id' => $recurrence->id, 'id' => $recurrence->id,
'title' => 'hello', 'title' => 'hello',
'first_date' => $tomorrow->format('Y-m-d'), 'first_date' => $tomorrow->format('Y-m-d'),
'repetition_type' => 'daily', 'repetition_type' => 'daily',
'skip' => 0, 'skip' => 0,
'recurring_description' => 'Some descr', 'recurring_description' => 'Some descr',
'active' => '1', 'active' => '1',
'apply_rules' => '1', 'apply_rules' => '1',
'return_to_edit' => '1', 'return_to_edit' => '1',
// mandatory for transaction: // mandatory for transaction:
'transaction_description' => 'Some descr', 'transaction_description' => 'Some descr',
'transaction_type' => 'withdrawal', 'transaction_type' => 'withdrawal',
'transaction_currency_id' => '1', 'transaction_currency_id' => '1',
'amount' => '30', 'amount' => '30',
// mandatory account info: // mandatory account info:
'source_id' => '1', 'source_id' => '1',
'source_name' => '', 'source_name' => '',
'destination_id' => '', 'withdrawal_destination_id' => $expense->id,
'destination_name' => 'Some Expense', 'destination_id' => '',
'destination_name' => 'Some Expense',
// optional fields: // optional fields:
'budget_id' => '1', 'budget_id' => '1',
'category' => 'CategoryA', 'category' => 'CategoryA',
'tags' => 'A,B,C', 'tags' => 'A,B,C',
'create_another' => '1', 'create_another' => '1',
'repetition_end' => 'times', 'repetition_end' => 'times',
'repetitions' => 3, 'repetitions' => 3,
]; ];

View File

@@ -25,6 +25,7 @@ namespace Tests\Feature\Controllers\Recurring;
use FireflyIII\Factory\CategoryFactory; use FireflyIII\Factory\CategoryFactory;
use FireflyIII\Models\Configuration; use FireflyIII\Models\Configuration;
use FireflyIII\Models\Preference;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
@@ -32,6 +33,7 @@ use FireflyIII\Transformers\RecurrenceTransformer;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log; use Log;
use Mockery; use Mockery;
use Preferences;
use Tests\TestCase; use Tests\TestCase;
/** /**
@@ -61,6 +63,13 @@ class IndexControllerTest extends TestCase
$categoryFactory = $this->mock(CategoryFactory::class); $categoryFactory = $this->mock(CategoryFactory::class);
$transformer = $this->mock(RecurrenceTransformer::class); $transformer = $this->mock(RecurrenceTransformer::class);
// mock calls
$pref = new Preference;
$pref->data = 50;
Preferences::shouldReceive('get')->withArgs(['listPageSize', 50])->atLeast()->once()->andReturn($pref);
$this->mockDefaultSession();
$transformer->shouldReceive('setParameters')->atLeast()->once(); $transformer->shouldReceive('setParameters')->atLeast()->once();
$transformer->shouldReceive('transform')->atLeast()->once()->andReturn( $transformer->shouldReceive('transform')->atLeast()->once()->andReturn(
[ [
@@ -83,7 +92,6 @@ class IndexControllerTest extends TestCase
// mock cron job config: // mock cron job config:
\FireflyConfig::shouldReceive('get')->withArgs(['last_rt_job', 0])->once()->andReturn($config); \FireflyConfig::shouldReceive('get')->withArgs(['last_rt_job', 0])->once()->andReturn($config);
\FireflyConfig::shouldReceive('get')->withArgs(['is_demo_site', false])->once()->andReturn($falseConfig);
$repository->shouldReceive('get')->andReturn($collection)->once(); $repository->shouldReceive('get')->andReturn($collection)->once();
@@ -94,6 +102,9 @@ class IndexControllerTest extends TestCase
$response->assertSee('<ol class="breadcrumb">'); $response->assertSee('<ol class="breadcrumb">');
} }
/**
* @covers \FireflyIII\Http\Controllers\Recurring\IndexController
*/
public function testShow(): void public function testShow(): void
{ {
$repository = $this->mock(RecurringRepositoryInterface::class); $repository = $this->mock(RecurringRepositoryInterface::class);
@@ -102,6 +113,8 @@ class IndexControllerTest extends TestCase
$categoryFactory = $this->mock(CategoryFactory::class); $categoryFactory = $this->mock(CategoryFactory::class);
$transformer = $this->mock(RecurrenceTransformer::class); $transformer = $this->mock(RecurrenceTransformer::class);
$this->mockDefaultSession();
$transformer->shouldReceive('setParameters')->atLeast()->once(); $transformer->shouldReceive('setParameters')->atLeast()->once();
$transformer->shouldReceive('transform')->atLeast()->once()->andReturn( $transformer->shouldReceive('transform')->atLeast()->once()->andReturn(
[ [
@@ -109,7 +122,13 @@ class IndexControllerTest extends TestCase
'first_date' => '2018-01-01', 'first_date' => '2018-01-01',
'repeat_until' => null, 'repeat_until' => null,
'latest_date' => null, 'latest_date' => null,
'recurrence_repetitions' => [], 'recurrence_repetitions' => [
[
'occurrences' => [
'2019-01-01'
]
]
],
] ]
); );

View File

@@ -22,14 +22,14 @@ declare(strict_types=1);
namespace Tests\Feature\Controllers\Report; namespace Tests\Feature\Controllers\Report;
use Amount;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Helpers\Fiscal\FiscalHelperInterface; use FireflyIII\Helpers\Fiscal\FiscalHelperInterface;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Illuminate\Support\Collection;
use Log; use Log;
use Preferences;
use Tests\TestCase; use Tests\TestCase;
/** /**
@@ -56,46 +56,30 @@ class ExpenseControllerTest extends TestCase
*/ */
public function testBudget(): void public function testBudget(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
$expense = $this->user()->accounts()->where('account_type_id', 4)->first();
$revenue = $this->user()->accounts()->where('account_type_id', 5)->first();
$repository = $this->mock(AccountRepositoryInterface::class); $repository = $this->mock(AccountRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class); $fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(GroupCollectorInterface::class);
$expense = $this->getRandomExpense();
$revenue = $this->getRandomRevenue();
$date = new Carbon; $date = new Carbon;
$transactions = [$this->getRandomWithdrawalAsArray()];
$this->mockDefaultSession();
Preferences::shouldReceive('lastActivity')->atLeast()->once();
$fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date); $fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date);
$fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date); $fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date);
$repository->shouldReceive('findByName')->once()->withArgs([$expense->name, [AccountType::REVENUE]])->andReturn($revenue); $repository->shouldReceive('findByName')->once()->withArgs([$expense->name, [AccountType::REVENUE]])->andReturn($revenue);
Amount::shouldReceive('formatAnything')->atLeast()->once()->andReturn('-100');
// fake collection:
$transA = new Transaction;
$transA->transaction_currency_id = 1;
$transA->transaction_budget_name = 'Budget';
$transA->transaction_budget_id = 1;
$transA->transaction_currency_symbol = 'A';
$transA->transaction_currency_dp = 2;
$transA->transaction_amount = '100';
$transB = new Transaction;
$transB->transaction_currency_id = 2;
$transB->transaction_budget_name = null;
$transB->transaction_budget_id = 0;
$transB->transaction_journal_budget_name = 'Budget2';
$transB->transaction_journal_budget_id = 2;
$transB->transaction_currency_symbol = 'A';
$transB->transaction_currency_dp = 2;
$transB->transaction_amount = '100';
$collection = new Collection([$transA, $transB]);
// mock collector for spentByBudget (complex)
$collector = $this->mock(TransactionCollectorInterface::class);
// dont care about any calls, just return a default set of fake transactions: // dont care about any calls, just return a default set of fake transactions:
$collector->shouldReceive('setRange')->andReturnSelf(); $collector->shouldReceive('setRange')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('setTypes')->andReturnSelf(); $collector->shouldReceive('setTypes')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('setAccounts')->andReturnSelf(); $collector->shouldReceive('setAccounts')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('setOpposingAccounts')->andReturnSelf(); $collector->shouldReceive('withBudgetInformation')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('withBudgetInformation')->andReturnSelf(); $collector->shouldReceive('getExtractedJournals')->andReturn($transactions)->atLeast()->once();
$collector->shouldReceive('getTransactions')->andReturn($collection);
$this->be($this->user()); $this->be($this->user());
@@ -108,57 +92,37 @@ class ExpenseControllerTest extends TestCase
*/ */
public function testCategory(): void public function testCategory(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
$expense = $this->user()->accounts()->where('account_type_id', 4)->first();
$revenue = $this->user()->accounts()->where('account_type_id', 5)->first();
$repository = $this->mock(AccountRepositoryInterface::class); $repository = $this->mock(AccountRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class); $fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(GroupCollectorInterface::class);
$expense = $this->getRandomExpense();
$revenue = $this->getRandomRevenue();
$date = new Carbon; $date = new Carbon;
$one = $this->getRandomWithdrawalAsArray();
$two = $this->getRandomWithdrawalAsArray();
// two categories
$oneCat = $this->getRandomCategory();
$twoCat = $this->user()->categories()->where('id', '!=', $oneCat->id)->inRandomOrder()->first();
$one['category_id'] = $oneCat->id;
$one['category_name'] = $oneCat->name;
$two['category_id'] = $twoCat->id;
$two['category_name'] = $twoCat->name;
$this->mockDefaultSession();
Preferences::shouldReceive('lastActivity')->atLeast()->once();
$fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date); $fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date);
$fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date); $fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date);
$repository->shouldReceive('findByName')->once()->withArgs([$expense->name, [AccountType::REVENUE]])->andReturn($revenue); $repository->shouldReceive('findByName')->once()->withArgs([$expense->name, [AccountType::REVENUE]])->andReturn($revenue);
Amount::shouldReceive('formatAnything')->atLeast()->once()->andReturn('-100');
// fake collection: $collector->shouldReceive('setRange')->andReturnSelf()->atLeast()->once();
$transA = new Transaction; $collector->shouldReceive('setTypes')->andReturnSelf()->atLeast()->once();
$transA->transaction_currency_id = 1; $collector->shouldReceive('setAccounts')->andReturnSelf()->atLeast()->once();
$transA->transaction_category_name = 'Category'; $collector->shouldReceive('withCategoryInformation')->andReturnSelf()->atLeast()->once();
$transA->transaction_category_id = 1; $collector->shouldReceive('getExtractedJournals')->andReturn([$one], [$two])->atLeast()->once();
$transA->transaction_currency_symbol = 'A';
$transA->transaction_currency_dp = 2;
$transA->transaction_amount = '100';
$transB = new Transaction;
$transB->transaction_currency_id = 2;
$transB->transaction_category_name = null;
$transB->transaction_category_id = 0;
$transB->transaction_journal_category_name = 'Category2';
$transB->transaction_journal_category_id = 2;
$transB->transaction_currency_symbol = 'A';
$transB->transaction_currency_dp = 2;
$transB->transaction_amount = '100';
$collection = new Collection([$transA, $transB]);
$transC = new Transaction;
$transC->transaction_currency_id = 3;
$transC->transaction_category_name = null;
$transC->transaction_category_id = 0;
$transC->transaction_journal_category_name = 'Category3';
$transC->transaction_journal_category_id = 3;
$transC->transaction_currency_symbol = 'A';
$transC->transaction_currency_dp = 2;
$transC->transaction_amount = '100';
$secondCollection = new Collection([$transC]);
// mock collector for spentByCategory and earnedByCategory (complex)
$collector = $this->mock(TransactionCollectorInterface::class);
// dont care about any calls, just return a default set of fake transactions:
$collector->shouldReceive('setRange')->andReturnSelf();
$collector->shouldReceive('setTypes')->andReturnSelf();
$collector->shouldReceive('setAccounts')->andReturnSelf();
$collector->shouldReceive('setOpposingAccounts')->andReturnSelf();
$collector->shouldReceive('withCategoryInformation')->andReturnSelf();
$collector->shouldReceive('getTransactions')->andReturn($collection, $secondCollection);
//$collector->shouldReceive('')->andReturnSelf();
$this->be($this->user()); $this->be($this->user());
$response = $this->get(route('report-data.expense.category', ['1', $expense->id, '20170101', '20170131'])); $response = $this->get(route('report-data.expense.category', ['1', $expense->id, '20170101', '20170131']));
@@ -170,45 +134,27 @@ class ExpenseControllerTest extends TestCase
*/ */
public function testSpent(): void public function testSpent(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
$expense = $this->user()->accounts()->where('account_type_id', 4)->first();
$revenue = $this->user()->accounts()->where('account_type_id', 5)->first();
$repository = $this->mock(AccountRepositoryInterface::class); $repository = $this->mock(AccountRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class); $fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(GroupCollectorInterface::class);
$expense = $this->getRandomExpense();
$revenue = $this->getRandomRevenue();
$date = new Carbon; $date = new Carbon;
$transactions = [$this->getRandomWithdrawalAsArray()];
$this->mockDefaultSession();
Preferences::shouldReceive('lastActivity')->atLeast()->once();
$fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date); $fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date);
$fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date); $fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date);
$repository->shouldReceive('findByName')->once()->withArgs([$expense->name, [AccountType::REVENUE]])->andReturn($revenue); $repository->shouldReceive('findByName')->once()->withArgs([$expense->name, [AccountType::REVENUE]])->andReturn($revenue);
// fake collection: $collector->shouldReceive('setRange')->andReturnSelf()->atLeast()->once();
$transA = new Transaction; $collector->shouldReceive('setTypes')->andReturnSelf()->atLeast()->once();
$transA->transaction_currency_id = 1; $collector->shouldReceive('setAccounts')->andReturnSelf()->atLeast()->once();
$transA->transaction_category_name = 'Category'; $collector->shouldReceive('getExtractedJournals')->andReturn($transactions)->atLeast()->once();
$transA->transaction_category_id = 1; Amount::shouldReceive('formatAnything')->atLeast()->once()->andReturn('-100');
$transA->transaction_currency_symbol = 'A';
$transA->transaction_currency_dp = 2;
$transA->transaction_amount = '100';
$transB = new Transaction;
$transB->transaction_currency_id = 2;
$transB->transaction_category_name = null;
$transB->transaction_category_id = 0;
$transB->transaction_journal_budget_name = 'Category2';
$transB->transaction_journal_budget_id = 2;
$transB->transaction_currency_symbol = 'A';
$transB->transaction_currency_dp = 2;
$transB->transaction_amount = '100';
$collection = new Collection([$transA, $transB]);
// mock collector for spentInPeriod and earnedInPeriod (complex)
$collector = $this->mock(TransactionCollectorInterface::class);
// dont care about any calls, just return a default set of fake transactions:
$collector->shouldReceive('setRange')->andReturnSelf();
$collector->shouldReceive('setTypes')->andReturnSelf();
$collector->shouldReceive('setAccounts')->andReturnSelf();
$collector->shouldReceive('setOpposingAccounts')->andReturnSelf();
$collector->shouldReceive('getTransactions')->andReturn($collection);
$this->be($this->user()); $this->be($this->user());
$response = $this->get(route('report-data.expense.spent', ['1', $expense->id, '20170101', '20170131'])); $response = $this->get(route('report-data.expense.spent', ['1', $expense->id, '20170101', '20170131']));
@@ -220,47 +166,29 @@ class ExpenseControllerTest extends TestCase
*/ */
public function testTopExpense(): void public function testTopExpense(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0'); Log::debug(sprintf('Now in test %s', __METHOD__));
return;
$expense = $this->user()->accounts()->where('account_type_id', 4)->first();
$revenue = $this->user()->accounts()->where('account_type_id', 5)->first();
$repository = $this->mock(AccountRepositoryInterface::class); $repository = $this->mock(AccountRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class); $fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(GroupCollectorInterface::class);
$expense = $this->getRandomExpense();
$revenue = $this->getRandomRevenue();
$date = new Carbon; $date = new Carbon;
$transactions = [$this->getRandomWithdrawalAsArray()];
$this->mockDefaultSession();
Preferences::shouldReceive('lastActivity')->atLeast()->once();
$fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date); $fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date);
$fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date); $fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date);
$repository->shouldReceive('findByName')->once()->withArgs([$expense->name, [AccountType::REVENUE]])->andReturn($revenue); $repository->shouldReceive('findByName')->once()->withArgs([$expense->name, [AccountType::REVENUE]])->andReturn($revenue);
// fake collection: $collector->shouldReceive('setRange')->andReturnSelf()->atLeast()->once();
$transA = new Transaction; $collector->shouldReceive('setTypes')->andReturnSelf()->atLeast()->once();
$transA->transaction_currency_id = 1; $collector->shouldReceive('withAccountInformation')->andReturnSelf()->atLeast()->once();
$transA->transaction_category_name = 'Category';
$transA->transaction_category_id = 1;
$transA->transaction_currency_symbol = 'A';
$transA->transaction_currency_dp = 2;
$transA->transaction_amount = '100';
$transA->opposing_account_id = $expense->id;
$transB = new Transaction;
$transB->transaction_currency_id = 2;
$transB->transaction_category_name = null;
$transB->transaction_category_id = 0;
$transB->transaction_journal_budget_name = 'Category2';
$transB->transaction_journal_budget_id = 2;
$transB->transaction_currency_symbol = 'A';
$transB->transaction_currency_dp = 2;
$transB->transaction_amount = '100';
$transB->opposing_account_id = $expense->id;
$collection = new Collection([$transA, $transB]);
// mock collector for topExpense (complex) $collector->shouldReceive('setAccounts')->andReturnSelf()->atLeast()->once();
$collector = $this->mock(TransactionCollectorInterface::class); $collector->shouldReceive('getExtractedJournals')->andReturn($transactions)->atLeast()->once();
// dont care about any calls, just return a default set of fake transactions:
$collector->shouldReceive('setRange')->andReturnSelf();
$collector->shouldReceive('setTypes')->andReturnSelf();
$collector->shouldReceive('setAccounts')->andReturnSelf();
$collector->shouldReceive('setOpposingAccounts')->andReturnSelf();
$collector->shouldReceive('getTransactions')->andReturn($collection);
$this->be($this->user()); $this->be($this->user());
$response = $this->get(route('report-data.expense.expenses', ['1', $expense->id, '20170101', '20170131'])); $response = $this->get(route('report-data.expense.expenses', ['1', $expense->id, '20170101', '20170131']));
@@ -272,46 +200,28 @@ class ExpenseControllerTest extends TestCase
*/ */
public function testTopIncome(): void public function testTopIncome(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0'); Log::debug(sprintf('Now in test %s', __METHOD__));
return;
$expense = $this->user()->accounts()->where('account_type_id', 4)->first();
$revenue = $this->user()->accounts()->where('account_type_id', 5)->first();
$repository = $this->mock(AccountRepositoryInterface::class); $repository = $this->mock(AccountRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class); $fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(GroupCollectorInterface::class);
$expense = $this->getRandomExpense();
$revenue = $this->getRandomRevenue();
$date = new Carbon; $date = new Carbon;
$transactions = [$this->getRandomWithdrawalAsArray()];
$this->mockDefaultSession();
Preferences::shouldReceive('lastActivity')->atLeast()->once();
$fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date); $fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date);
$fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date); $fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date);
$repository->shouldReceive('findByName')->once()->withArgs([$expense->name, [AccountType::REVENUE]])->andReturn($revenue); $repository->shouldReceive('findByName')->once()->withArgs([$expense->name, [AccountType::REVENUE]])->andReturn($revenue);
// fake collection: $collector->shouldReceive('setRange')->andReturnSelf()->atLeast()->once();
$transA = new Transaction; $collector->shouldReceive('setTypes')->andReturnSelf()->atLeast()->once();
$transA->transaction_currency_id = 1; $collector->shouldReceive('setAccounts')->andReturnSelf()->atLeast()->once();
$transA->transaction_category_name = 'Category'; $collector->shouldReceive('withAccountInformation')->andReturnSelf()->atLeast()->once();
$transA->transaction_category_id = 1; $collector->shouldReceive('getExtractedJournals')->andReturn($transactions)->atLeast()->once();
$transA->transaction_currency_symbol = 'A';
$transA->transaction_currency_dp = 2;
$transA->transaction_amount = '100';
$transA->opposing_account_id = $expense->id;
$transB = new Transaction;
$transB->transaction_currency_id = 2;
$transB->transaction_category_name = null;
$transB->transaction_category_id = 0;
$transB->transaction_journal_budget_name = 'Category2';
$transB->transaction_journal_budget_id = 2;
$transB->transaction_currency_symbol = 'A';
$transB->transaction_currency_dp = 2;
$transB->transaction_amount = '100';
$transB->opposing_account_id = $expense->id;
$collection = new Collection([$transA, $transB]);
$collector = $this->mock(TransactionCollectorInterface::class);
$collector->shouldReceive('setRange')->andReturnSelf();
$collector->shouldReceive('setTypes')->andReturnSelf();
$collector->shouldReceive('setAccounts')->andReturnSelf();
$collector->shouldReceive('setOpposingAccounts')->andReturnSelf();
$collector->shouldReceive('getTransactions')->andReturn($collection);
//$collector->shouldReceive('')->andReturnSelf();
$this->be($this->user()); $this->be($this->user());

View File

@@ -33,6 +33,7 @@ use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use Log; use Log;
use Mockery; use Mockery;
use Preferences;
use Tests\TestCase; use Tests\TestCase;
/** /**
@@ -56,12 +57,13 @@ class CreateControllerTest extends TestCase
public function testCreate(): void public function testCreate(): void
{ {
// mock stuff // mock stuff
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$billRepos = $this->mock(BillRepositoryInterface::class); $billRepos = $this->mock(BillRepositoryInterface::class);
$ruleRepos = $this->mock(RuleRepositoryInterface::class); $ruleRepos = $this->mock(RuleRepositoryInterface::class);
$ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class); $ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$this->mockDefaultSession();
$this->mockIntroPreference('shown_demo_rules_create');
$ruleGroupRepos->shouldReceive('count')->atLeast()->once()->andReturn(1); $ruleGroupRepos->shouldReceive('count')->atLeast()->once()->andReturn(1);
$ruleRepos->shouldReceive('count')->atLeast()->once()->andReturn(1); $ruleRepos->shouldReceive('count')->atLeast()->once()->andReturn(1);
@@ -80,17 +82,17 @@ class CreateControllerTest extends TestCase
public function testCreateFromBill(): void public function testCreateFromBill(): void
{ {
// mock stuff // mock stuff
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$billRepos = $this->mock(BillRepositoryInterface::class); $billRepos = $this->mock(BillRepositoryInterface::class);
$ruleRepos = $this->mock(RuleRepositoryInterface::class); $ruleRepos = $this->mock(RuleRepositoryInterface::class);
$ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class); $ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$this->mockDefaultSession();
$ruleGroupRepos->shouldReceive('count')->atLeast()->once()->andReturn(1); $ruleGroupRepos->shouldReceive('count')->atLeast()->once()->andReturn(1);
$ruleRepos->shouldReceive('count')->atLeast()->once()->andReturn(1); $ruleRepos->shouldReceive('count')->atLeast()->once()->andReturn(1);
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true); $userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$this->be($this->user()); $this->be($this->user());
$response = $this->get(route('rules.create-from-bill', [1, 1])); $response = $this->get(route('rules.create-from-bill', [1, 1]));
@@ -114,17 +116,17 @@ class CreateControllerTest extends TestCase
$this->session(['_old_input' => $old]); $this->session(['_old_input' => $old]);
// mock stuff // mock stuff
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$ruleRepos = $this->mock(RuleRepositoryInterface::class); $ruleRepos = $this->mock(RuleRepositoryInterface::class);
$ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class); $ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$this->mockDefaultSession();
$this->mockIntroPreference('shown_demo_rules_create');
$ruleGroupRepos->shouldReceive('count')->atLeast()->once()->andReturn(1); $ruleGroupRepos->shouldReceive('count')->atLeast()->once()->andReturn(1);
$ruleRepos->shouldReceive('count')->atLeast()->once()->andReturn(1); $ruleRepos->shouldReceive('count')->atLeast()->once()->andReturn(1);
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true); $userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$this->be($this->user()); $this->be($this->user());
$response = $this->get(route('rules.create', [1])); $response = $this->get(route('rules.create', [1]));
$response->assertStatus(200); $response->assertStatus(200);
@@ -139,12 +141,13 @@ class CreateControllerTest extends TestCase
{ {
// mock stuff // mock stuff
$repository = $this->mock(RuleRepositoryInterface::class); $repository = $this->mock(RuleRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class); $ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$this->mockDefaultSession();
Preferences::shouldReceive('mark')->atLeast()->once();
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('store')->andReturn(new Rule); $repository->shouldReceive('store')->andReturn(new Rule);
$this->session(['rules.create.uri' => 'http://localhost']); $this->session(['rules.create.uri' => 'http://localhost']);

View File

@@ -30,6 +30,7 @@ use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use Log; use Log;
use Mockery; use Mockery;
use Preferences;
use Tests\TestCase; use Tests\TestCase;
/** /**
@@ -54,13 +55,14 @@ class DeleteControllerTest extends TestCase
public function testDelete(): void public function testDelete(): void
{ {
// mock stuff // mock stuff
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$ruleRepos = $this->mock(RuleRepositoryInterface::class); $ruleRepos = $this->mock(RuleRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$this->mockDefaultSession();
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true); $userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$this->be($this->user()); $this->be($this->user());
$response = $this->get(route('rules.delete', [1])); $response = $this->get(route('rules.delete', [1]));
@@ -75,10 +77,11 @@ class DeleteControllerTest extends TestCase
{ {
// mock stuff // mock stuff
$repository = $this->mock(RuleRepositoryInterface::class); $repository = $this->mock(RuleRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('destroy'); $repository->shouldReceive('destroy');
$this->mockDefaultSession();
Preferences::shouldReceive('mark')->atLeast()->once();
$this->session(['rules.delete.uri' => 'http://localhost']); $this->session(['rules.delete.uri' => 'http://localhost']);
$this->be($this->user()); $this->be($this->user());
$response = $this->post(route('rules.destroy', [1])); $response = $this->post(route('rules.destroy', [1]));

View File

@@ -33,6 +33,7 @@ use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log; use Log;
use Mockery; use Mockery;
use Preferences;
use Tests\TestCase; use Tests\TestCase;
/** /**
@@ -57,12 +58,11 @@ class EditControllerTest extends TestCase
// mock stuff // mock stuff
$groupRepos = $this->mock(RuleGroupRepositoryInterface::class); $groupRepos = $this->mock(RuleGroupRepositoryInterface::class);
$repository = $this->mock(RuleRepositoryInterface::class); $repository = $this->mock(RuleRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$this->mockDefaultSession();
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true); $userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('getPrimaryTrigger')->andReturn(new Rule); $repository->shouldReceive('getPrimaryTrigger')->andReturn(new Rule);
$groupRepos->shouldReceive('get')->andReturn(new Collection); $groupRepos->shouldReceive('get')->andReturn(new Collection);
@@ -90,11 +90,11 @@ class EditControllerTest extends TestCase
// mock stuff // mock stuff
$groupRepos = $this->mock(RuleGroupRepositoryInterface::class); $groupRepos = $this->mock(RuleGroupRepositoryInterface::class);
$repository = $this->mock(RuleRepositoryInterface::class); $repository = $this->mock(RuleRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true); $userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true);
$this->mockDefaultSession();
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('getPrimaryTrigger')->andReturn(new Rule); $repository->shouldReceive('getPrimaryTrigger')->andReturn(new Rule);
$groupRepos->shouldReceive('get')->andReturn(new Collection); $groupRepos->shouldReceive('get')->andReturn(new Collection);
@@ -112,11 +112,12 @@ class EditControllerTest extends TestCase
{ {
// mock stuff // mock stuff
$repository = $this->mock(RuleRepositoryInterface::class); $repository = $this->mock(RuleRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$rule = Rule::find(1);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal); $this->mockDefaultSession();
Preferences::shouldReceive('mark')->atLeast()->once();
$repository->shouldReceive('update'); $repository->shouldReceive('update');
$data = [ $data = [

View File

@@ -59,11 +59,11 @@ class IndexControllerTest extends TestCase
{ {
// mock stuff // mock stuff
$repository = $this->mock(RuleRepositoryInterface::class); $repository = $this->mock(RuleRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class); $ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('moveDown'); $repository->shouldReceive('moveDown');
$this->mockDefaultSession();
$this->be($this->user()); $this->be($this->user());
@@ -81,11 +81,12 @@ class IndexControllerTest extends TestCase
// mock stuff // mock stuff
$repository = $this->mock(RuleRepositoryInterface::class); $repository = $this->mock(RuleRepositoryInterface::class);
$ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class); $ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$this->mockDefaultSession();
$this->mockIntroPreference('shown_demo_rules_index');
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true); $userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$ruleGroupRepos->shouldReceive('count')->andReturn(0); $ruleGroupRepos->shouldReceive('count')->andReturn(0);
$ruleGroupRepos->shouldReceive('store'); $ruleGroupRepos->shouldReceive('store');
$repository->shouldReceive('getFirstRuleGroup')->andReturn(new RuleGroup); $repository->shouldReceive('getFirstRuleGroup')->andReturn(new RuleGroup);
@@ -106,11 +107,10 @@ class IndexControllerTest extends TestCase
{ {
// mock stuff // mock stuff
$repository = $this->mock(RuleRepositoryInterface::class); $repository = $this->mock(RuleRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class); $ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$this->mockDefaultSession();
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$data = ['actions' => [1, 2, 3]]; $data = ['actions' => [1, 2, 3]];
$repository->shouldReceive('reorderRuleActions')->once(); $repository->shouldReceive('reorderRuleActions')->once();
@@ -127,11 +127,10 @@ class IndexControllerTest extends TestCase
{ {
// mock stuff // mock stuff
$repository = $this->mock(RuleRepositoryInterface::class); $repository = $this->mock(RuleRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class); $ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$this->mockDefaultSession();
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$data = ['triggers' => [1, 2, 3]]; $data = ['triggers' => [1, 2, 3]];
$repository->shouldReceive('reorderRuleTriggers')->once(); $repository->shouldReceive('reorderRuleTriggers')->once();
@@ -148,12 +147,11 @@ class IndexControllerTest extends TestCase
{ {
// mock stuff // mock stuff
$repository = $this->mock(RuleRepositoryInterface::class); $repository = $this->mock(RuleRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class); $ruleGroupRepos = $this->mock(RuleGroupRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$this->mockDefaultSession();
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('moveUp'); $repository->shouldReceive('moveUp');
$this->be($this->user()); $this->be($this->user());

View File

@@ -63,6 +63,7 @@ class SelectControllerTest extends TestCase
$accountRepos = $this->mock(AccountRepositoryInterface::class); $accountRepos = $this->mock(AccountRepositoryInterface::class);
$repository = $this->mock(RuleRepositoryInterface::class); $repository = $this->mock(RuleRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$this->mockDefaultSession();
$this->session(['first' => new Carbon('2010-01-01')]); $this->session(['first' => new Carbon('2010-01-01')]);
$accountRepos->shouldReceive('getAccountsById')->andReturn(new Collection([$account])); $accountRepos->shouldReceive('getAccountsById')->andReturn(new Collection([$account]));
@@ -81,7 +82,7 @@ class SelectControllerTest extends TestCase
Queue::assertPushed( Queue::assertPushed(
ExecuteRuleOnExistingTransactions::class, function (Job $job) { ExecuteRuleOnExistingTransactions::class, function (Job $job) {
return $job->getRule()->id === 1; return 1=== $job->getRule()->id;
} }
); );
} }
@@ -93,6 +94,7 @@ class SelectControllerTest extends TestCase
{ {
$accountRepos = $this->mock(AccountRepositoryInterface::class); $accountRepos = $this->mock(AccountRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$this->mockDefaultSession();
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true); $userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true);
$accountRepos->shouldReceive('getAccountsByType')->andReturn(new Collection); $accountRepos->shouldReceive('getAccountsByType')->andReturn(new Collection);
@@ -119,11 +121,9 @@ class SelectControllerTest extends TestCase
// mock stuff // mock stuff
$matcher = $this->mock(TransactionMatcher::class); $matcher = $this->mock(TransactionMatcher::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class); $accountRepos = $this->mock(AccountRepositoryInterface::class);
$this->mockDefaultSession();
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$matcher->shouldReceive('setStrict')->once()->withArgs([false])->andReturnSelf(); $matcher->shouldReceive('setStrict')->once()->withArgs([false])->andReturnSelf();
$matcher->shouldReceive('setTriggeredLimit')->withArgs([10])->andReturnSelf()->once(); $matcher->shouldReceive('setTriggeredLimit')->withArgs([10])->andReturnSelf()->once();
@@ -144,11 +144,12 @@ class SelectControllerTest extends TestCase
{ {
$matcher = $this->mock(TransactionMatcher::class); $matcher = $this->mock(TransactionMatcher::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class); $accountRepos = $this->mock(AccountRepositoryInterface::class);
$this->mockDefaultSession();
$matcher->shouldReceive('setTriggeredLimit')->withArgs([10])->andReturnSelf()->once(); $matcher->shouldReceive('setTriggeredLimit')->withArgs([10])->andReturnSelf()->once();
$matcher->shouldReceive('setSearchLimit')->withArgs([200])->andReturnSelf()->once(); $matcher->shouldReceive('setSearchLimit')->withArgs([200])->andReturnSelf()->once();
$matcher->shouldReceive('setRule')->andReturnSelf()->once(); $matcher->shouldReceive('setRule')->andReturnSelf()->once();
$matcher->shouldReceive('findTransactionsByRule')->andReturn(new Collection); $matcher->shouldReceive('findTransactionsByRule')->andReturn([]);
$this->be($this->user()); $this->be($this->user());
$response = $this->get(route('rules.test-triggers-rule', [1])); $response = $this->get(route('rules.test-triggers-rule', [1]));
@@ -163,11 +164,9 @@ class SelectControllerTest extends TestCase
*/ */
public function testTestTriggersError(): void public function testTestTriggersError(): void
{ {
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class); $accountRepos = $this->mock(AccountRepositoryInterface::class);
$this->mockDefaultSession();
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$this->be($this->user()); $this->be($this->user());
$uri = route('rules.test-triggers'); $uri = route('rules.test-triggers');

View File

@@ -30,7 +30,6 @@ use DB;
use Exception; use Exception;
use FireflyConfig; use FireflyConfig;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
@@ -167,7 +166,9 @@ abstract class TestCase extends BaseTestCase
'currency_decimal_places' => $euro->decimal_places, 'currency_decimal_places' => $euro->decimal_places,
'amount' => '-30', 'amount' => '-30',
'budget_id' => $budget->id, 'budget_id' => $budget->id,
'budget_name' => $budget->name,
'category_id' => $category->id, 'category_id' => $category->id,
'category_name' => $category->name,
]; ];
} }

View File

@@ -491,25 +491,35 @@ class TransferCurrenciesCorrectionsTest extends TestCase
} }
/** /**
* Basic test. Source transaction has bad currency. * Basic test. Source transaction has bad currency, and this must be fixed.
*
* TODO something in this test is too random, and it fails. Not sure why.
* *
* @covers \FireflyIII\Console\Commands\Upgrade\TransferCurrenciesCorrections * @covers \FireflyIII\Console\Commands\Upgrade\TransferCurrenciesCorrections
*/ */
public function testHandleTransferBadDestCurrency(): void public function testHandleTransferBadDestCurrency(): void
{ {
Log::warning(sprintf('Now in test %s.', __METHOD__));
$accountRepos = $this->mock(AccountRepositoryInterface::class); $accountRepos = $this->mock(AccountRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class); $currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class); $journalRepos = $this->mock(JournalRepositoryInterface::class);
$transfer = $this->getRandomTransfer(); $transfer = $this->getRandomTransfer();
$euro = $this->getEuro(); $euro = $this->getEuro();
$dollar = $this->getDollar();
// get destination transaction and remove currency: // get destination transaction and remove currency:
$transfer->transaction_currency_id = $euro->id;
$transfer->save();
Log::debug(sprintf('Gave transfer #%d currency EUR', $transfer->id));
/** @var Transaction $destination */ /** @var Transaction $destination */
$destination = $transfer->transactions()->where('amount', '>', 0)->first(); $destination = $transfer->transactions()->where('amount', '>', 0)->first();
$destination->transaction_currency_id = 2; $destination->transaction_currency_id = $dollar->id;
$destination->save(); $destination->save();
Log::debug(sprintf('Gave transaction #%d currency USD', $destination->id));
// mock calls: // mock calls:
$journalRepos->shouldReceive('getAllJournals') $journalRepos->shouldReceive('getAllJournals')
->withArgs([[TransactionType::TRANSFER]]) ->withArgs([[TransactionType::TRANSFER]])
@@ -518,12 +528,10 @@ class TransferCurrenciesCorrectionsTest extends TestCase
// account repos // account repos
$accountRepos->shouldReceive('getMetaValue') $accountRepos->shouldReceive('getMetaValue')
->atLeast()->once() ->atLeast()->once()
->withArgs([Mockery::any(), 'currency_id'])->andReturn('1'); ->withArgs([Mockery::any(), 'currency_id'])->andReturn((string)$euro->id);
// currency repos // currency repos
$currencyRepos->shouldReceive('findNull') $currencyRepos->shouldReceive('findNull')->atLeast()->once()->withArgs([$euro->id])->andReturn($euro);
->atLeast()->once()
->withArgs([1])->andReturn($euro);
// configuration // configuration
$false = new Configuration; $false = new Configuration;
@@ -536,7 +544,7 @@ class TransferCurrenciesCorrectionsTest extends TestCase
->assertExitCode(0); ->assertExitCode(0);
// assume problem is fixed: // assume problem is fixed:
$this->assertCount(1, Transaction::where('id', $destination->id)->where('transaction_currency_id', 1)->get()); $this->assertCount(1, Transaction::where('id', $destination->id)->where('transaction_currency_id', $euro->id)->get());
} }
} }

View File

@@ -26,6 +26,7 @@ namespace Tests\Unit\Factory;
use Amount; use Amount;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\AccountFactory; use FireflyIII\Factory\AccountFactory;
use FireflyIII\Factory\BudgetFactory; use FireflyIII\Factory\BudgetFactory;
use FireflyIII\Factory\CategoryFactory; use FireflyIII\Factory\CategoryFactory;
@@ -831,8 +832,13 @@ class RecurrenceFactoryTest extends TestCase
/** @var RecurrenceFactory $factory */ /** @var RecurrenceFactory $factory */
$factory = app(RecurrenceFactory::class); $factory = app(RecurrenceFactory::class);
$factory->setUser($this->user()); $factory->setUser($this->user());
$result = null;
$result = $factory->create($data); try {
$result = $factory->create($data);
} catch (FireflyException $e) {
$this->assertEquals('Cannot make a recurring transaction of type "bad type"', $e->getMessage());
$this->assertTrue(true);
}
$this->assertNull($result); $this->assertNull($result);
} }