Code that allows transaction reconciliation. #736

This commit is contained in:
James Cole 2017-11-05 19:48:43 +01:00
parent bb46d034cd
commit 33d89d52c2
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
16 changed files with 387 additions and 35 deletions

View File

@ -80,6 +80,7 @@ class JournalCollector implements JournalCollectorInterface
'transactions.id as id', 'transactions.id as id',
'transactions.description as transaction_description', 'transactions.description as transaction_description',
'transactions.account_id', 'transactions.account_id',
'transactions.reconciled',
'transactions.identifier', 'transactions.identifier',
'transactions.transaction_journal_id', 'transactions.transaction_journal_id',
'transactions.amount as transaction_amount', 'transactions.amount as transaction_amount',
@ -171,7 +172,7 @@ class JournalCollector implements JournalCollectorInterface
$q1->where( $q1->where(
function (EloquentBuilder $q2) use ($amount) { function (EloquentBuilder $q2) use ($amount) {
// amount < 0 and .amount > -$amount // amount < 0 and .amount > -$amount
$amount = bcmul($amount,'-1'); $amount = bcmul($amount, '-1');
$q2->where('transactions.amount', '<', 0)->where('transactions.amount', '>', $amount); $q2->where('transactions.amount', '<', 0)->where('transactions.amount', '>', $amount);
} }
) )
@ -199,7 +200,7 @@ class JournalCollector implements JournalCollectorInterface
$q1->where( $q1->where(
function (EloquentBuilder $q2) use ($amount) { function (EloquentBuilder $q2) use ($amount) {
// amount < 0 and .amount < -$amount // amount < 0 and .amount < -$amount
$amount = bcmul($amount,'-1'); $amount = bcmul($amount, '-1');
$q2->where('transactions.amount', '<', 0)->where('transactions.amount', '<', $amount); $q2->where('transactions.amount', '<', 0)->where('transactions.amount', '<', $amount);
} }
) )
@ -211,6 +212,7 @@ class JournalCollector implements JournalCollectorInterface
); );
} }
); );
return $this; return $this;
} }

View File

