Improve mass controller and test controllers.

This commit is contained in:
James Cole
2019-07-20 16:02:50 +02:00
parent 6d34cfb940
commit 889b7e9a18
18 changed files with 525 additions and 542 deletions

View File

@@ -124,6 +124,37 @@ class AutoCompleteController extends Controller
return response()->json($return); return response()->json($return);
} }
/**
* An auto-complete specifically for expense accounts, used when mass updating mostly.
* @param Request $request
*
* @return JsonResponse
*/
public function expenseAccounts(Request $request): JsonResponse
{
$search = $request->get('search');
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
// filter the account types:
$allowedAccountTypes = [AccountType::EXPENSE];
Log::debug('Now in accounts(). Filtering results.', $allowedAccountTypes);
$return = [];
$result = $repository->searchAccount((string)$search, $allowedAccountTypes);
/** @var Account $account */
foreach ($result as $account) {
$return[] = [
'id' => $account->id,
'name' => $account->name,
'type' => $account->accountType->type,
];
}
return response()->json($return);
}
/** /**
* Searches in the titles of all transaction journals. * Searches in the titles of all transaction journals.
* The result is limited to the top 15 unique results. * The result is limited to the top 15 unique results.

View File

@@ -63,6 +63,8 @@ class BulkController extends Controller
/** /**
* Edit a set of journals in bulk. * Edit a set of journals in bulk.
* *
* TODO user wont be able to tell if journal is part of split.
*
* @param Collection $journals * @param Collection $journals
* *
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
@@ -71,6 +73,8 @@ class BulkController extends Controller
{ {
$subTitle = (string)trans('firefly.mass_bulk_journals'); $subTitle = (string)trans('firefly.mass_bulk_journals');
// make amounts positive.
// get list of budgets: // get list of budgets:
/** @var BudgetRepositoryInterface $repository */ /** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class); $repository = app(BudgetRepositoryInterface::class);

View File

@@ -25,23 +25,19 @@ namespace FireflyIII\Http\Controllers\Transaction;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Events\UpdatedTransactionGroup; use FireflyIII\Events\UpdatedTransactionGroup;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Helpers\Filter\TransactionViewFilter;
use FireflyIII\Helpers\Filter\TransferFilter;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\MassDeleteJournalRequest; use FireflyIII\Http\Requests\MassDeleteJournalRequest;
use FireflyIII\Http\Requests\MassEditJournalRequest; use FireflyIII\Http\Requests\MassEditJournalRequest;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Transformers\TransactionTransformer; use FireflyIII\Services\Internal\Update\JournalUpdateService;
use FireflyIII\User;
use Illuminate\Support\Collection;
use Illuminate\View\View as IlluminateView; use Illuminate\View\View as IlluminateView;
use Symfony\Component\HttpFoundation\ParameterBag; use InvalidArgumentException;
use Log;
/** /**
* Class MassController. * Class MassController.
@@ -55,6 +51,7 @@ class MassController extends Controller
/** /**
* MassController constructor. * MassController constructor.
* @codeCoverageIgnore
*/ */
public function __construct() public function __construct()
{ {
@@ -65,7 +62,6 @@ class MassController extends Controller
app('view')->share('title', (string)trans('firefly.transactions')); app('view')->share('title', (string)trans('firefly.transactions'));
app('view')->share('mainTitleIcon', 'fa-repeat'); app('view')->share('mainTitleIcon', 'fa-repeat');
$this->repository = app(JournalRepositoryInterface::class); $this->repository = app(JournalRepositoryInterface::class);
return $next($request); return $next($request);
} }
); );
@@ -74,11 +70,11 @@ class MassController extends Controller
/** /**
* Mass delete transactions. * Mass delete transactions.
* *
* @param Collection $journals * @param array $journals
* *
* @return IlluminateView * @return IlluminateView
*/ */
public function delete(Collection $journals): IlluminateView public function delete(array $journals): IlluminateView
{ {
$subTitle = (string)trans('firefly.mass_delete_journals'); $subTitle = (string)trans('firefly.mass_delete_journals');
@@ -103,10 +99,11 @@ class MassController extends Controller
if (is_array($ids)) { if (is_array($ids)) {
/** @var string $journalId */ /** @var string $journalId */
foreach ($ids as $journalId) { foreach ($ids as $journalId) {
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
$journal = $this->repository->findNull((int)$journalId); $journal = $this->repository->findNull((int)$journalId);
if (null !== $journal && (int)$journalId === $journal->id) { if (null !== $journal && (int)$journalId === $journal->id) {
$this->repository->destroy($journal); $this->repository->destroyJournal($journal);
++$count; ++$count;
} }
} }
@@ -123,137 +120,69 @@ class MassController extends Controller
/** /**
* Mass edit of journals. * Mass edit of journals.
* *
* @param Collection $journals * @param array $journals
* *
* @return IlluminateView * @return IlluminateView
*
* TODO rebuild this feature.
* @throws FireflyException
*/ */
public function edit(Collection $journals): IlluminateView public function edit(array $journals): IlluminateView
{ {
throw new FireflyException(sprintf('The mass-editor is not available in v%s of Firefly III. Sorry about that. It will be back soon.', config('firefly.version')));
/** @var User $user */
$user = auth()->user();
$subTitle = (string)trans('firefly.mass_edit_journals'); $subTitle = (string)trans('firefly.mass_edit_journals');
/** @var AccountRepositoryInterface $repository */ /** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class); $repository = app(AccountRepositoryInterface::class);
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
// valid withdrawal sources:
$array = array_keys(config(sprintf('firefly.source_dests.%s', TransactionType::WITHDRAWAL)));
$withdrawalSources = $repository->getAccountsByType($array);
// valid deposit destinations:
$array = config(sprintf('firefly.source_dests.%s.%s', TransactionType::DEPOSIT, AccountType::REVENUE));
$depositDestinations = $repository->getAccountsByType($array);
/** @var BudgetRepositoryInterface $budgetRepository */ /** @var BudgetRepositoryInterface $budgetRepository */
$budgetRepository = app(BudgetRepositoryInterface::class); $budgetRepository = app(BudgetRepositoryInterface::class);
$budgets = $budgetRepository->getBudgets(); $budgets = $budgetRepository->getBudgets();
// reverse amounts
foreach ($journals as $index => $journal) {
$journals[$index]['amount'] = app('steam')->positive($journal['amount']);
$journals[$index]['foreign_amount'] = null === $journal['foreign_amount'] ?
null : app('steam')->positive($journal['foreign_amount']);
}
$this->rememberPreviousUri('transactions.mass-edit.uri'); $this->rememberPreviousUri('transactions.mass-edit.uri');
/** @var TransactionTransformer $transformer */ return view('transactions.mass.edit', compact('journals', 'subTitle', 'withdrawalSources', 'depositDestinations', 'budgets'));
$transformer = app(TransactionTransformer::class);
$transformer->setParameters(new ParameterBag);
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setUser($user);
$collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation();
$collector->setJournals($journals);
$collector->addFilter(TransactionViewFilter::class);
$collector->addFilter(TransferFilter::class);
$collection = $collector->getTransactions();
$transactions = $collection->map(
function (Transaction $transaction) use ($transformer) {
$transformed = $transformer->transform($transaction);
// make sure amount is positive:
$transformed['amount'] = app('steam')->positive((string)$transformed['amount']);
$transformed['foreign_amount'] = app('steam')->positive((string)$transformed['foreign_amount']);
return $transformed;
}
);
return view('transactions.mass.edit', compact('transactions', 'subTitle', 'accounts', 'budgets'));
} }
/** /**
* Mass update of journals. * Mass update of journals.
* *
* @param MassEditJournalRequest $request * @param MassEditJournalRequest $request
* @param JournalRepositoryInterface $repository * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* * @throws FireflyException
* @return mixed
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/ */
public function update(MassEditJournalRequest $request, JournalRepositoryInterface $repository) public function update(MassEditJournalRequest $request)
{ {
throw new FireflyException('Needs refactor');
$journalIds = $request->get('journals'); $journalIds = $request->get('journals');
$count = 0; if (!is_array($journalIds)) {
if (is_array($journalIds)) { // TODO something error.
foreach ($journalIds as $journalId) { throw new FireflyException('This is not an array.'); // @codeCoverageIgnore
$journal = $repository->findNull((int)$journalId); }
if (null !== $journal) { $count = 0;
// get optional fields: /** @var string $journalId */
$what = strtolower($this->repository->getTransactionType($journal)); foreach ($journalIds as $journalId) {
$sourceAccountId = $request->get('source_id')[$journal->id] ?? null; $integer = (int)$journalId;
$currencyId = $request->get('transaction_currency_id')[$journal->id] ?? 1; try {
$sourceAccountName = $request->get('source_name')[$journal->id] ?? null; $this->updateJournal($integer, $request);
$destAccountId = $request->get('destination_id')[$journal->id] ?? null; $count++;
$destAccountName = $request->get('destination_name')[$journal->id] ?? null; } catch (FireflyException $e) { // @codeCoverageIgnore
$budgetId = (int)($request->get('budget_id')[$journal->id] ?? 0.0); // do something with error.
$category = $request->get('category')[$journal->id]; //echo $e->getMessage();
$tags = $journal->tags->pluck('tag')->toArray(); //exit;
$amount = round($request->get('amount')[$journal->id], 12);
$foreignAmount = isset($request->get('foreign_amount')[$journal->id]) ? round($request->get('foreign_amount')[$journal->id], 12) : null;
$foreignCurrencyId = isset($request->get('foreign_currency_id')[$journal->id]) ?
(int)$request->get('foreign_currency_id')[$journal->id] : null;
// build data array
$data = [
'id' => $journal->id,
'what' => $what,
'description' => $request->get('description')[$journal->id],
'date' => new Carbon($request->get('date')[$journal->id]),
'bill_id' => null,
'bill_name' => null,
'notes' => $repository->getNoteText($journal),
'transactions' => [[
'category_id' => null,
'category_name' => $category,
'budget_id' => $budgetId,
'budget_name' => null,
'source_id' => (int)$sourceAccountId,
'source_name' => $sourceAccountName,
'destination_id' => (int)$destAccountId,
'destination_name' => $destAccountName,
'amount' => $amount,
'identifier' => 0,
'reconciled' => false,
'currency_id' => (int)$currencyId,
'currency_code' => null,
'description' => null,
'foreign_amount' => $foreignAmount,
'foreign_currency_id' => $foreignCurrencyId,
'foreign_currency_code' => null,
]],
'currency_id' => $foreignCurrencyId,
'tags' => $tags,
'interest_date' => $journal->interest_date,
'book_date' => $journal->book_date,
'process_date' => $journal->process_date,
];
// call repository update function.
$repository->update($journal, $data);
// trigger rules
event(new UpdatedTransactionGroup($group));
++$count;
}
} }
} }
app('preferences')->mark(); app('preferences')->mark();
session()->flash('success', (string)trans('firefly.mass_edited_transactions_success', ['amount' => $count])); session()->flash('success', (string)trans('firefly.mass_edited_transactions_success', ['amount' => $count]));
@@ -261,4 +190,106 @@ class MassController extends Controller
return redirect($this->getPreviousUri('transactions.mass-edit.uri')); return redirect($this->getPreviousUri('transactions.mass-edit.uri'));
} }
/**
* @param int $journalId
* @param MassEditJournalRequest $request
* @throws FireflyException
*/
private function updateJournal(int $journalId, MassEditJournalRequest $request): void
{
$journal = $this->repository->findNull($journalId);
if (null === $journal) {
throw new FireflyException(sprintf('Trying to edit non-existent or deleted journal #%d', $journalId)); // @codeCoverageIgnore
}
$service = app(JournalUpdateService::class);
// for each field, call the update service.
$service->setTransactionJournal($journal);
$data = [
'date' => $this->getDateFromRequest($request, $journal->id, 'date'),
'description' => $this->getStringFromRequest($request, $journal->id, 'description'),
'source_id' => $this->getIntFromRequest($request, $journal->id, 'source_id'),
'source_name' => $this->getStringFromRequest($request, $journal->id, 'source_name'),
'destination_id' => $this->getIntFromRequest($request, $journal->id, 'destination_id'),
'destination_name' => $this->getStringFromRequest($request, $journal->id, 'destination_name'),
'budget_id' => $this->getIntFromRequest($request, $journal->id, 'budget_id'),
'category_name' => $this->getStringFromRequest($request, $journal->id, 'category'),
'amount' => $this->getStringFromRequest($request, $journal->id, 'amount'),
'foreign_amount' => $this->getStringFromRequest($request, $journal->id, 'foreign_amount'),
];
Log::debug(sprintf('Will update journal #%d with data.', $journal->id), $data);
// call service to update.
$service->setData($data);
$service->update();
// trigger rules
event(new UpdatedTransactionGroup($journal->transactionGroup));
}
/**
* @param MassEditJournalRequest $request
* @param int $journalId
* @param string $string
* @return int|null
* @codeCoverageIgnore
*/
private function getIntFromRequest(MassEditJournalRequest $request, int $journalId, string $string): ?int
{
$value = $request->get($string);
if (!is_array($value)) {
return null;
}
if (!isset($value[$journalId])) {
return null;
}
return (int)$value[$journalId];
}
/**
* @param MassEditJournalRequest $request
* @param int $journalId
* @param string $string
* @return string|null
* @codeCoverageIgnore
*/
private function getStringFromRequest(MassEditJournalRequest $request, int $journalId, string $string): ?string
{
$value = $request->get($string);
if (!is_array($value)) {
return null;
}
if (!isset($value[$journalId])) {
return null;
}
return (string)$value[$journalId];
}
/**
* @param MassEditJournalRequest $request
* @param int $journalId
* @param string $string
* @return Carbon|null
* @codeCoverageIgnore
*/
private function getDateFromRequest(MassEditJournalRequest $request, int $journalId, string $string): ?Carbon
{
$value = $request->get($string);
if (!is_array($value)) {
return null;
}
if (!isset($value[$journalId])) {
return null;
}
try {
$carbon = Carbon::parse($value[$journalId]);
} catch (InvalidArgumentException $e) {
$e->getMessage();
return null;
}
return $carbon;
}
} }

View File

@@ -41,7 +41,7 @@ class ShowController extends Controller
private $repository; private $repository;
/** /**
* ConvertController constructor. * ShowController constructor.
*/ */
public function __construct() public function __construct()
{ {
@@ -81,8 +81,29 @@ class ShowController extends Controller
$groupArray = $transformer->transformObject($transactionGroup); $groupArray = $transformer->transformObject($transactionGroup);
// do some amount calculations: // do some amount calculations:
$amounts = $this->getAmounts($groupArray);
$events = $this->repository->getPiggyEvents($transactionGroup);
$attachments = $this->repository->getAttachments($transactionGroup);
$links = $this->repository->getLinks($transactionGroup);
return view(
'transactions.show', compact(
'transactionGroup', 'amounts', 'first', 'type', 'subTitle', 'splits', 'groupArray',
'events', 'attachments', 'links', 'message'
)
);
}
/**
* @param array $group
* @return array
*/
private function getAmounts(array $group): array
{
$amounts = []; $amounts = [];
foreach ($groupArray['transactions'] as $transaction) { foreach ($group['transactions'] as $transaction) {
$symbol = $transaction['currency_symbol']; $symbol = $transaction['currency_symbol'];
if (!isset($amounts[$symbol])) { if (!isset($amounts[$symbol])) {
$amounts[$symbol] = [ $amounts[$symbol] = [
@@ -106,15 +127,6 @@ class ShowController extends Controller
} }
} }
$events = $this->repository->getPiggyEvents($transactionGroup); return $amounts;
$attachments = $this->repository->getAttachments($transactionGroup);
$links = $this->repository->getLinks($transactionGroup);
return view(
'transactions.show', compact(
'transactionGroup', 'amounts', 'first', 'type', 'subTitle', 'splits', 'groupArray',
'events', 'attachments', 'links', 'message'
)
);
} }
} }

View File

@@ -53,6 +53,7 @@ class MassEditJournalRequest extends Request
'description.*' => 'required|min:1,max:255', 'description.*' => 'required|min:1,max:255',
'source_id.*' => 'numeric|belongsToUser:accounts,id', 'source_id.*' => 'numeric|belongsToUser:accounts,id',
'destination_id.*' => 'numeric|belongsToUser:accounts,id', 'destination_id.*' => 'numeric|belongsToUser:accounts,id',
'journals.*' => 'numeric|belongsToUser:transaction_journals,id',
'revenue_account' => 'max:255', 'revenue_account' => 'max:255',
'expense_account' => 'max:255', 'expense_account' => 'max:255',
]; ];

