Finalise account tests

This commit is contained in:
James Cole 2019-06-23 05:53:01 +02:00
parent 311659ba0d
commit 9f50c5db3d
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
25 changed files with 470 additions and 841 deletions

View File

@ -61,6 +61,7 @@ class RenameAccountMeta extends Command
$array = [ $array = [
'accountRole' => 'account_role', 'accountRole' => 'account_role',
'ccType' => 'cc_type', 'ccType' => 'cc_type',
'accountNumber' => 'account_number',
'ccMonthlyPaymentDate' => 'cc_monthly_payment_date', 'ccMonthlyPaymentDate' => 'cc_monthly_payment_date',
]; ];
$count = 0; $count = 0;
@ -71,6 +72,9 @@ class RenameAccountMeta extends Command
*/ */
foreach ($array as $old => $new) { foreach ($array as $old => $new) {
$count += AccountMeta::where('name', $old)->update(['name' => $new]); $count += AccountMeta::where('name', $old)->update(['name' => $new]);
// delete empty entries while we're at it.
AccountMeta::where('name', $new)->where('data','""')->delete();
} }
$this->markAsExecuted(); $this->markAsExecuted();

View File

@ -55,6 +55,7 @@ class UpgradeDatabase extends Command
$commands = [ $commands = [
// there are 12 upgrade commands. // there are 12 upgrade commands.
'firefly-iii:transaction-identifiers', 'firefly-iii:transaction-identifiers',
'firefly-iii:migrate-to-groups',
'firefly-iii:account-currencies', 'firefly-iii:account-currencies',
'firefly-iii:transfer-currencies', 'firefly-iii:transfer-currencies',
'firefly-iii:other-currencies', 'firefly-iii:other-currencies',
@ -63,7 +64,6 @@ class UpgradeDatabase extends Command
'firefly-iii:bills-to-rules', 'firefly-iii:bills-to-rules',
'firefly-iii:bl-currency', 'firefly-iii:bl-currency',
'firefly-iii:cc-liabilities', 'firefly-iii:cc-liabilities',
'firefly-iii:migrate-to-groups',
'firefly-iii:back-to-journals', 'firefly-iii:back-to-journals',
'firefly-iii:rename-account-meta' 'firefly-iii:rename-account-meta'
]; ];

View File

@ -100,17 +100,12 @@ class EditController extends Controller
$openingBalanceAmount = (string)$repository->getOpeningBalanceAmount($account); $openingBalanceAmount = (string)$repository->getOpeningBalanceAmount($account);
$openingBalanceDate = $repository->getOpeningBalanceDate($account); $openingBalanceDate = $repository->getOpeningBalanceDate($account);
$default = app('amount')->getDefaultCurrency(); $currency = $this->repository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency();
$currency = $this->currencyRepos->findNull((int)$repository->getMetaValue($account, 'currency_id'));
// include this account in net-worth charts? // include this account in net-worth charts?
$includeNetWorth = $repository->getMetaValue($account, 'include_net_worth'); $includeNetWorth = $repository->getMetaValue($account, 'include_net_worth');
$includeNetWorth = null === $includeNetWorth ? true : '1' === $includeNetWorth; $includeNetWorth = null === $includeNetWorth ? true : '1' === $includeNetWorth;
if (null === $currency) {
$currency = $default;
}
// code to handle active-checkboxes // code to handle active-checkboxes
$hasOldInput = null !== $request->old('_token'); $hasOldInput = null !== $request->old('_token');
$preFilled = [ $preFilled = [

View File

@ -25,15 +25,18 @@ namespace FireflyIII\Http\Controllers\Account;
use Carbon\Carbon; use Carbon\Carbon;
use Exception; use Exception;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\TransactionGroupFactory;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\ReconciliationStoreRequest; use FireflyIII\Http\Requests\ReconciliationStoreRequest;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Support\Http\Controllers\UserNavigation; use FireflyIII\Support\Http\Controllers\UserNavigation;
use FireflyIII\User;
use Log; use Log;
/** /**
@ -83,13 +86,12 @@ class ReconcileController extends Controller
*/ */
public function reconcile(Account $account, Carbon $start = null, Carbon $end = null) public function reconcile(Account $account, Carbon $start = null, Carbon $end = null)
{ {
if (AccountType::INITIAL_BALANCE === $account->accountType->type) {
return $this->redirectToOriginalAccount($account);
}
if (AccountType::ASSET !== $account->accountType->type) { if (AccountType::ASSET !== $account->accountType->type) {
// @codeCoverageIgnoreStart
session()->flash('error', (string)trans('firefly.must_be_asset_account')); session()->flash('error', (string)trans('firefly.must_be_asset_account'));
return redirect(route('accounts.index', [config(sprintf('firefly.shortNamesByFullName.%s', $account->accountType->type))])); return redirect(route('accounts.index', [config(sprintf('firefly.shortNamesByFullName.%s', $account->accountType->type))]));
// @codeCoverageIgnoreEnd
} }
$currency = $this->accountRepos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency(); $currency = $this->accountRepos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency();
@ -97,16 +99,20 @@ class ReconcileController extends Controller
$range = app('preferences')->get('viewRange', '1M')->data; $range = app('preferences')->get('viewRange', '1M')->data;
// get start and end // get start and end
// @codeCoverageIgnoreStart
if (null === $start && null === $end) { if (null === $start && null === $end) {
/** @var Carbon $start */ /** @var Carbon $start */
$start = clone session('start', app('navigation')->startOfPeriod(new Carbon, $range)); $start = clone session('start', app('navigation')->startOfPeriod(new Carbon, $range));
/** @var Carbon $end */ /** @var Carbon $end */
$end = clone session('end', app('navigation')->endOfPeriod(new Carbon, $range)); $end = clone session('end', app('navigation')->endOfPeriod(new Carbon, $range));
} }
if (null === $end) { if (null === $end) {
/** @var Carbon $end */ /** @var Carbon $end */
$end = app('navigation')->endOfPeriod($start, $range); $end = app('navigation')->endOfPeriod($start, $range);
} }
// @codeCoverageIgnoreEnd
$startDate = clone $start; $startDate = clone $start;
$startDate->subDay(); $startDate->subDay();
@ -142,70 +148,78 @@ class ReconcileController extends Controller
Log::debug('In ReconcileController::submit()'); Log::debug('In ReconcileController::submit()');
$data = $request->getAll(); $data = $request->getAll();
/** @var Transaction $transaction */ /** @var string $journalId */
foreach ($data['transactions'] as $transactionId) { foreach ($data['journals'] as $journalId) {
$this->repository->reconcileById((int)$transactionId); $this->repository->reconcileById((int)$journalId);
} }
Log::debug('Reconciled all transactions.'); Log::debug('Reconciled all transactions.');
// create reconciliation transaction (if necessary): // create reconciliation transaction (if necessary):
$result = '';
if ('create' === $data['reconcile']) { if ('create' === $data['reconcile']) {
// get "opposing" account. $result = $this->createReconciliation($account, $start, $end, $data['difference']);
$reconciliation = $this->accountRepos->getReconciliation($account);
$difference = $data['difference'];
$source = $reconciliation;
$destination = $account;
if (1 === bccomp($difference, '0')) {
// amount is positive. Add it to reconciliation?
$source = $account;
$destination = $reconciliation;
}
// data for journal
$description = trans(
'firefly.reconcilliation_transaction_title',
['from' => $start->formatLocalized($this->monthAndDayFormat), 'to' => $end->formatLocalized($this->monthAndDayFormat)]
);
$journalData = [
'type' => 'Reconciliation',
'description' => $description,
'user' => auth()->user()->id,
'date' => $data['end'],
'bill_id' => null,
'bill_name' => null,
'piggy_bank_id' => null,
'piggy_bank_name' => null,
'tags' => null,
'interest_date' => null,
'transactions' => [[
'currency_id' => (int)$this->accountRepos->getMetaValue($account, 'currency_id'),
'currency_code' => null,
'description' => null,
'amount' => app('steam')->positive($difference),
'source_id' => $source->id,
'source_name' => null,
'destination_id' => $destination->id,
'destination_name' => null,
'reconciled' => true,
'identifier' => 0,
'foreign_currency_id' => null,
'foreign_currency_code' => null,
'foreign_amount' => null,
'budget_id' => null,
'budget_name' => null,
'category_id' => null,
'category_name' => null,
],
],
'notes' => implode(', ', $data['transactions']),
];
$this->repository->store($journalData);
} }
Log::debug('End of routine.'); Log::debug('End of routine.');
app('preferences')->mark(); app('preferences')->mark();
session()->flash('success', (string)trans('firefly.reconciliation_stored')); if ('' === $result) {
session()->flash('success', (string)trans('firefly.reconciliation_stored'));
}
if ('' !== $result) {
session()->flash('error', (string)trans('firefly.reconciliation_error', ['error' => $result]));
}
return redirect(route('accounts.show', [$account->id])); return redirect(route('accounts.show', [$account->id]));
} }
/**
* Creates a reconciliation group.
* @return string
*/
private function createReconciliation(Account $account, Carbon $start, Carbon $end, string $difference): string
{
$reconciliation = $this->accountRepos->getReconciliation($account);
$currency = $this->accountRepos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency();
$source = $reconciliation;
$destination = $account;
if (1 === bccomp($difference, '0')) {
$source = $account;
$destination = $reconciliation;
}
// title:
$description = trans('firefly.reconciliation_transaction_title',
['from' => $start->formatLocalized($this->monthAndDayFormat), 'to' => $end->formatLocalized($this->monthAndDayFormat)]);
$submission = [
'user' => auth()->user()->id,
'group_title' => null,
'transactions' => [
[
'user' => auth()->user()->id,
'type' => strtolower(TransactionType::RECONCILIATION),
'date' => $end,
'order' => 0,
'currency_id' => $currency->id,
'foreign_currency_id' => null,
'amount' => $difference,
'foreign_amount' => null,
'description' => $description,
'source_id' => $source->id,
'destination_id' => $destination->id,
'reconciled' => true,
],
],
];
/** @var TransactionGroupFactory $factory */
$factory = app(TransactionGroupFactory::class);
/** @var User $user */
$user = auth()->user();
$factory->setUser($user);
try {
$factory->create($submission);
} catch (FireflyException $e) {
return $e->getMessage();
}
return '';
}
} }