@ -0,0 +1,93 @@
<?php
/**
* TransactionController.php
* Copyright (c) 2017 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\Http\Controllers\Json;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Support\SingleCacheProperties;
use Illuminate\Http\Request;
use Response;
class TransactionController extends Controller
{
public function amounts(Request $request, JournalRepositoryInterface $repository)
{
$ids = $request->get('transactions');
$cache = new SingleCacheProperties;
$cache->addProperty('json-reconcile-amounts');
$cache->addProperty($ids);
if ($cache->has()) {
return Response::json($cache->get());
}
$totals = [];
// for each transaction, get amount(s)
foreach ($ids as $transactionId) {
$transaction = $repository->findTransaction(intval($transactionId));
$transactionType = $transaction->transactionJournal->transactionType->type;
// default amount:
$currencyId = $transaction->transaction_currency_id;
if (!isset($totals[$currencyId])) {
$totals[$currencyId] = [
'amount' => '0',
'currency' => $transaction->transactionCurrency,
'type' => $transactionType,
];
}
// add default amount:
$totals[$currencyId]['amount'] = bcadd($totals[$currencyId]['amount'], app('steam')->positive($transaction->amount));
// foreign amount:
if (!is_null($transaction->foreign_amount)) {
$currencyId = $transaction->foreign_currency_id;
if (!isset($totals[$currencyId])) {
$totals[$currencyId] = [
'amount' => '0',
'currency' => $transaction->foreignCurrency,
'type' => $transactionType,
];
}
// add foreign amount:
$totals[$currencyId]['amount'] = bcadd($totals[$currencyId]['amount'], app('steam')->positive($transaction->foreign_amount));
}
}
$entries = [];
foreach ($totals as $entry) {
$amount = $entry['amount'];
if ($entry['type'] === TransactionType::WITHDRAWAL) {
$amount = bcmul($entry['amount'], '-1');
}
$entries[] = app('amount')->formatAnything($entry['currency'], $amount, false);
}
$result = ['amounts' => join(' / ', $entries)];
$cache->store($result);
return Response::json($result);
}
}

View File

@ -133,6 +133,22 @@ class TransactionController extends Controller
} }
/**
* @param Request $request
*/
public function reconcile(Request $request, JournalRepositoryInterface $repository)
{
$transactionIds = $request->get('transactions');
foreach ($transactionIds as $transactionId) {
$transactionId = intval($transactionId);
$transaction = $repository->findTransaction($transactionId);
Log::debug(sprintf('Transaction ID is %d', $transaction->id));
$repository->reconcile($transaction);
}
}
/** /**
* @param Request $request * @param Request $request
* @param JournalRepositoryInterface $repository * @param JournalRepositoryInterface $repository
@ -181,8 +197,6 @@ class TransactionController extends Controller
$subTitle = trans('firefly.' . $what) . ' "' . $journal->description . '"'; $subTitle = trans('firefly.' . $what) . ' "' . $journal->description . '"';
return view('transactions.show', compact('journal', 'events', 'subTitle', 'what', 'transactions', 'linkTypes', 'links')); return view('transactions.show', compact('journal', 'events', 'subTitle', 'what', 'transactions', 'linkTypes', 'links'));
} }
/** /**

View File

@ -93,6 +93,7 @@ class Transaction extends Model
'identifier' => 'int', 'identifier' => 'int',
'encrypted' => 'boolean', // model does not have these fields though 'encrypted' => 'boolean', // model does not have these fields though
'bill_name_encrypted' => 'boolean', 'bill_name_encrypted' => 'boolean',
'reconciled' => 'boolean',
]; ];
protected $fillable protected $fillable
= ['account_id', 'transaction_journal_id', 'description', 'amount', 'identifier', 'transaction_currency_id', 'foreign_currency_id', = ['account_id', 'transaction_journal_id', 'description', 'amount', 'identifier', 'transaction_currency_id', 'foreign_currency_id',

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\Journal; namespace FireflyIII\Repositories\Journal;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\User; use FireflyIII\User;
@ -123,6 +124,38 @@ class JournalRepository implements JournalRepositoryInterface
return $journal; return $journal;
} }
/**
* @param Transaction $transaction
*
* @return Transaction|null
*/
public function findOpposingTransaction(Transaction $transaction): ?Transaction
{
$opposing = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.user_id', $this->user->id)
->where('transactions.transaction_journal_id', $transaction->transaction_journal_id)
->where('transactions.identifier', $transaction->identifier)
->where('amount', bcmul($transaction->amount, '-1'))
->first(['transactions.*']);
return $opposing;
}
/**
* @param int $transactionid
*
* @return Transaction|null
*/
public function findTransaction(int $transactionid): ?Transaction
{
$transaction = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.user_id', $this->user->id)
->where('transactions.id', $transactionid)
->first(['transactions.*']);
return $transaction;
}
/** /**
* Get users first transaction journal * Get users first transaction journal
* *
@ -158,6 +191,32 @@ class JournalRepository implements JournalRepositoryInterface
return $journal->transactionType->type === TransactionType::TRANSFER; return $journal->transactionType->type === TransactionType::TRANSFER;
} }
/**
* @param Transaction $transaction
*
* @return bool
*/
public function reconcile(Transaction $transaction): bool
{
Log::debug(sprintf('Going to reconcile transaction #%d', $transaction->id));
$opposing = $this->findOpposingTransaction($transaction);
if (is_null($opposing)) {
Log::debug('Opposing transaction is NULL. Cannot reconcile.');
return false;
}
Log::debug(sprintf('Opposing transaction ID is #%d', $opposing->id));
$transaction->reconciled = true;
$opposing->reconciled = true;
$transaction->save();
$opposing->save();
return true;
}
/** /**
* @param TransactionJournal $journal * @param TransactionJournal $journal
* @param int $order * @param int $order
@ -303,7 +362,7 @@ class JournalRepository implements JournalRepositoryInterface
} }
// update note: // update note:
if (isset($data['notes']) && !is_null($data['notes']) ) { if (isset($data['notes']) && !is_null($data['notes'])) {
$this->updateNote($journal, strval($data['notes'])); $this->updateNote($journal, strval($data['notes']));
} }
@ -345,7 +404,7 @@ class JournalRepository implements JournalRepositoryInterface
$journal->budgets()->detach(); $journal->budgets()->detach();
// update note: // update note:
if (isset($data['notes']) && !is_null($data['notes']) ) { if (isset($data['notes']) && !is_null($data['notes'])) {
$this->updateNote($journal, strval($data['notes'])); $this->updateNote($journal, strval($data['notes']));
} }

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\Journal; namespace FireflyIII\Repositories\Journal;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\User; use FireflyIII\User;
@ -73,6 +74,20 @@ interface JournalRepositoryInterface
*/ */
public function find(int $journalId): TransactionJournal; public function find(int $journalId): TransactionJournal;
/**
* @param Transaction $transaction
*
* @return Transaction|null
*/
public function findOpposingTransaction(Transaction $transaction): ?Transaction;
/**
* @param int $transactionid
*
* @return Transaction|null
*/
public function findTransaction(int $transactionid): ?Transaction;
/** /**
* Get users very first transaction journal * Get users very first transaction journal
* *
@ -92,6 +107,13 @@ interface JournalRepositoryInterface
*/ */
public function isTransfer(TransactionJournal $journal): bool; public function isTransfer(TransactionJournal $journal): bool;
/**
* @param Transaction $transaction
*
* @return bool
*/
public function reconcile(Transaction $transaction): bool;
/** /**
* @param TransactionJournal $journal * @param TransactionJournal $journal
* @param int $order * @param int $order

View File

@ -28,6 +28,7 @@ use FireflyIII\Models\AccountType;
use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Support\SingleCacheProperties;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\JoinClause; use Illuminate\Database\Query\JoinClause;
@ -74,6 +75,13 @@ class JournalTasker implements JournalTaskerInterface
*/ */
public function getTransactionsOverview(TransactionJournal $journal): array public function getTransactionsOverview(TransactionJournal $journal): array
{ {
$cache = new SingleCacheProperties;
$cache->addProperty('transaction-overview');
$cache->addProperty($journal->id);
$cache->addProperty($journal->updated_at);
if ($cache->has()) {
return $cache->get();
}
// get all transaction data + the opposite site in one list. // get all transaction data + the opposite site in one list.
$set = $journal $set = $journal
->transactions()// "source" ->transactions()// "source"
@ -136,6 +144,7 @@ class JournalTasker implements JournalTaskerInterface
'journal_type' => $transactionType, 'journal_type' => $transactionType,
'updated_at' => $journal->updated_at, 'updated_at' => $journal->updated_at,
'source_id' => $entry->id, 'source_id' => $entry->id,
'source' => $journal->transactions()->find($entry->id),
'source_amount' => $entry->amount, 'source_amount' => $entry->amount,
'foreign_source_amount' => $entry->foreign_amount, 'foreign_source_amount' => $entry->foreign_amount,
'description' => $entry->description, 'description' => $entry->description,
@ -174,6 +183,7 @@ class JournalTasker implements JournalTaskerInterface
$transactions[] = $transaction; $transactions[] = $transaction;
} }
$cache->store($transactions);
return $transactions; return $transactions;
} }

