Implemented new link thing.

This commit is contained in:
James Cole
2019-07-20 06:47:34 +02:00
parent 63832b31f8
commit 6d34cfb940
15 changed files with 581 additions and 390 deletions

View File

@@ -149,7 +149,42 @@ class AutoCompleteController extends Controller
return response()->json($array); 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() public function __construct()
{ {
@@ -48,7 +49,7 @@ class EditController extends Controller
// some useful repositories: // some useful repositories:
$this->middleware( $this->middleware(
function ($request, $next) { static function ($request, $next) {
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');

View File

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

View File

@@ -113,7 +113,7 @@ class ShowController extends Controller
return view( return view(
'transactions.show', compact( 'transactions.show', compact(
'transactionGroup', 'amounts', 'first', 'type', 'subTitle', 'splits', 'groupArray', 'transactionGroup', 'amounts', 'first', 'type', 'subTitle', 'splits', 'groupArray',
'events', 'attachments', 'links','message' 'events', 'attachments', 'links', 'message'
) )
); );
} }

View File

@@ -52,12 +52,9 @@ class JournalLinkRequest extends Request
$linkType = $this->get('link_type'); $linkType = $this->get('link_type');
$parts = explode('_', $linkType); $parts = explode('_', $linkType);
$return['link_type_id'] = (int)$parts[0]; $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['notes'] = $this->string('notes');
$return['direction'] = $parts[1]; $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; return $return;
} }
@@ -82,7 +79,7 @@ class JournalLinkRequest extends Request
// fixed // fixed
return [ return [
'link_type' => sprintf('required|in:%s', $string), 'link_type' => sprintf('required|in:%s', $string),
'link_journal_id' => 'belongsToUser:transaction_journals', 'opposing' => 'belongsToUser:transaction_journals',
]; ];
} }
} }

View File

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

View File