View File

@ -36,6 +36,7 @@ use FireflyIII\Support\Http\Controllers\UserNavigation;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use View; use View;
use Exception;
/** /**
* Class ShowController * Class ShowController
@ -53,6 +54,7 @@ class ShowController extends Controller
/** /**
* ShowController constructor. * ShowController constructor.
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@ -82,43 +84,34 @@ class ShowController extends Controller
* @param Carbon|null $end * @param Carbon|null $end
* *
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
* * @throws Exception
* @throws FireflyException
*
*/ */
public function show(Request $request, Account $account, Carbon $start = null, Carbon $end = null) public function show(Request $request, Account $account, Carbon $start = null, Carbon $end = null)
{ {
if (AccountType::INITIAL_BALANCE === $account->accountType->type) { if (in_array($account->accountType->type, [AccountType::INITIAL_BALANCE, AccountType::RECONCILIATION], true)) {
return $this->redirectToOriginalAccount($account); return $this->redirectToOriginalAccount($account); // @codeCoverageIgnore
}
// a basic thing to determin if this account is a liability:
if ($this->repository->isLiability($account)) {
return redirect(route('accounts.show.all', [$account->id]));
} }
/** @var Carbon $start */ /** @var Carbon $start */
$start = $start ?? session('start'); $start = $start ?? session('start');
/** @var Carbon $end */ /** @var Carbon $end */
$end = $end ?? session('end'); $end = $end ?? session('end');
if ($end < $start) { if ($end < $start) {
throw new FireflyException('End is after start!'); // @codeCoverageIgnore [$start, $end] = [$end, $start]; // @codeCoverageIgnore
} }
$what = config(sprintf('firefly.shortNamesByFullName.%s', $account->accountType->type)); // used for menu $objectType = config(sprintf('firefly.shortNamesByFullName.%s', $account->accountType->type));
$today = new Carbon; $today = new Carbon;
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $account->accountType->type)); $subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $account->accountType->type));
$page = (int)$request->get('page'); $page = (int)$request->get('page');
$pageSize = (int)app('preferences')->get('listPageSize', 50)->data; $pageSize = (int)app('preferences')->get('listPageSize', 50)->data;
$currencyId = (int)$this->repository->getMetaValue($account, 'currency_id'); $currency = $this->repository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency();
$currency = $this->currencyRepos->findNull($currencyId); $fStart = $start->formatLocalized($this->monthAndDayFormat);
if (0 === $currencyId) { $fEnd = $end->formatLocalized($this->monthAndDayFormat);
$currency = app('amount')->getDefaultCurrency(); // @codeCoverageIgnore $subTitle = (string)trans('firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $fStart, 'end' => $fEnd]);
} $chartUri = route('chart.account.period', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]);
$fStart = $start->formatLocalized($this->monthAndDayFormat); $periods = $this->getAccountPeriodOverview($account, $end);
$fEnd = $end->formatLocalized($this->monthAndDayFormat);
$subTitle = (string)trans('firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $fStart, 'end' => $fEnd]);
$chartUri = route('chart.account.period', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]);
$periods = $this->getAccountPeriodOverview($account, $end);
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
@ -134,7 +127,7 @@ class ShowController extends Controller
return view( return view(
'accounts.show', 'accounts.show',
compact( compact(
'account', 'showAll', 'what', 'currency', 'today', 'periods', 'subTitleIcon', 'groups', 'subTitle', 'start', 'end', 'account', 'showAll', 'objectType', 'currency', 'today', 'periods', 'subTitleIcon', 'groups', 'subTitle', 'start', 'end',
'chartUri' 'chartUri'
) )
); );
@ -145,7 +138,7 @@ class ShowController extends Controller
* *
* @param Request $request * @param Request $request
* @param Account $account * @param Account $account
* * @throws Exception
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
* *
* *
@ -156,19 +149,16 @@ class ShowController extends Controller
return $this->redirectToOriginalAccount($account); // @codeCoverageIgnore return $this->redirectToOriginalAccount($account); // @codeCoverageIgnore
} }
$isLiability = $this->repository->isLiability($account); $isLiability = $this->repository->isLiability($account);
$objectType = config(sprintf('firefly.shortNamesByFullName.%s', $account->accountType->type));
$end = new Carbon; $end = new Carbon;
$today = new Carbon; $today = new Carbon;
$start = $this->repository->oldestJournalDate($account) ?? Carbon::now()->startOfMonth(); $start = $this->repository->oldestJournalDate($account) ?? Carbon::now()->startOfMonth();
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type); $subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
$page = (int)$request->get('page'); $page = (int)$request->get('page');
$pageSize = (int)app('preferences')->get('listPageSize', 50)->data; $pageSize = (int)app('preferences')->get('listPageSize', 50)->data;
$currencyId = (int)$this->repository->getMetaValue($account, 'currency_id'); $currency = $this->repository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency();
$currency = $this->currencyRepos->findNull($currencyId); $subTitle = (string)trans('firefly.all_journals_for_account', ['name' => $account->name]);
if (0 === $currencyId) { $periods = new Collection;
$currency = app('amount')->getDefaultCurrency(); // @codeCoverageIgnore
}
$subTitle = (string)trans('firefly.all_journals_for_account', ['name' => $account->name]);
$periods = new Collection;
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page)->withAccountInformation(); $collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page)->withAccountInformation();
@ -179,7 +169,8 @@ class ShowController extends Controller
return view( return view(
'accounts.show', 'accounts.show',
compact('account', 'showAll', 'isLiability', 'currency', 'today', 'chartUri', 'periods', 'subTitleIcon', 'groups', 'subTitle', 'start', 'end') compact('account', 'showAll', 'objectType', 'isLiability', 'currency', 'today',
'chartUri', 'periods', 'subTitleIcon', 'groups', 'subTitle', 'start', 'end')
); );
} }

View File

@ -139,7 +139,7 @@ class ReconcileController extends Controller
'accounts.reconcile.overview', compact( 'accounts.reconcile.overview', compact(
'account', 'start', 'diffCompare', 'difference', 'end', 'clearedAmount', 'account', 'start', 'diffCompare', 'difference', 'end', 'clearedAmount',
'startBalance', 'endBalance', 'amount', 'startBalance', 'endBalance', 'amount',
'route', 'countCleared', 'reconSum' 'route', 'countCleared', 'reconSum', 'selectedIds'
) )
)->render(); )->render();
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart

View File