View File

@@ -131,7 +131,6 @@ class JournalUpdateService
public function update(): void public function update(): void
{ {
Log::debug(sprintf('Now in JournalUpdateService for journal #%d.', $this->transactionJournal->id)); Log::debug(sprintf('Now in JournalUpdateService for journal #%d.', $this->transactionJournal->id));
// can we update account data using the new type? // can we update account data using the new type?
if ($this->hasValidAccounts()) { if ($this->hasValidAccounts()) {
Log::info('-- account info is valid, now update.'); Log::info('-- account info is valid, now update.');
@@ -142,7 +141,6 @@ class JournalUpdateService
$this->updateType(); $this->updateType();
$this->transactionJournal->refresh(); $this->transactionJournal->refresh();
} }
// find and update bill, if possible. // find and update bill, if possible.
$this->updateBill(); $this->updateBill();
@@ -276,6 +274,7 @@ class JournalUpdateService
if (null === $this->sourceTransaction) { if (null === $this->sourceTransaction) {
$this->sourceTransaction = $this->transactionJournal->transactions()->with(['account'])->where('amount', '<', 0)->first(); $this->sourceTransaction = $this->transactionJournal->transactions()->with(['account'])->where('amount', '<', 0)->first();
} }
Log::debug(sprintf('getSourceTransaction: %s', $this->sourceTransaction->amount));
return $this->sourceTransaction; return $this->sourceTransaction;
} }
@@ -447,9 +446,14 @@ class JournalUpdateService
$sourceTransaction->account()->associate($source); $sourceTransaction->account()->associate($source);
$sourceTransaction->save(); $sourceTransaction->save();
$destinationTransaction = $this->getDestinationTransaction(); $destTransaction = $this->getDestinationTransaction();
$destinationTransaction->account()->associate($destination); $destTransaction->account()->associate($destination);
$destinationTransaction->save(); $destTransaction->save();
// refresh transactions.
$this->sourceTransaction->refresh();
$this->destinationTransaction->refresh();
Log::debug(sprintf('Will set source to #%d ("%s")', $source->id, $source->name)); Log::debug(sprintf('Will set source to #%d ("%s")', $source->id, $source->name));
Log::debug(sprintf('Will set dest to #%d ("%s")', $destination->id, $destination->name)); Log::debug(sprintf('Will set dest to #%d ("%s")', $destination->id, $destination->name));
@@ -468,14 +472,20 @@ class JournalUpdateService
return; return;
} }
Log::debug(sprintf('Updated amount to %s', $amount));
$sourceTransaction = $this->getSourceTransaction(); $sourceTransaction = $this->getSourceTransaction();
$sourceTransaction->amount = app('steam')->negative($value); $sourceTransaction->amount = app('steam')->negative($amount);
$sourceTransaction->save(); $sourceTransaction->save();
$destinationTransaction = $this->getDestinationTransaction();
$destinationTransaction->amount = app('steam')->positive($value); $destTransaction = $this->getDestinationTransaction();
$destinationTransaction->save(); $destTransaction->amount = app('steam')->positive($amount);
$destTransaction->save();
// refresh transactions.
$this->sourceTransaction->refresh();
$this->destinationTransaction->refresh();
Log::debug(sprintf('Updated amount to "%s"', $amount));
} }
/** /**
@@ -518,6 +528,10 @@ class JournalUpdateService
$dest = $this->getDestinationTransaction(); $dest = $this->getDestinationTransaction();
$dest->transaction_currency_id = $currency->id; $dest->transaction_currency_id = $currency->id;
$dest->save(); $dest->save();
// refresh transactions.
$this->sourceTransaction->refresh();
$this->destinationTransaction->refresh();
Log::debug(sprintf('Updated currency to #%d (%s)', $currency->id, $currency->code)); Log::debug(sprintf('Updated currency to #%d (%s)', $currency->id, $currency->code));
} }
} }
@@ -572,6 +586,10 @@ class JournalUpdateService
Log::debug(sprintf('Update foreign info to %s (#%d) %s', $foreignCurrency->code, $foreignCurrency->id, $foreignAmount)); Log::debug(sprintf('Update foreign info to %s (#%d) %s', $foreignCurrency->code, $foreignCurrency->id, $foreignAmount));
// refresh transactions.
$this->sourceTransaction->refresh();
$this->destinationTransaction->refresh();
return; return;
} }
if ('0' === $amount) { if ('0' === $amount) {
@@ -585,6 +603,10 @@ class JournalUpdateService
Log::debug(sprintf('Foreign amount is "%s" so remove foreign amount info.', $amount)); Log::debug(sprintf('Foreign amount is "%s" so remove foreign amount info.', $amount));
} }
Log::info('Not enough info to update foreign currency info.'); Log::info('Not enough info to update foreign currency info.');
// refresh transactions.
$this->sourceTransaction->refresh();
$this->destinationTransaction->refresh();
} }
/** /**

View File

@@ -22,8 +22,9 @@ declare(strict_types=1);
namespace FireflyIII\Support\Binder; namespace FireflyIII\Support\Binder;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\TransactionType;
use Illuminate\Routing\Route; use Illuminate\Routing\Route;
use Illuminate\Support\Collection;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/** /**
@@ -38,24 +39,38 @@ class JournalList implements BinderInterface
* @return mixed * @return mixed
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/ */
public static function routeBinder(string $value, Route $route): Collection public static function routeBinder(string $value, Route $route): array
{ {
if (auth()->check()) { if (auth()->check()) {
$list = array_unique(array_map('\intval', explode(',', $value))); $list = self::parseList($value);
if (0 === count($list)) {
throw new NotFoundHttpException; // @codeCoverageIgnore // get the journals by using the collector.
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER]);
$collector->withCategoryInformation()->withBudgetInformation()->withTagInformation()->withAccountInformation();
$collector->setJournalIds($list);
$result = $collector->getExtractedJournals();
if (0 === count($result)) {
throw new NotFoundHttpException;
} }
/** @var \Illuminate\Support\Collection $collection */ return $result;
$collection = auth()->user()->transactionJournals()
->whereIn('transaction_journals.id', $list)
->where('transaction_journals.completed', 1)
->get(['transaction_journals.*']);
if ($collection->count() > 0) {
return $collection;
}
} }
throw new NotFoundHttpException; throw new NotFoundHttpException;
} }
/**
* @param string $value
* @return array
*/
protected static function parseList(string $value): array
{
$list = array_unique(array_map('\intval', explode(',', $value)));
if (0 === count($list)) {
throw new NotFoundHttpException; // @codeCoverageIgnore
}
return $list;
}
} }