View File

@ -39,6 +39,7 @@ use Twig_Extension;
*/ */
class Transaction extends Twig_Extension class Transaction extends Twig_Extension
{ {
/** /**
* Can show the amount of a transaction, if that transaction has been collected by the journal collector. * Can show the amount of a transaction, if that transaction has been collected by the journal collector.
* *
@ -127,13 +128,13 @@ class Transaction extends Twig_Extension
$string = app('amount')->formatAnything($fakeCurrency, $amount, true); $string = app('amount')->formatAnything($fakeCurrency, $amount, true);
// then display (if present) the foreign amount: // then display (if present) the foreign amount:
if(!is_null($transaction['foreign_source_amount'])) { if (!is_null($transaction['foreign_source_amount'])) {
$amount = $transaction['journal_type'] === TransactionType::WITHDRAWAL ? $transaction['foreign_source_amount'] $amount = $transaction['journal_type'] === TransactionType::WITHDRAWAL ? $transaction['foreign_source_amount']
: $transaction['foreign_destination_amount']; : $transaction['foreign_destination_amount'];
$fakeCurrency = new TransactionCurrency; $fakeCurrency = new TransactionCurrency;
$fakeCurrency->decimal_places = $transaction['foreign_currency_dp']; $fakeCurrency->decimal_places = $transaction['foreign_currency_dp'];
$fakeCurrency->symbol = $transaction['foreign_currency_symbol']; $fakeCurrency->symbol = $transaction['foreign_currency_symbol'];
$string .= ' ('.app('amount')->formatAnything($fakeCurrency, $amount, true).')'; $string .= ' (' . app('amount')->formatAnything($fakeCurrency, $amount, true) . ')';
} }
$cache->store($string); $cache->store($string);
@ -401,6 +402,30 @@ class Transaction extends Twig_Extension
return $txt; return $txt;
} }
/**
* @param TransactionModel $transaction
*
* @return string
*/
public function isReconciled(TransactionModel $transaction): string
{
$cache = new SingleCacheProperties;
$cache->addProperty('transaction-reconciled');
$cache->addProperty($transaction->id);
$cache->addProperty($transaction->updated_at);
if ($cache->has()) {
return $cache->get();
}
$icon = '';
if (intval($transaction->reconciled) === 1) {
$icon = '<i class="fa fa-check"></i>';
}
$cache->store($icon);
return $icon;
}
/** /**
* @param TransactionModel $transaction * @param TransactionModel $transaction
* *

View File

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class ChangesForV470a extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
/**
* Run the migrations.
*
* @SuppressWarnings(PHPMD.ShortMethodName)
* @return void
*/
public function up()
{
Schema::table(
'transactions', function (Blueprint $table) {
$table->boolean('reconciled')->after('deleted_at')->default(0);
}
);
}
}

View File

@ -20,6 +20,9 @@
/** global: edit_selected_txt, delete_selected_txt */ /** global: edit_selected_txt, delete_selected_txt */
/**
*
*/
$(document).ready(function () { $(document).ready(function () {
"use strict"; "use strict";
$('.mass_edit_all').show(); $('.mass_edit_all').show();
@ -44,8 +47,43 @@ $(document).ready(function () {
$('.mass_edit').click(goToMassEdit); $('.mass_edit').click(goToMassEdit);
// click the delete button: // click the delete button:
$('.mass_delete').click(goToMassDelete); $('.mass_delete').click(goToMassDelete);
// click reconcile button
$('.mass_reconcile').click(goToReconcile);
}); });
/**
*
* @returns {boolean}
*/
function goToReconcile() {
var checked = $('.select_all_single:checked');
var ids = [];
$.each(checked, function (i, v) {
ids.push(parseInt($(v).data('transaction')));
});
// go to specially crafted URL:
var bases = document.getElementsByTagName('base');
var baseHref = null;
if (bases.length > 0) {
baseHref = bases[0].href;
}
$.post(baseHref + 'transactions/reconcile', {transactions: ids}).done(function () {
alert('OK then.');
}).fail(function () {
alert('Could not reconcile transactions: please check the logs and try again later.');
});
return false;
}
/**
*
* @returns {boolean}
*/
function goToMassEdit() { function goToMassEdit() {
"use strict"; "use strict";
var checkedArray = getCheckboxes(); var checkedArray = getCheckboxes();
@ -62,6 +100,10 @@ function goToMassEdit() {
return false; return false;
} }
/**
*
* @returns {boolean}
*/
function goToMassDelete() { function goToMassDelete() {
"use strict"; "use strict";
var checkedArray = getCheckboxes(); var checkedArray = getCheckboxes();
@ -77,6 +119,10 @@ function goToMassDelete() {
return false; return false;
} }
/**
*
* @returns {Array}
*/
function getCheckboxes() { function getCheckboxes() {
"use strict"; "use strict";
var list = []; var list = [];
@ -90,13 +136,19 @@ function getCheckboxes() {
return list; return list;
} }
/**
*
*/
function countChecked() { function countChecked() {
"use strict"; "use strict";
var checked = $('.select_all_single:checked').length; var checked = $('.select_all_single:checked').length;
if (checked > 0) { if (checked > 0) {
$('.mass_edit span').text(edit_selected_txt + ' (' + checked + ')'); $('.mass_edit span').text(edit_selected_txt + ' (' + checked + ')');
$('.mass_delete span').text(delete_selected_txt + ' (' + checked + ')'); $('.mass_delete span').text(delete_selected_txt + ' (' + checked + ')');
// get amount for the transactions:
getAmounts();
$('.mass_button_options').show(); $('.mass_button_options').show();
} else { } else {
@ -104,17 +156,49 @@ function countChecked() {
} }
} }
function getAmounts() {
$('.mass_reconcile span').html(reconcile_selected_txt + ' (<i class="fa fa-spinner fa-spin "></i>)');
var checked = $('.select_all_single:checked');
var ids = [];
$.each(checked, function (i, v) {
ids.push(parseInt($(v).data('transaction')));
});
// go to specially crafted URL:
var bases = document.getElementsByTagName('base');
var baseHref = null;
if (bases.length > 0) {
baseHref = bases[0].href;
}
$.getJSON(baseHref + 'json/transactions/amount', {transactions: ids}).done(function (data) {
$('.mass_reconcile span').text(reconcile_selected_txt + ' (' + data.amounts + ')');
console.log(data);
});
return;
}
/**
*
*/
function checkAll() { function checkAll() {
"use strict"; "use strict";
$('.select_all_single').prop('checked', true); $('.select_all_single').prop('checked', true);
} }
/**
*
*/
function uncheckAll() { function uncheckAll() {
"use strict"; "use strict";
$('.select_all_single').prop('checked', false); $('.select_all_single').prop('checked', false);
} }
/**
*
* @returns {boolean}
*/
function stopMassSelect() { function stopMassSelect() {
"use strict"; "use strict";
@ -145,6 +229,10 @@ function stopMassSelect() {
return false; return false;
} }
/**
*
* @returns {boolean}
*/
function startMassSelect() { function startMassSelect() {
"use strict"; "use strict";
// show "select all" box in table header. // show "select all" box in table header.

View File

@ -663,8 +663,7 @@ return [
'stored_journal' => 'Successfully created new transaction ":description"', 'stored_journal' => 'Successfully created new transaction ":description"',
'select_transactions' => 'Select transactions', 'select_transactions' => 'Select transactions',
'stop_selection' => 'Stop selecting transactions', 'stop_selection' => 'Stop selecting transactions',
'edit_selected' => 'Edit selected', 'reconcile_selected' => 'Reconcile',
'delete_selected' => 'Delete selected',
'mass_delete_journals' => 'Delete a number of transactions', 'mass_delete_journals' => 'Delete a number of transactions',
'mass_edit_journals' => 'Edit a number of transactions', 'mass_edit_journals' => 'Edit a number of transactions',
'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.',

View File

@ -33,13 +33,15 @@
</tbody> </tbody>
</table> </table>
<div class="row mass_edit_all hidden-xs" style="display: none;"> <div class="row mass_edit_all hidden-xs" style="display: none;">
<div class="col-lg-6 col-md-12 col-sm-12 col-xs-12"> <div class="col-lg-8 col-md-12 col-sm-12 col-xs-12">
<div class="mass_button_options btn-group btn-group" style="display:none;"> <div class="mass_button_options btn-group btn-group" style="display:none;">
<a href="#" class="btn btn-default mass_edit"><i class="fa fa-fw fa-pencil"></i> <span>{{ 'edit_selected'|_ }}</span></a> <a href="#" class="btn btn-default mass_edit"><i class="fa fa-fw fa-pencil"></i> <span>{{ 'edit'|_ }}</span></a>
<a href="#" class="btn btn-danger mass_delete"><i class="fa fa-fw fa-trash"></i> <span>{{ 'delete_selected'|_ }}</span></a> <a href="#" class="btn btn-default mass_reconcile"><i class="fa fa-fw fa-check"></i> <span>{{ 'reconcile_selected'|_ }} (<i class="fa fa-spinner fa-spin"></i>)</span></a>
<a href="#" class="btn btn-danger mass_delete"><i class="fa fa-fw fa-trash"></i> <span>{{ 'delete'|_ }}</span></a>
</div> </div>
</div> </div>
<div class="col-lg-6 col-md-12 col-sm-12 col-xs-12 hidden-xs"> <div class="col-lg-4 col-md-12 col-sm-12 col-xs-12 hidden-xs">
<div class="mass_buttons btn-group btn-group pull-right"> <div class="mass_buttons btn-group btn-group pull-right">
<a href="#" class="btn btn-default mass_select"><i class="fa fa-fw fa-check-square-o"></i> {{ 'select_transactions'|_ }}</a> <a href="#" class="btn btn-default mass_select"><i class="fa fa-fw fa-check-square-o"></i> {{ 'select_transactions'|_ }}</a>
@ -54,6 +56,7 @@
</div> </div>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
var edit_selected_txt = "{{ 'edit_selected'|_ }}"; var edit_selected_txt = "{{ 'edit'|_ }}";
var delete_selected_txt = "{{ 'delete_selected'|_ }}"; var delete_selected_txt = "{{ 'delete'|_ }}";
var reconcile_selected_txt = "{{ 'reconcile_selected'|_ }}";
</script> </script>

View File

@ -1,9 +1,11 @@
<tr class="drag" data-date="{{ transaction.date.format('Y-m-d') }}" data-id="{{ transaction.journal_id }}"> <tr class="drag" data-date="{{ transaction.date.format('Y-m-d') }}" data-id="{{ transaction.journal_id }}"
data-transaction-id="{{ transaction.id }}"
>
{# input buttons #} {# input buttons #}
<td class="hidden-xs"> <td class="hidden-xs">
<div class="select_single" style="display:none;"> <div class="select_single" style="display:none;">
<input name="select_all_single[]" class="select_all_single" value="{{ transaction.journal_id }}" type="checkbox"/> <input name="select_all_single[]" class="select_all_single" data-transaction="{{ transaction.id }}" value="{{ transaction.journal_id }}" type="checkbox"/>
</div> </div>
<div class="btn-group btn-group-xs edit_buttons edit_tr_buttons">{% if sorting %}<a href="#" class="handle btn btn-default btn-xs"><i <div class="btn-group btn-group-xs edit_buttons edit_tr_buttons">{% if sorting %}<a href="#" class="handle btn btn-default btn-xs"><i
class="fa fa-fw fa-arrows-v"></i></a>{% endif %}<a href="{{ route('transactions.edit',transaction.journal_id) }}" class="fa fa-fw fa-arrows-v"></i></a>{% endif %}<a href="{{ route('transactions.edit',transaction.journal_id) }}"
@ -19,13 +21,19 @@
{# description #} {# description #}
<td> <td>
{# count attachments #}
{{ transaction|transactionReconciled }}
<a href="{{ route('transactions.show',transaction.journal_id) }}"> <a href="{{ route('transactions.show',transaction.journal_id) }}">
{{ transaction|transactionDescription }} {{ transaction|transactionDescription }}
</a> </a>
{# is a split journal #} {# is a split journal #}
{{ transaction|transactionIsSplit }} {{ transaction|transactionIsSplit }}
{# count attachments #} {# count attachments #}
{{ transaction|transactionHasAtt }} {{ transaction|transactionHasAtt }}
</td> </td>
<td style="text-align: right;"><span style="margin-right:5px;">{{ transaction|transactionAmount }}</span></td> <td style="text-align: right;"><span style="margin-right:5px;">{{ transaction|transactionAmount }}</span></td>
<td class="hidden-sm hidden-xs"> <td class="hidden-sm hidden-xs">

View File

@ -107,8 +107,10 @@
{% endblock %} {% endblock %}
{% block scripts %} {% block scripts %}
<script type="text/javascript"> <script type="text/javascript">
var edit_selected_txt = "{{ 'edit_selected'|_ }}"; var edit_selected_txt = "{{ 'edit'|_ }}";
var delete_selected_txt = "{{ 'delete_selected'|_ }}"; var delete_selected_txt = "{{ 'delete'|_ }}";
var reconcile_selected_txt = "{{ 'reconcile_selected'|_ }}";
var searchQuery = "{{ fullQuery|escape('js') }}"; var searchQuery = "{{ fullQuery|escape('js') }}";
var searchUri = "{{ route('search.search') }}"; var searchUri = "{{ route('search.search') }}";
</script> </script>

View File

@ -413,24 +413,13 @@
{{ formatDestinationBefore(transaction) }} &rarr; {{ formatDestinationAfter(transaction) }} {{ formatDestinationBefore(transaction) }} &rarr; {{ formatDestinationAfter(transaction) }}
</td> </td>
<td> <td>
{% if journal.transactionType.type == "Withdrawal" %} {{ transaction|transactionArrayAmount }}
{{ formatAmountBySymbol(transaction.source_amount, transaction.transaction_currency_symbol, transaction.transaction_currency_dp, true) }}
{% if(transaction.foreign_source_amount) %}
({{ formatAmountBySymbol(transaction.foreign_source_amount, transaction.foreign_currency_symbol, transaction.foreign_currency_dp, true) }})
{% endif %}
{% else %}
{{ formatAmountBySymbol(transaction.destination_amount, transaction.transaction_currency_symbol,2) }}
{% if(transaction.foreign_source_amount) %}
({{ formatAmountBySymbol(transaction.foreign_destination_amount, transaction.foreign_currency_symbol, transaction.foreign_currency_dp, true) }})
{% endif %}
{% endif %}
</td> </td>
<td class="hidden-md hidden-xs"> <td class="hidden-md hidden-xs">
{{ transactionIdBudgets(transaction.source_id) }} {{ transaction.source|transactionBudgets }}
</td> </td>
<td class="hidden-md hidden-xs"> <td class="hidden-md hidden-xs">
{{ transactionIdCategories(transaction.source_id) }} {{ transaction.source|transactionCategories }}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View File

@ -462,6 +462,9 @@ Route::group(
// frontpage // frontpage
Route::get('frontpage/piggy-banks', ['uses' => 'Json\FrontpageController@piggyBanks', 'as' => 'fp.piggy-banks']); Route::get('frontpage/piggy-banks', ['uses' => 'Json\FrontpageController@piggyBanks', 'as' => 'fp.piggy-banks']);
// amount reconciliation
Route::get('transactions/amount', ['uses' => 'Json\TransactionController@amounts', 'as' => 'transactions.amounts']);
// currency conversion: // currency conversion:
Route::get('rate/{fromCurrencyCode}/{toCurrencyCode}/{date}', ['uses' => 'Json\ExchangeController@getRate', 'as' => 'rate']); Route::get('rate/{fromCurrencyCode}/{toCurrencyCode}/{date}', ['uses' => 'Json\ExchangeController@getRate', 'as' => 'rate']);
@ -700,6 +703,7 @@ Route::group(
Route::get('{what}/{moment?}', ['uses' => 'TransactionController@index', 'as' => 'index'])->where(['what' => 'withdrawal|deposit|transfers|transfer']); Route::get('{what}/{moment?}', ['uses' => 'TransactionController@index', 'as' => 'index'])->where(['what' => 'withdrawal|deposit|transfers|transfer']);
Route::get('show/{tj}', ['uses' => 'TransactionController@show', 'as' => 'show']); Route::get('show/{tj}', ['uses' => 'TransactionController@show', 'as' => 'show']);
Route::post('reorder', ['uses' => 'TransactionController@reorder', 'as' => 'reorder']); Route::post('reorder', ['uses' => 'TransactionController@reorder', 'as' => 'reorder']);
Route::post('reconcile', ['uses' => 'TransactionController@reconcile', 'as' => 'reconcile']);
} }
); );