@ -65,29 +65,6 @@ class TransactionController extends Controller
); );
} }
/**
* Do a reconciliation.
*
* @param Request $request
*
* @return JsonResponse
*/
public function reconcile(Request $request): JsonResponse
{
$transactionIds = $request->get('transactions');
foreach ($transactionIds as $transactionId) {
$transactionId = (int)$transactionId;
$transaction = $this->repository->findTransaction($transactionId);
if (null !== $transaction) {
Log::debug(sprintf('Transaction ID is %d', $transaction->id));
$this->repository->reconcile($transaction);
}
}
return response()->json(['ok' => 'reconciled']);
}
/** /**
* Reorder transactions. * Reorder transactions.
* *

View File

@ -23,7 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Requests; namespace FireflyIII\Http\Requests;
use FireflyIII\Rules\ValidTransactions; use FireflyIII\Rules\ValidJournals;
use Log; use Log;
/** /**
@ -59,7 +59,7 @@ class ReconciliationStoreRequest extends Request
'start_balance' => $this->string('startBalance'), 'start_balance' => $this->string('startBalance'),
'end_balance' => $this->string('endBalance'), 'end_balance' => $this->string('endBalance'),
'difference' => $this->string('difference'), 'difference' => $this->string('difference'),
'journals' => $transactions, 'journals' => $transactions,
'reconcile' => $this->string('reconcile'), 'reconcile' => $this->string('reconcile'),
]; ];
Log::debug('In ReconciliationStoreRequest::getAll(). Will now return data.'); Log::debug('In ReconciliationStoreRequest::getAll(). Will now return data.');
@ -80,7 +80,7 @@ class ReconciliationStoreRequest extends Request
'startBalance' => 'numeric', 'startBalance' => 'numeric',
'endBalance' => 'numeric', 'endBalance' => 'numeric',
'difference' => 'required|numeric', 'difference' => 'required|numeric',
'journals' => [new ValidJournals], 'journals' => [new ValidJournals],
'reconcile' => 'required|in:create,nothing', 'reconcile' => 'required|in:create,nothing',
]; ];
} }

View File

@ -230,6 +230,7 @@ class AccountRepository implements AccountRepositoryInterface
if (count($accountIds) > 0) { if (count($accountIds) > 0) {
$query->whereIn('accounts.id', $accountIds); $query->whereIn('accounts.id', $accountIds);
} }
$query->orderBy('accounts.active', 'DESC');
$query->orderBy('accounts.name', 'ASC'); $query->orderBy('accounts.name', 'ASC');
$result = $query->get(['accounts.*']); $result = $query->get(['accounts.*']);
@ -249,6 +250,7 @@ class AccountRepository implements AccountRepositoryInterface
if (count($types) > 0) { if (count($types) > 0) {
$query->accountTypeIn($types); $query->accountTypeIn($types);
} }
$query->orderBy('accounts.active', 'DESC');
$query->orderBy('accounts.name', 'ASC'); $query->orderBy('accounts.name', 'ASC');
$result = $query->get(['accounts.*']); $result = $query->get(['accounts.*']);

View File

@ -688,18 +688,14 @@ class JournalRepository implements JournalRepositoryInterface
/** /**
* @param int $transactionId * @param int $transactionId
*
* @return bool
*/ */
public function reconcileById(int $transactionId): bool public function reconcileById(int $journalId): void
{ {
/** @var Transaction $transaction */ /** @var TransactionJournal $journal */
$transaction = $this->user->transactions()->find($transactionId); $journal = $this->user->transactionJournals()->find($journalId);
if (null !== $transaction) { if (null !== $journal) {
return $this->reconcile($transaction); $journal->transactions()->update(['reconciled' => true]);
} }
return false;
} }
/** /**

View File

@ -309,11 +309,9 @@ interface JournalRepositoryInterface
public function reconcile(Transaction $transaction): bool; public function reconcile(Transaction $transaction): bool;
/** /**
* @param int $transactionId * @param int $journalId
*
* @return bool
*/ */
public function reconcileById(int $transactionId): bool; public function reconcileById(int $journalId): void;
/** /**
* @param TransactionJournal $journal * @param TransactionJournal $journal

View File

@ -87,7 +87,7 @@ class AmountFormat extends Twig_Extension
static function (AccountModel $account, string $amount, bool $coloured = null): string { static function (AccountModel $account, string $amount, bool $coloured = null): string {
if ('testing' === config('app.env')) { if ('testing' === config('app.env')) {
Log::warning(sprintf('%s should NOT be called in the TEST environment!', __METHOD__)); Log::warning('AmountFormat::formatAmountByAccount should NOT be called in the TEST environment!');
} }
$coloured = $coloured ?? true; $coloured = $coloured ?? true;

View File

@ -153,10 +153,6 @@ class General extends Twig_Extension
/** @var Carbon $date */ /** @var Carbon $date */
$date = session('end', Carbon::now()->endOfMonth()); $date = session('end', Carbon::now()->endOfMonth());
if ('testing' === config('app.env')) {
Log::warning(sprintf('%s should NOT be called in the TEST environment!', __METHOD__));
}
return app('steam')->balance($account, $date); return app('steam')->balance($account, $date);
} }
); );
@ -213,7 +209,7 @@ class General extends Twig_Extension
'accountGetMetaField', 'accountGetMetaField',
static function (Account $account, string $field): string { static function (Account $account, string $field): string {
if ('testing' === config('app.env')) { if ('testing' === config('app.env')) {
Log::warning(sprintf('%s should NOT be called in the TEST environment!', __METHOD__)); Log::warning('Twig General::getMetaField should NOT be called in the TEST environment!');
} }
/** @var AccountRepositoryInterface $repository */ /** @var AccountRepositoryInterface $repository */
@ -238,9 +234,6 @@ class General extends Twig_Extension
return new Twig_SimpleFunction( return new Twig_SimpleFunction(
'hasRole', 'hasRole',
static function (string $role): bool { static function (string $role): bool {
if ('testing' === config('app.env')) {
Log::warning(sprintf('%s should NOT be called in the TEST environment!', __METHOD__));
}
$repository = app(UserRepositoryInterface::class); $repository = app(UserRepositoryInterface::class);
if ($repository->hasRole(auth()->user(), $role)) { if ($repository->hasRole(auth()->user(), $role)) {
return true; return true;
@ -258,7 +251,7 @@ class General extends Twig_Extension
{ {
return new Twig_SimpleFilter( return new Twig_SimpleFilter(
'markdown', 'markdown',
function (string $text): string { static function (string $text): string {
$converter = new CommonMarkConverter; $converter = new CommonMarkConverter;
return $converter->convertToHtml($text); return $converter->convertToHtml($text);
@ -275,7 +268,7 @@ class General extends Twig_Extension
{ {
return new Twig_SimpleFilter( return new Twig_SimpleFilter(
'mimeIcon', 'mimeIcon',
function (string $string): string { static function (string $string): string {
switch ($string) { switch ($string) {
default: default:
return 'fa-file-o'; return 'fa-file-o';
@ -354,7 +347,7 @@ class General extends Twig_Extension
{ {
return new Twig_SimpleFunction( return new Twig_SimpleFunction(
'phpdate', 'phpdate',
function (string $str): string { static function (string $str): string {
return date($str); return date($str);
} }
); );

View File

@ -96,7 +96,7 @@ class TransactionGroupTwig extends Twig_Extension
'journalGetMetaDate', 'journalGetMetaDate',
static function (int $journalId, string $metaField) { static function (int $journalId, string $metaField) {
if ('testing' === config('app.env')) { if ('testing' === config('app.env')) {
Log::warning(sprintf('%s should NOT be called in the TEST environment!', __METHOD__)); Log::warning('Twig TransactionGroup::journalGetMetaDate should NOT be called in the TEST environment!');
} }
$entry = DB::table('journal_meta') $entry = DB::table('journal_meta')
->where('name', $metaField) ->where('name', $metaField)
@ -121,7 +121,7 @@ class TransactionGroupTwig extends Twig_Extension
'journalGetMetaField', 'journalGetMetaField',
static function (int $journalId, string $metaField) { static function (int $journalId, string $metaField) {
if ('testing' === config('app.env')) { if ('testing' === config('app.env')) {
Log::warning(sprintf('%s should NOT be called in the TEST environment!', __METHOD__)); Log::warning('Twig TransactionGroup::journalGetMetaField should NOT be called in the TEST environment!');
} }
$entry = DB::table('journal_meta') $entry = DB::table('journal_meta')
->where('name', $metaField) ->where('name', $metaField)
@ -146,7 +146,7 @@ class TransactionGroupTwig extends Twig_Extension
'journalHasMeta', 'journalHasMeta',
static function (int $journalId, string $metaField) { static function (int $journalId, string $metaField) {
if ('testing' === config('app.env')) { if ('testing' === config('app.env')) {
Log::warning(sprintf('%s should NOT be called in the TEST environment!', __METHOD__)); Log::warning('Twig TransactionGroup::journalHasMeta should NOT be called in the TEST environment!');
} }
$count = DB::table('journal_meta') $count = DB::table('journal_meta')
->where('name', $metaField) ->where('name', $metaField)

View File

@ -124,6 +124,9 @@ class AccountValidator
case TransactionType::OPENING_BALANCE: case TransactionType::OPENING_BALANCE:
$result = $this->validateOBDestination($destinationId, $destinationName); $result = $this->validateOBDestination($destinationId, $destinationName);
break; break;
case TransactionType::RECONCILIATION:
$result = $this->validateReconciliationDestination($destinationId);
break;
//case TransactionType::OPENING_BALANCE: //case TransactionType::OPENING_BALANCE:
//case TransactionType::RECONCILIATION: //case TransactionType::RECONCILIATION:
// die(sprintf('Cannot handle type "%s"', $this->transactionType)); // die(sprintf('Cannot handle type "%s"', $this->transactionType));
@ -159,6 +162,9 @@ class AccountValidator
case TransactionType::OPENING_BALANCE: case TransactionType::OPENING_BALANCE:
$result = $this->validateOBSource($accountId, $accountName); $result = $this->validateOBSource($accountId, $accountName);
break; break;
case TransactionType::RECONCILIATION:
$result = $this->validateReconciliationSource($accountId);
break;
//case TransactionType::OPENING_BALANCE: //case TransactionType::OPENING_BALANCE:
//case TransactionType::RECONCILIATION: //case TransactionType::RECONCILIATION:
// die(sprintf('Cannot handle type "%s"', $this->transactionType)); // die(sprintf('Cannot handle type "%s"', $this->transactionType));
@ -582,5 +588,51 @@ class AccountValidator
return true; return true;
} }
/**
* @param int|null $accountId
* @return bool
*/
private function validateReconciliationSource(?int $accountId): bool
{
if (null === $accountId) {
return false;
}
$result = $this->accountRepository->findNull($accountId);
$types = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE, AccountType::RECONCILIATION];
if (null === $result) {
return false;
}
if (in_array($result->accountType->type, $types, true)) {
$this->source = $result;
return true;
}
return false;
}
/**
* @param int|null $accountId
* @return bool
*/
private function validateReconciliationDestination(?int $accountId): bool
{
if (null === $accountId) {
return false;
}
$result = $this->accountRepository->findNull($accountId);
$types = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE, AccountType::RECONCILIATION];
if (null === $result) {
return false;
}
if (in_array($result->accountType->type, $types, true)) {
$this->destination = $result;
return true;
}
return false;
}
} }

View File

@ -780,7 +780,9 @@ return [
'reconcile_go_back' => 'You can always edit or delete a correction later.', 'reconcile_go_back' => 'You can always edit or delete a correction later.',
'must_be_asset_account' => 'You can only reconcile asset accounts', 'must_be_asset_account' => 'You can only reconcile asset accounts',
'reconciliation_stored' => 'Reconciliation stored', 'reconciliation_stored' => 'Reconciliation stored',
'reconcilliation_transaction_title' => 'Reconciliation (:from to :to)', 'reconciliation_error' => 'Due to an error the transactions were marked as reconciled but the correction has not been stored: :error.',
'reconciliation_transaction_title' => 'Reconciliation (:from to :to)',
'sum_of_reconciliation' => 'Sum of reconciliation',
'reconcile_this_account' => 'Reconcile this account', 'reconcile_this_account' => 'Reconcile this account',
'confirm_reconciliation' => 'Confirm reconciliation', 'confirm_reconciliation' => 'Confirm reconciliation',
'submitted_start_balance' => 'Submitted start balance', 'submitted_start_balance' => 'Submitted start balance',
@ -1381,13 +1383,13 @@ return [
'recurrence_deleted' => 'Recurring transaction ":title" deleted', 'recurrence_deleted' => 'Recurring transaction ":title" deleted',
// new lines for summary controller. // new lines for summary controller.
'box_balance_in_currency' => 'Balance (:currency)', 'box_balance_in_currency' => 'Balance (:currency)',
'box_spent_in_currency' => 'Spent (:currency)', 'box_spent_in_currency' => 'Spent (:currency)',
'box_earned_in_currency' => 'Earned (:currency)', 'box_earned_in_currency' => 'Earned (:currency)',
'box_bill_paid_in_currency' => 'Bills paid (:currency)', 'box_bill_paid_in_currency' => 'Bills paid (:currency)',
'box_bill_unpaid_in_currency' => 'Bills unpaid (:currency)', 'box_bill_unpaid_in_currency' => 'Bills unpaid (:currency)',
'box_left_to_spend_in_currency' => 'Left to spend (:currency)', 'box_left_to_spend_in_currency' => 'Left to spend (:currency)',
'box_net_worth_in_currency' => 'Net worth (:currency)', 'box_net_worth_in_currency' => 'Net worth (:currency)',
'box_spend_per_day' => 'Left to spend per day: :amount', 'box_spend_per_day' => 'Left to spend per day: :amount',
]; ];

View File

@ -15,8 +15,8 @@
<input type="hidden" name="end" value="{{ end.format('Y-m-d') }}"/> <input type="hidden" name="end" value="{{ end.format('Y-m-d') }}"/>
<input type="hidden" name="startBalance" value="{{ startBalance }}"/> <input type="hidden" name="startBalance" value="{{ startBalance }}"/>
<input type="hidden" name="endBalance" value="{{ endBalance }}"/> <input type="hidden" name="endBalance" value="{{ endBalance }}"/>
{% for id in transactionIds %} {% for id in selectedIds %}
<input type="hidden" name="transactions[]" value="{{ id }}"/> <input type="hidden" name="journals[]" value="{{ id }}"/>
{% endfor %} {% endfor %}
<table class="table table-striped table-bordered"> <table class="table table-striped table-bordered">
@ -25,7 +25,7 @@
<td>{{ formatAmountByAccount(account, startBalance) }}</td> <td>{{ formatAmountByAccount(account, startBalance) }}</td>
</tr> </tr>
<tr> <tr>
<td>{{ trans('firefly.selected_transactions', {count: transactionIds|length}) }}</td> <td>{{ trans('firefly.selected_transactions', {count: selectedIds|length}) }}</td>
<td>{{ formatAmountByAccount(account, amount) }}</td> <td>{{ formatAmountByAccount(account, amount) }}</td>
</tr> </tr>
<tr> <tr>

View File

@ -102,7 +102,6 @@
</td> </td>
<td> <td>
{% if journal.date >= start and journal.date <= end %} {% if journal.date >= start and journal.date <= end %}
{% if journal.reconciled %} {% if journal.reconciled %}
<i class="fa fa-check" aria-hidden="true"></i> <i class="fa fa-check" aria-hidden="true"></i>

View File

@ -13,76 +13,19 @@ TODO: hide and show columns
<tbody> <tbody>
{% for group in groups %} {% for group in groups %}
{% if group.count > 1 %} {% if group.count > 1 %}
<tr> <tr>
<td colspan="2" style="border-top:1px #aaa solid;"> <td colspan="2" style="border-top:1px #aaa solid;">
<small><strong> <small><strong>
<a href="{{ route('transactions.edit', [group.id]) }}" title="{{ group.title }}">{{ group.title }}</a> <a href="{{ route('transactions.edit', [group.id]) }}" title="{{ group.title }}">{{ group.title }}</a>
</strong></small> </strong></small>
</td> </td>
<td colspan="2" style="border-top:1px #aaa solid;"> <td colspan="2" style="border-top:1px #aaa solid;">
{% for sum in group.sums %} {% for sum in group.sums %}
{{ formatAmountBySymbol(sum.amount, sum.currency_symbol, sum.currency_symbol_decimal_places) }}{% if loop.index != group.sums|length %},{% endif %} {{ formatAmountBySymbol(sum.amount, sum.currency_symbol, sum.currency_symbol_decimal_places) }}{% if loop.index != group.sums|length %},{% endif %}
{% endfor %} {% endfor %}
</td> </td>
<td colspan="2" style="border-top:1px #aaa solid;">&nbsp;</td> <td colspan="2" style="border-top:1px #aaa solid;">&nbsp;</td>
<td style="border-top:1px #aaa solid;"> <td style="border-top:1px #aaa solid;">
<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>
<ul class="dropdown-menu dropdown-menu-right" role="menu">
<li><a href="{{ route('transactions.edit', [group.id]) }}"><i class="fa fa-fw fa-pencil"></i> {{ 'edit'|_ }}</a></li>
<li><a href="{{ route('transactions.delete', [group.id]) }}"><i class="fa fa-fw fa-trash"></i> {{ 'delete'|_ }}</a></li>
<li><a href="{{ route('transactions.clone', [group.id]) }}"><i class="fa fa-copy fa-fw"></i> {{ 'clone'|_ }}</a></li>
</ul>
</div>
</td>
</tr>
{% endif %}
{% 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;'" %}
{% endif %}
<tr>
<td {{ style|raw }}>
{% if journal.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' %}
<i class="fa fa-long-arrow-right fa-fw" title="{{ trans('firefly.Deposit') }}"></i>
{% endif %}
{% if journal.transaction_type_type == 'Transfer' %}
<i class="fa fa-exchange fa-fw" title="{{ trans('firefly.Deposit') }}"></i>
{% endif %}
{% if journal.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' %}
<i class="fa-fw fa fa-star-o" title="{{ trans('firefly.Opening balance') }}"></i>
{% endif %}
</td>
<td {{ style|raw }}>
<a href="{{ route('transactions.show', [group.id]) }}" title="{{ transaction.description }}">{{ transaction.description }}</a>
</td>
<td {{ 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 }}>
<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 }}>
<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 }}>
{% if group.count == 1 %}
<div class="btn-group btn-group-xs"> <div class="btn-group btn-group-xs">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ 'actions'|_ }} <span class="caret"></span></button> {{ 'actions'|_ }} <span class="caret"></span></button>
@ -92,10 +35,72 @@ TODO: hide and show columns
<li><a href="{{ route('transactions.clone', [group.id]) }}"><i class="fa fa-copy fa-fw"></i> {{ 'clone'|_ }}</a></li> <li><a href="{{ route('transactions.clone', [group.id]) }}"><i class="fa fa-copy fa-fw"></i> {{ 'clone'|_ }}</a></li>
</ul> </ul>
</div> </div>
{% endif %} </td>
</td> </tr>
</tr> {% endif %}
{% endfor %} {% 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;'" %}
{% endif %}
<tr>
<td {{ style|raw }}>
{% if journal.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' %}
<i class="fa fa-long-arrow-right fa-fw" title="{{ trans('firefly.Deposit') }}"></i>
{% endif %}
{% if journal.transaction_type_type == 'Transfer' %}
<i class="fa fa-exchange fa-fw" title="{{ trans('firefly.Deposit') }}"></i>
{% endif %}
{% if journal.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' %}
<i class="fa-fw fa fa-star-o" title="{{ trans('firefly.Opening balance') }}"></i>
{% endif %}
</td>
<td {{ 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 }}>
{{ 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 }}>
<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 }}>
<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 }}>
{% 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">
{{ 'actions'|_ }} <span class="caret"></span></button>
<ul class="dropdown-menu dropdown-menu-right" role="menu">
<li><a href="{{ route('transactions.edit', [group.id]) }}"><i class="fa fa-fw fa-pencil"></i> {{ 'edit'|_ }}</a></li>
<li><a href="{{ route('transactions.delete', [group.id]) }}"><i class="fa fa-fw fa-trash"></i> {{ 'delete'|_ }}</a></li>
<li><a href="{{ route('transactions.clone', [group.id]) }}"><i class="fa fa-copy fa-fw"></i> {{ 'clone'|_ }}</a></li>
</ul>
</div>
{% endif %}
</td>
</tr>
{% endfor %}
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>

View File

@ -74,17 +74,18 @@ class EditControllerTest extends TestCase
// mock preferences: // mock preferences:
$this->mockDefaultPreferences(); $this->mockDefaultPreferences();
$repository->shouldReceive('findNull')->withArgs([1])->andReturn($euro)->atLeast()->once(); //$repository->shouldReceive('findNull')->withArgs([1])->andReturn($euro)->atLeast()->once();
// mock hasRole for user repository: // mock hasRole for user repository:
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->andReturn(true)->atLeast()->once(); $userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->andReturn(true)->atLeast()->once();
$repository->shouldReceive('get')->andReturn(new Collection)->atLeast()->once(); $repository->shouldReceive('get')->andReturn(new Collection)->atLeast()->once();
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal); $journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$accountRepos->shouldReceive('getAccountCurrency')->andReturn($euro)->once();
$accountRepos->shouldReceive('getNoteText')->andReturn('Some text')->once(); $accountRepos->shouldReceive('getNoteText')->andReturn('Some text')->once();
$accountRepos->shouldReceive('getOpeningBalanceAmount')->andReturnNull()->atLeast()->once(); $accountRepos->shouldReceive('getOpeningBalanceAmount')->andReturnNull()->atLeast()->once();
$accountRepos->shouldReceive('getOpeningBalanceDate')->andReturnNull()->atLeast()->once(); $accountRepos->shouldReceive('getOpeningBalanceDate')->andReturnNull()->atLeast()->once();
$accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'currency_id'])->andReturn('1')->atLeast()->once(); //$accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'currency_id'])->andReturn('1')->atLeast()->once();
$accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'account_number'])->andReturn('123')->atLeast()->once(); $accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'account_number'])->andReturn('123')->atLeast()->once();
$accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'account_role'])->andReturn('defaultAsset')->atLeast()->once(); $accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'account_role'])->andReturn('defaultAsset')->atLeast()->once();
$accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'cc_type'])->andReturn('')->atLeast()->once(); $accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'cc_type'])->andReturn('')->atLeast()->once();
@ -128,13 +129,13 @@ class EditControllerTest extends TestCase
// mock hasRole for user repository: // mock hasRole for user repository:
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->andReturn(true)->atLeast()->once(); $userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->andReturn(true)->atLeast()->once();
$repository->shouldReceive('findNull')->once()->andReturn($euro); //$repository->shouldReceive('findNull')->once()->andReturn($euro);
$repository->shouldReceive('get')->andReturn(new Collection); $repository->shouldReceive('get')->andReturn(new Collection);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal); $journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$accountRepos->shouldReceive('getNoteText')->andReturn('Some text')->once(); $accountRepos->shouldReceive('getNoteText')->andReturn('Some text')->once();
$accountRepos->shouldReceive('getOpeningBalanceAmount')->andReturnNull(); $accountRepos->shouldReceive('getOpeningBalanceAmount')->andReturnNull();
$accountRepos->shouldReceive('getOpeningBalanceDate')->andReturnNull(); $accountRepos->shouldReceive('getOpeningBalanceDate')->andReturnNull();
$accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'currency_id'])->andReturn('1'); //$accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'currency_id'])->andReturn('1');
$accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'account_number'])->andReturn('123'); $accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'account_number'])->andReturn('123');
$accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'account_role'])->andReturn('defaultAsset'); $accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'account_role'])->andReturn('defaultAsset');
$accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'cc_type'])->andReturn(''); $accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'cc_type'])->andReturn('');
@ -143,6 +144,7 @@ class EditControllerTest extends TestCase
$accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'BIC'])->andReturn('BIC'); $accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'BIC'])->andReturn('BIC');
$accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'interest'])->andReturn('1'); $accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'interest'])->andReturn('1');
$accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'interest_period'])->andReturn('monthly'); $accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'interest_period'])->andReturn('monthly');
$accountRepos->shouldReceive('getAccountCurrency')->andReturn($euro)->once();
// get all types: // get all types:
$accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Debt'])->andReturn(AccountType::find(11))->once(); $accountRepos->shouldReceive('getAccountTypeByType')->withArgs(['Debt'])->andReturn(AccountType::find(11))->once();
@ -179,12 +181,12 @@ class EditControllerTest extends TestCase
$repository = $this->mock(CurrencyRepositoryInterface::class); $repository = $this->mock(CurrencyRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class); $accountRepos = $this->mock(AccountRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$euro = $this->getEuro();
// mock hasRole for user repository: // mock hasRole for user repository:
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->andReturn(true)->atLeast()->once(); $userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->andReturn(true)->atLeast()->once();
Amount::shouldReceive('getDefaultCurrency')->andReturn(TransactionCurrency::find(2)); Amount::shouldReceive('getDefaultCurrency')->andReturn(TransactionCurrency::find(2));
$repository->shouldReceive('findNull')->once()->andReturn(null); //$repository->shouldReceive('findNull')->once()->andReturn(null);
$repository->shouldReceive('get')->andReturn(new Collection); $repository->shouldReceive('get')->andReturn(new Collection);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal); $journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$accountRepos->shouldReceive('getNoteText')->andReturn('Some text')->once(); $accountRepos->shouldReceive('getNoteText')->andReturn('Some text')->once();
@ -199,6 +201,7 @@ class EditControllerTest extends TestCase
$accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'BIC'])->andReturn('BIC'); $accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'BIC'])->andReturn('BIC');
$accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'interest'])->andReturn('1'); $accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'interest'])->andReturn('1');
$accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'interest_period'])->andReturn('monthly'); $accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'interest_period'])->andReturn('monthly');
$accountRepos->shouldReceive('getAccountCurrency')->andReturn($euro)->once();
// mock calls to Preferences: // mock calls to Preferences:
$this->mockDefaultPreferences(); $this->mockDefaultPreferences();

