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
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
63 changed files with 1251 additions and 812 deletions

View File

@ -153,7 +153,7 @@ class BudgetReportController extends Controller
$cache->addProperty($start);
$cache->addProperty($end);
if ($cache->has()) {
//return response()->json($cache->get()); // @codeCoverageIgnore
return response()->json($cache->get()); // @codeCoverageIgnore
}
$format = app('navigation')->preferredCarbonLocalizedFormat($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\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Repositories\TransactionType\TransactionTypeRepositoryInterface;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Log;
@ -54,7 +56,7 @@ class AutoCompleteController extends Controller
public function accounts(Request $request): JsonResponse
{
$accountTypes = explode(',', $request->get('types') ?? '');
$search = $request->get('query');
$search = $request->get('search');
/** @var AccountRepositoryInterface $repository */
$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
* @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 */
$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
{
$query = (string)$request->get('query');
$query = (string)$request->get('search');
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$result = $repository->searchCategory($query);
@ -119,6 +151,44 @@ class AutoCompleteController extends Controller
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
* @codeCoverageIgnore
@ -164,13 +234,18 @@ class AutoCompleteController extends Controller
*/
public function tags(Request $request): JsonResponse
{
$query = (string)$request->get('query');
$search = (string)$request->get('search');
/** @var TagRepositoryInterface $repository */
$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.
* @codeCoverageIgnore
*/
public function __construct()
{

View File

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

View File

@ -47,6 +47,7 @@ class EditController extends Controller
/**
* EditController constructor.
* @codeCoverageIgnore
*/
public function __construct()
{
@ -70,7 +71,7 @@ class EditController extends Controller
/**
* Edit a recurring transaction.
*
* @param Request $request
* @param Request $request
* @param Recurrence $recurrence
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
@ -92,7 +93,7 @@ class EditController extends Controller
$repetition = $recurrence->recurrenceRepetitions()->first();
$currentRepType = $repetition->repetition_type;
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").
@ -123,9 +124,12 @@ class EditController extends Controller
$hasOldInput = null !== $request->old('_token');
$preFilled = [
'transaction_type' => strtolower($recurrence->transactionType->type),
'active' => $hasOldInput ? (bool)$request->old('active') : $recurrence->active,
'apply_rules' => $hasOldInput ? (bool)$request->old('apply_rules') : $recurrence->apply_rules,
'transaction_type' => strtolower($recurrence->transactionType->type),
'active' => $hasOldInput ? (bool)$request->old('active') : $recurrence->active,
'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(
@ -138,7 +142,7 @@ class EditController extends Controller
* Update the recurring transaction.
*
* @param RecurrenceFormRequest $request
* @param Recurrence $recurrence
* @param Recurrence $recurrence
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @throws \FireflyIII\Exceptions\FireflyException

View File

@ -48,6 +48,7 @@ class IndexController extends Controller
/**
* IndexController constructor.
* @codeCoverageIgnore
*/
public function __construct()
{
@ -121,8 +122,8 @@ class IndexController extends Controller
$transformer = app(RecurrenceTransformer::class);
$transformer->setParameters(new ParameterBag);
$array = $transformer->transform($recurrence);
$transactions = $this->recurring->getTransactions($recurrence);
$array = $transformer->transform($recurrence);
$groups = $this->recurring->getTransactions($recurrence);
// transform dates back to Carbon objects:
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]);
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
* @codeCoverageIgnore
*/
public function __construct()
{
@ -252,11 +253,11 @@ class ExpenseController extends Controller
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('expense-budget');
$cache->addProperty('top-expense');
$cache->addProperty($accounts->pluck('id')->toArray());
$cache->addProperty($expense->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
//return $cache->get(); // @codeCoverageIgnore
}
$combined = $this->combineAccounts($expense);
$all = new Collection;
@ -268,11 +269,11 @@ class ExpenseController extends Controller
$collector = app(GroupCollectorInterface::class);
$collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($accounts);
$collector->setAccounts($all);
$set = $collector->getExtractedJournals();
$collector->setAccounts($all)->withAccountInformation();
$sorted = $collector->getExtractedJournals();
usort($set, function ($a, $b) {
return $a['amount'] <=> $b['amount'];
usort($sorted, function ($a, $b) {
return $a['amount'] <=> $b['amount']; // @codeCoverageIgnore
});
try {
@ -304,11 +305,11 @@ class ExpenseController extends Controller
$cache = new CacheProperties;
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('expense-budget');
$cache->addProperty('top-income');
$cache->addProperty($accounts->pluck('id')->toArray());
$cache->addProperty($expense->pluck('id')->toArray());
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
//return $cache->get(); // @codeCoverageIgnore
}
$combined = $this->combineAccounts($expense);
$all = new Collection;
@ -321,11 +322,15 @@ class ExpenseController extends Controller
$collector = app(GroupCollectorInterface::class);
$total = $accounts->merge($all);
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($total);
$journals = $collector->getExtractedJournals();
$collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($total)->withAccountInformation();
$sorted = $collector->getExtractedJournals();
usort($journals, function ($a, $b) {
return $a['amount'] <=> $b['amount'];
foreach (array_keys($sorted) as $key) {
$sorted[$key]['amount'] = bcmul($sorted[$key]['amount'], '-1');
}
usort($sorted, function ($a, $b) {
return $a['amount'] <=> $b['amount']; // @codeCoverageIgnore
});
try {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -76,7 +76,7 @@ class SelectController extends Controller
* Execute the given rule on a set of existing transactions.
*
* @param SelectTransactionsRequest $request
* @param Rule $rule
* @param Rule $rule
*
* @return RedirectResponse
*/
@ -181,11 +181,12 @@ class SelectController extends Controller
// Return json response
$view = 'ERROR, see logs.';
try {
$view = view('list.journals-tiny', ['transactions' => $matchingTransactions])->render();
$view = view('list.journals-array-tiny', ['journals' => $matchingTransactions])->render();
// @codeCoverageIgnoreStart
} catch (Throwable $exception) {
Log::error(sprintf('Could not render view in testTriggers(): %s', $exception->getMessage()));
Log::error($exception->getTraceAsString());
$view = sprintf('Could not render list.journals-tiny: %s', $exception->getMessage());
}
// @codeCoverageIgnoreEnd
@ -236,17 +237,17 @@ class SelectController extends Controller
// Warn the user if only a subset of transactions is returned
$warning = '';
if ($matchingTransactions->count() === $limit) {
if (count($matchingTransactions) === $limit) {
$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
}
// Return json response
$view = 'ERROR, see logs.';
try {
$view = view('list.journals-tiny', ['transactions' => $matchingTransactions])->render();
$view = view('list.journals-array-tiny', ['journals' => $matchingTransactions])->render();
// @codeCoverageIgnoreStart
} catch (Throwable $exception) {
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
{
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.
@ -54,6 +78,7 @@ class IndexController extends Controller
$types = config('firefly.transactionTypesByType.' . $objectType);
$page = (int)$request->get('page');
$pageSize = (int)app('preferences')->get('listPageSize', 50)->data;
$pageSize =3;
if (null === $start) {
$start = session('start');
$end = session('end');
@ -62,16 +87,16 @@ class IndexController extends Controller
$end = session('end');
}
if ($end < $start) {
[$start, $end] = [$end, $start];
}
$path = route('transactions.index', [$objectType, $start->format('Y-m-d'), $end->format('Y-m-d')]);
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
$path = route('transactions.index', [$objectType, $start->format('Y-m-d'), $end->format('Y-m-d')]);
$startStr = $start->formatLocalized($this->monthAndDayFormat);
$endStr = $end->formatLocalized($this->monthAndDayFormat);
$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 */
$collector = app(GroupCollectorInterface::class);

View File

@ -53,10 +53,10 @@ class StartFireflySession extends StartSession
&& 'GET' === $request->method()
&& !$request->ajax()) {
$session->setPreviousUrl($uri);
Log::debug(sprintf('Will set previous URL to %s', $uri));
//Log::debug(sprintf('Will set previous URL to %s', $uri));
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\Rules\ValidRecurrenceRepetitionType;
use FireflyIII\Rules\ValidRecurrenceRepetitionValue;
use FireflyIII\Validation\AccountValidator;
use Illuminate\Validation\Validator;
use Log;
/**
* Class RecurrenceFormRequest
@ -136,6 +139,86 @@ class RecurrenceFormRequest extends Request
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.
*

View File

@ -647,9 +647,13 @@ class BudgetRepository implements BudgetRepositoryInterface
*/
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();
}
/**
* @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:
*
@ -156,7 +140,7 @@ class CategoryRepository implements CategoryRepositoryInterface
$currencyId = (int)$journal['currency_id'];
if (!isset($return[$currencyId])) {
$return[$currencyId] = [
'earned' => '0',
'earned' => '0',
'currency_id' => $currencyId,
'currency_symbol' => $journal['currency_symbol'],
'currency_code' => $journal['currency_code'],
@ -264,8 +248,6 @@ class CategoryRepository implements CategoryRepositoryInterface
return $result;
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* Find a category or return NULL
*
@ -278,6 +260,8 @@ class CategoryRepository implements CategoryRepositoryInterface
return $this->user->categories()->find($categoryId);
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* Find a category.
*
@ -297,8 +281,6 @@ class CategoryRepository implements CategoryRepositoryInterface
return null;
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* @param array $data
*
@ -313,6 +295,8 @@ class CategoryRepository implements CategoryRepositoryInterface
return $factory->findOrCreate(null, $data['name']);
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* @param Category $category
*
@ -341,47 +325,6 @@ class CategoryRepository implements CategoryRepositoryInterface
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.
*
@ -394,8 +337,6 @@ class CategoryRepository implements CategoryRepositoryInterface
return $this->user->categories()->whereIn('id', $categoryIds)->get();
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* @param Category $category
* @param Collection $accounts
@ -426,55 +367,7 @@ class CategoryRepository implements CategoryRepositoryInterface
return $lastJournalDate;
}
/**
* @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;
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* @param Collection $categories
@ -518,6 +411,8 @@ class CategoryRepository implements CategoryRepositoryInterface
return $data;
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* @param Collection $accounts
* @param Carbon $start
@ -555,6 +450,8 @@ class CategoryRepository implements CategoryRepositoryInterface
return $result;
}
/** @noinspection MoreThanThreeArgumentsInspection */
/**
* @param Collection $categories
* @param Collection $accounts
@ -644,9 +541,12 @@ class CategoryRepository implements CategoryRepositoryInterface
*/
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);
}
/**
* @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.
*
* @param int|null $currencyId
* @param int|null $currencyId
* @param string|null $currencyCode
*
* @return TransactionCurrency|null
@ -288,7 +288,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
/**
* Find by object, ID or code. Returns NULL if nothing found.
*
* @param int|null $currencyId
* @param int|null $currencyId
* @param string|null $currencyCode
*
* @return TransactionCurrency|null
@ -369,7 +369,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
*
* @param TransactionCurrency $fromCurrency
* @param TransactionCurrency $toCurrency
* @param Carbon $date
* @param Carbon $date
*
* @return CurrencyExchangeRate|null
*/
@ -438,7 +438,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
/**
* @param TransactionCurrency $currency
* @param array $data
* @param array $data
*
* @return TransactionCurrency
*/
@ -449,4 +449,18 @@ class CurrencyRepository implements CurrencyRepositoryInterface
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
{
/**
* @param string $search
* @return Collection
*/
public function searchCurrency(string $search): Collection;
/**
* @param TransactionCurrency $currency
*

View File

@ -54,6 +54,8 @@ use stdClass;
*/
class JournalRepository implements JournalRepositoryInterface
{
/** @var 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 */
/**
* @param TransactionJournal $journal
* @param TransactionType $type

View File

@ -41,6 +41,14 @@ use Illuminate\Support\MessageBag;
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.
*

View File

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

View File

@ -141,9 +141,9 @@ interface RecurringRepositoryInterface
/**
* @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.

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\TransactionType;
use FireflyIII\Models\TransactionType;
use Illuminate\Support\Collection;
use Log;
/**
@ -46,7 +47,7 @@ class TransactionTypeRepository implements TransactionTypeRepositoryInterface
/**
* @param TransactionType|null $type
* @param string|null $typeString
* @param string|null $typeString
*
* @return TransactionType
*/
@ -67,4 +68,17 @@ class TransactionTypeRepository implements TransactionTypeRepositoryInterface
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;
use FireflyIII\Models\TransactionType;
use Illuminate\Support\Collection;
/**
* Interface TransactionTypeRepositoryInterface
@ -44,4 +45,10 @@ interface TransactionTypeRepositoryInterface
* @return TransactionType|null
*/
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 Route $route
* @param Route $route
*
* @return Collection
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
@ -44,25 +44,29 @@ class AccountList implements BinderInterface
*/
public static function routeBinder(string $value, Route $route): Collection
{
Log::debug(sprintf('Now in AccountList::routeBinder("%s")', $value));
if (auth()->check()) {
Log::debug('User is logged in.');
$collection = new Collection;
if ('allAssetAccounts' === $value) {
/** @var \Illuminate\Support\Collection $collection */
/** @var Collection $collection */
$collection = auth()->user()->accounts()
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->where('account_types.type', AccountType::ASSET)
->orderBy('accounts.name', 'ASC')
->get(['accounts.*']);
Log::debug(sprintf('Collection length is %d', $collection->count()));
}
if ('allAssetAccounts' !== $value) {
$incoming = array_map('\intval', explode(',', $value));
$list = array_merge(array_unique($incoming), [0]);
/** @var \Illuminate\Support\Collection $collection */
/** @var Collection $collection */
$collection = auth()->user()->accounts()
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->whereIn('accounts.id', $list)
->orderBy('accounts.name', 'ASC')
->get(['accounts.*']);
Log::debug(sprintf('Collection length is %d', $collection->count()));
}
if ($collection->count() > 0) {

View File

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

View File

@ -102,10 +102,7 @@ class ExpandedForm
{
// make repositories
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
/** @var CurrencyRepositoryInterface $currencyRepos */
$currencyRepos = app(CurrencyRepositoryInterface::class);
$repository = app(AccountRepositoryInterface::class);
$accountList = $repository->getActiveAccountsByType(
[AccountType::ASSET, AccountType::DEFAULT, AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN,]
);
@ -113,12 +110,15 @@ class ExpandedForm
$defaultCurrency = app('amount')->getDefaultCurrency();
$grouped = [];
// group accounts:
/** @var Account $account */
foreach ($accountList as $account) {
$balance = app('steam')->balance($account, new Carbon);
$currencyId = (int)$repository->getMetaValue($account, 'currency_id');
$currency = $currencyRepos->findNull($currencyId);
$role = $repository->getMetaValue($account, 'account_role');
$balance = app('steam')->balance($account, new Carbon);
$currency = $repository->getAccountCurrency($account) ?? $defaultCurrency;
$role = $repository->getMetaValue($account, 'account_role');
if ('' === $role && !in_array($account->accountType->type, $liabilityTypes, true)) {
$role = 'no_account_type'; // @codeCoverageIgnore
}
@ -126,15 +126,11 @@ class ExpandedForm
if (in_array($account->accountType->type, $liabilityTypes, true)) {
$role = 'l_' . $account->accountType->type; // @codeCoverageIgnore
}
if (null === $currency) {
$currency = $defaultCurrency;
}
$key = (string)trans('firefly.opt_group_' . $role);
$grouped[$key][$account->id] = $account->name . ' (' . app('amount')->formatAnything($currency, $balance, false) . ')';
}
return $this->select($name, $grouped, $value, $options);
}
@ -152,8 +148,6 @@ class ExpandedForm
// make repositories
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
/** @var CurrencyRepositoryInterface $currencyRepos */
$currencyRepos = app(CurrencyRepositoryInterface::class);
$accountList = $repository->getActiveAccountsByType(
[
@ -176,10 +170,9 @@ class ExpandedForm
// group accounts:
/** @var Account $account */
foreach ($accountList as $account) {
$balance = app('steam')->balance($account, new Carbon);
$currencyId = (int)$repository->getMetaValue($account, 'currency_id');
$currency = $currencyRepos->findNull($currencyId);
$role = (string)$repository->getMetaValue($account, 'account_role');
$balance = app('steam')->balance($account, new Carbon);
$currency = $repository->getAccountCurrency($account) ?? $defaultCurrency;
$role = (string)$repository->getMetaValue($account, 'account_role');
if ('' === $role && !in_array($account->accountType->type, $liabilityTypes, true)) {
$role = 'no_account_type'; // @codeCoverageIgnore
}
@ -217,8 +210,6 @@ class ExpandedForm
// make repositories
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
/** @var CurrencyRepositoryInterface $currencyRepos */
$currencyRepos = app(CurrencyRepositoryInterface::class);
$accountList = $repository->getActiveAccountsByType(
[
@ -241,10 +232,9 @@ class ExpandedForm
// group accounts:
/** @var Account $account */
foreach ($accountList as $account) {
$balance = app('steam')->balance($account, new Carbon);
$currencyId = (int)$repository->getMetaValue($account, 'currency_id');
$currency = $currencyRepos->findNull($currencyId);
$role = (string)$repository->getMetaValue($account, 'account_role');
$balance = app('steam')->balance($account, new Carbon);
$currency = $repository->getAccountCurrency($account) ?? $defaultCurrency;
$role = (string)$repository->getMetaValue($account, 'account_role');
if ('' === $role && !in_array($account->accountType->type, $liabilityTypes, true)) {
$role = 'no_account_type'; // @codeCoverageIgnore
}
@ -257,10 +247,6 @@ class ExpandedForm
$role = 'l_' . $account->accountType->type; // @codeCoverageIgnore
}
if (null === $currency) {
$currency = $defaultCurrency;
}
$key = (string)trans('firefly.opt_group_' . $role);
$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\Transaction;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Support\Collection;
@ -81,10 +80,7 @@ trait PeriodOverview
protected function getAccountPeriodOverview(Account $account, Carbon $start, Carbon $end): array
{
$range = app('preferences')->get('viewRange', '1M')->data;
if ($end < $start) {
[$start, $end] = [$end, $start]; // @codeCoverageIgnore
}
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
// properties for cache
$cache = new CacheProperties;
@ -159,10 +155,7 @@ trait PeriodOverview
protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array
{
$range = app('preferences')->get('viewRange', '1M')->data;
if ($end < $start) {
[$start, $end] = [$end, $start]; // @codeCoverageIgnore
}
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
// properties for entries with their amounts.
$cache = new CacheProperties();
@ -173,7 +166,7 @@ trait PeriodOverview
$cache->addProperty($category->id);
if ($cache->has()) {
//return $cache->get(); // @codeCoverageIgnore
return $cache->get(); // @codeCoverageIgnore
}
/** @var array $dates */
$dates = app('navigation')->blockPeriods($start, $end, $range);
@ -240,9 +233,7 @@ trait PeriodOverview
{
$range = app('preferences')->get('viewRange', '1M')->data;
if ($end < $start) {
[$start, $end] = [$end, $start]; // @codeCoverageIgnore
}
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
$cache = new CacheProperties;
$cache->addProperty($start);
@ -250,7 +241,7 @@ trait PeriodOverview
$cache->addProperty('no-budget-period-entries');
if ($cache->has()) {
//return $cache->get(); // @codeCoverageIgnore
return $cache->get(); // @codeCoverageIgnore
}
/** @var array $dates */
@ -284,6 +275,8 @@ trait PeriodOverview
}
/**
* TODO fix date.
*
* Show period overview for no category view.
*
* @param Carbon $theDate
@ -309,7 +302,7 @@ trait PeriodOverview
$cache->addProperty('no-category-period-entries');
if ($cache->has()) {
//return $cache->get(); // @codeCoverageIgnore
return $cache->get(); // @codeCoverageIgnore
}
$dates = app('navigation')->blockPeriods($start, $end, $range);
@ -379,11 +372,7 @@ trait PeriodOverview
/** @var Carbon $end */
$start = clone $date;
$end = $repository->firstUseDate($tag) ?? new Carbon;
if ($end < $start) {
[$start, $end] = [$end, $start]; // @codeCoverageIgnore
}
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
// properties for entries with their amounts.
$cache = new CacheProperties;
@ -431,63 +420,60 @@ trait PeriodOverview
* @param string $transactionType
* @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');
/** @var JournalRepositoryInterface $repository */
$repository = app(JournalRepositoryInterface::class);
$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);
$range = app('preferences')->get('viewRange', '1M')->data;
$types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType));
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
if ($end < $start) {
[$start, $end] = [$end, $start]; // @codeCoverageIgnore
// properties for cache
$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 */
$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) {
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setTypes($types)->setRange($currentDate['start'], $currentDate['end']);
$journals = $collector->getExtractedJournals();
$amounts = $this->getJournalsSum($journals);
$spent = [];
$earned = [];
$transferred = [];
$title = app('navigation')->periodShow($currentDate['end'], $currentDate['period']);
// set to correct array
if ('expenses' === $transactionType || 'withdrawal' === $transactionType) {
$spent = $amounts;
$spent = $this->filterJournalsByDate($genericSet, $currentDate['start'], $currentDate['end']);
}
if ('revenue' === $transactionType || 'deposit' === $transactionType) {
$earned = $amounts;
$earned = $this->filterJournalsByDate($genericSet, $currentDate['start'], $currentDate['end']);
}
if ('transfer' === $transactionType || 'transfers' === $transactionType) {
$transferred = $amounts;
$transferred = $this->filterJournalsByDate($genericSet, $currentDate['start'], $currentDate['end']);
}
$title = app('navigation')->periodShow($currentDate['end'], $currentDate['period']);
$entries->push(
$entries[] =
[
'transactions' => $amounts['count'],
'title' => $title,
'spent' => $spent,
'earned' => $earned,
'transferred' => $transferred,
'route' => route(
'transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]
),
]
);
'title' => $title,
'route' =>
route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]),
'total_transactions' => count($spent) + count($earned) + count($transferred),
'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred),
];
}
return $entries;

View File

@ -178,7 +178,7 @@ trait RequestInformation
// both must be array and either must be > 0
if (count($intro) > 0 || count($specialIntro) > 0) {
$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)) {
$shownDemo = true;

View File

@ -50,7 +50,7 @@ trait UserNavigation
*/
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:
// if these are in the previous URI, don't refer back there.
$array = [
@ -67,24 +67,24 @@ trait UserNavigation
'transactions.mass-delete.uri' => '/transactions/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);
Log::debug(sprintf('The URI is %s', $uri));
//Log::debug(sprintf('The URI is %s', $uri));
if (
!(false === strpos($identifier, 'delete'))
&& !(false === strpos($uri, $forbidden))) {
$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'))) {
$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:
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($identifier, "delete"): %s', var_export(strpos($identifier, 'delete'), true)));
//Log::debug(sprintf('strpos($uri, $forbidden): %s', var_export(strpos($uri, $forbidden), true)));
return $uri;
}
@ -154,10 +154,10 @@ trait UserNavigation
if (null === $errors || (null !== $errors && 0 === $errors->count())) {
$url = app('url')->previous();
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;
}
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
// - the maximum number of transactions to return has been found
// - the maximum number of transactions to search in have been searched
$pageSize = min($this->searchLimit, min($this->triggeredLimit, 50));
$processed = 0;
$page = 1;
$result = [];
$pageSize = min($this->searchLimit, min($this->triggeredLimit, 50));
$processed = 0;
$page = 1;
$totalResult = [];
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)));
// merge:
$result = $result + $filtered;
Log::debug(sprintf('Total count is now %d', count($result)));
$totalResult = $totalResult + $filtered;
Log::debug(sprintf('Total count is now %d', count($totalResult)));
// Update counters
++$page;
@ -333,7 +333,7 @@ class TransactionMatcher
// Check for conditions to finish the loop
$reachedEndOfList = count($journals) < 1;
$foundEnough = count($result) >= $this->triggeredLimit;
$foundEnough = count($totalResult) >= $this->triggeredLimit;
$searchedEnough = ($processed >= $this->searchLimit);
Log::debug(sprintf('reachedEndOfList: %s', var_export($reachedEndOfList, true)));
@ -342,6 +342,6 @@ class TransactionMatcher
} while (!$reachedEndOfList && !$foundEnough && !$searchedEnough);
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 */
$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) {
/** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) {

View File

@ -41,7 +41,7 @@ trait TransactionValidation
*/
public function validateAccountInformation(Validator $validator): void
{
Log::debug('Now in validateAccountInformation()');
//Log::debug('Now in validateAccountInformation()');
$data = $validator->getData();
$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 {
font-size: 14px !important;
line-height: 1.42857143;

View File

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

View File

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

View File

@ -307,7 +307,7 @@ function updateTriggerInput(selectList) {
case 'to_account_is':
case 'to_account_contains':
console.log('Select list value is ' + selectList.val() + ', so input needs auto complete.');
createAutoComplete(inputResult, 'json/all-accounts');
createAutoComplete(inputResult, 'json/accounts');
break;
case 'tag_is':
console.log('Select list value is ' + selectList.val() + ', so input needs auto complete.');
@ -377,8 +377,8 @@ function createAutoComplete(input, URI) {
prefetch: {
url: URI + '?uid=' + uid,
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
return $.map(list, function (item) {
return {name: item.name};
});
}
},
@ -386,8 +386,8 @@ function createAutoComplete(input, URI) {
url: URI + '?search=%QUERY&uid=' + uid,
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (name) {
return {name: name};
return $.map(list, function (item) {
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_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".',
'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">
<thead>
<tr>
<th>
{{ groups.render|raw }}
</th>
<td colspan="7" class="no-margin-pagination">{{ groups.render|raw }}</td>
<td colspan="1">
<div class="pull-right">
<input id="list_ALL" value="1" name="all" type="checkbox" class="mass-select-all form-check-inline"/>
</div>
</td>
</tr>
</thead>
<tbody>
@ -16,7 +19,7 @@ TODO: hide and show columns
<tr>
<td colspan="2" style="border-top:1px #aaa solid;">
<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>
</td>
<td colspan="2" style="border-top:1px #aaa solid;">
@ -25,7 +28,7 @@ TODO: hide and show columns
{% endfor %}
</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">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ 'actions'|_ }} <span class="caret"></span></button>
@ -41,52 +44,54 @@ TODO: hide and show columns
{% for index, transaction in group.transactions %}
{% set style="" %}
{% 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 %}
<tr>
<td {{ style|raw }}>
{% if journal.transaction_type_type == 'Withdrawal' %}
<td style=" {{ style|raw }}">
{% if transaction.transaction_type_type == 'Withdrawal' %}
<i class="fa fa-long-arrow-left fa-fw" title="{{ trans('firefly.Withdrawal') }}"></i>
{% 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>
{% 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>
{% 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>
{% 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>
{% endif %}
</td>
<td {{ style|raw }}>
<td style=" {{ style|raw }}">
{% if transaction.reconciled %}
<i class="fa fa-check"></i>
{% endif %}
<a href="{{ route('transactions.show', [group.id]) }}" title="{{ transaction.description }}">{{ transaction.description }}</a>
</td>
<td {{ style|raw }}>
<td style=" {{ style|raw }}">
{{ 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 %}
</td>
<td {{ style|raw }}>{{ transaction.date.formatLocalized(monthAndDayFormat) }}</td>
<td {{ style|raw }}>
<td style=" {{ style|raw }}">
{{ transaction.date.formatLocalized(monthAndDayFormat) }}
</td>
<td style=" {{ style|raw }}">
<a href="{{ route('accounts.show', [transaction.source_account_id]) }}"
title="{{ transaction.source_account_iban|default(transaction.source_account_name) }}">{{ transaction.source_account_name }}</a>
</td>
<td {{ style|raw }}>
<td style=" {{ style|raw }}">
<a href="{{ route('accounts.show', [transaction.destination_account_id]) }}"
title="{{ transaction.destination_account_iban|default(transaction.destination_account_name) }}">{{ transaction.destination_account_name }}</a>
</td>
<td {{ style|raw }}>
<td style=" {{ style|raw }}">
{% if group.count == 1 %}
<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">
@ -99,10 +104,35 @@ TODO: hide and show columns
</div>
{% endif %}
</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>
{% endfor %}
{% endfor %}
</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>

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')}) }}
{# 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: #}
{{ ExpandedForm.longAccountList('destination_id', array.transactions[0].destination_id, {label: trans('form.asset_destination_account')} ) }}
{# 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>

View File

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

View File

@ -31,7 +31,7 @@
{{ '0'|formatAmount }}
{% else %}
{% 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 %}
{% endif %}
</td>

View File

@ -23,7 +23,7 @@
{{ '0'|formatAmount }}
{% endif %}
{% 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 %}
</td>
</tr>

View File

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

View File

@ -7,7 +7,7 @@
{% block content %}
{# upper show-all instruction #}
{% if periods.count > 0 %}
{% if periods|length > 0 %}
<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">
<p class="small text-center"><a href="{{ route('transactions.index.all',[transactionType]) }}">{{ 'showEverything'|_ }}</a></p>
@ -17,7 +17,7 @@
{# list with journals #}
<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-header with-border">
<h3 class="box-title">{{ subTitle }}</h3>
@ -31,7 +31,7 @@
</div>
<div class="box-footer">
{# links for other views #}
{% if periods.count > 0 %}
{% if periods|length > 0 %}
<p>
<i class="fa fa-calendar"></i>
<a href="{{ route('transactions.index.all', [transactionType]) }}">{{ 'show_all_no_filter'|_ }}</a>
@ -47,7 +47,7 @@
</div>
{# 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">
{% include 'list.periods' %}
</div>
@ -56,7 +56,7 @@
</div>
{# lower show-all instruction #}
{% if periods.count > 0 %}
{% if periods|length > 0 %}
<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">
<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('piggy-banks', ['uses' => 'Json\AutoCompleteController@piggyBanks', 'as' => 'autocomplete.piggy-banks']);
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->start_date = new Carbon('2012-01-01');
$limit3->end_date = new Carbon('2012-01-31');
$limit3->amount = '100';
$limit3->save();
$fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date);

View File

@ -24,8 +24,9 @@ declare(strict_types=1);
namespace Tests\Feature\Controllers\Recurring;
use Amount;
use Carbon\Carbon;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
@ -33,14 +34,18 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Validation\AccountValidator;
use Illuminate\Support\Collection;
use Log;
use Mockery;
use Preferences;
use Steam;
use Tests\TestCase;
/**
*
* Class CreateControllerTest
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
class CreateControllerTest extends TestCase
{
@ -58,76 +63,100 @@ class CreateControllerTest extends TestCase
*/
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);
$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());
$response = $this->get(route('recurring.create'));
$response->assertStatus(200);
$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\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);
$budgetRepos = $this->mock(BudgetRepositoryInterface::class);
$categoryRepos = $this->mock(CategoryRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$piggyRepos = $this->mock(PiggyBankRepositoryInterface::class);
$validator = $this->mock(AccountValidator::class);
$source = $this->getRandomAsset();
$destination = $this->getRandomExpense();
$tomorrow = Carbon::now()->addDays(2);
$recurrence = $this->user()->recurrences()->first();
$tomorrow = Carbon::now()->addDays(2);
$recurrence = $this->user()->recurrences()->first();
$data = [
'title' => 'hello' . $this->randomInt(),
'first_date' => $tomorrow->format('Y-m-d'),
'repetition_type' => 'daily',
'skip' => 0,
'recurring_description' => 'Some descr' . $this->randomInt(),
'active' => '1',
'apply_rules' => '1',
'foreign_amount' => '1',
'foreign_currency_id' => '2',
$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);
$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',
'transaction_description' => 'Some descr',
'transaction_type' => 'withdrawal',
'transaction_currency_id' => '1',
'amount' => '30',
// mandatory account info:
'source_id' => '1',
'destination_name' => 'Some Expense',
'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,
'budget_id' => '1',
'category' => 'CategoryA',
'tags' => 'A,B,C',
'create_another' => '1',
'repetition_end' => 'times',
'repetitions' => 3,
];
$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\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);
$budgetRepos = $this->mock(BudgetRepositoryInterface::class);
$categoryRepos = $this->mock(CategoryRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$piggyRepos = $this->mock(PiggyBankRepositoryInterface::class);
$validator = $this->mock(AccountValidator::class);
$source = $this->getRandomAsset();
$destination = $this->getRandomExpense();
$tomorrow = Carbon::now()->addDays(2);
$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);
$recurrence = $this->user()->recurrences()->first();
@ -177,10 +272,8 @@ class CreateControllerTest extends TestCase
'amount' => '30',
// mandatory account info:
'source_id' => '2',
'source_name' => 'Some source',
'destination_id' => '1',
'destination_name' => 'Some Expense',
'deposit_source_id' => $source->id,
'destination_id' => $destination->id,
// optional fields:
'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\Requests\RecurrenceFormRequest
*/
public function testStoreMonthly(): void
{
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
$this->mock(BudgetRepositoryInterface::class);
return;
$recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$budgetRepos = $this->mock(BudgetRepositoryInterface::class);
$categoryRepos = $this->mock(CategoryRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$piggyRepos = $this->mock(PiggyBankRepositoryInterface::class);
$validator = $this->mock(AccountValidator::class);
$source = $this->getRandomAsset();
$destination = $this->getRandomExpense();
$tomorrow = Carbon::now()->addDays(2);
$recurrence = $this->user()->recurrences()->first();
$tomorrow = Carbon::now()->addDays(2);
$recurrence = $this->user()->recurrences()->first();
$data = [
'title' => 'hello' . $this->randomInt(),
'first_date' => $tomorrow->format('Y-m-d'),
'repetition_type' => 'monthly,5',
'skip' => 0,
'recurring_description' => 'Some descr' . $this->randomInt(),
'active' => '1',
'apply_rules' => '1',
'foreign_amount' => '1',
'foreign_currency_id' => '2',
$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);
$data = [
'title' => sprintf('hello %d', $this->randomInt()),
'first_date' => $tomorrow->format('Y-m-d'),
'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:
'transaction_description' => 'Some descr',
'transaction_type' => 'withdrawal',
'transaction_currency_id' => '1',
'amount' => '30',
'transaction_description' => 'Some descr',
'transaction_type' => 'withdrawal',
'transaction_currency_id' => '1',
'amount' => '30',
// mandatory account info:
'source_id' => '1',
'destination_name' => 'Some Expense',
'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,
'budget_id' => '1',
'category' => 'CategoryA',
'tags' => 'A,B,C',
'create_another' => '1',
'repetition_end' => 'times',
'repetitions' => 3,
];
$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\Requests\RecurrenceFormRequest
*/
public function testStoreNdom(): void
{
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
$this->mock(BudgetRepositoryInterface::class);
return;
$recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$budgetRepos = $this->mock(BudgetRepositoryInterface::class);
$categoryRepos = $this->mock(CategoryRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$piggyRepos = $this->mock(PiggyBankRepositoryInterface::class);
$validator = $this->mock(AccountValidator::class);
$source = $this->getRandomAsset();
$destination = $this->getRandomExpense();
$tomorrow = Carbon::now()->addDays(2);
$recurrence = $this->user()->recurrences()->first();
$tomorrow = Carbon::now()->addDays(2);
$recurrence = $this->user()->recurrences()->first();
$data = [
'title' => 'hello' . $this->randomInt(),
'first_date' => $tomorrow->format('Y-m-d'),
'repetition_type' => 'ndom,3,5',
'skip' => 0,
'recurring_description' => 'Some descr' . $this->randomInt(),
'active' => '1',
'apply_rules' => '1',
'foreign_amount' => '1',
'foreign_currency_id' => '2',
$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);
$data = [
'title' => sprintf('hello %d', $this->randomInt()),
'first_date' => $tomorrow->format('Y-m-d'),
'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:
'transaction_description' => 'Some descr',
'transaction_type' => 'withdrawal',
'transaction_currency_id' => '1',
'amount' => '30',
'transaction_description' => 'Some descr',
'transaction_type' => 'withdrawal',
'transaction_currency_id' => '1',
'amount' => '30',
// mandatory account info:
'source_id' => '1',
'destination_name' => 'Some Expense',
'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,
'budget_id' => '1',
'category' => 'CategoryA',
'tags' => 'A,B,C',
'create_another' => '1',
'repetition_end' => 'times',
'repetitions' => 3,
];
$recurringRepos->shouldReceive('store')->andReturn($recurrence)->once();
@ -321,17 +424,20 @@ class CreateControllerTest extends TestCase
*/
public function testStoreTransfer(): void
{
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
$this->mock(BudgetRepositoryInterface::class);
return;
$recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$budgetRepos = $this->mock(BudgetRepositoryInterface::class);
$categoryRepos = $this->mock(CategoryRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$piggyRepos = $this->mock(PiggyBankRepositoryInterface::class);
$validator = $this->mock(AccountValidator::class);
$source = $this->getRandomAsset();
$destination = $this->getRandomAsset($source->id);
$this->mockDefaultSession();
Preferences::shouldReceive('mark')->atLeast()->once();
// 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);
@ -354,10 +460,8 @@ class CreateControllerTest extends TestCase
'amount' => '30',
// mandatory account info:
'source_id' => '2',
'source_name' => 'Some source',
'destination_id' => '1',
'destination_name' => 'Some Expense',
'source_id' => $source->id,
'destination_id' => $destination->id,
// optional fields:
'budget_id' => '1',
@ -382,49 +486,51 @@ class CreateControllerTest extends TestCase
*/
public function testStoreUntilDate(): void
{
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
$this->mock(BudgetRepositoryInterface::class);
return;
$recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$budgetRepos = $this->mock(BudgetRepositoryInterface::class);
$categoryRepos = $this->mock(CategoryRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$piggyRepos = $this->mock(PiggyBankRepositoryInterface::class);
$validator = $this->mock(AccountValidator::class);
$source = $this->getRandomAsset();
$destination = $this->getRandomExpense();
$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);
$recurrence = $this->user()->recurrences()->first();
$data = [
'title' => 'hello' . $this->randomInt(),
'first_date' => $tomorrow->format('Y-m-d'),
'repetition_type' => 'daily',
'skip' => 0,
'recurring_description' => 'Some descr' . $this->randomInt(),
'active' => '1',
'apply_rules' => '1',
'foreign_amount' => '1',
'foreign_currency_id' => '2',
'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',
'transaction_description' => 'Some descr',
'transaction_type' => 'withdrawal',
'transaction_currency_id' => '1',
'amount' => '30',
// mandatory account info:
'source_id' => '1',
'destination_name' => 'Some Expense',
'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' => 'until_date',
'repeat_until' => $tomorrow->format('Y-m-d'),
'budget_id' => '1',
'category' => 'CategoryA',
'tags' => 'A,B,C',
'create_another' => '1',
'repetition_end' => 'until_date',
'repeat_until' => $tomorrow->format('Y-m-d'),
];
$recurringRepos->shouldReceive('store')->andReturn($recurrence)->once();
@ -441,48 +547,51 @@ class CreateControllerTest extends TestCase
*/
public function testStoreYearly(): void
{
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
$this->mock(BudgetRepositoryInterface::class);
return;
$recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$budgetRepos = $this->mock(BudgetRepositoryInterface::class);
$categoryRepos = $this->mock(CategoryRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$piggyRepos = $this->mock(PiggyBankRepositoryInterface::class);
$validator = $this->mock(AccountValidator::class);
$source = $this->getRandomAsset();
$destination = $this->getRandomExpense();
$tomorrow = Carbon::now()->addDays(2);
$recurrence = $this->user()->recurrences()->first();
$tomorrow = Carbon::now()->addDays(2);
$recurrence = $this->user()->recurrences()->first();
$data = [
'title' => 'hello' . $this->randomInt(),
'first_date' => $tomorrow->format('Y-m-d'),
'repetition_type' => 'yearly,2018-01-01',
'skip' => 0,
'recurring_description' => 'Some descr' . $this->randomInt(),
'active' => '1',
'apply_rules' => '1',
'foreign_amount' => '1',
'foreign_currency_id' => '2',
$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);
$data = [
'title' => sprintf('hello %d', $this->randomInt()),
'first_date' => $tomorrow->format('Y-m-d'),
'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:
'transaction_description' => 'Some descr',
'transaction_type' => 'withdrawal',
'transaction_currency_id' => '1',
'amount' => '30',
'transaction_description' => 'Some descr',
'transaction_type' => 'withdrawal',
'transaction_currency_id' => '1',
'amount' => '30',
// mandatory account info:
'source_id' => '1',
'destination_name' => 'Some Expense',
'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,
'budget_id' => '1',
'category' => 'CategoryA',
'tags' => 'A,B,C',
'create_another' => '1',
'repetition_end' => 'times',
'repetitions' => 3,
];
$recurringRepos->shouldReceive('store')->andReturn($recurrence)->once();

View File

@ -28,6 +28,7 @@ use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Support\Collection;
use Log;
use Mockery;
use Preferences;
use Tests\TestCase;
/**
@ -53,7 +54,9 @@ class DeleteControllerTest extends TestCase
$recurringRepos = $this->mock(RecurringRepositoryInterface::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);
$this->be($this->user());
@ -70,6 +73,9 @@ class DeleteControllerTest extends TestCase
$recurringRepos = $this->mock(RecurringRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$this->mockDefaultSession();
Preferences::shouldReceive('mark')->atLeast()->once();
$recurringRepos->shouldReceive('destroy')->once();

View File

@ -23,19 +23,21 @@ declare(strict_types=1);
namespace Tests\Feature\Controllers\Recurring;
use Amount;
use Carbon\Carbon;
use FireflyIII\Factory\CategoryFactory;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Transformers\RecurrenceTransformer;
use FireflyIII\Validation\AccountValidator;
use Illuminate\Support\Collection;
use Log;
use Mockery;
use Preferences;
use Steam;
use Tests\TestCase;
/**
@ -58,23 +60,44 @@ class EditControllerTest extends TestCase
*/
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);
$budgetRepos = $this->mock(BudgetRepositoryInterface::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 = $this->mock(RecurringRepositoryInterface::class);
$budgetRepos = $this->mock(BudgetRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$transformer = $this->mock(RecurrenceTransformer::class);
$asset = $this->getRandomAsset();
$euro = $this->getEuro();
$cash = $this->getRandomAsset();
$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('transform')->atLeast()->once();
$transformer->shouldReceive('transform')->atLeast()->once()->andReturn($transformed);
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true);
// get stuff from recurrence.
$recurringRepos->shouldReceive('setUser');
$recurringRepos->shouldReceive('getNoteText')->andReturn('Note!');
$recurringRepos->shouldReceive('repetitionDescription')->andReturn('dunno');
@ -90,6 +113,8 @@ class EditControllerTest extends TestCase
$response = $this->get(route('recurring.edit', [1]));
$response->assertStatus(200);
$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
{
$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;
$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);
$this->mockDefaultSession();
$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);
$recurrence = $this->user()->recurrences()->first();
$data = [
'id' => $recurrence->id,
'title' => 'hello',
'first_date' => $tomorrow->format('Y-m-d'),
'repetition_type' => 'daily',
'skip' => 0,
'recurring_description' => 'Some descr',
'active' => '1',
'apply_rules' => '1',
'return_to_edit' => '1',
'id' => $recurrence->id,
'title' => 'hello',
'first_date' => $tomorrow->format('Y-m-d'),
'repetition_type' => 'daily',
'skip' => 0,
'recurring_description' => 'Some descr',
'active' => '1',
'apply_rules' => '1',
'return_to_edit' => '1',
// mandatory for transaction:
'transaction_description' => 'Some descr',
'transaction_type' => 'withdrawal',
'transaction_currency_id' => '1',
'amount' => '30',
'transaction_description' => 'Some descr',
'transaction_type' => 'withdrawal',
'transaction_currency_id' => '1',
'amount' => '30',
// mandatory account info:
'source_id' => '1',
'source_name' => '',
'destination_id' => '',
'destination_name' => 'Some Expense',
'source_id' => '1',
'source_name' => '',
'withdrawal_destination_id' => $expense->id,
'destination_id' => '',
'destination_name' => 'Some Expense',
// optional fields:
'budget_id' => '1',
'category' => 'CategoryA',
'tags' => 'A,B,C',
'create_another' => '1',
'repetition_end' => 'times',
'repetitions' => 3,
'budget_id' => '1',
'category' => 'CategoryA',
'tags' => 'A,B,C',
'create_another' => '1',
'repetition_end' => 'times',
'repetitions' => 3,
];

View File

@ -25,6 +25,7 @@ namespace Tests\Feature\Controllers\Recurring;
use FireflyIII\Factory\CategoryFactory;
use FireflyIII\Models\Configuration;
use FireflyIII\Models\Preference;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
@ -32,6 +33,7 @@ use FireflyIII\Transformers\RecurrenceTransformer;
use Illuminate\Support\Collection;
use Log;
use Mockery;
use Preferences;
use Tests\TestCase;
/**
@ -61,6 +63,13 @@ class IndexControllerTest extends TestCase
$categoryFactory = $this->mock(CategoryFactory::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('transform')->atLeast()->once()->andReturn(
[
@ -83,7 +92,6 @@ class IndexControllerTest extends TestCase
// mock cron job 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();
@ -94,6 +102,9 @@ class IndexControllerTest extends TestCase
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\Recurring\IndexController
*/
public function testShow(): void
{
$repository = $this->mock(RecurringRepositoryInterface::class);
@ -102,6 +113,8 @@ class IndexControllerTest extends TestCase
$categoryFactory = $this->mock(CategoryFactory::class);
$transformer = $this->mock(RecurrenceTransformer::class);
$this->mockDefaultSession();
$transformer->shouldReceive('setParameters')->atLeast()->once();
$transformer->shouldReceive('transform')->atLeast()->once()->andReturn(
[
@ -109,7 +122,13 @@ class IndexControllerTest extends TestCase
'first_date' => '2018-01-01',
'repeat_until' => 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;
use Amount;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Helpers\Fiscal\FiscalHelperInterface;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Illuminate\Support\Collection;
use Log;
use Preferences;
use Tests\TestCase;
/**
@ -56,46 +56,30 @@ class ExpenseControllerTest extends TestCase
*/
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);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(GroupCollectorInterface::class);
$expense = $this->getRandomExpense();
$revenue = $this->getRandomRevenue();
$date = new Carbon;
$transactions = [$this->getRandomWithdrawalAsArray()];
$this->mockDefaultSession();
Preferences::shouldReceive('lastActivity')->atLeast()->once();
$fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date);
$fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date);
$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:
$collector->shouldReceive('setRange')->andReturnSelf();
$collector->shouldReceive('setTypes')->andReturnSelf();
$collector->shouldReceive('setAccounts')->andReturnSelf();
$collector->shouldReceive('setOpposingAccounts')->andReturnSelf();
$collector->shouldReceive('withBudgetInformation')->andReturnSelf();
$collector->shouldReceive('getTransactions')->andReturn($collection);
$collector->shouldReceive('setRange')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('setTypes')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('setAccounts')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('withBudgetInformation')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('getExtractedJournals')->andReturn($transactions)->atLeast()->once();
$this->be($this->user());
@ -108,57 +92,37 @@ class ExpenseControllerTest extends TestCase
*/
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);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(GroupCollectorInterface::class);
$expense = $this->getRandomExpense();
$revenue = $this->getRandomRevenue();
$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('startOfFiscalYear')->atLeast()->once()->andReturn($date);
$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_category_name = 'Category';
$transA->transaction_category_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_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();
$collector->shouldReceive('setRange')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('setTypes')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('setAccounts')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('withCategoryInformation')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('getExtractedJournals')->andReturn([$one], [$two])->atLeast()->once();
$this->be($this->user());
$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
{
$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);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(GroupCollectorInterface::class);
$expense = $this->getRandomExpense();
$revenue = $this->getRandomRevenue();
$date = new Carbon;
$transactions = [$this->getRandomWithdrawalAsArray()];
$this->mockDefaultSession();
Preferences::shouldReceive('lastActivity')->atLeast()->once();
$fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date);
$fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date);
$repository->shouldReceive('findByName')->once()->withArgs([$expense->name, [AccountType::REVENUE]])->andReturn($revenue);
// fake collection:
$transA = new Transaction;
$transA->transaction_currency_id = 1;
$transA->transaction_category_name = 'Category';
$transA->transaction_category_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_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);
$collector->shouldReceive('setRange')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('setTypes')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('setAccounts')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('getExtractedJournals')->andReturn($transactions)->atLeast()->once();
Amount::shouldReceive('formatAnything')->atLeast()->once()->andReturn('-100');
$this->be($this->user());
$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
{
$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();
Log::debug(sprintf('Now in test %s', __METHOD__));
$repository = $this->mock(AccountRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(GroupCollectorInterface::class);
$expense = $this->getRandomExpense();
$revenue = $this->getRandomRevenue();
$date = new Carbon;
$transactions = [$this->getRandomWithdrawalAsArray()];
$this->mockDefaultSession();
Preferences::shouldReceive('lastActivity')->atLeast()->once();
$fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date);
$fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date);
$repository->shouldReceive('findByName')->once()->withArgs([$expense->name, [AccountType::REVENUE]])->andReturn($revenue);
// fake collection:
$transA = new Transaction;
$transA->transaction_currency_id = 1;
$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]);
$collector->shouldReceive('setRange')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('setTypes')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('withAccountInformation')->andReturnSelf()->atLeast()->once();
// mock collector for topExpense (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);
$collector->shouldReceive('setAccounts')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('getExtractedJournals')->andReturn($transactions)->atLeast()->once();
$this->be($this->user());
$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
{
$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();
Log::debug(sprintf('Now in test %s', __METHOD__));
$repository = $this->mock(AccountRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(GroupCollectorInterface::class);
$expense = $this->getRandomExpense();
$revenue = $this->getRandomRevenue();
$date = new Carbon;
$transactions = [$this->getRandomWithdrawalAsArray()];
$this->mockDefaultSession();
Preferences::shouldReceive('lastActivity')->atLeast()->once();
$fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date);
$fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date);
$repository->shouldReceive('findByName')->once()->withArgs([$expense->name, [AccountType::REVENUE]])->andReturn($revenue);
// fake collection:
$transA = new Transaction;
$transA->transaction_currency_id = 1;
$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]);
$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();
$collector->shouldReceive('setRange')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('setTypes')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('setAccounts')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('withAccountInformation')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('getExtractedJournals')->andReturn($transactions)->atLeast()->once();
$this->be($this->user());

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -30,7 +30,6 @@ use DB;
use Exception;
use FireflyConfig;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget;
@ -167,7 +166,9 @@ abstract class TestCase extends BaseTestCase
'currency_decimal_places' => $euro->decimal_places,
'amount' => '-30',
'budget_id' => $budget->id,
'budget_name' => $budget->name,
'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
*/
public function testHandleTransferBadDestCurrency(): void
{
Log::warning(sprintf('Now in test %s.', __METHOD__));
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$transfer = $this->getRandomTransfer();
$euro = $this->getEuro();
$dollar = $this->getDollar();
// 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 */
$destination = $transfer->transactions()->where('amount', '>', 0)->first();
$destination->transaction_currency_id = 2;
$destination->transaction_currency_id = $dollar->id;
$destination->save();
Log::debug(sprintf('Gave transaction #%d currency USD', $destination->id));
// mock calls:
$journalRepos->shouldReceive('getAllJournals')
->withArgs([[TransactionType::TRANSFER]])
@ -518,12 +528,10 @@ class TransferCurrenciesCorrectionsTest extends TestCase
// account repos
$accountRepos->shouldReceive('getMetaValue')
->atLeast()->once()
->withArgs([Mockery::any(), 'currency_id'])->andReturn('1');
->withArgs([Mockery::any(), 'currency_id'])->andReturn((string)$euro->id);
// currency repos
$currencyRepos->shouldReceive('findNull')
->atLeast()->once()
->withArgs([1])->andReturn($euro);
$currencyRepos->shouldReceive('findNull')->atLeast()->once()->withArgs([$euro->id])->andReturn($euro);
// configuration
$false = new Configuration;
@ -536,7 +544,7 @@ class TransferCurrenciesCorrectionsTest extends TestCase
->assertExitCode(0);
// 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 Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\AccountFactory;
use FireflyIII\Factory\BudgetFactory;
use FireflyIII\Factory\CategoryFactory;
@ -831,8 +832,13 @@ class RecurrenceFactoryTest extends TestCase
/** @var RecurrenceFactory $factory */
$factory = app(RecurrenceFactory::class);
$factory->setUser($this->user());
$result = $factory->create($data);
$result = null;
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);
}