Implemented new link thing.

This commit is contained in:
James Cole 2019-07-20 06:47:34 +02:00
parent 63832b31f8
commit 6d34cfb940
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
15 changed files with 581 additions and 390 deletions

View File

@ -149,7 +149,42 @@ class AutoCompleteController extends Controller
return response()->json($array);
}
/**
* Searches in the titles of all transaction journals.
* The result is limited to the top 15 unique results.
*
* If the query is numeric, it will append the journal with that particular ID.
*
* @param Request $request
* @return JsonResponse
*/
public function allJournalsWithID(Request $request): JsonResponse
{
$search = (string)$request->get('search');
/** @var JournalRepositoryInterface $repository */
$repository = app(JournalRepositoryInterface::class);
$result = $repository->searchJournalDescriptions($search);
$array = [];
if (is_numeric($search)) {
$firstResult = $repository->findNull((int)$search);
if (null !== $firstResult) {
$array[] = $firstResult->toArray();
}
}
// if not numeric, search ahead!
// limit and unique
$limited = $result->slice(0, 15);
$array = array_merge($array, $limited->toArray());
foreach ($array as $index => $item) {
// give another key for consistency
$array[$index]['name'] = sprintf('#%d: %s', $item['id'], $item['description']);
}
return response()->json($array);
}
/**

View File

@ -33,7 +33,8 @@ class EditController extends Controller
{
/**
* SingleController constructor.
* EditController constructor.
* @codeCoverageIgnore
*/
public function __construct()
{
@ -48,7 +49,7 @@ class EditController extends Controller
// some useful repositories:
$this->middleware(
function ($request, $next) {
static function ($request, $next) {
app('view')->share('title', (string)trans('firefly.transactions'));
app('view')->share('mainTitleIcon', 'fa-repeat');

View File

@ -28,6 +28,8 @@ use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionJournalLink;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
use Illuminate\Contracts\View\Factory;
use Illuminate\View\View;
use Log;
use URL;
@ -43,6 +45,7 @@ class LinkController extends Controller
/**
* LinkController constructor.
* @codeCoverageIgnore
*/
public function __construct()
{
@ -61,12 +64,23 @@ class LinkController extends Controller
);
}
/**
* @param TransactionJournal $journal
* @return Factory|View
*/
public function modal(TransactionJournal $journal)
{
$linkTypes = $this->repository->get();
return view('transactions.links.modal', compact('journal', 'linkTypes'));
}
/**
* Delete a link.
*
* @param TransactionJournalLink $link
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @return Factory|View
*/
public function delete(TransactionJournalLink $link)
{
@ -104,9 +118,9 @@ class LinkController extends Controller
*/
public function store(JournalLinkRequest $request, TransactionJournal $journal)
{
$linkInfo = $request->getLinkInfo();
Log::debug('We are here (store)');
$linkInfo = $request->getLinkInfo();
$other = $this->journalRepository->findNull($linkInfo['transaction_journal_id']);
if (null === $other) {
session()->flash('error', (string)trans('firefly.invalid_link_selection'));

View File

@ -73,7 +73,7 @@ class ShowController extends Controller
$type = (string)trans(sprintf('firefly.%s', strtolower($first->transactionType->type)));
$title = 1 === $splits ? $first->description : $transactionGroup->title;
$subTitle = sprintf('%s: "%s"', $type, $title);
$message = $request->get('message');
$message = $request->get('message');
/** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionGroupTransformer::class);
@ -112,9 +112,9 @@ class ShowController extends Controller
return view(
'transactions.show', compact(
'transactionGroup', 'amounts', 'first', 'type', 'subTitle', 'splits', 'groupArray',
'events', 'attachments', 'links','message'
)
'transactionGroup', 'amounts', 'first', 'type', 'subTitle', 'splits', 'groupArray',
'events', 'attachments', 'links', 'message'
)
);
}
}

View File

@ -52,12 +52,9 @@ class JournalLinkRequest extends Request
$linkType = $this->get('link_type');
$parts = explode('_', $linkType);
$return['link_type_id'] = (int)$parts[0];
$return['transaction_journal_id'] = $this->integer('link_journal_id');
$return['transaction_journal_id'] = $this->integer('opposing');
$return['notes'] = $this->string('notes');
$return['direction'] = $parts[1];
if (0 === $return['transaction_journal_id'] && ctype_digit($this->string('link_other'))) {
$return['transaction_journal_id'] = $this->integer('link_other');
}
return $return;
}
@ -81,8 +78,8 @@ class JournalLinkRequest extends Request
// fixed
return [
'link_type' => sprintf('required|in:%s', $string),
'link_journal_id' => 'belongsToUser:transaction_journals',
'link_type' => sprintf('required|in:%s', $string),
'opposing' => 'belongsToUser:transaction_journals',
];
}
}

View File

@ -174,14 +174,12 @@ class TransactionJournal extends Model
*/
public static function routeBinder(string $value): TransactionJournal
{
throw new FireflyException('Journal binder is permanently out of order.');
if (auth()->check()) {
$journalId = (int)$value;
/** @var User $user */
$user = auth()->user();
/** @var TransactionJournal $journal */
$journal = $user->transactionJournals()->where('transaction_journals.id', $journalId)
->first(['transaction_journals.*']);
$journal = $user->transactionJournals()->where('transaction_journals.id', $journalId)->first(['transaction_journals.*']);
if (null !== $journal) {
return $journal;
}

View File

@ -119,6 +119,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
$amount = $this->getFormattedAmount($entry->destination);
$foreignAmount = $this->getFormattedForeignAmount($entry->destination);
$return[$journalId][] = [
'id' => $entry->id,
'link' => $entry->outward,
'group' => $entry->destination->transaction_group_id,
'description' => $entry->destination->description,
@ -130,6 +131,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
$amount = $this->getFormattedAmount($entry->source);
$foreignAmount = $this->getFormattedForeignAmount($entry->source);
$return[$journalId][] = [
'id' => $entry->id,
'link' => $entry->inward,
'group' => $entry->source->transaction_group_id,
'description' => $entry->source->description,
@ -145,7 +147,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
/**
* Return object with all found meta field things as Carbon objects.
*
* @param int $journalId
* @param int $journalId
* @param array $fields
*
* @return NullArrayObject
@ -171,7 +173,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
/**
* Return object with all found meta field things.
*
* @param int $journalId
* @param int $journalId
* @param array $fields
*
* @return NullArrayObject
@ -247,9 +249,9 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
$return[$journalId] = $return[$journalId] ?? [];
$return[$journalId][] = [
'piggy' => $row->piggyBank->name,
'piggy' => $row->piggyBank->name,
'piggy_id' => $row->piggy_bank_id,
'amount' => app('amount')->formatAnything($currency, $row->amount),
'amount' => app('amount')->formatAnything($currency, $row->amount),
];
}
@ -300,7 +302,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
/**
* @param TransactionGroup $transactionGroup
* @param array $data
* @param array $data
*
* @return TransactionGroup
*

View File

@ -214,10 +214,6 @@ return [
'application/vnd.oasis.opendocument.image',
],
'list_length' => 10,
'export_formats' => [
'csv' => CsvExporter::class,
],
'default_export_format' => 'csv',
'default_import_format' => 'csv',
'bill_periods' => ['weekly', 'monthly', 'quarterly', 'half-year', 'yearly'],
'accountRoles' => ['defaultAsset', 'sharedAsset', 'savingAsset', 'ccAsset', 'cashWalletAsset'],
@ -381,7 +377,6 @@ return [
'recurrence' => Recurrence::class,
'rule' => Rule::class,
'ruleGroup' => RuleGroup::class,
'exportJob' => ExportJob::class,
'importJob' => ImportJob::class,
'transaction' => Transaction::class,
'transactionGroup' => TransactionGroup::class,

View File

@ -22,5 +22,60 @@
$(function () {
"use strict";
$('.link-modal').click(getLinkModal);
$('#linkJournalModal').on('shown.bs.modal', function () {
makeAutoComplete();
})
});
});
function getLinkModal(e) {
var button = $(e.currentTarget);
var journalId = parseInt(button.data('journal'));
var url = modalDialogURI.replace('%JOURNAL%', journalId);
console.log(url);
$.get(url).done(function (data) {
$('#linkJournalModal').html(data).modal('show');
}).fail(function () {
alert('Could not load the data to link journals. Sorry :(');
button.prop('disabled', true);
});
return false;
}
function makeAutoComplete() {
// input link-journal
var source = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: acURI + '?uid=' + uid,
filter: function (list) {
return $.map(list, function (item) {
return item;
});
}
},
remote: {
url: acURI + '?search=%QUERY&uid=' + uid,
wildcard: '%QUERY',
filter: function (list) {
return $.map(list, function (item) {
return item;
});
}
}
});
source.initialize();
$('.link-journal').typeahead({hint: true, highlight: true,}, {source: source, displayKey: 'name', autoSelect: false})
.on('typeahead:select', selectedJournal);
}
function selectedJournal(event, journal) {
$('#journal-selector').hide();
$('#journal-selection').show();
$('#selected-journal').html('<a href="' + groupURI.replace('%GROUP%', journal.transaction_group_id) + '">' + journal.description + '</a>').show();
$('input[name="opposing"]').val(journal.id);
}

View File

@ -1215,12 +1215,13 @@ return [
'do_not_save_connection' => '(do not save connection)',
'link_transaction' => 'Link transaction',
'link_to_other_transaction' => 'Link this transaction to another transaction',
'select_transaction_to_link' => 'Select a transaction to link this transaction to',
'select_transaction_to_link' => 'Select a transaction to link this transaction to. The links are currently unused in Firefly III (apart from being shown), but I plan to change this in the future. Use the search box to select a transaction either by title or by ID. If you want to add custom link types, check out the administration section.',
'this_transaction' => 'This transaction',
'transaction' => 'Transaction',
'comments' => 'Comments',
'to_link_not_found' => 'If the transaction you want to link to is not listed, simply enter its ID.',
'link_notes' => 'Any notes you wish to store with the link.',
'invalid_link_selection' => 'Cannot link these transactions',
'selected_transaction' => 'Selected transaction',
'journals_linked' => 'Transactions are linked.',
'journals_error_linked' => 'These transactions are already linked.',
'journals_link_to_self' => 'You cannot link a transaction to itself',

View File

@ -0,0 +1,52 @@
<form action="{{ route('transactions.link.store', [journal.id]) }}" method="post" class="form-horizontal">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<input type="hidden" name="opposing" value="">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'link_to_other_transaction'|_ }}</h4>
</div>
<div class="modal-body" id="helpBody">
<p>{{ 'select_transaction_to_link'|_ }}</p>
<div class="form-group">
<label for="link_type" class="col-sm-4 control-label">{{ 'this_transaction'|_ }}</label>
<div class="col-sm-8">
<select id="link_type" class="form-control" name="link_type">
{% for linkType in linkTypes %}
<option label="{{ journalLinkTranslation('inward', linkType.inward) }}"
value="{{ linkType.id }}_inward">{{ journalLinkTranslation('inward', linkType.inward) }}</option>
<option label="{{ journalLinkTranslation('outward', linkType.outward) }}"
value="{{ linkType.id }}_outward">{{ journalLinkTranslation('outward', linkType.outward) }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group" id="journal-selection">
<label for="selected" class="col-sm-4 control-label">{{ 'selected_transaction'|_ }}</label>
<div class="col-sm-8">
<p class="form-control-static" id="selected-journal">
</p>
</div>
</div>
<div class="form-group" id="journal-selector">
<label for="link_other" class="col-sm-4 control-label">{{ 'transaction'|_ }}</label>
<div class="col-sm-8">
<input type="text" name="link_other" autocomplete="off" id="link_other" value="" class="form-control link-journal">
</div>
</div>
<div class="form-group">
<label for="notes" class="col-sm-4 control-label">{{ 'link_notes'|_ }}</label>
<div class="col-sm-8">
<textarea id="notes" name="notes" class="form-control"></textarea>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'close'|_ }}</button>
<button type="submit" class="btn btn-primary">{{ 'submit'|_ }}</button>
</div>
</div>
</div>
</form>

View File

@ -6,18 +6,18 @@
{% block content %}
{% if message == 'created' %}
<div class="row">
<div class="col-lg-12">
<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert">
<span>&times;</span><span class="sr-only">{{ 'close'|_ }}</span>
</button>
<strong>{{ 'flash_success'|_ }}</strong>
{{ trans('firefly.stored_journal_no_descr') }}
<div class="row">
<div class="col-lg-12">
<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert">
<span>&times;</span><span class="sr-only">{{ 'close'|_ }}</span>
</button>
<strong>{{ 'flash_success'|_ }}</strong>
{{ trans('firefly.stored_journal_no_descr') }}
</div>
</div>
</div>
</div>
{% endif %}
{% if message == 'updated' %}
@ -69,335 +69,362 @@
</div>
<div class="box-footer">
<div class="btn-group btn-group-xs">
<a href="{{ route('transactions.edit', [transactionGroup.id]) }}" class="btn btn-default"><i class="fa fa-pencil"></i> {{ 'edit'|_ }}</a>
<a href="{{ route('transactions.edit', [transactionGroup.id]) }}" class="btn btn-default"><i class="fa fa-pencil"></i> {{ 'edit'|_ }}
</a>
{% if groupArray.transactions[0].type != 'withdrawal' %}
<a href="{{ route('transactions.convert.index', ['withdrawal', transactionGroup.id]) }}" class="btn btn-default"><i class="fa fa-exchange"></i> {{ 'convert_to_withdrawal'|_ }}</a>
<a href="{{ route('transactions.convert.index', ['withdrawal', transactionGroup.id]) }}" class="btn btn-default"><i
class="fa fa-exchange"></i> {{ 'convert_to_withdrawal'|_ }}</a>
{% endif %}
{% if groupArray.transactions[0].type != 'deposit' %}
<a href="{{ route('transactions.convert.index', ['deposit', transactionGroup.id]) }}" class="btn btn-default"><i class="fa fa-exchange"></i> {{ 'convert_to_deposit'|_ }}</a>
<a href="{{ route('transactions.convert.index', ['deposit', transactionGroup.id]) }}" class="btn btn-default"><i
class="fa fa-exchange"></i> {{ 'convert_to_deposit'|_ }}</a>
{% endif %}
{% if groupArray.transactions[0].type != 'transfer' %}
<a href="{{ route('transactions.convert.index', ['transfer', transactionGroup.id]) }}" class="btn btn-default"><i class="fa fa-exchange"></i> {{ 'convert_to_transfer'|_ }}</a>
<a href="{{ route('transactions.convert.index', ['transfer', transactionGroup.id]) }}" class="btn btn-default"><i
class="fa fa-exchange"></i> {{ 'convert_to_transfer'|_ }}</a>
{% endif %}
{% if groupArray.transactions[0].type != 'opening balance' and groupArray.transactions[0].type != 'reconciliation' %}
CLONE
{#<a href="{{ route('transactions.clone', [transactionGroup.id]) }}" class="btn btn-default"><i class="fa fa-copy"></i> {{ 'clone'|_ }}</a>#}
{% endif %}
{% endif %}
{#
<a href="{{ route('transactions.delete', [transactionGroup.id]) }}" class="btn btn-danger"><i class="fa fa-trash"></i> {{ 'delete'|_ }}</a>
#}
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">{{ 'transaction_journal_meta'|_ }}</h3>
</div>
<div class="box-body no-padding">
<table class="table table-hover">
<tbody>
{% if type != 'Withdrawal' or splits == 1 %}
<tr>
<td>
{{ 'source_accounts'|_ }}
</td>
<td>
{% for journal in groupArray.transactions %}
<a href="{{ route('accounts.show',journal.source_id) }}"
title="{{ journal.source_iban|default(journal.source_name) }}">
{{ journal.source_name }}
</a>
{% if loop.index0 != groupArray.transactions|length -1 %}, {% endif %}
{% endfor %}
</td>
</tr>
{% endif %}
{% if type != 'Deposit' or splits == 1 %}
<tr>
<td>
{{ 'destination_accounts'|_ }}
</td>
<td>
{% for journal in groupArray.transactions %}
<a href="{{ route('accounts.show',journal.source_id) }}"
title="{{ journal.destination_iban|default(journal.destination_name) }}">
{{ journal.destination_name }}
</a>
{% if loop.index0 != groupArray.transactions|length -1 %}, {% endif %}
{% endfor %}
</td>
</tr>
{% endif %}
<tr>
<td style="width:30%;">{{ 'total_amount'|_ }}</td>
<td>
{% for amount in amounts %}
{% if type == 'Withdrawal' or type == 'Deposit' %}
{{ formatAmountBySymbol(amount.amount*-1,amount.symbol, amount.decimal_places) }},
{% elseif type == 'Transfer' %}
<span class="text-info">
{{ formatAmountBySymbol(amount.amount, amount.symbol, amount.decimal_places, false) }},
</span>
{% endif %}
{% endfor %}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="row">
</div>
{% if splits > 1 %}
<div class="row">
<div class="col-lg-12">
<h3>{{ 'splits'|_ }}</h3>
</div>
</div>
{% endif %}
{% set boxSize=12 %}
{% if(splits == 2) %}
{% set boxSize=6 %}
{% endif %}
{% if (splits > 2) %}
{% set boxSize = 4 %}
{% endif %}
<div class="row">
{% for index,journal in groupArray.transactions %}
<div class="col-lg-{{ boxSize }}">
<div class="box">
<div class="col-lg-6">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">
{{ journal.description }}
{% if journal.reconciled %}
<i class="fa fa-check"></i>
{% endif %}
{% if splits > 1 %}
<small>
{{ index+1 }} / {{ splits }}
</small>
{% endif %}
</h3>
<h3 class="box-title">{{ 'transaction_journal_meta'|_ }}</h3>
</div>
<div class="box-body no-padding">
<table class="table">
<tr>
<td colspan="2">
<a href="{{ route('accounts.show', journal.source_id) }}"
title="{{ journal.source_iban|default(journal.source_name) }}">{{ journal.source_name }}</a> &rarr;
{% if type == 'Withdrawal' or type == 'Deposit' %}
{{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_decimal_places) }}
{% elseif type == 'Transfer' %}
<span class="text-info">
{{ formatAmountBySymbol(journal.amount, journal.currency_symbol, journal.currency_decimal_places, false) }}
</span>
{% endif %}
<!-- do foreign amount -->
{% if null != journal.foreign_amount %}
{% if type == 'Withdrawal' or type == 'Deposit' %}
({{ formatAmountBySymbol(journal.foreign_amount*-1, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places) }})
{% elseif type == 'Transfer' %}
<span class="text-info">
({{ formatAmountBySymbol(journal.foreign_amount, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }})
</span>
{% endif %}
{% endif %}
&rarr;
<a href="{{ route('accounts.show', journal.destination_id) }}"
title="{{ journal.destination_iban|default(journal.destination_name) }}">{{ journal.destination_name }}</a>
</td>
</tr>
{% if null != journal.category_id %}
<table class="table table-hover">
<tbody>
{% if type != 'Withdrawal' or splits == 1 %}
<tr>
<td style="width:30%;">{{ 'category'|_ }}</td>
<td><a href="{{ route('categories.show', [journal.category_id]) }}">{{ journal.category_name }}</a></td>
</tr>
{% endif %}
{% if null != journal.budget_id and type == 'Withdrawal' %}
<tr>
<td>{{ 'budget'|_ }}</td>
<td><a href="{{ route('budgets.show', [journal.budget_id]) }}">{{ journal.budget_name }}</a></td>
</tr>
{% endif %}
{% if null != journal.bill_id and type == 'Withdrawal' %}
<tr>
<td>{{ 'bill'|_ }}</td>
<td><a href="{{ route('bills.show', [journal.bill_id]) }}">{{ journal.bill_name }}</a></td>
</tr>
{% endif %}
<!-- other fields -->
{% for dateField in ['interest_date','book_date','process_date','due_date','payment_date','invoice_date'] %}
{% if journalHasMeta(journal.transaction_journal_id, dateField) %}
<tr>
<td>{{ trans('list.'~dateField) }}</td>
<td>{{ journalGetMetaDate(journal.transaction_journal_id, dateField).formatLocalized(monthAndDayFormat) }}</td>
</tr>
{% endif %}
{% endfor %}
{% for metaField in ['external_id','bunq_payment_id','internal_reference','sepa_batch_id','sepa_ct_id','sepa_ct_op','sepa_db','sepa_country','sepa_cc','sepa_ep','sepa_ci'] %}
{% if journalHasMeta(journal.transaction_journal_id, metaField) %}
<tr>
<td>{{ trans('list.'~metaField) }}</td>
<td>{{ journalGetMetaField(journal.transaction_journal_id, metaField) }}</td>
</tr>
{% endif %}
{% endfor %}
{% if null != journal.notes and '' != journal.notes %}
<tr>
<td>{{ trans('list.notes') }}</td>
<td class="markdown">{{ journal.notes|markdown }}</td>
</tr>
{% endif %}
{% if journal.tags|length > 0 %}
<tr>
<td>{{ 'tags'|_ }}</td>
<td>
{% for tag in journal.tags %}
<h4 style="display: inline;"><a class="label label-success" href="{{ route('tags.show', tag) }}">
<i class="fa fa-fw fa-tag"></i>
{{ tag }}</a>
</h4>
{{ 'source_accounts'|_ }}
</td>
<td>
{% for journal in groupArray.transactions %}
<a href="{{ route('accounts.show',journal.source_id) }}"
title="{{ journal.source_iban|default(journal.source_name) }}">
{{ journal.source_name }}
</a>
{% if loop.index0 != groupArray.transactions|length -1 %}, {% endif %}
{% endfor %}
</td>
</tr>
{% endif %}
{% if type != 'Deposit' or splits == 1 %}
<tr>
<td>
{{ 'destination_accounts'|_ }}
</td>
<td>
{% for journal in groupArray.transactions %}
<a href="{{ route('accounts.show',journal.source_id) }}"
title="{{ journal.destination_iban|default(journal.destination_name) }}">
{{ journal.destination_name }}
</a>
{% if loop.index0 != groupArray.transactions|length -1 %}, {% endif %}
{% endfor %}
</td>
</tr>
{% endif %}
<tr>
<td style="width:30%;">{{ 'total_amount'|_ }}</td>
<td>
{% for amount in amounts %}
{% if type == 'Withdrawal' or type == 'Deposit' %}
{{ formatAmountBySymbol(amount.amount*-1,amount.symbol, amount.decimal_places) }},
{% elseif type == 'Transfer' %}
<span class="text-info">
{{ formatAmountBySymbol(amount.amount, amount.symbol, amount.decimal_places, false) }},
</span>
{% endif %}
{% endfor %}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="row">
<!-- Transaction links -->
{% if links[journal.transaction_journal_id]|length > 0 %}
</div>
{% if splits > 1 %}
<div class="row">
<div class="col-lg-12">
<h3>{{ 'splits'|_ }}</h3>
</div>
</div>
{% endif %}
{% set boxSize=6 %}
{% if(splits == 2) %}
{% set boxSize=6 %}
{% endif %}
{% if (splits > 2) %}
{% set boxSize = 4 %}
{% endif %}
<div class="row">
{% for index,journal in groupArray.transactions %}
<div class="col-lg-{{ boxSize }}">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">
{{ 'journal_links'|_ }}
{{ journal.description }}
{% if journal.reconciled %}
<i class="fa fa-check"></i>
{% endif %}
{% if splits > 1 %}
<small>
{{ index+1 }} / {{ splits }}
</small>
{% endif %}
</h3>
</div>
<div class="box-body no-padding">
<table class="table">
{% for link in links[journal.transaction_journal_id] %}
<tr>
<td style="width:30%;">
<div class="btn-group btn-group-xs">
<a href="#" class="btn btn-default"><i class="fa fa-pencil"></i></a>
<a href="#" class="btn btn-danger"><i class="fa fa-trash"></i></a>
</div>
</td>
<td>{{ link.link }} "<a href="{{ route('transactions.show', link.group) }}"
title="{{ link.description }}">{{ link.description }}</a>"
<tr>
<td colspan="2">
<a href="{{ route('accounts.show', journal.source_id) }}"
title="{{ journal.source_iban|default(journal.source_name) }}">{{ journal.source_name }}</a> &rarr;
{% if type == 'Withdrawal' or type == 'Deposit' %}
{{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_decimal_places) }}
{% elseif type == 'Transfer' %}
<span class="text-info">
{{ formatAmountBySymbol(journal.amount, journal.currency_symbol, journal.currency_decimal_places, false) }}
</span>
{% endif %}
({{ link.amount|raw }})
{% if '' != link.foreign_amount %}
({{ link.foreign_amount|raw }})
<!-- do foreign amount -->
{% if null != journal.foreign_amount %}
{% if type == 'Withdrawal' or type == 'Deposit' %}
({{ formatAmountBySymbol(journal.foreign_amount*-1, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places) }})
{% elseif type == 'Transfer' %}
<span class="text-info">
({{ formatAmountBySymbol(journal.foreign_amount, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }})
</span>
{% endif %}
{% endif %}
&rarr;
<a href="{{ route('accounts.show', journal.destination_id) }}"
title="{{ journal.destination_iban|default(journal.destination_name) }}">{{ journal.destination_name }}</a>
</td>
</tr>
{% if null != journal.category_id %}
<tr>
<td style="width:30%;">{{ 'category'|_ }}</td>
<td><a href="{{ route('categories.show', [journal.category_id]) }}">{{ journal.category_name }}</a></td>
</tr>
{% endif %}
{% if null != journal.budget_id and type == 'Withdrawal' %}
<tr>
<td>{{ 'budget'|_ }}</td>
<td><a href="{{ route('budgets.show', [journal.budget_id]) }}">{{ journal.budget_name }}</a></td>
</tr>
{% endif %}
{% if null != journal.bill_id and type == 'Withdrawal' %}
<tr>
<td>{{ 'bill'|_ }}</td>
<td><a href="{{ route('bills.show', [journal.bill_id]) }}">{{ journal.bill_name }}</a></td>
</tr>
{% endif %}
<!-- other fields -->
{% for dateField in ['interest_date','book_date','process_date','due_date','payment_date','invoice_date'] %}
{% if journalHasMeta(journal.transaction_journal_id, dateField) %}
<tr>
<td>{{ trans('list.'~dateField) }}</td>
<td>{{ journalGetMetaDate(journal.transaction_journal_id, dateField).formatLocalized(monthAndDayFormat) }}</td>
</tr>
{% endif %}
{% endfor %}
{% for metaField in ['external_id','bunq_payment_id','internal_reference','sepa_batch_id','sepa_ct_id','sepa_ct_op','sepa_db','sepa_country','sepa_cc','sepa_ep','sepa_ci'] %}
{% if journalHasMeta(journal.transaction_journal_id, metaField) %}
<tr>
<td>{{ trans('list.'~metaField) }}</td>
<td>{{ journalGetMetaField(journal.transaction_journal_id, metaField) }}</td>
</tr>
{% endif %}
{% endfor %}
{% if null != journal.notes and '' != journal.notes %}
<tr>
<td>{{ trans('list.notes') }}</td>
<td class="markdown">{{ journal.notes|markdown }}</td>
</tr>
{% endif %}
{% if journal.tags|length > 0 %}
<tr>
<td>{{ 'tags'|_ }}</td>
<td>
{% for tag in journal.tags %}
<h4 style="display: inline;"><a class="label label-success" href="{{ route('tags.show', tag) }}">
<i class="fa fa-fw fa-tag"></i>
{{ tag }}</a>
</h4>
{% endfor %}
</td>
</tr>
{% endfor %}
{% endif %}
</table>
</div>
</div>
{% endif %}
<!-- Attachments -->
{% if attachments[journal.transaction_journal_id]|length > 0 %}
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'attachments'|_ }}</h3>
<div class="box-footer">
<div class="btn-group btn-group-xs">
<a href="#" class="btn btn-default link-modal" data-journal="{{ journal.transaction_journal_id }}">
<i class="fa fa-fw fa-link"></i>
{{ 'link_transaction'|_ }}
</a>
</div>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
{% for attachment in attachments[journal.transaction_journal_id] %}
<tr>
<td style="width:30%;">
<div class="btn-group btn-group-xs">
<a href="{{ route('attachments.edit', attachment.id) }}" class="btn btn-default"><i
class="fa fa-pencil"></i></a>
<a href="{{ route('attachments.delete', attachment.id) }}" class="btn btn-danger"><i
class="fa fa-trash"></i></a>
</div>
<!-- Transaction links -->
{% if links[journal.transaction_journal_id]|length > 0 %}
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">
{{ 'journal_links'|_ }}
</h3>
</div>
<div class="box-body no-padding">
<table class="table">
{% for link in links[journal.transaction_journal_id] %}
<tr>
<td style="width:120px;">
<div class="btn-group btn-group-xs">
<a href="{{ route('transactions.link.switch', [link.id]) }}" class="btn btn-default"><i class="fa fa-fw fa-arrows-h"></i></a>
<a href="{{ route('transactions.link.delete', [link.id]) }}" class="btn btn-danger"><i class="fa fa-trash"></i></a>
</div>
</td>
<td>{{ link.link }} "<a href="{{ route('transactions.show', link.group) }}"
title="{{ link.description }}">{{ link.description }}</a>"
({{ link.amount|raw }})
{% if '' != link.foreign_amount %}
({{ link.foreign_amount|raw }})
{% endif %}
</td>
</tr>
{% endfor %}
</table>
</div>
</div>
{% endif %}
<!-- Attachments -->
{% if attachments[journal.transaction_journal_id]|length > 0 %}
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'attachments'|_ }}</h3>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
{% for attachment in attachments[journal.transaction_journal_id] %}
<tr>
<td style="width:120px;">
<div class="btn-group btn-group-xs">
<a href="{{ route('attachments.edit', attachment.id) }}" class="btn btn-default"><i
class="fa fa-pencil"></i></a>
<a href="{{ route('attachments.delete', attachment.id) }}" class="btn btn-danger"><i
class="fa fa-trash"></i></a>
{% if attachment.file_exists %}
<a href="{{ route('attachments.download', attachment.id) }}" class="btn btn-default"><i
class="fa fa-download"></i></a>
{% endif %}
{% if not attachment.file_exists %}
<a href="#" class="btn btn-danger"><i class="fa fa-exclamation-triangle"></i></a>
{% endif %}
</div>
</td>
<td>
{% if attachment.file_exists %}
<a href="{{ route('attachments.download', attachment.id) }}" class="btn btn-default"><i
class="fa fa-download"></i></a>
<i class="fa {{ attachment.mime|mimeIcon }}"></i>
<a href="{{ route('attachments.view', attachment.id) }}" title="{{ attachment.filename }}">
{% if attachment.title %}
{{ attachment.title }}
{% else %}
{{ attachment.filename }}
{% endif %}
</a>
({{ attachment.size|filesize }})
{% if null != attachment.notes and '' != attachment.notes %}
{{ attachment.notes|markdown }}
{% endif %}
{% endif %}
{% if not attachment.file_exists %}
<a href="#" class="btn btn-danger"><i class="fa fa-exclamation-triangle"></i></a>
{% endif %}
</div>
</td>
<td>
{% if attachment.file_exists %}
<i class="fa {{ attachment.mime|mimeIcon }}"></i>
<a href="{{ route('attachments.view', attachment.id) }}" title="{{ attachment.filename }}">
<i class="fa fa-fw fa-exclamation-triangle"></i>
{% if attachment.title %}
{{ attachment.title }}
{% else %}
{{ attachment.filename }}
{% endif %}
</a>
({{ attachment.size|filesize }})
{% if null != attachment.notes and '' != attachment.notes %}
{{ attachment.notes|markdown }}
<br>
<span class="text-danger">{{ 'attachment_not_found'|_ }}</span>
{% endif %}
{% endif %}
{% if not attachment.file_exists %}
<i class="fa fa-fw fa-exclamation-triangle"></i>
{% if attachment.title %}
{{ attachment.title }}
{% else %}
{{ attachment.filename }}
{% endif %}
<br>
<span class="text-danger">{{ 'attachment_not_found'|_ }}</span>
{% endif %}
</td>
</tr>
{% endfor %}
</table>
</td>
</tr>
{% endfor %}
</table>
</div>
</div>
</div>
{% endif %}
{% endif %}
<!-- Piggy bank events -->
{% if events[journal.transaction_journal_id]|length > 0 %}
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'piggy_events'|_ }}</h3>
<!-- Piggy bank events -->
{% if events[journal.transaction_journal_id]|length > 0 %}
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'piggy_events'|_ }}</h3>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
{% for event in events[journal.transaction_journal_id] %}
<tr>
<td style="width:30%;">{{ event.amount|raw }}</td>
<td>
<a href="{{ route('piggy-banks.show', [event.piggy_id]) }}">{{ event.piggy }}</a></td>
</tr>
{% endfor %}
</table>
</div>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
{% for event in events[journal.transaction_journal_id] %}
<tr>
<td style="width:30%;">{{ event.amount|raw }}</td>
<td>
<a href="{{ route('piggy-banks.show', [event.piggy_id]) }}">{{ event.piggy }}</a></td>
{% endif %}
</div>
{% endfor %}
</div>
{# modal for linking journals. Will be filled by AJAX #}
<div class="modal fade" tabindex="-1" role="dialog" id="linkJournalModal">
</div>
</tr>
{% endfor %}
</table>
</div>
</div>
{% endif %}
</div>
{% endfor %}
</div>
{% endblock %}
{% block scripts %}
<script type="text/javascript">
var modalDialogURI = '{{ route('transactions.link.modal', ['%JOURNAL%']) }}';
var acURI = '{{ route('json.autocomplete.all-journals-with-id') }}';
var groupURI = '{{ route('transactions.show',['%GROUP']) }}';
</script>
<script type="text/javascript" src="v1/js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_VERSION }}"></script>
<script type="text/javascript" src="v1/js/ff/transactions/show.js?v={{ FF_VERSION }}"></script>
{% endblock %}
{#

View File

@ -540,6 +540,7 @@ Route::group(
Route::get('piggy-banks', ['uses' => 'Json\AutoCompleteController@piggyBanks', 'as' => 'autocomplete.piggy-banks']);
Route::get('tags', ['uses' => 'Json\AutoCompleteController@tags', 'as' => 'autocomplete.tags']);
Route::get('transaction-journals/all', ['uses' => 'Json\AutoCompleteController@allJournals', 'as' => 'autocomplete.all-journals']);
Route::get('transaction-journals/with-id', ['uses' => 'Json\AutoCompleteController@allJournalsWithID', 'as' => 'autocomplete.all-journals-with-id']);
Route::get('currency-names', ['uses' => 'Json\AutoCompleteController@currencyNames', 'as' => 'autocomplete.currency-names']);
@ -884,8 +885,8 @@ Route::group(
// clone group:
Route::get('clone/{transactionGroup}', ['uses' => 'Transaction\CloneController@clone', 'as' => 'clone']);
Route::get('debug/{tj}', ['uses' => 'Transaction\SingleController@debugShow', 'as' => 'debug']);
Route::get('debug/{tj}', ['uses' => 'Transaction\SingleController@debugShow', 'as' => 'debug']);
//Route::get('debug/{tj}', ['uses' => 'Transaction\SingleController@debugShow', 'as' => 'debug']);
//Route::get('debug/{tj}', ['uses' => 'Transaction\SingleController@debugShow', 'as' => 'debug']);
Route::post('reorder', ['uses' => 'TransactionController@reorder', 'as' => 'reorder']);
Route::post('reconcile', ['uses' => 'TransactionController@reconcile', 'as' => 'reconcile']);
@ -942,16 +943,16 @@ Route::group(
/**
* Transaction Split Controller
*/
Route::group(
['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers\Transaction', 'prefix' => 'transactions/split',
'as' => 'transactions.split.'], function () {
// TODO improve these routes
Route::get('edit/{tj}', ['uses' => 'SplitController@edit', 'as' => 'edit']);
Route::post('update/{tj}', ['uses' => 'SplitController@update', 'as' => 'update']);
// TODO end of todo.
}
);
//Route::group(
// ['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers\Transaction', 'prefix' => 'transactions/split',
// 'as' => 'transactions.split.'], function () {
// // TODO improve these routes
// Route::get('edit/{tj}', ['uses' => 'SplitController@edit', 'as' => 'edit']);
// Route::post('update/{tj}', ['uses' => 'SplitController@update', 'as' => 'update']);
// // TODO end of todo.
//
//}
//);
/**
* Transaction Convert Controller
@ -970,6 +971,9 @@ Route::group(
Route::group(
['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers\Transaction', 'prefix' => 'transactions/link', 'as' => 'transactions.link.'],
function () {
Route::get('modal/{tj}', ['uses' => 'LinkController@modal', 'as' => 'modal']);
// TODO improve this route:
Route::post('store/{tj}', ['uses' => 'LinkController@store', 'as' => 'store']);
Route::get('delete/{journalLink}', ['uses' => 'LinkController@delete', 'as' => 'delete']);

View File

@ -28,8 +28,10 @@ use FireflyIII\Models\TransactionJournalLink;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Support\Collection;
use Log;
use Mockery;
use Preferences;
use Tests\TestCase;
/**
@ -48,23 +50,39 @@ class LinkControllerTest extends TestCase
/**
* @covers \FireflyIII\Http\Controllers\Transaction\LinkController
* @covers \FireflyIII\Http\Controllers\Transaction\LinkController
*/
public function testDelete(): void
{ $this->markTestIncomplete('Needs to be rewritten for v4.8.0');
{
$this->mock(LinkTypeRepositoryInterface::class);
$link = $this->getRandomLink();
$userRepos = $this->mock(UserRepositoryInterface::class);
$this->mockDefaultSession();
return;
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$linkRepos = $this->mock(LinkTypeRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true);
$this->be($this->user());
$response = $this->get(route('transactions.link.delete', [$link->id]));
$response->assertStatus(200);
}
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
/**
* @covers \FireflyIII\Http\Controllers\Transaction\LinkController
*/
public function testModal(): void
{
$journal = $this->getRandomWithdrawal();
$linkRepos = $this->mock(LinkTypeRepositoryInterface::class);
$this->mockDefaultSession();
$linkRepos->shouldReceive('get')->atLeast()->once()->andReturn(new Collection);
$this->be($this->user());
$response = $this->get(route('transactions.link.delete', [1]));
$response = $this->get(route('transactions.link.modal', [$journal->id]));
$response->assertStatus(200);
}
@ -72,22 +90,18 @@ class LinkControllerTest extends TestCase
* @covers \FireflyIII\Http\Controllers\Transaction\LinkController
*/
public function testDestroy(): void
{ $this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
$journalRepos = $this->mock(JournalRepositoryInterface::class);
{
$link = $this->getRandomLink();
$repository = $this->mock(LinkTypeRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
Preferences::shouldReceive('mark')->once();
$this->mockDefaultSession();
$repository->shouldReceive('destroyLink');
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('destroyLink')->atLeast()->once();
$this->be($this->user());
$this->session(['journal_links.delete.uri' => 'http://localhost/']);
$response = $this->post(route('transactions.link.destroy', [1]));
$response = $this->post(route('transactions.link.destroy', [$link->id]));
$response->assertStatus(302);
$response->assertSessionHas('success');
@ -98,29 +112,28 @@ class LinkControllerTest extends TestCase
* @covers \FireflyIII\Http\Requests\JournalLinkRequest
*/
public function testStore(): void
{ $this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
{
$withdrawal = $this->getRandomWithdrawal();
$deposit = $this->getRandomDeposit();
$repository = $this->mock(LinkTypeRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$journalRepos = $this->mockDefaultSession();
$data = [
'link_other' => 8,
'link_type' => '1_inward',
'opposing' => $deposit->id,
'link_type' => '1_inward',
];
$journalRepos->shouldReceive('firstNull')->andReturn(new TransactionJournal);
$journalRepos->shouldReceive('findNull')->andReturn(new TransactionJournal);
$repository->shouldReceive('findLink')->andReturn(false);
$repository->shouldReceive('storeLink')->andReturn(new TransactionJournalLink);
//$journalRepos->shouldReceive('firstNull')->andReturn(new TransactionJournal);
$journalRepos->shouldReceive('findNull')->andReturn($deposit)->atLeast()->once();
$repository->shouldReceive('findLink')->andReturn(false)->atLeast()->once();
$repository->shouldReceive('storeLink')->andReturn(new TransactionJournalLink)->atLeast()->once();
$this->be($this->user());
$response = $this->post(route('transactions.link.store', [1]), $data);
$response = $this->post(route('transactions.link.store', [$withdrawal->id]), $data);
$response->assertStatus(302);
$response->assertSessionHas('success');
$response->assertRedirect(route('transactions.show', [1]));
$response->assertRedirect(route('transactions.show', [$withdrawal->id]));
}
/**
@ -128,28 +141,25 @@ class LinkControllerTest extends TestCase
* @covers \FireflyIII\Http\Requests\JournalLinkRequest
*/
public function testStoreAlreadyLinked(): void
{ $this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
{
$repository = $this->mock(LinkTypeRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$journalRepos = $this->mockDefaultSession();
$link = $this->getRandomLink();
$data = [
'link_other' => 8,
'link_type' => '1_inward',
'opposing' => $link->source_id,
'link_type' => '1_inward',
];
$journalRepos->shouldReceive('firstNull')->andReturn(new TransactionJournal);
$journalRepos->shouldReceive('findNull')->andReturn(new TransactionJournal);
$repository->shouldReceive('findLink')->andReturn(true);
$journalRepos->shouldReceive('findNull')->andReturn(new TransactionJournal)->atLeast()->once();
$repository->shouldReceive('findLink')->andReturn(true)->atLeast()->once();
$this->be($this->user());
$response = $this->post(route('transactions.link.store', [1]), $data);
$response = $this->post(route('transactions.link.store', [$link->destination_id]), $data);
$response->assertStatus(302);
$response->assertSessionHas('error');
$response->assertRedirect(route('transactions.show', [1]));
$response->assertRedirect(route('transactions.show', [$link->destination_id]));
}
/**
@ -157,26 +167,23 @@ class LinkControllerTest extends TestCase
* @covers \FireflyIII\Http\Requests\JournalLinkRequest
*/
public function testStoreInvalid(): void
{ $this->markTestIncomplete('Needs to be rewritten for v4.8.0');
{
$this->mock(LinkTypeRepositoryInterface::class);
$journalRepos = $this->mockDefaultSession();
$withdrawal = $this->getRandomWithdrawal();
return;
$data = [
'link_other' => 0,
'link_type' => '1_inward',
'opposing' => 0,
'link_type' => '1_inward',
];
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$repository = $this->mock(LinkTypeRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->andReturn(null);
$journalRepos->shouldReceive('findNull')->andReturn(null);
$journalRepos->shouldReceive('findNull')->andReturn(null)->atLeast()->once();
$this->be($this->user());
$response = $this->post(route('transactions.link.store', [1]), $data);
$response = $this->post(route('transactions.link.store', [$withdrawal->id]), $data);
$response->assertStatus(302);
$response->assertSessionHas('error');
$response->assertRedirect(route('transactions.show', [1]));
$response->assertRedirect(route('transactions.show', [$withdrawal->id]));
}
/**
@ -184,39 +191,33 @@ class LinkControllerTest extends TestCase
* @covers \FireflyIII\Http\Requests\JournalLinkRequest
*/
public function testStoreSame(): void
{ $this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
{
$withdrawal = $this->getRandomWithdrawal();
$repository = $this->mock(LinkTypeRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
$journalRepos = $this->mockDefaultSession();
$data = [
'link_other' => 8,
'link_other' => $withdrawal->id,
'link_type' => '1_inward',
];
$journal = $this->user()->transactionJournals()->first();
$journalRepos->shouldReceive('firstNull')->andReturn($journal);
$journalRepos->shouldReceive('findNull')->andReturn($journal);
$repository->shouldReceive('findLink')->andReturn(false);
$repository->shouldReceive('storeLink')->andReturn(new TransactionJournalLink);
$journalRepos->shouldReceive('findNull')->andReturn($withdrawal)->atLeast()->once();
$repository->shouldReceive('findLink')->andReturn(false)->atLeast()->once();
$this->be($this->user());
$response = $this->post(route('transactions.link.store', [$journal->id]), $data);
$response = $this->post(route('transactions.link.store', [$withdrawal->id]), $data);
$response->assertStatus(302);
$response->assertSessionHas('error');
$response->assertRedirect(route('transactions.show', [1]));
$response->assertRedirect(route('transactions.show', [$withdrawal->id]));
}
/**
* @covers \FireflyIII\Http\Controllers\Transaction\LinkController
*/
public function testSwitchLink(): void
{ $this->markTestIncomplete('Needs to be rewritten for v4.8.0');
return;
{
$link = $this->getRandomLink();
$withdrawal = $this->getRandomWithdrawal();
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$repository = $this->mock(LinkTypeRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
@ -224,7 +225,7 @@ class LinkControllerTest extends TestCase
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('switchLink')->andReturn(false);
$this->be($this->user());
$response = $this->get(route('transactions.link.switch', [1]));
$response = $this->get(route('transactions.link.switch', [$link->id]));
$response->assertStatus(302);

View File

@ -40,6 +40,7 @@ use FireflyIII\Models\Preference;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionJournalLink;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Transformers\TransactionTransformer;
@ -58,6 +59,14 @@ use RuntimeException;
abstract class TestCase extends BaseTestCase
{
/**
* @return TransactionJournalLink
*/
public function getRandomLink(): TransactionJournalLink
{
return TransactionJournalLink::inRandomOrder()->first();
}
/**
* @return Budget
*/