View File

@ -23,20 +23,21 @@ declare(strict_types=1);
namespace Tests\Feature\Controllers\Account; namespace Tests\Feature\Controllers\Account;
use Amount;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\FiscalHelperInterface; use FireflyIII\Factory\TransactionGroupFactory;
use FireflyIII\Models\Account; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Transaction; use FireflyIII\Helpers\Fiscal\FiscalHelperInterface;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Support\Collection;
use Log; use Log;
use Mockery; use Mockery;
use Preferences;
use Steam;
use Tests\TestCase; use Tests\TestCase;
/** /**
@ -53,64 +54,6 @@ class ReconcileControllerTest extends TestCase
Log::info(sprintf('Now in %s.', get_class($this))); Log::info(sprintf('Now in %s.', get_class($this)));
} }
/**
* Test editing a reconciliation.
*
* @covers \FireflyIII\Http\Controllers\Account\ReconcileController
*/
public function testEdit(): void
{
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
$repository = $this->mock(JournalRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(TransactionCollectorInterface::class);
// mock hasRole for user repository:
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->andReturn(true)->atLeast()->once();
$journal = $this->user()->transactionJournals()->where('transaction_type_id', 5)->first();
$transaction = $journal->transactions()->where('amount', '>', 0)->first();
$repository->shouldReceive('firstNull')->andReturn($journal);
$repository->shouldReceive('getFirstPosTransaction')->andReturn($transaction);
$repository->shouldReceive('getJournalDate')->andReturn('2018-01-01');
$repository->shouldReceive('getJournalCategoryName')->andReturn('');
$repository->shouldReceive('getJournalBudgetid')->andReturn(0);
$this->be($this->user());
$response = $this->get(route('accounts.reconcile.edit', [$journal->id]));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* Test the redirect if journal is not a reconciliation.
*
* @covers \FireflyIII\Http\Controllers\Account\ReconcileController
*/
public function testEditRedirect(): void
{
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(TransactionCollectorInterface::class);
$journal = $this->user()->transactionJournals()->where('transaction_type_id', '!=', 5)->first();
$this->be($this->user());
$response = $this->get(route('accounts.reconcile.edit', [$journal->id]));
$response->assertStatus(302);
$response->assertRedirect(route('transactions.edit', [$journal->id]));
}
/** /**
* Test showing the reconciliation. * Test showing the reconciliation.
@ -119,241 +62,37 @@ class ReconcileControllerTest extends TestCase
*/ */
public function testReconcile(): void public function testReconcile(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0'); $userRepos = $this->mock(UserRepositoryInterface::class);
$this->mock(CurrencyRepositoryInterface::class);
return;
$userRepos = $this->mock(UserRepositoryInterface::class);
$repository = $this->mock(CurrencyRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class); $accountRepos = $this->mock(AccountRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class); $fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(TransactionCollectorInterface::class); $this->mock(GroupCollectorInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$euro = $this->getEuro();
$asset = $this->getRandomAsset();
$date = new Carbon; $date = new Carbon;
// used for session range.
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$userRepos->shouldReceive('hasRole')->atLeast()->once()->withArgs([Mockery::any(), 'owner'])->andReturnTrue();
$fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date); $fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date);
$fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date); $fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date);
$this->mockDefaultConfiguration();
$this->mockDefaultPreferences();
Amount::shouldReceive('getDefaultCurrency')->atLeast()->once()->andReturn($euro);
Steam::shouldReceive('balance')->atLeast()->once()->andReturn('100');
$accountRepos->shouldReceive('getAccountCurrency')->atLeast()->once()->andReturn($euro);
$accountRepos->shouldReceive('getMetaValue')
->withArgs([Mockery::any(), 'currency_id'])->andReturn('1')->atLeast()->once();
// mock hasRole for user repository:
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->andReturn(true)->atLeast()->once();
$repository->shouldReceive('findNull')->once()->andReturn(TransactionCurrency::find(1));
$this->be($this->user()); $this->be($this->user());
$response = $this->get(route('accounts.reconcile', [1, '20170101', '20170131'])); $response = $this->get(route('accounts.reconcile', [$asset->id, '20170101', '20170131']));
$response->assertStatus(200); $response->assertStatus(200);
// has bread crumb // has bread crumb
$response->assertSee('<ol class="breadcrumb">'); $response->assertSee('<ol class="breadcrumb">');
} }
/**
* Test showing the reconciliation (its a initial balance).
*
* @covers \FireflyIII\Http\Controllers\Account\ReconcileController
*/
public function testReconcileInitialBalance(): void
{
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
$userRepos = $this->mock(UserRepositoryInterface::class);
$repository = $this->mock(CurrencyRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(TransactionCollectorInterface::class);
$date = new Carbon;
$fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date);
$fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date);
$transaction = Transaction::leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->where('accounts.user_id', $this->user()->id)->where('accounts.account_type_id', 6)->first(['account_id']);
$this->be($this->user());
$response = $this->get(route('accounts.reconcile', [$transaction->account_id, '20170101', '20170131']));
$response->assertStatus(302);
}
/**
* Test reconcile view (without date info).
*
* @covers \FireflyIII\Http\Controllers\Account\ReconcileController
*/
public function testReconcileNoDates(): void
{
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
$userRepos = $this->mock(UserRepositoryInterface::class);
$repository = $this->mock(CurrencyRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(TransactionCollectorInterface::class);
$accountRepos->shouldReceive('getMetaValue')
->withArgs([Mockery::any(), 'currency_id'])->andReturn('1')->atLeast()->once();
// mock hasRole for user repository:
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->andReturn(true)->atLeast()->once();
$repository->shouldReceive('findNull')->once()->andReturn(TransactionCurrency::find(1));
$this->be($this->user());
$response = $this->get(route('accounts.reconcile', [1]));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* Test reconcile view (without end date).
*
* @covers \FireflyIII\Http\Controllers\Account\ReconcileController
*/
public function testReconcileNoEndDate(): void
{
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
$userRepos = $this->mock(UserRepositoryInterface::class);
$repository = $this->mock(CurrencyRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(TransactionCollectorInterface::class);
$date = new Carbon;
$fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date);
$fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date);
$accountRepos->shouldReceive('getMetaValue')
->withArgs([Mockery::any(), 'currency_id'])->andReturn('1')->atLeast()->once();
// mock hasRole for user repository:
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->andReturn(true)->atLeast()->once();
$repository->shouldReceive('findNull')->once()->andReturn(TransactionCurrency::find(1));
$this->be($this->user());
$response = $this->get(route('accounts.reconcile', [1, '20170101']));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* Test reconcile view when account is not an asset.
*
* @covers \FireflyIII\Http\Controllers\Account\ReconcileController
*/
public function testReconcileNotAsset(): void
{
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(TransactionCollectorInterface::class);
$date = new Carbon;
$fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date);
$fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date);
$account = $this->user()->accounts()->where('account_type_id', '!=', 6)->where('account_type_id', '!=', 3)->first();
$this->be($this->user());
$response = $this->get(route('accounts.reconcile', [$account->id, '20170101', '20170131']));
$response->assertStatus(302);
}
/**
* Test show for actual reconciliation.
*
* @covers \FireflyIII\Http\Controllers\Account\ReconcileController
*/
public function testShow(): void
{
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
$userRepos = $this->mock(UserRepositoryInterface::class);
$repository = $this->mock(JournalRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(TransactionCollectorInterface::class);
$accountRepos->shouldReceive('getMetaValue')
->withArgs([Mockery::any(), 'currency_id'])->andReturn('1')->atLeast()->once();
$currencyRepos->shouldReceive('findNull')->atLeast()->once()->withArgs([1])->andReturn(TransactionCurrency::find(1));
// mock hasRole for user repository:
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->andReturn(true)->atLeast()->once();
$journal = $this->user()->transactionJournals()->where('transaction_type_id', 5)->first();
$repository->shouldReceive('firstNull')->andReturn(new TransactionJournal);
$repository->shouldReceive('getAssetTransaction')->once()->andReturn($journal->transactions()->first());
$this->be($this->user());
$response = $this->get(route('accounts.reconcile.show', [$journal->id]));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* Test show for actual reconciliation.
*
* @covers \FireflyIII\Http\Controllers\Account\ReconcileController
*/
public function testShowError(): void
{
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$repository = $this->mock(JournalRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(TransactionCollectorInterface::class);
$journal = $this->user()->transactionJournals()->where('transaction_type_id', 5)->first();
$repository->shouldReceive('firstNull')->andReturn(new TransactionJournal);
$repository->shouldReceive('getAssetTransaction')->once()->andReturnNull();
$this->be($this->user());
$response = $this->get(route('accounts.reconcile.show', [$journal->id]));
$response->assertStatus(500);
// has bread crumb
$response->assertSee('The transaction data is incomplete. This is probably a bug. Apologies.');
}
/**
* Test show for actual reconciliation, but its not a reconciliation.
*
* @covers \FireflyIII\Http\Controllers\Account\ReconcileController
*/
public function testShowSomethingElse(): void
{
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(TransactionCollectorInterface::class);
$journal = $this->user()->transactionJournals()->where('transaction_type_id', '!=', 5)->first();
$this->be($this->user());
$response = $this->get(route('accounts.reconcile.show', [$journal->id]));
$response->assertStatus(302);
$response->assertRedirect(route('transactions.show', [$journal->id]));
}
/** /**
* Submit reconciliation. * Submit reconciliation.
* *
@ -362,115 +101,97 @@ class ReconcileControllerTest extends TestCase
*/ */
public function testSubmit(): void public function testSubmit(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0'); $repository = $this->mock(AccountRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$asset = $this->getRandomAsset();
$euro = $this->getEuro();
$date = new Carbon;
$factory = $this->mock(TransactionGroupFactory::class);
$group = $this->getRandomWithdrawalGroup();
$this->mock(CurrencyRepositoryInterface::class);
$this->mock(GroupCollectorInterface::class);
// used for session range.
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
Amount::shouldReceive('getDefaultCurrency')->atLeast()->once()->andReturn($euro);
$this->mockDefaultPreferences();
$this->mockDefaultConfiguration();
Preferences::shouldReceive('mark')->atLeast()->once();
return;
$repository = $this->mock(AccountRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(TransactionCollectorInterface::class);
$date = new Carbon;
$fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date); $fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date);
$fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date); $fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date);
$journalRepos->shouldReceive('reconcileById')->times(3);
$journalRepos->shouldReceive('firstNull')->andReturn(new TransactionJournal); $repository->shouldReceive('getReconciliation')->atLeast()->once()->andReturn($asset);
$journalRepos->shouldReceive('reconcileById')->andReturn(true); $repository->shouldReceive('getAccountCurrency')->atLeast()->once()->andReturn($euro);
$journalRepos->shouldReceive('store')->andReturn(new TransactionJournal); $factory->shouldReceive('setUser')->atLeast()->once();
$repository->shouldReceive('getReconciliation')->andReturn(new Account); $factory->shouldReceive('create')->andReturn($group);
$repository->shouldReceive('findNull')->andReturn(new Account);
$repository->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'currency_id'])->andReturn('1');
$data = [ $data = [
'transactions' => [1, 2, 3], 'journals' => [1, 2, 3],
'reconcile' => 'create', 'reconcile' => 'create',
'difference' => '5', 'difference' => '5',
'start' => '20170101', 'start' => '20170101',
'end' => '20170131', 'end' => '20170131',
]; ];
$this->be($this->user()); $this->be($this->user());
$response = $this->post(route('accounts.reconcile.submit', [1, '20170101', '20170131']), $data); $response = $this->post(route('accounts.reconcile.submit', [$asset->id, '20170101', '20170131']), $data);
$response->assertStatus(302); $response->assertStatus(302);
$response->assertSessionHas('success'); $response->assertSessionHas('success');
} }
/** /**
* Submit reconciliation, but throw an error.
*
* @covers \FireflyIII\Http\Controllers\Account\ReconcileController * @covers \FireflyIII\Http\Controllers\Account\ReconcileController
* @covers \FireflyIII\Http\Requests\ReconciliationUpdateRequest * @covers \FireflyIII\Http\Requests\ReconciliationStoreRequest
*/ */
public function testUpdate(): void public function testSubmitError(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0'); $repository = $this->mock(AccountRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$asset = $this->getRandomAsset();
$euro = $this->getEuro();
$date = new Carbon;
$factory = $this->mock(TransactionGroupFactory::class);
$group = $this->getRandomWithdrawalGroup();
$this->mock(CurrencyRepositoryInterface::class);
$this->mock(GroupCollectorInterface::class);
return;
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(TransactionCollectorInterface::class);
$journalRepos->shouldReceive('firstNull')->andReturn(new TransactionJournal); // used for session range.
$journalRepos->shouldReceive('getJournalSourceAccounts')->andReturn(new Collection([new Account])); $journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$journalRepos->shouldReceive('getJournalDestinationAccounts')->andReturn(new Collection([new Account])); Amount::shouldReceive('getDefaultCurrency')->atLeast()->once()->andReturn($euro);
$journalRepos->shouldReceive('getNoteText')->andReturn('');
$journalRepos->shouldReceive('update')->once();
$journal = $this->user()->transactionJournals()->where('transaction_type_id', 5)->first(); $this->mockDefaultPreferences();
$data = [ $this->mockDefaultConfiguration();
'amount' => '5',
Preferences::shouldReceive('mark')->atLeast()->once();
$fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date);
$fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date);
$journalRepos->shouldReceive('reconcileById')->times(3);
$repository->shouldReceive('getReconciliation')->atLeast()->once()->andReturn($asset);
$repository->shouldReceive('getAccountCurrency')->atLeast()->once()->andReturn($euro);
$factory->shouldReceive('setUser')->atLeast()->once();
$factory->shouldReceive('create')->andThrow(new FireflyException('Some error'));
$data = [
'journals' => [1, 2, 3],
'reconcile' => 'create',
'difference' => '5',
'start' => '20170101',
'end' => '20170131',
]; ];
$this->be($this->user()); $this->be($this->user());
$response = $this->post(route('accounts.reconcile.update', [$journal->id]), $data); $response = $this->post(route('accounts.reconcile.submit', [$asset->id, '20170101', '20170131']), $data);
$response->assertStatus(302);
}
/**
* @covers \FireflyIII\Http\Controllers\Account\ReconcileController
* @covers \FireflyIII\Http\Requests\ReconciliationUpdateRequest
*/
public function testUpdateNotReconcile(): void
{
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(TransactionCollectorInterface::class);
$journal = $this->user()->transactionJournals()->where('transaction_type_id', '!=', 5)->first();
$data = ['amount' => '5',];
$this->be($this->user());
$response = $this->post(route('accounts.reconcile.update', [$journal->id]), $data);
$response->assertStatus(302);
$response->assertRedirect(route('transactions.show', [$journal->id]));
}
/**
* @covers \FireflyIII\Http\Controllers\Account\ReconcileController
* @covers \FireflyIII\Http\Requests\ReconciliationUpdateRequest
*/
public function testUpdateZero(): void
{
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$collector = $this->mock(TransactionCollectorInterface::class);
$journal = $this->user()->transactionJournals()->where('transaction_type_id', 5)->first();
$data = ['amount' => '0',];
$this->be($this->user());
$response = $this->post(route('accounts.reconcile.update', [$journal->id]), $data);
$response->assertStatus(302); $response->assertStatus(302);
$response->assertSessionHas('error'); $response->assertSessionHas('error');
} }
} }