@@ -119,6 +119,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
$amount = $this->getFormattedAmount($entry->destination); $amount = $this->getFormattedAmount($entry->destination);
$foreignAmount = $this->getFormattedForeignAmount($entry->destination); $foreignAmount = $this->getFormattedForeignAmount($entry->destination);
$return[$journalId][] = [ $return[$journalId][] = [
'id' => $entry->id,
'link' => $entry->outward, 'link' => $entry->outward,
'group' => $entry->destination->transaction_group_id, 'group' => $entry->destination->transaction_group_id,
'description' => $entry->destination->description, 'description' => $entry->destination->description,
@@ -130,6 +131,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
$amount = $this->getFormattedAmount($entry->source); $amount = $this->getFormattedAmount($entry->source);
$foreignAmount = $this->getFormattedForeignAmount($entry->source); $foreignAmount = $this->getFormattedForeignAmount($entry->source);
$return[$journalId][] = [ $return[$journalId][] = [
'id' => $entry->id,
'link' => $entry->inward, 'link' => $entry->inward,
'group' => $entry->source->transaction_group_id, 'group' => $entry->source->transaction_group_id,
'description' => $entry->source->description, 'description' => $entry->source->description,

View File

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

View File

@@ -22,5 +22,60 @@
$(function () { $(function () {
"use strict"; "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)', 'do_not_save_connection' => '(do not save connection)',
'link_transaction' => 'Link transaction', 'link_transaction' => 'Link transaction',
'link_to_other_transaction' => 'Link this transaction to another 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', 'this_transaction' => 'This transaction',
'transaction' => 'Transaction', 'transaction' => 'Transaction',
'comments' => 'Comments', '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', 'invalid_link_selection' => 'Cannot link these transactions',
'selected_transaction' => 'Selected transaction',
'journals_linked' => 'Transactions are linked.', 'journals_linked' => 'Transactions are linked.',
'journals_error_linked' => 'These transactions are already linked.', 'journals_error_linked' => 'These transactions are already linked.',
'journals_link_to_self' => 'You cannot link a transaction to itself', '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

@@ -69,18 +69,22 @@
</div> </div>
<div class="box-footer"> <div class="box-footer">
<div class="btn-group btn-group-xs"> <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' %} {% 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 %} {% endif %}
{% if groupArray.transactions[0].type != 'deposit' %} {% 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 %} {% endif %}
{% if groupArray.transactions[0].type != 'transfer' %} {% 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 %} {% endif %}
@@ -156,25 +160,25 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
</div> </div>
{% if splits > 1 %} {% if splits > 1 %}
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-lg-12">
<h3>{{ 'splits'|_ }}</h3> <h3>{{ 'splits'|_ }}</h3>
</div> </div>
</div> </div>
{% endif %} {% endif %}
{% set boxSize=12 %}
{% if(splits == 2) %}
{% set boxSize=6 %} {% set boxSize=6 %}
{% endif %} {% if(splits == 2) %}
{% if (splits > 2) %} {% set boxSize=6 %}
{% endif %}
{% if (splits > 2) %}
{% set boxSize = 4 %} {% set boxSize = 4 %}
{% endif %} {% endif %}
<div class="row"> <div class="row">
{% for index,journal in groupArray.transactions %} {% for index,journal in groupArray.transactions %}
<div class="col-lg-{{ boxSize }}"> <div class="col-lg-{{ boxSize }}">
<div class="box"> <div class="box">
@@ -278,6 +282,14 @@
{% endif %} {% endif %}
</table> </table>
</div> </div>
<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> </div>
<!-- Transaction links --> <!-- Transaction links -->
@@ -292,10 +304,10 @@
<table class="table"> <table class="table">
{% for link in links[journal.transaction_journal_id] %} {% for link in links[journal.transaction_journal_id] %}
<tr> <tr>
<td style="width:30%;"> <td style="width:120px;">
<div class="btn-group btn-group-xs"> <div class="btn-group btn-group-xs">
<a href="#" class="btn btn-default"><i class="fa fa-pencil"></i></a> <a href="{{ route('transactions.link.switch', [link.id]) }}" class="btn btn-default"><i class="fa fa-fw fa-arrows-h"></i></a>
<a href="#" class="btn btn-danger"><i class="fa fa-trash"></i></a> <a href="{{ route('transactions.link.delete', [link.id]) }}" class="btn btn-danger"><i class="fa fa-trash"></i></a>
</div> </div>
</td> </td>
<td>{{ link.link }} "<a href="{{ route('transactions.show', link.group) }}" <td>{{ link.link }} "<a href="{{ route('transactions.show', link.group) }}"
@@ -324,7 +336,7 @@
<table class="table table-hover"> <table class="table table-hover">
{% for attachment in attachments[journal.transaction_journal_id] %} {% for attachment in attachments[journal.transaction_journal_id] %}
<tr> <tr>
<td style="width:30%;"> <td style="width:120px;">
<div class="btn-group btn-group-xs"> <div class="btn-group btn-group-xs">
<a href="{{ route('attachments.edit', attachment.id) }}" class="btn btn-default"><i <a href="{{ route('attachments.edit', attachment.id) }}" class="btn btn-default"><i
class="fa fa-pencil"></i></a> class="fa fa-pencil"></i></a>
@@ -394,10 +406,25 @@
{% endif %} {% endif %}
</div> </div>
{% endfor %} {% endfor %}
</div>
{# modal for linking journals. Will be filled by AJAX #}
<div class="modal fade" tabindex="-1" role="dialog" id="linkJournalModal">
</div>
</div>
{% endblock %} {% endblock %}
{% block scripts %} {% 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 %} {% endblock %}
{# {#

View File

@@ -540,6 +540,7 @@ Route::group(
Route::get('piggy-banks', ['uses' => 'Json\AutoCompleteController@piggyBanks', 'as' => 'autocomplete.piggy-banks']); Route::get('piggy-banks', ['uses' => 'Json\AutoCompleteController@piggyBanks', 'as' => 'autocomplete.piggy-banks']);
Route::get('tags', ['uses' => 'Json\AutoCompleteController@tags', 'as' => 'autocomplete.tags']); Route::get('tags', ['uses' => 'Json\AutoCompleteController@tags', 'as' => 'autocomplete.tags']);
Route::get('transaction-journals/all', ['uses' => 'Json\AutoCompleteController@allJournals', 'as' => 'autocomplete.all-journals']); Route::get('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']); Route::get('currency-names', ['uses' => 'Json\AutoCompleteController@currencyNames', 'as' => 'autocomplete.currency-names']);
@@ -884,8 +885,8 @@ Route::group(
// clone group: // clone group:
Route::get('clone/{transactionGroup}', ['uses' => 'Transaction\CloneController@clone', 'as' => 'clone']); 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('reorder', ['uses' => 'TransactionController@reorder', 'as' => 'reorder']);
Route::post('reconcile', ['uses' => 'TransactionController@reconcile', 'as' => 'reconcile']); Route::post('reconcile', ['uses' => 'TransactionController@reconcile', 'as' => 'reconcile']);
@@ -942,16 +943,16 @@ Route::group(
/** /**
* Transaction Split Controller * Transaction Split Controller
*/ */
Route::group( //Route::group(
['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers\Transaction', 'prefix' => 'transactions/split', // ['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers\Transaction', 'prefix' => 'transactions/split',
'as' => 'transactions.split.'], function () { // 'as' => 'transactions.split.'], function () {
// TODO improve these routes // // TODO improve these routes
Route::get('edit/{tj}', ['uses' => 'SplitController@edit', 'as' => 'edit']); // Route::get('edit/{tj}', ['uses' => 'SplitController@edit', 'as' => 'edit']);
Route::post('update/{tj}', ['uses' => 'SplitController@update', 'as' => 'update']); // Route::post('update/{tj}', ['uses' => 'SplitController@update', 'as' => 'update']);
// TODO end of todo. // // TODO end of todo.
//
} //}
); //);
/** /**
* Transaction Convert Controller * Transaction Convert Controller
@@ -970,6 +971,9 @@ Route::group(
Route::group( Route::group(
['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers\Transaction', 'prefix' => 'transactions/link', 'as' => 'transactions.link.'], ['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers\Transaction', 'prefix' => 'transactions/link', 'as' => 'transactions.link.'],
function () { function () {
Route::get('modal/{tj}', ['uses' => 'LinkController@modal', 'as' => 'modal']);
// TODO improve this route: // TODO improve this route:
Route::post('store/{tj}', ['uses' => 'LinkController@store', 'as' => 'store']); Route::post('store/{tj}', ['uses' => 'LinkController@store', 'as' => 'store']);
Route::get('delete/{journalLink}', ['uses' => 'LinkController@delete', 'as' => 'delete']); 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\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Support\Collection;
use Log; use Log;
use Mockery; use Mockery;
use Preferences;
use Tests\TestCase; use Tests\TestCase;
/** /**
@@ -48,23 +50,39 @@ class LinkControllerTest extends TestCase
/** /**
* @covers \FireflyIII\Http\Controllers\Transaction\LinkController
* @covers \FireflyIII\Http\Controllers\Transaction\LinkController * @covers \FireflyIII\Http\Controllers\Transaction\LinkController
*/ */
public function testDelete(): void public function testDelete(): void
{ $this->markTestIncomplete('Needs to be rewritten for v4.8.0'); {
$this->mock(LinkTypeRepositoryInterface::class);
return; $link = $this->getRandomLink();
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$linkRepos = $this->mock(LinkTypeRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
$this->mockDefaultSession();
$userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true); $userRepos->shouldReceive('hasRole')->withArgs([Mockery::any(), 'owner'])->atLeast()->once()->andReturn(true);
$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()); $this->be($this->user());
$response = $this->get(route('transactions.link.delete', [1])); $response = $this->get(route('transactions.link.modal', [$journal->id]));
$response->assertStatus(200); $response->assertStatus(200);
} }
@@ -72,22 +90,18 @@ class LinkControllerTest extends TestCase
* @covers \FireflyIII\Http\Controllers\Transaction\LinkController * @covers \FireflyIII\Http\Controllers\Transaction\LinkController
*/ */
public function testDestroy(): void public function testDestroy(): void
{ $this->markTestIncomplete('Needs to be rewritten for v4.8.0'); {
$link = $this->getRandomLink();
return;
$journalRepos = $this->mock(JournalRepositoryInterface::class);
$repository = $this->mock(LinkTypeRepositoryInterface::class); $repository = $this->mock(LinkTypeRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class);
Preferences::shouldReceive('mark')->once();
$this->mockDefaultSession();
$repository->shouldReceive('destroyLink'); $repository->shouldReceive('destroyLink')->atLeast()->once();
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$this->be($this->user()); $this->be($this->user());
$this->session(['journal_links.delete.uri' => 'http://localhost/']); $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->assertStatus(302);
$response->assertSessionHas('success'); $response->assertSessionHas('success');
@@ -98,29 +112,28 @@ class LinkControllerTest extends TestCase
* @covers \FireflyIII\Http\Requests\JournalLinkRequest * @covers \FireflyIII\Http\Requests\JournalLinkRequest
*/ */
public function testStore(): void public function testStore(): void
{ $this->markTestIncomplete('Needs to be rewritten for v4.8.0'); {
$withdrawal = $this->getRandomWithdrawal();
return; $deposit = $this->getRandomDeposit();
$repository = $this->mock(LinkTypeRepositoryInterface::class); $repository = $this->mock(LinkTypeRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class); $journalRepos = $this->mockDefaultSession();
$userRepos = $this->mock(UserRepositoryInterface::class);
$data = [ $data = [
'link_other' => 8, 'opposing' => $deposit->id,
'link_type' => '1_inward', 'link_type' => '1_inward',
]; ];
$journalRepos->shouldReceive('firstNull')->andReturn(new TransactionJournal); //$journalRepos->shouldReceive('firstNull')->andReturn(new TransactionJournal);
$journalRepos->shouldReceive('findNull')->andReturn(new TransactionJournal); $journalRepos->shouldReceive('findNull')->andReturn($deposit)->atLeast()->once();
$repository->shouldReceive('findLink')->andReturn(false); $repository->shouldReceive('findLink')->andReturn(false)->atLeast()->once();
$repository->shouldReceive('storeLink')->andReturn(new TransactionJournalLink); $repository->shouldReceive('storeLink')->andReturn(new TransactionJournalLink)->atLeast()->once();
$this->be($this->user()); $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->assertStatus(302);
$response->assertSessionHas('success'); $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 * @covers \FireflyIII\Http\Requests\JournalLinkRequest
*/ */
public function testStoreAlreadyLinked(): void public function testStoreAlreadyLinked(): void
{ $this->markTestIncomplete('Needs to be rewritten for v4.8.0'); {
return;
$repository = $this->mock(LinkTypeRepositoryInterface::class); $repository = $this->mock(LinkTypeRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class); $journalRepos = $this->mockDefaultSession();
$userRepos = $this->mock(UserRepositoryInterface::class); $link = $this->getRandomLink();
$data = [ $data = [
'link_other' => 8, 'opposing' => $link->source_id,
'link_type' => '1_inward', 'link_type' => '1_inward',
]; ];
$journalRepos->shouldReceive('firstNull')->andReturn(new TransactionJournal); $journalRepos->shouldReceive('findNull')->andReturn(new TransactionJournal)->atLeast()->once();
$journalRepos->shouldReceive('findNull')->andReturn(new TransactionJournal); $repository->shouldReceive('findLink')->andReturn(true)->atLeast()->once();
$repository->shouldReceive('findLink')->andReturn(true);
$this->be($this->user()); $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->assertStatus(302);
$response->assertSessionHas('error'); $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 * @covers \FireflyIII\Http\Requests\JournalLinkRequest
*/ */
public function testStoreInvalid(): void 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 = [ $data = [
'link_other' => 0, 'opposing' => 0,
'link_type' => '1_inward', 'link_type' => '1_inward',
]; ];
$journalRepos = $this->mock(JournalRepositoryInterface::class); $journalRepos->shouldReceive('findNull')->andReturn(null)->atLeast()->once();
$userRepos = $this->mock(UserRepositoryInterface::class);
$repository = $this->mock(LinkTypeRepositoryInterface::class);
$journalRepos->shouldReceive('firstNull')->andReturn(null);
$journalRepos->shouldReceive('findNull')->andReturn(null);
$this->be($this->user()); $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->assertStatus(302);
$response->assertSessionHas('error'); $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 * @covers \FireflyIII\Http\Requests\JournalLinkRequest
*/ */
public function testStoreSame(): void public function testStoreSame(): void
{ $this->markTestIncomplete('Needs to be rewritten for v4.8.0'); {
$withdrawal = $this->getRandomWithdrawal();
return;
$repository = $this->mock(LinkTypeRepositoryInterface::class); $repository = $this->mock(LinkTypeRepositoryInterface::class);
$journalRepos = $this->mock(JournalRepositoryInterface::class); $journalRepos = $this->mockDefaultSession();
$userRepos = $this->mock(UserRepositoryInterface::class);
$data = [ $data = [
'link_other' => 8, 'link_other' => $withdrawal->id,
'link_type' => '1_inward', 'link_type' => '1_inward',
]; ];
$journal = $this->user()->transactionJournals()->first(); $journalRepos->shouldReceive('findNull')->andReturn($withdrawal)->atLeast()->once();
$repository->shouldReceive('findLink')->andReturn(false)->atLeast()->once();
$journalRepos->shouldReceive('firstNull')->andReturn($journal);
$journalRepos->shouldReceive('findNull')->andReturn($journal);
$repository->shouldReceive('findLink')->andReturn(false);
$repository->shouldReceive('storeLink')->andReturn(new TransactionJournalLink);
$this->be($this->user()); $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->assertStatus(302);
$response->assertSessionHas('error'); $response->assertSessionHas('error');
$response->assertRedirect(route('transactions.show', [1])); $response->assertRedirect(route('transactions.show', [$withdrawal->id]));
} }
/** /**
* @covers \FireflyIII\Http\Controllers\Transaction\LinkController * @covers \FireflyIII\Http\Controllers\Transaction\LinkController
*/ */
public function testSwitchLink(): void public function testSwitchLink(): void
{ $this->markTestIncomplete('Needs to be rewritten for v4.8.0'); {
$link = $this->getRandomLink();
return; $withdrawal = $this->getRandomWithdrawal();
$journalRepos = $this->mock(JournalRepositoryInterface::class); $journalRepos = $this->mock(JournalRepositoryInterface::class);
$repository = $this->mock(LinkTypeRepositoryInterface::class); $repository = $this->mock(LinkTypeRepositoryInterface::class);
$userRepos = $this->mock(UserRepositoryInterface::class); $userRepos = $this->mock(UserRepositoryInterface::class);
@@ -224,7 +225,7 @@ class LinkControllerTest extends TestCase
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal); $journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('switchLink')->andReturn(false); $repository->shouldReceive('switchLink')->andReturn(false);
$this->be($this->user()); $this->be($this->user());
$response = $this->get(route('transactions.link.switch', [1])); $response = $this->get(route('transactions.link.switch', [$link->id]));
$response->assertStatus(302); $response->assertStatus(302);

View File

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