View File

@@ -1,78 +0,0 @@
<?php
/**
* SimpleJournalList.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Support\Binder;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\TransactionType;
use Illuminate\Routing\Route;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class SimpleJournalList
*/
class SimpleJournalList implements BinderInterface
{
/**
* @param string $value
* @param Route $route
*
* @return mixed
* @throws NotFoundHttpException
*/
public static function routeBinder(string $value, Route $route): array
{
if (auth()->check()) {
$list = self::parseList($value);
// get the journals by using the collector.
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER]);
$collector->withCategoryInformation()->withBudgetInformation()->withTagInformation();
$collector->setJournalIds($list);
$result = $collector->getExtractedJournals();
if (0 === count($result)) {
throw new NotFoundHttpException;
}
return $result;
}
throw new NotFoundHttpException;
}
/**
* @param string $value
* @return array
*/
protected static function parseList(string $value): array
{
$list = array_unique(array_map('\intval', explode(',', $value)));
if (0 === count($list)) {
throw new NotFoundHttpException; // @codeCoverageIgnore
}
return $list;
}
}

View File

@@ -58,7 +58,6 @@ use FireflyIII\Support\Binder\CurrencyCode;
use FireflyIII\Support\Binder\Date; use FireflyIII\Support\Binder\Date;
use FireflyIII\Support\Binder\ImportProvider; use FireflyIII\Support\Binder\ImportProvider;
use FireflyIII\Support\Binder\JournalList; use FireflyIII\Support\Binder\JournalList;
use FireflyIII\Support\Binder\SimpleJournalList;
use FireflyIII\Support\Binder\TagList; use FireflyIII\Support\Binder\TagList;
use FireflyIII\Support\Binder\TagOrId; use FireflyIII\Support\Binder\TagOrId;
use FireflyIII\Support\Binder\UnfinishedJournal; use FireflyIII\Support\Binder\UnfinishedJournal;
@@ -398,7 +397,6 @@ return [
'journalList' => JournalList::class, 'journalList' => JournalList::class,
'categoryList' => CategoryList::class, 'categoryList' => CategoryList::class,
'tagList' => TagList::class, 'tagList' => TagList::class,
'simpleJournalList' => SimpleJournalList::class,
// others // others
'fromCurrencyCode' => CurrencyCode::class, 'fromCurrencyCode' => CurrencyCode::class,

View File

@@ -18,22 +18,20 @@
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>. * along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** global: what */
$(document).ready(function () { $(document).ready(function () {
"use strict"; "use strict";
// description // description
if ($('input[name^="description["]').length > 0) { if ($('input[name^="description["]').length > 0) {
console.log('descr'); console.log('Description.');
var journalNames = new Bloodhound({ var journalNames = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'), datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace, queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: { prefetch: {
url: 'json/transaction-journals/all?uid=' + uid, url: 'json/transaction-journals/all?uid=' + uid,
filter: function (list) { filter: function (list) {
return $.map(list, function (name) { return $.map(list, function (obj) {
return {name: name}; return obj;
}); });
} }
}, },
@@ -41,8 +39,8 @@ $(document).ready(function () {
url: 'json/transaction-journals/all?search=%QUERY&uid=' + uid, url: 'json/transaction-journals/all?search=%QUERY&uid=' + uid,
wildcard: '%QUERY', wildcard: '%QUERY',
filter: function (list) { filter: function (list) {
return $.map(list, function (name) { return $.map(list, function (obj) {
return {name: name}; return obj;
}); });
} }
} }
@@ -52,15 +50,15 @@ $(document).ready(function () {
} }
// destination account names: // destination account names:
if ($('input[name^="destination_name["]').length > 0) { if ($('input[name^="destination_name["]').length > 0) {
console.log('Destination.');
var destNames = new Bloodhound({ var destNames = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'), datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace, queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: { prefetch: {
url: 'json/expense-accounts?uid=' + uid, url: 'json/expense-accounts?uid=' + uid,
filter: function (list) { filter: function (list) {
return $.map(list, function (name) { return $.map(list, function (obj) {
return {name: name}; return obj;
}); });
} }
}, },
@@ -68,8 +66,8 @@ $(document).ready(function () {
url: 'json/expense-accounts?search=%QUERY&uid=' + uid, url: 'json/expense-accounts?search=%QUERY&uid=' + uid,
wildcard: '%QUERY', wildcard: '%QUERY',
filter: function (list) { filter: function (list) {
return $.map(list, function (name) { return $.map(list, function (obj) {
return {name: name}; return obj;
}); });
} }
} }
@@ -80,15 +78,15 @@ $(document).ready(function () {
// source account name // source account name
if ($('input[name^="source_name["]').length > 0) { if ($('input[name^="source_name["]').length > 0) {
console.log('Source.');
var sourceNames = new Bloodhound({ var sourceNames = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'), datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace, queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: { prefetch: {
url: 'json/revenue-accounts?uid=' + uid, url: 'json/revenue-accounts?uid=' + uid,
filter: function (list) { filter: function (list) {
return $.map(list, function (name) { return $.map(list, function (obj) {
return {name: name}; return obj;
}); });
} }
}, },
@@ -96,8 +94,8 @@ $(document).ready(function () {
url: 'json/revenue-accounts?search=%QUERY&uid=' + uid, url: 'json/revenue-accounts?search=%QUERY&uid=' + uid,
wildcard: '%QUERY', wildcard: '%QUERY',
filter: function (list) { filter: function (list) {
return $.map(list, function (name) { return $.map(list, function (obj) {
return {name: name}; return obj;
}); });
} }
} }
@@ -113,8 +111,8 @@ $(document).ready(function () {
prefetch: { prefetch: {
url: 'json/categories?uid=' + uid, url: 'json/categories?uid=' + uid,
filter: function (list) { filter: function (list) {
return $.map(list, function (name) { return $.map(list, function (obj) {
return {name: name}; return obj;
}); });
} }
}, },
@@ -122,8 +120,8 @@ $(document).ready(function () {
url: 'json/categories?search=%QUERY&uid=' + uid, url: 'json/categories?search=%QUERY&uid=' + uid,
wildcard: '%QUERY', wildcard: '%QUERY',
filter: function (list) { filter: function (list) {
return $.map(list, function (name) { return $.map(list, function (obj) {
return {name: name}; return obj;
}); });
} }
} }

View File

@@ -851,7 +851,7 @@ return [
'cannot_edit_other_fields' => 'You cannot mass-edit other fields than the ones here, because there is no room to show them. Please follow the link and edit them by one-by-one, if you need to edit these fields.', 'cannot_edit_other_fields' => 'You cannot mass-edit other fields than the ones here, because there is no room to show them. Please follow the link and edit them by one-by-one, if you need to edit these fields.',
'no_budget' => '(no budget)', 'no_budget' => '(no budget)',
'no_budget_squared' => '(no budget)', 'no_budget_squared' => '(no budget)',
'perm-delete-many' => 'Deleting many items in one go can be very disruptive. Please be cautious.', 'perm-delete-many' => 'Deleting many items in one go can be very disruptive. Please be cautious. You can delete part of a split transaction from this page, so take care.',
'mass_deleted_transactions_success' => 'Deleted :amount transaction(s).', 'mass_deleted_transactions_success' => 'Deleted :amount transaction(s).',
'mass_edited_transactions_success' => 'Updated :amount transaction(s)', 'mass_edited_transactions_success' => 'Updated :amount transaction(s)',
'opt_group_' => '(no account type)', 'opt_group_' => '(no account type)',

View File

@@ -23,64 +23,65 @@
declare(strict_types=1); declare(strict_types=1);
return [ return [
'buttons' => 'Buttons', 'buttons' => 'Buttons',
'icon' => 'Icon', 'icon' => 'Icon',
'id' => 'ID', 'id' => 'ID',
'create_date' => 'Created at', 'create_date' => 'Created at',
'update_date' => 'Updated at', 'update_date' => 'Updated at',
'updated_at' => 'Updated at', 'updated_at' => 'Updated at',
'balance_before' => 'Balance before', 'balance_before' => 'Balance before',
'balance_after' => 'Balance after', 'balance_after' => 'Balance after',
'name' => 'Name', 'name' => 'Name',
'role' => 'Role', 'role' => 'Role',
'currentBalance' => 'Current balance', 'currentBalance' => 'Current balance',
'linked_to_rules' => 'Relevant rules', 'linked_to_rules' => 'Relevant rules',
'active' => 'Is active?', 'active' => 'Is active?',
'lastActivity' => 'Last activity', 'transaction_type' => 'Type',
'balanceDiff' => 'Balance difference', 'lastActivity' => 'Last activity',
'matchesOn' => 'Matched on', 'balanceDiff' => 'Balance difference',
'account_type' => 'Account type', 'matchesOn' => 'Matched on',
'created_at' => 'Created at', 'account_type' => 'Account type',
'account' => 'Account', 'created_at' => 'Created at',
'matchingAmount' => 'Amount', 'account' => 'Account',
'split_number' => 'Split #', 'matchingAmount' => 'Amount',
'destination' => 'Destination', 'split_number' => 'Split #',
'source' => 'Source', 'destination' => 'Destination',
'next_expected_match' => 'Next expected match', 'source' => 'Source',
'automatch' => 'Auto match?', 'next_expected_match' => 'Next expected match',
'repeat_freq' => 'Repeats', 'automatch' => 'Auto match?',
'description' => 'Description', 'repeat_freq' => 'Repeats',
'amount' => 'Amount', 'description' => 'Description',
'internal_reference' => 'Internal reference', 'amount' => 'Amount',
'date' => 'Date', 'internal_reference' => 'Internal reference',
'interest_date' => 'Interest date', 'date' => 'Date',
'book_date' => 'Book date', 'interest_date' => 'Interest date',
'process_date' => 'Processing date', 'book_date' => 'Book date',
'due_date' => 'Due date', 'process_date' => 'Processing date',
'payment_date' => 'Payment date', 'due_date' => 'Due date',
'invoice_date' => 'Invoice date', 'payment_date' => 'Payment date',
'interal_reference' => 'Internal reference', 'invoice_date' => 'Invoice date',
'notes' => 'Notes', 'interal_reference' => 'Internal reference',
'from' => 'From', 'notes' => 'Notes',
'piggy_bank' => 'Piggy bank', 'from' => 'From',
'to' => 'To', 'piggy_bank' => 'Piggy bank',
'budget' => 'Budget', 'to' => 'To',
'category' => 'Category', 'budget' => 'Budget',
'bill' => 'Bill', 'category' => 'Category',
'withdrawal' => 'Withdrawal', 'bill' => 'Bill',
'deposit' => 'Deposit', 'withdrawal' => 'Withdrawal',
'transfer' => 'Transfer', 'deposit' => 'Deposit',
'type' => 'Type', 'transfer' => 'Transfer',
'completed' => 'Completed', 'type' => 'Type',
'iban' => 'IBAN', 'completed' => 'Completed',
'paid_current_period' => 'Paid this period', 'iban' => 'IBAN',
'email' => 'Email', 'paid_current_period' => 'Paid this period',
'registered_at' => 'Registered at', 'email' => 'Email',
'is_blocked' => 'Is blocked', 'registered_at' => 'Registered at',
'is_admin' => 'Is admin', 'is_blocked' => 'Is blocked',
'has_two_factor' => 'Has 2FA', 'is_admin' => 'Is admin',
'blocked_code' => 'Block code', 'has_two_factor' => 'Has 2FA',
'source_account' => 'Source account', 'blocked_code' => 'Block code',
'source_account' => 'Source account',
'destination_account' => 'Destination account', 'destination_account' => 'Destination account',
'accounts_count' => 'Number of accounts', 'accounts_count' => 'Number of accounts',
'journals_count' => 'Number of transactions', 'journals_count' => 'Number of transactions',

View File

@@ -19,7 +19,6 @@
{{ trans('form.permDeleteWarning') }} {{ trans('form.permDeleteWarning') }}
{{ 'perm-delete-many'|_ }} {{ 'perm-delete-many'|_ }}
</p> </p>
<p> <p>
{{ trans('form.mass_journal_are_you_sure') }} {{ trans('form.mass_journal_are_you_sure') }}
{{ trans('form.mass_make_selection') }} {{ trans('form.mass_make_selection') }}
@@ -28,8 +27,9 @@
<table class="table table-striped table-condensed"> <table class="table table-striped table-condensed">
<tr> <tr>
<th>&nbsp;</th> <th>&nbsp;</th>
<th>{{ trans('list.transaction_type') }}</th>
<th>{{ trans('list.description') }}</th> <th>{{ trans('list.description') }}</th>
<th>{{ trans('list.total_amount') }}</th> <th>{{ trans('list.amount') }}</th>
<th class="hidden-sm hidden-xs">{{ trans('list.date') }}</th> <th class="hidden-sm hidden-xs">{{ trans('list.date') }}</th>
<th class="hidden-xs">{{ trans('list.from') }}</th> <th class="hidden-xs">{{ trans('list.from') }}</th>
<th class="hidden-xs">{{ trans('list.to') }}</th> <th class="hidden-xs">{{ trans('list.to') }}</th>
@@ -37,22 +37,61 @@
{% for journal in journals %} {% for journal in journals %}
<tr> <tr>
<td> <td>
<input type="checkbox" name="confirm_mass_delete[]" value="{{ journal.id }}" checked/> <input type="checkbox" name="confirm_mass_delete[]" value="{{ journal.transaction_journal_id }}" checked/>
</td> </td>
<td> <td>
<a href="{{ route('transactions.show',journal.id) }}" title="{{ journal.description }}">{{ journal.description }}</a> {% 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>
<td> <td>
{{ journal|journalTotalAmount }} <a href="{{ route('transactions.show',journal.transaction_journal_id) }}"
title="{{ journal.description }}">{{ journal.description }}</a>
</td>
<td>
{% if journal.transaction_type_type == 'Deposit' %}
{{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_symbol_decimal_places) }}
{% if null != journal.foreign_amount %}
({{ formatAmountBySymbol(journal.foreign_amount*-1, journal.foreign_currency_symbol, journal.foreign_currency_symbol_decimal_places) }})
{% endif %}
{% elseif journal.transaction_type_type == 'Transfer' %}
<span class="text-info">{{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_symbol_decimal_places, false) }}
{% if null != journal.foreign_amount %}
({{ formatAmountBySymbol(journal.foreign_amount*-1, journal.foreign_currency_symbol, journal.foreign_currency_symbol_decimal_places, false) }})
{% endif %}
</span>
{% else %}
{{ formatAmountBySymbol(journal.amount, journal.currency_symbol, journal.currency_symbol_decimal_places) }}
{% if null != journal.foreign_amount %}
({{ formatAmountBySymbol(journal.foreign_amount, journal.foreign_currency_symbol, journal.foreign_currency_symbol_decimal_places) }})
{% endif %}
{% endif %}
</td> </td>
<td> <td>
{{ journal.date.formatLocalized(monthAndDayFormat) }} {{ journal.date.formatLocalized(monthAndDayFormat) }}
</td> </td>
<td> <td>
{{ sourceAccount(journal)|raw }} <a href="{{ route('accounts.show', [journal.source_account_id]) }}"
title="{{ journal.source_account_iban|default(journal.source_account_name) }}">{{ journal.source_account_name }}</a>
</td> </td>
<td> <td>
{{ destinationXAccount(journal)|raw }} <a href="{{ route('accounts.show', [journal.destination_account_id]) }}"
title="{{ journal.destination_account_iban|default(journal.destination_account_name) }}">{{ journal.destination_account_name }}</a>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View File

@@ -1,7 +1,7 @@
{% extends "./layout/default" %} {% extends "./layout/default" %}
{% block breadcrumbs %} {% block breadcrumbs %}
{{ Breadcrumbs.render(Route.getCurrentRoute.getName, transactions) }} {{ Breadcrumbs.render(Route.getCurrentRoute.getName, journals) }}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@@ -29,91 +29,107 @@
<th class="col-lg-2 col-md-2 col-sm-2">{{ trans('list.category') }}</th> <th class="col-lg-2 col-md-2 col-sm-2">{{ trans('list.category') }}</th>
<th class="col-lg-2 col-md-2 col-sm-2">{{ trans('list.budget') }}</th> <th class="col-lg-2 col-md-2 col-sm-2">{{ trans('list.budget') }}</th>
</tr> </tr>
{% for transaction in transactions %} {% for journal in journals %}
<tr> <tr>
<td> <td>
{# LINK TO EDIT FORM #} {# LINK TO EDIT FORM #}
<a href="{{ route('transactions.edit', transaction.journal_id) }}" class="btn btn-xs btn-default"><i <a href="{{ route('transactions.edit', journal.transaction_group_id) }}" class="btn btn-xs btn-default"><i
class="fa fa-fw fa-pencil"></i></a> class="fa fa-fw fa-pencil"></i></a>
<input type="hidden" name="journals[]" value="{{ transaction.journal_id }}"/> <input type="hidden" name="journals[]" value="{{ journal.transaction_journal_id }}"/>
</td> </td>
<td> <td>
{# DESCRIPTION #} {# DESCRIPTION #}
<input class="form-control input-sm" autocomplete="off" <input class="form-control input-sm" autocomplete="off"
placeholder="{{ transaction.description }}" name="description[{{ transaction.journal_id }}]" placeholder="{{ journal.description }}" name="description[{{ journal.transaction_journal_id }}]"
type="text" value="{{ transaction.description }}"> type="text" value="{{ journal.description }}">
</td> </td>
{# AMOUNT #} {# AMOUNT #}
<td> <td>
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<span class="input-group-addon">{{ transaction.currency_symbol }}</span>
<input name="amount[{{ transaction.journal_id }}]" class="form-control" autocomplete="off"
step="any" type="number" value="{{ transaction.amount }}"> <span class="input-group-addon">{{ journal.currency_symbol }}</span>
<input type="hidden" name="transaction_currency_id[{{ transaction.journal_id }}]" <input name="amount[{{ journal.transaction_journal_id }}]" class="form-control" autocomplete="off"
value="{{ transaction.currency_id }}"> step="any" type="number" value="{{ journal.amount }}">
<input type="hidden" name="transaction_currency_id[{{ journal.transaction_journal_id }}]"
value="{{ journal.currency_id }}">
</div> </div>
{% if transaction.foreign_amount %} {% if journal.foreign_amount %}
{# insert foreign data #} {# insert foreign data #}
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<span class="input-group-addon">{{ transaction.foreign_currency_symbol }}</span> <span class="input-group-addon">{{ journal.foreign_currency_symbol }}</span>
<input name="foreign_amount[{{ transaction.journal_id }}]" class="form-control" autocomplete="off" <input name="foreign_amount[{{ journal.transaction_journal_id }}]" class="form-control" autocomplete="off"
step="any" type="number" value="{{ transaction.foreign_amount }}"> step="any" type="number" value="{{ journal.foreign_amount }}">
<input type="hidden" name="foreign_currency_id[{{ transaction.journal_id }}]" value="{{ transaction.foreign_currency_id }}"> <input type="hidden" name="foreign_currency_id[{{ journal.transaction_journal_id }}]"
value="{{ journal.foreign_currency_id }}">
</div> </div>
{% endif %} {% endif %}
</td> </td>
<td> <td>
{# DATE #} {# DATE #}
<input class="form-control input-sm" autocomplete="off" <input class="form-control input-sm" autocomplete="off"
name="date[{{ transaction.journal_id }}]" type="date" value="{{ transaction.date|slice(0,10) }}"> name="date[{{ journal.transaction_journal_id }}]" type="date" value="{{ journal.date|slice(0,10) }}">
</td> </td>
<!-- {{ journal.transaction_type_type }} -->
<!-- Source: {{ journal.source_account_name }} ({{ journal.source_account_id }}) -->
<!-- Destination: {{ journal.destination_account_name }} ({{ journal.destination_account_id }}) -->
<td style="position: relative;"> <td style="position: relative;">
{# SOURCE ACCOUNT ID FOR TRANSFER OR WITHDRAWAL #} {# SOURCE ACCOUNT ID FOR TRANSFER OR WITHDRAWAL #}
{% if transaction.type == 'Transfer' or transaction.type == 'Withdrawal' %} {% if journal.transaction_type_type == 'Transfer' or journal.transaction_type_type == 'Withdrawal' %}
<select class="form-control input-sm" name="source_id[{{ transaction.journal_id }}]"> <select class="form-control input-sm" name="source_id[{{ journal.transaction_journal_id }}]">
{% for account in accounts %} {% for account in withdrawalSources %}
<!-- {{ transaction.type }}: {{ transaction.source_name }} --> <option value="{{ account.id }}"{% if account.id == journal.source_account_id %} selected{% endif %}
<option value="{{ account.id }}"{% if account.id == transaction.source_id %} selected{% endif %} label="{{ account.name }}">{{ account.name }}</option>
{% endfor %}
</select>
{% else %}
{# SOURCE ACCOUNT NAME FOR DEPOSIT #}
<input class="form-control input-sm" placeholder="{% if transaction.source_type != 'Cash account' %}{{ transaction.source_name }}{% endif %}" autocomplete="off"
name="source_name[{{ transaction.journal_id }}]" type="text" value="{% if transaction.source_type != 'Cash account' %}{{ transaction.source_name }}{% endif %}">
{% endif %}
</td>
<td style="position: relative;">
{% if transaction.type == 'Transfer' or transaction.type == 'Deposit' %}
{# DESTINATION ACCOUNT NAME FOR TRANSFER AND DEPOSIT #}
<select class="form-control input-sm" name="destination_id[{{ transaction.journal_id }}]">
{% for account in accounts %}
<option value="{{ account.id }}"{% if account.id == transaction.destination_id %} selected="selected"{% endif %}
label="{{ account.name }}">{{ account.name }}</option> label="{{ account.name }}">{{ account.name }}</option>
{% endfor %} {% endfor %}
</select> </select>
{% else %} {% endif %}
{# DESTINATION ACCOUNT NAME FOR EXPENSE #} {# SOURCE ACCOUNT NAME FOR DEPOSIT #}
<input class="form-control input-sm" placeholder="{% if transaction.destination_type != 'Cash account' %}{{ transaction.destination_name }}{% endif %}" {% if journal.transaction_type_type == 'Deposit' %}
name="destination_name[{{ transaction.journal_id }}]" type="text" autocomplete="off" <input class="form-control input-sm"
value="{% if transaction.destination_type != 'Cash account' %}{{ transaction.destination_name }}{% endif %}"> placeholder="{% if journal.source_type != 'Cash account' %}{{ journal.source_account_name }}{% endif %}"
autocomplete="off"
name="source_name[{{ journal.transaction_journal_id }}]" type="text"
value="{% if journal.source_type != 'Cash account' %}{{ journal.source_account_name }}{% endif %}">
{% endif %}
</td>
<td style="position: relative;">
{# DESTINATION ACCOUNT NAME FOR TRANSFER AND DEPOSIT #}
{% if journal.transaction_type_type == 'Transfer' or journal.transaction_type_type == 'Deposit' %}
<select class="form-control input-sm" name="destination_id[{{ journal.transaction_journal_id }}]">
{% for account in depositDestinations %}
<option value="{{ account.id }}"{% if account.id == journal.destination_account_id %} selected="selected"{% endif %}
label="{{ account.name }}">{{ account.name }}</option>
{% endfor %}
</select>
{% endif %}
{# DESTINATION ACCOUNT NAME FOR WITHDRAWAL #}
{% if journal.transaction_type_type == 'Withdrawal' %}
<input class="form-control input-sm"
placeholder="{% if journal.destination_type != 'Cash account' %}{{ journal.destination_account_name }}{% endif %}"
name="destination_name[{{ journal.transaction_journal_id }}]" type="text" autocomplete="off"
value="{% if journal.destination_type != 'Cash account' %}{{ journal.destination_account_name }}{% endif %}">
{% endif %} {% endif %}
</td> </td>
{# category #} {# category #}
<td style="position: relative;"> <td style="position: relative;">
<input class="form-control input-sm" placeholder="{{ transaction.category_name }}" autocomplete="off" <input class="form-control input-sm" placeholder="{{ journal.category_name }}" autocomplete="off"
name="category[{{ transaction.journal_id }}]" type="text" value="{{ transaction.category_name }}"> name="category[{{ journal.transaction_journal_id }}]" type="text" value="{{ journal.category_name }}">
</td> </td>
{# budget #} {# budget #}
<td> <td>
{% if transaction.type == 'Withdrawal' %} {% if journal.transaction_type_type == 'Withdrawal' %}
<select class="form-control input-sm" name="budget_id[{{ transaction.journal_id }}]"> <select class="form-control input-sm" name="budget_id[{{ journal.transaction_journal_id }}]">
<option value="0" label="({{ 'no_budget'|_ }})" <option value="0" label="({{ 'no_budget'|_ }})"
{% if transaction.budget_id == 0 %}selected="selected"{% endif %} {% if journal.budget_id == 0 %}selected="selected"{% endif %}
>({{ 'no_budget'|_ }}) >({{ 'no_budget'|_ }})
</option> </option>
{% for budget in budgets %} {% for budget in budgets %}
<option value="{{ budget.id }}"{% if budget.id == transaction.budget_id %} selected="selected"{% endif %} <option value="{{ budget.id }}"{% if budget.id == journal.budget_id %} selected="selected"{% endif %}
label="{{ budget.name }}">{{ budget.name }}</option> label="{{ budget.name }}">{{ budget.name }}</option>
{% endfor %} {% endfor %}
</select> </select>

View File

@@ -41,7 +41,6 @@ use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionJournalLink; use FireflyIII\Models\TransactionJournalLink;
use FireflyIII\Models\TransactionType;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@@ -1096,12 +1095,11 @@ try {
// MASS TRANSACTION EDIT / DELETE // MASS TRANSACTION EDIT / DELETE
Breadcrumbs::register( Breadcrumbs::register(
'transactions.mass.edit', 'transactions.mass.edit',
function (BreadcrumbsGenerator $breadcrumbs, Collection $journals): void { static function (BreadcrumbsGenerator $breadcrumbs, array $journals): void {
if (\count($journals) > 0) { if (count($journals) > 0) {
$journalIds = $journals->pluck('id')->toArray(); $objectType = strtolower(reset($journals)['transaction_type_type']);
$what = strtolower($journals->first()['type']); $breadcrumbs->parent('transactions.index', $objectType);
$breadcrumbs->parent('transactions.index', $what); $breadcrumbs->push(trans('firefly.mass_edit_journals'), route('transactions.mass.edit', ['']));
$breadcrumbs->push(trans('firefly.mass_edit_journals'), route('transactions.mass.edit', $journalIds));
return; return;
} }
@@ -1111,11 +1109,10 @@ try {
Breadcrumbs::register( Breadcrumbs::register(
'transactions.mass.delete', 'transactions.mass.delete',
function (BreadcrumbsGenerator $breadcrumbs, Collection $journals) { static function (BreadcrumbsGenerator $breadcrumbs, array $journals) {
$journalIds = $journals->pluck('id')->toArray(); $objectType= strtolower(reset($journals)['transaction_type_type']);
$what = strtolower($journals->first()->transactionType->type); $breadcrumbs->parent('transactions.index', $objectType);
$breadcrumbs->parent('transactions.index', $what); $breadcrumbs->push(trans('firefly.mass_edit_journals'), route('transactions.mass.delete', ['']));
$breadcrumbs->push(trans('firefly.mass_edit_journals'), route('transactions.mass.delete', $journalIds));
} }
); );

View File

@@ -922,7 +922,7 @@ Route::group(
Route::group( Route::group(
['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers\Transaction', 'prefix' => 'transactions/mass', 'as' => 'transactions.mass.'], ['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers\Transaction', 'prefix' => 'transactions/mass', 'as' => 'transactions.mass.'],
function () { function () {
Route::get('edit/{simpleJournalList}', ['uses' => 'MassController@edit', 'as' => 'edit']); Route::get('edit/{journalList}', ['uses' => 'MassController@edit', 'as' => 'edit']);
Route::get('delete/{journalList}', ['uses' => 'MassController@delete', 'as' => 'delete']); Route::get('delete/{journalList}', ['uses' => 'MassController@delete', 'as' => 'delete']);
Route::post('update', ['uses' => 'MassController@update', 'as' => 'update']); Route::post('update', ['uses' => 'MassController@update', 'as' => 'update']);
Route::post('destroy', ['uses' => 'MassController@destroy', 'as' => 'destroy']); Route::post('destroy', ['uses' => 'MassController@destroy', 'as' => 'destroy']);
@@ -935,7 +935,7 @@ Route::group(
Route::group( Route::group(
['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers\Transaction', 'prefix' => 'transactions/bulk', 'as' => 'transactions.bulk.'], ['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers\Transaction', 'prefix' => 'transactions/bulk', 'as' => 'transactions.bulk.'],
function () { function () {
Route::get('edit/{simpleJournalList}', ['uses' => 'BulkController@edit', 'as' => 'edit']); Route::get('edit/{journalList}', ['uses' => 'BulkController@edit', 'as' => 'edit']);
Route::post('update', ['uses' => 'BulkController@update', 'as' => 'update']); Route::post('update', ['uses' => 'BulkController@update', 'as' => 'update']);
} }
); );

View File

@@ -23,17 +23,18 @@ declare(strict_types=1);
namespace Tests\Feature\Controllers\Transaction; namespace Tests\Feature\Controllers\Transaction;
use FireflyIII\Models\AccountType; use Amount;
use FireflyIII\Models\Transaction; use FireflyIII\Events\UpdatedTransactionGroup;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Transformers\TransactionTransformer; use FireflyIII\Services\Internal\Update\JournalUpdateService;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Log; use Log;
use Mockery; use Mockery;
use Preferences;
use Tests\TestCase; use Tests\TestCase;
/** /**
@@ -56,26 +57,32 @@ class MassControllerTest extends TestCase
/** /**
* @covers \FireflyIII\Http\Controllers\Transaction\MassController
* @covers \FireflyIII\Http\Controllers\Transaction\MassController * @covers \FireflyIII\Http\Controllers\Transaction\MassController
*/ */
public function testDelete(): void public function testDelete(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return; $this->mockDefaultSession();
$journalRepos = $this->mock(JournalRepositoryInterface::class); $withdrawal = $this->getRandomWithdrawal();
$userRepos = $this->mock(UserRepositoryInterface::class); $withdrawalArray = $this->getRandomWithdrawalAsArray();
$userRepos = $this->mock(UserRepositoryInterface::class);
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true); $userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal); $collector = $this->mock(GroupCollectorInterface::class);
$journalRepos->shouldReceive('getJournalSourceAccounts')->andReturn(new Collection)->once(); $collector->shouldReceive('setTypes')
$journalRepos->shouldReceive('getJournalDestinationAccounts')->andReturn(new Collection)->once(); ->withArgs([[TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER]])->atLeast()->once()->andReturnSelf();
$collector->shouldReceive('withCategoryInformation')->atLeast()->once()->andReturnSelf();
$collector->shouldReceive('withBudgetInformation')->atLeast()->once()->andReturnSelf();
$collector->shouldReceive('withTagInformation')->atLeast()->once()->andReturnSelf();
$collector->shouldReceive('withAccountInformation')->atLeast()->once()->andReturnSelf();
$collector->shouldReceive('setJournalIds')->withArgs([[$withdrawal->id]])->atLeast()->once()->andReturnSelf();
$collector->shouldReceive('getExtractedJournals')->atLeast()->once()->andReturn([$withdrawalArray]);
Amount::shouldReceive('formatAnything')->atLeast()->once()->andReturn('x');
$withdrawals = TransactionJournal::where('transaction_type_id', 1)->where('user_id', $this->user()->id)->take(2)->get()->pluck('id')->toArray();
$this->be($this->user()); $this->be($this->user());
$response = $this->get(route('transactions.mass.delete', $withdrawals)); $response = $this->get(route('transactions.mass.delete', [$withdrawal->id]));
$response->assertStatus(200); $response->assertStatus(200);
$response->assertSee('Delete a number of transactions'); $response->assertSee('Delete a number of transactions');
// has bread crumb // has bread crumb
@@ -87,26 +94,16 @@ class MassControllerTest extends TestCase
*/ */
public function testDestroy(): void public function testDestroy(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0'); $repository = $this->mockDefaultSession();
$deposit = $this->getRandomDeposit();
return; $repository->shouldReceive('findNull')->atLeast()->once()->andReturn($deposit);
$repository->shouldReceive('destroyJournal')->atLeast()->once();
$deposits = TransactionJournal::where('transaction_type_id', 2)->where('user_id', $this->user()->id)->take(2)->get(); Preferences::shouldReceive('mark')->atLeast()->once();
$depositIds = $deposits->pluck('id')->toArray();
// mock deletion:
$repository = $this->mock(JournalRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$repository->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('findNull')->andReturnValues([$deposits[0], $deposits[1]])->times(2);
$repository->shouldReceive('destroy')->times(2);
$this->session(['transactions.mass-delete.uri' => 'http://localhost']); $this->session(['transactions.mass-delete.uri' => 'http://localhost']);
$data = [ $data = ['confirm_mass_delete' => [$deposit->id],];
'confirm_mass_delete' => $depositIds,
];
$this->be($this->user()); $this->be($this->user());
$response = $this->post(route('transactions.mass.destroy'), $data); $response = $this->post(route('transactions.mass.destroy'), $data);
$response->assertSessionHas('success'); $response->assertSessionHas('success');
@@ -118,161 +115,59 @@ class MassControllerTest extends TestCase
*/ */
public function testEdit(): void public function testEdit(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0'); $withdrawal = $this->getRandomWithdrawal();
$withdrawalArray = $this->getRandomWithdrawalAsArray();
$asset = $this->getRandomAsset();
$budget = $this->getRandomBudget();
return; $journalRepos = $this->mockDefaultSession();
// mock things
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$transformer = $this->mock(TransactionTransformer::class); $repository = $this->mock(AccountRepositoryInterface::class);
$collector = $this->mock(TransactionCollectorInterface::class);
// data:
$transfers = TransactionJournal::where('transaction_type_id', 3)->where('user_id', $this->user()->id)->take(2)->get();
$transfersArray = $transfers->pluck('id')->toArray();
$source = $this->user()->accounts()->first();
$transaction = new Transaction;
// mock calls:
$transformer->shouldReceive('setParameters')->atLeast()->once();
$transformer->shouldReceive('transform')->atLeast()->once()->andReturn(
[
'amount' => '10',
'foreign_amount' => '',
'type' => 'transfer',
'id' => 3,
'journal_id' => 1,
]
);
$collector->shouldReceive('setUser')->atLeast()->once()->andReturnSelf();
$collector->shouldReceive('withOpposingAccount')->atLeast()->once()->andReturnSelf();
$collector->shouldReceive('withCategoryInformation')->atLeast()->once()->andReturnSelf();
$collector->shouldReceive('withBudgetInformation')->atLeast()->once()->andReturnSelf();
$collector->shouldReceive('setJournals')->atLeast()->once()->andReturnSelf();
$collector->shouldReceive('addFilter')->atLeast()->once()->andReturnSelf();
$collector->shouldReceive('getTransactions')->atLeast()->once()->andReturn(new Collection([new Transaction]));
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true);
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
// mock data for edit page:
$journalRepos->shouldReceive('getJournalSourceAccounts')->andReturn(new Collection([$source]))->atLeast()->once();
$journalRepos->shouldReceive('getJournalDestinationAccounts')->andReturn(new Collection([$source]))->atLeast()->once();
$journalRepos->shouldReceive('getTransactionType')->andReturn('Transfer')->atLeast()->once();
$journalRepos->shouldReceive('isJournalReconciled')->andReturn(false)->atLeast()->once();
// mock stuff:
$repository = $this->mock(AccountRepositoryInterface::class);
$repository->shouldReceive('getAccountsByType')->once()->withArgs([[AccountType::DEFAULT, AccountType::ASSET]])->andReturn(new Collection);
// mock more stuff:
$budgetRepos = $this->mock(BudgetRepositoryInterface::class);
$budgetRepos->shouldReceive('getBudgets')->andReturn(new Collection)->atLeast()->once();
$this->be($this->user());
$response = $this->get(route('transactions.mass.edit', $transfersArray));
$response->assertStatus(200);
$response->assertSee('Edit a number of transactions');
// has bread crumb
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\Transaction\MassController
*/
public function testEditMultiple(): void
{
$this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
$budgetRepos = $this->mock(BudgetRepositoryInterface::class); $budgetRepos = $this->mock(BudgetRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$transformer = $this->mock(TransactionTransformer::class);
$collector = $this->mock(TransactionCollectorInterface::class);
// mock calls:
$transformer->shouldReceive('setParameters')->atLeast()->once();
$transformer->shouldReceive('transform')->atLeast()->once()->andReturn(
[
'amount' => '10',
'foreign_amount' => '',
'type' => 'transfer',
'id' => 3,
'journal_id' => 1,
]
);
$collector->shouldReceive('setUser')->atLeast()->once()->andReturnSelf(); $collector = $this->mock(GroupCollectorInterface::class);
$collector->shouldReceive('withOpposingAccount')->atLeast()->once()->andReturnSelf(); $collector->shouldReceive('setTypes')
->withArgs([[TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER]])->atLeast()->once()->andReturnSelf();
$collector->shouldReceive('withCategoryInformation')->atLeast()->once()->andReturnSelf(); $collector->shouldReceive('withCategoryInformation')->atLeast()->once()->andReturnSelf();
$collector->shouldReceive('withBudgetInformation')->atLeast()->once()->andReturnSelf(); $collector->shouldReceive('withBudgetInformation')->atLeast()->once()->andReturnSelf();
$collector->shouldReceive('setJournals')->atLeast()->once()->andReturnSelf(); $collector->shouldReceive('withTagInformation')->atLeast()->once()->andReturnSelf();
$collector->shouldReceive('addFilter')->atLeast()->once()->andReturnSelf(); $collector->shouldReceive('withAccountInformation')->atLeast()->once()->andReturnSelf();
$collector->shouldReceive('getTransactions')->atLeast()->once()->andReturn(new Collection([new Transaction])); $collector->shouldReceive('setJournalIds')->withArgs([[$withdrawal->id]])->atLeast()->once()->andReturnSelf();
$collector->shouldReceive('getExtractedJournals')->atLeast()->once()->andReturn([$withdrawalArray]);
$repository->shouldReceive('getAccountsByType')->atLeast()->once()->andReturn(new Collection([$asset]));
$budgetRepos->shouldReceive('getBudgets')->atLeast()->once()->andReturn(new Collection([$budget]));
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true); $userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true);
$budgetRepos->shouldReceive('getBudgets')->andReturn(new Collection)->atLeast()->once();
// mock stuff:
$repository = $this->mock(AccountRepositoryInterface::class);
$repository->shouldReceive('getAccountsByType')->once()->withArgs([[AccountType::DEFAULT, AccountType::ASSET]])->andReturn(new Collection);
$journalRepos->shouldReceive('firstNull')->andReturn(new TransactionJournal)->atLeast()->once();
$journalRepos->shouldReceive('getJournalSourceAccounts')
->andReturn(new Collection([1, 2, 3]), new Collection, new Collection, new Collection, new Collection([1]))->atLeast()->once();
$journalRepos->shouldReceive('getJournalDestinationAccounts')
->andReturn(new Collection, new Collection([1, 2, 3]), new Collection, new Collection, new Collection([1]))->atLeast()->once();
$journalRepos->shouldReceive('getTransactionType')
->andReturn('Withdrawal', 'Opening balance', 'Withdrawal', 'Withdrawal', 'Withdrawal')->atLeast()->once();
$journalRepos->shouldReceive('isJournalReconciled')
->andReturn(true, false, false, false, false)->atLeast()->once();
// default transactions
$collection = $this->user()->transactionJournals()->take(5)->get();
$allIds = $collection->pluck('id')->toArray();
$route = route('transactions.mass.edit', implode(',', $allIds));
$this->be($this->user()); $this->be($this->user());
$response = $this->get($route); $response = $this->get(route('transactions.mass.edit', [$withdrawal->id]));
$response->assertStatus(200); $response->assertStatus(200);
$response->assertSee('Edit a number of transactions'); $response->assertSee('Edit a number of transactions');
// has bread crumb // has bread crumb
$response->assertSee('<ol class="breadcrumb">'); $response->assertSee('<ol class="breadcrumb">');
$response->assertSee('marked as reconciled');
$response->assertSee('multiple source accounts');
$response->assertSee('multiple destination accounts');
} }
/** /**
* @covers \FireflyIII\Http\Controllers\Transaction\MassController * @covers \FireflyIII\Http\Controllers\Transaction\MassController
*/ */
public function testUpdate(): void public function testUpdate(): void
{ {
$this->markTestIncomplete('Needs to be rewritten for v4.8.0'); $deposit = $this->getRandomDeposit();
$repository = $this->mockDefaultSession();
$userRepos = $this->mock(UserRepositoryInterface::class);
$updateService = $this->mock(JournalUpdateService::class);
return; $this->expectsEvents(UpdatedTransactionGroup::class);
$deposit = TransactionJournal::where('transaction_type_id', 2)->where('user_id', $this->user()->id)
->whereNull('deleted_at')
->first();
// mock stuff $updateService->shouldReceive('setTransactionJournal')->atLeast()->once();
$repository = $this->mock(JournalRepositoryInterface::class); $updateService->shouldReceive('setData')->atLeast()->once();
$userRepos = $this->mock(UserRepositoryInterface::class); $updateService->shouldReceive('update')->atLeast()->once();
Preferences::shouldReceive('mark')->atLeast()->once();
$repository->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal); $repository->shouldReceive('findNull')->atLeast()->once()->andReturn($deposit);
$repository->shouldReceive('update')->once();
$repository->shouldReceive('findNull')->once()->andReturn($deposit);
$repository->shouldReceive('getTransactionType')->andReturn('Deposit');
$repository->shouldReceive('getNoteText')->andReturn('Some note');
$this->session(['transactions.mass-edit.uri' => 'http://localhost']); $this->session(['transactions.mass-edit.uri' => 'http://localhost']);
@@ -280,7 +175,6 @@ class MassControllerTest extends TestCase
'journals' => [$deposit->id], 'journals' => [$deposit->id],
'description' => [$deposit->id => 'Updated salary thing'], 'description' => [$deposit->id => 'Updated salary thing'],
'amount' => [$deposit->id => 1600], 'amount' => [$deposit->id => 1600],
'amount_currency_id_amount_' . $deposit->id => 1,
'date' => [$deposit->id => '2014-07-24'], 'date' => [$deposit->id => '2014-07-24'],
'source_name' => [$deposit->id => 'Job'], 'source_name' => [$deposit->id => 'Job'],
'destination_id' => [$deposit->id => 1], 'destination_id' => [$deposit->id => 1],

View File

@@ -159,6 +159,7 @@ abstract class TestCase extends BaseTestCase
} }
return [ return [
'transaction_group_id' => $withdrawal->transaction_group_id,
'transaction_journal_id' => $withdrawal->id, 'transaction_journal_id' => $withdrawal->id,
'transaction_type_type' => 'Withdrawal', 'transaction_type_type' => 'Withdrawal',
'currency_id' => $euro->id, 'currency_id' => $euro->id,
@@ -166,6 +167,7 @@ abstract class TestCase extends BaseTestCase
'date' => $date, 'date' => $date,
'description' => sprintf('I am descr #%d', $this->randomInt()), 'description' => sprintf('I am descr #%d', $this->randomInt()),
'source_account_id' => 1, 'source_account_id' => 1,
'foreign_amount' => null,
'destination_account_id' => $expense->id, 'destination_account_id' => $expense->id,
'destination_account_name' => $expense->name, 'destination_account_name' => $expense->name,
'currency_name' => $euro->name, 'currency_name' => $euro->name,