View File

@ -23,11 +23,10 @@ declare(strict_types=1);
namespace Tests\Feature\Controllers\Account; namespace Tests\Feature\Controllers\Account;
use Amount;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Helpers\FiscalHelperInterface; use FireflyIII\Models\Preference;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Account\AccountTaskerInterface; use FireflyIII\Repositories\Account\AccountTaskerInterface;
@ -35,9 +34,9 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Log; use Log;
use Mockery; use Mockery;
use Preferences;
use Tests\TestCase; use Tests\TestCase;
/** /**
@ -64,9 +63,6 @@ class ShowControllerTest extends TestCase
*/ */
public function testShow(string $range): void public function testShow(string $range): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
Log::info(sprintf('testShow(%s)', $range)); Log::info(sprintf('testShow(%s)', $range));
$date = new Carbon; $date = new Carbon;
$this->session(['start' => $date, 'end' => clone $date]); $this->session(['start' => $date, 'end' => clone $date]);
@ -78,36 +74,48 @@ class ShowControllerTest extends TestCase
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class); $currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class); $accountRepos = $this->mock(AccountRepositoryInterface::class);
$collector = $this->mock(GroupCollectorInterface::class);
$repository = $this->mock(AccountRepositoryInterface::class);
$journal = $this->getRandomWithdrawalAsArray();
$group = $this->getRandomWithdrawalGroup();
$asset = $this->getRandomAsset();
$euro = $this->getEuro();
// mock stuff
$this->mockDefaultConfiguration();
$this->mockDefaultPreferences();
// amount mocks:
Amount::shouldReceive('getDefaultCurrency')->atLeast()->once()->andReturn($euro);
Amount::shouldReceive('formatAnything')->atLeast()->once()->andReturn('-100');
$repository->shouldReceive('getAccountCurrency')->andReturn($euro)->atLeast()->once();
$repository->shouldReceive('oldestJournalDate')->andReturn(clone $date)->once();
// used for session range.
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
// list size
$pref = new Preference;
$pref->data = 50;
Preferences::shouldReceive('get')->withArgs(['listPageSize', 50])->atLeast()->once()->andReturn($pref);
Preferences::shouldReceive('lastActivity')->atLeast()->once();
// mock hasRole for user repository: // mock hasRole for user repository:
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->andReturn(true)->atLeast()->once(); $userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->andReturn(true)->atLeast()->once();
$currencyRepos->shouldReceive('findNull')->andReturn(TransactionCurrency::find(1)); $collector->shouldReceive('setAccounts')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('setRange')->andReturnSelf()->atLeast()->once();
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal); $collector->shouldReceive('setLimit')->andReturnSelf()->atLeast()->once();
$tasker->shouldReceive('amountOutInPeriod')->withAnyArgs()->andReturn('-1');
$tasker->shouldReceive('amountInInPeriod')->withAnyArgs()->andReturn('1');
$repository = $this->mock(AccountRepositoryInterface::class);
$repository->shouldReceive('oldestJournalDate')->andReturn(clone $date)->once();
$repository->shouldReceive('getMetaValue')->andReturn('');
$repository->shouldReceive('isLiability')->andReturn(false);
$transaction = factory(Transaction::class)->make();
$collector = $this->mock(TransactionCollectorInterface::class);
$collector->shouldReceive('setAccounts')->andReturnSelf();
$collector->shouldReceive('setRange')->andReturnSelf();
$collector->shouldReceive('setLimit')->andReturnSelf();
$collector->shouldReceive('withOpposingAccount')->andReturnSelf();
$collector->shouldReceive('setPage')->andReturnSelf(); $collector->shouldReceive('setPage')->andReturnSelf();
$collector->shouldReceive('setTypes')->andReturnSelf(); $collector->shouldReceive('setTypes')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('getTransactions')->andReturn(new Collection([$transaction])); $collector->shouldReceive('getExtractedJournals')->andReturn([$journal]);
$collector->shouldReceive('getPaginatedTransactions')->andReturn(new LengthAwarePaginator([$transaction], 0, 10)); $collector->shouldReceive('withAccountInformation')->andReturnSelf();
$collector->shouldReceive('getPaginatedGroups')->andReturn(new LengthAwarePaginator([$group], 0, 10));
$this->be($this->user()); $this->be($this->user());
$this->changeDateRange($this->user(), $range); $this->changeDateRange($this->user(), $range);
$response = $this->get(route('accounts.show', [1])); $response = $this->get(route('accounts.show', [$asset->id]));
$response->assertStatus(200); $response->assertStatus(200);
// has bread crumb // has bread crumb
$response->assertSee('<ol class="breadcrumb">'); $response->assertSee('<ol class="breadcrumb">');
@ -121,212 +129,57 @@ class ShowControllerTest extends TestCase
*/ */
public function testShowAll(string $range): void public function testShowAll(string $range): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
Log::info(sprintf('testShowAll(%s)', $range));
$date = new Carbon; $date = new Carbon;
$this->session(['start' => $date, 'end' => clone $date]); $this->session(['start' => $date, 'end' => clone $date]);
// mock stuff: // mock stuff:
$tasker = $this->mock(AccountTaskerInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class); $journalRepos = $this->mock(JournalRepositoryInterface::class);
$tasker = $this->mock(AccountTaskerInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class); $currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class); $accountRepos = $this->mock(AccountRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $collector = $this->mock(GroupCollectorInterface::class);
$repository = $this->mock(AccountRepositoryInterface::class);
$journal = $this->getRandomWithdrawalAsArray();
$group = $this->getRandomWithdrawalGroup();
$euro = $this->getEuro();
$asset = $this->getRandomAsset();
// mock hasRole for user repository: // mock stuff
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->andReturn(true)->atLeast()->once(); $this->mockDefaultConfiguration();
$this->mockDefaultPreferences();
$currencyRepos->shouldReceive('findNull')->andReturn(TransactionCurrency::find(1)); // amount mocks:
Amount::shouldReceive('getDefaultCurrency')->atLeast()->once()->andReturn($euro);
// Amount::shouldReceive('formatAnything')->atLeast()->once()->andReturn('-100');
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal); $repository->shouldReceive('isLiability')->andReturn(false)->atLeast()->once();
$tasker->shouldReceive('amountOutInPeriod')->withAnyArgs()->andReturn('-1'); $repository->shouldReceive('getAccountCurrency')->andReturn($euro)->atLeast()->once();
$tasker->shouldReceive('amountInInPeriod')->withAnyArgs()->andReturn('1');
$repository = $this->mock(AccountRepositoryInterface::class);
$repository->shouldReceive('oldestJournalDate')->andReturn(clone $date)->once(); $repository->shouldReceive('oldestJournalDate')->andReturn(clone $date)->once();
$repository->shouldReceive('getMetaValue')->andReturn('');
$repository->shouldReceive('isLiability')->andReturn(false);
$transaction = factory(Transaction::class)->make(); // used for session range.
$collector = $this->mock(TransactionCollectorInterface::class);
$collector->shouldReceive('setAccounts')->andReturnSelf();
$collector->shouldReceive('setRange')->andReturnSelf();
$collector->shouldReceive('setLimit')->andReturnSelf();
$collector->shouldReceive('withOpposingAccount')->andReturnSelf();
$collector->shouldReceive('setPage')->andReturnSelf();
$collector->shouldReceive('setTypes')->andReturnSelf();
$collector->shouldReceive('getTransactions')->andReturn(new Collection([$transaction]));
$collector->shouldReceive('getPaginatedTransactions')->andReturn(new LengthAwarePaginator([$transaction], 0, 10));
$this->be($this->user());
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('accounts.show.all', [1]));
$response->assertStatus(200);
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\Account\ShowController
*/
public function testShowBrokenBadDates(): void
{
Log::info(sprintf('testShowBrokenBadDates(%s)', ''));
// mock
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$date = new Carbon;
$fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date);
$fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date);
$accountRepos->shouldReceive('isLiability')->atLeast()->once()->andReturn(false);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal); $journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$this->session(['start' => '2018-01-01', 'end' => '2017-12-01']);
$this->be($this->user()); // list size
$account = $this->user()->accounts()->where('account_type_id', 3)->orderBy('id', 'ASC')->whereNull('deleted_at')->first(); $pref = new Preference;
$response = $this->get(route('accounts.show', [$account->id, '2018-01-01', '2017-12-01'])); $pref->data = 50;
$response->assertStatus(500); Preferences::shouldReceive('get')->withArgs(['listPageSize', 50])->atLeast()->once()->andReturn($pref);
$response->assertSee('End is after start!');
}
/**
* @covers \FireflyIII\Http\Controllers\Account\ShowController
*/
public function testShowBrokenInitial(): void
{
Log::info(sprintf('testShowBrokenInitial(%s)', ''));
// mock
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$date = new Carbon;
$this->session(['start' => $date, 'end' => clone $date]);
$this->be($this->user());
$account = $this->user()->accounts()->where('account_type_id', 6)->orderBy('id', 'ASC')->whereNull('deleted_at')->first();
$response = $this->get(route('accounts.show', [$account->id]));
$response->assertStatus(302);
$response->assertRedirect(route('index'));
$response->assertSessionHas('error');
}
/**
* @covers \FireflyIII\Http\Controllers\Account\ShowController
* @dataProvider dateRangeProvider
*
* @param string $range
*/
public function testShowByDateEmpty(string $range): void
{
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
Log::info(sprintf('testShowByDateEmpty(%s)', $range));
// mock stuff
$collector = $this->mock(TransactionCollectorInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$repository = $this->mock(AccountRepositoryInterface::class);
$fiscalHelper = $this->mock(FiscalHelperInterface::class);
$date = new Carbon;
$fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date);
$fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date);
// mock hasRole for user repository: // mock hasRole for user repository:
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->andReturn(true)->atLeast()->once(); $userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->andReturn(true)->atLeast()->once();
$collector->shouldReceive('setAccounts')->andReturnSelf()->atLeast()->once();
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal); $collector->shouldReceive('setLimit')->andReturnSelf()->atLeast()->once();
$collector->shouldReceive('setAccounts')->andReturnSelf();
$collector->shouldReceive('setRange')->andReturnSelf();
$collector->shouldReceive('setLimit')->andReturnSelf();
$collector->shouldReceive('setPage')->andReturnSelf(); $collector->shouldReceive('setPage')->andReturnSelf();
$collector->shouldReceive('getPaginatedTransactions')->andReturn(new LengthAwarePaginator([], 0, 10)); $collector->shouldReceive('getExtractedJournals')->andReturn([$journal]);
$collector->shouldReceive('withAccountInformation')->andReturnSelf();
$collector->shouldReceive('getPaginatedGroups')->andReturn(new LengthAwarePaginator([$group], 0, 10));
$repository->shouldReceive('oldestJournalDate')->andReturn(new Carbon);
$repository->shouldReceive('getMetaValue')->andReturn('');
$repository->shouldReceive('isLiability')->andReturn(false);
$collector->shouldReceive('setTypes')->andReturnSelf();
$collector->shouldReceive('withOpposingAccount')->andReturnSelf();
$collector->shouldReceive('getTransactions')->andReturn(new Collection);
$currencyRepos->shouldReceive('findNull')->andReturn(TransactionCurrency::find(1));
$this->be($this->user()); $this->be($this->user());
$this->changeDateRange($this->user(), $range); $this->changeDateRange($this->user(), $range);
$response = $this->get(route('accounts.show', [1, '2016-01-01'])); $response = $this->get(route('accounts.show.all', [$asset->id]));
$response->assertStatus(200); $response->assertStatus(200);
// has bread crumb // has bread crumb
$response->assertSee('<ol class="breadcrumb">'); $response->assertSee('<ol class="breadcrumb">');
} }
/**
* @covers \FireflyIII\Http\Controllers\Account\ShowController
*/
public function testShowInitial(): void
{
Log::info(sprintf('testShowInitial(%s)', ''));
// mock stuff
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$accountRepos = $this->mock(AccountRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$date = new Carbon;
$this->session(['start' => $date, 'end' => clone $date]);
$this->be($this->user());
$account = $this->user()->accounts()->where('account_type_id', 6)->orderBy('id', 'DESC')->whereNull('deleted_at')->first();
$response = $this->get(route('accounts.show', [$account->id]));
$response->assertStatus(302);
}
/**
* @covers \FireflyIII\Http\Controllers\Account\ShowController
* @dataProvider dateRangeProvider
*
* @param string $range
*/
public function testShowLiability(string $range): void
{
Log::info(sprintf('testShowLiability(%s)', $range));
$date = new Carbon;
$this->session(['start' => $date, 'end' => clone $date]);
$account = $this->user()->accounts()->where('account_type_id', 12)->whereNull('deleted_at')->first();
// mock stuff:
$tasker = $this->mock(AccountTaskerInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$currencyRepos = $this->mock(CurrencyRepositoryInterface::class);
$repository = $this->mock(AccountRepositoryInterface::class);
$currencyRepos->shouldReceive('findNull')->andReturn(TransactionCurrency::find(1));
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$tasker->shouldReceive('amountOutInPeriod')->withAnyArgs()->andReturn('-1');
$tasker->shouldReceive('amountInInPeriod')->withAnyArgs()->andReturn('1');
$repository->shouldReceive('getMetaValue')->andReturn('');
$repository->shouldReceive('isLiability')->andReturn(true);
$this->be($this->user());
$this->changeDateRange($this->user(), $range);
$response = $this->get(route('accounts.show', [$account->id]));
$response->assertStatus(302);
$response->assertRedirect(route('accounts.show.all', [$account->id]));
}
} }

View File

@ -83,6 +83,24 @@ abstract class TestCase extends BaseTestCase
FireflyConfig::shouldReceive('get')->withArgs(['is_demo_site', false])->once()->andReturn($falseConfig); FireflyConfig::shouldReceive('get')->withArgs(['is_demo_site', false])->once()->andReturn($falseConfig);
} }
/**
* @return array
*/
public function getRandomWithdrawalAsArray(): array
{
$withdrawal = $this->getRandomWithdrawal();
$euro = $this->getEuro();
return [
'transaction_journal_id' => $withdrawal->id,
'currency_id' => $euro->id,
'currency_name' => $euro->name,
'currency_symbol' => $euro->symbol,
'currency_decimal_places' => $euro->decimal_places,
'amount' => '-30',
];
}
/** /**
* Mock default preferences. * Mock default preferences.
*/ */
@ -388,7 +406,10 @@ abstract class TestCase extends BaseTestCase
->where('transaction_type_id', $transactionType->id)->inRandomOrder()->first(); ->where('transaction_type_id', $transactionType->id)->inRandomOrder()->first();
/** @var TransactionGroup $group */ /** @var TransactionGroup $group */
$group = $journal->transactionGroup; $group = $journal->transactionGroup;
$count = $group->transactionJournals()->count(); $count = 0;
if (null !== $group) {
$count = $group->transactionJournals()->count();
}
Log::debug(sprintf('Count is %d', $count)); Log::debug(sprintf('Count is %d', $count));
} while (1 !== $count); } while (1 !== $count);

View File

@ -172,15 +172,17 @@ class AccountCurrenciesTest extends TestCase
*/ */
public function testHandleDifferent(): void public function testHandleDifferent(): void
{ {
$false = new Configuration; $false = new Configuration;
$false->data = false; $false->data = false;
$pref = new Preference; $pref = new Preference;
$pref->data = 'USD'; $pref->data = 'USD';
$accountRepos = $this->mock(AccountRepositoryInterface::class); $accountRepos = $this->mock(AccountRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$journal = $this->getRandomWithdrawal(); $journal = $this->getRandomWithdrawal();
$account = $this->getRandomAsset(); $account = $this->getRandomAsset();
$euro = TransactionCurrency::where('code', 'EUR')->first(); $euro = $this->getEuro();
$journal->transaction_currency_id = $euro->id;
$journal->save();
// delete meta data of account just in case: // delete meta data of account just in case:
AccountMeta::where('account_id', $account->id)->where('name', 'currency_id')->forceDelete(); AccountMeta::where('account_id', $account->id)->where('name', 'currency_id')->forceDelete();

View File

@ -251,7 +251,8 @@ class UserEventHandlerTest extends TestCase
Mail::fake(); Mail::fake();
$user = $this->emptyUser(); $user = $this->emptyUser();
$event = new RegisteredUser($user, '127.0.0.1'); $event = new RegisteredUser($user, '127.0.0.1');
//config('firefly.send_registration_mail', true);
\Config::set('firefly.send_registration_mail', true);
$listener = new UserEventHandler; $listener = new UserEventHandler;
$listener->sendRegistrationMail($event); $listener->sendRegistrationMail($event);