Update code for reconciliation.

This commit is contained in:
James Cole 2017-11-22 16:54:49 +01:00
parent 4b87e5be55
commit 69bb76b6fe
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
10 changed files with 196 additions and 54 deletions

View File

@ -27,7 +27,9 @@ use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Navigation; use Navigation;
@ -68,16 +70,38 @@ class ReconcileController extends Controller
*/ */
public function overview(Request $request, Account $account, Carbon $start, Carbon $end) public function overview(Request $request, Account $account, Carbon $start, Carbon $end)
{ {
$startBalance = $request->get('startBalance'); $startBalance = $request->get('startBalance');
$endBalance = $request->get('endBalance'); $endBalance = $request->get('endBalance');
$transactions = $request->get('transactions'); $transactionIds = $request->get('transactions') ?? [];
$clearedIds = $request->get('cleared') ?? [];
$amount = '0';
$clearedAmount = '0';
$route = route('accounts.reconcile.submit', [$account->id, $start->format('Ymd'), $end->format('Ymd')]);
// get sum of transaction amounts:
/** @var JournalRepositoryInterface $repository */
$repository = app(JournalRepositoryInterface::class);
$transactions = $repository->getTransactionsById($transactionIds);
$cleared = $repository->getTransactionsById($clearedIds);
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$amount = bcadd($amount, $transaction->amount);
}
/** @var Transaction $transaction */
foreach ($cleared as $transaction) {
$clearedAmount = bcadd($clearedAmount, $transaction->amount);
}
$return = [ $return = [
'is_zero' => false, 'is_zero' => false,
'post_uri' => route('accounts.reconcile.submit', [$account->id, $start->format('Ymd'), $end->format('Ymd')]), 'post_uri' => $route,
'html' => '', 'html' => '',
]; ];
$return['html'] = view('accounts.reconcile.overview', compact('account', 'start', 'end'))->render(); $return['html'] = view(
'accounts.reconcile.overview',
compact('account', 'start', 'end', 'clearedIds', 'transactionIds', 'clearedAmount', 'startBalance', 'endBalance', 'amount', 'route')
)->render();
return Response::json($return); return Response::json($return);
} }
@ -175,4 +199,13 @@ class ReconcileController extends Controller
return Response::json(['html' => $html]); return Response::json(['html' => $html]);
} }
/**
* @param Account $account
* @param Carbon $start
* @param Carbon $end
*/
public function submit(Request $request, Account $account, Carbon $start, Carbon $end) {
var_dump($request->all());
}
} }

View File

@ -88,7 +88,7 @@ class Amount implements ConverterInterface
Log::debug(sprintf('No decimal character found. Converted amount from "%s" to "%s".', $oldValue, $value)); Log::debug(sprintf('No decimal character found. Converted amount from "%s" to "%s".', $oldValue, $value));
} }
$number = strval(number_format(round(floatval($value), 12), 12)); $number = strval(number_format(round(floatval($value), 12), 12,'',''));
return $number; return $number;
} }
} }

View File

@ -177,6 +177,23 @@ class JournalRepository implements JournalRepositoryInterface
return TransactionType::orderBy('type', 'ASC')->get(); return TransactionType::orderBy('type', 'ASC')->get();
} }
/**
* @param array $transactionIds
*
* @return Collection
*/
public function getTransactionsById(array $transactionIds): Collection
{
$set = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->whereIn('transactions.id', $transactionIds)
->where('transaction_journals.user_id', $this->user->id)
->whereNull('transaction_journals.deleted_at')
->whereNull('transactions.deleted_at')
->get( ['transactions.*']);
return $set;
}
/** /**
* @param TransactionJournal $journal * @param TransactionJournal $journal
* *

View File

@ -35,6 +35,15 @@ use Illuminate\Support\MessageBag;
*/ */
interface JournalRepositoryInterface interface JournalRepositoryInterface
{ {
/**
* @param array $transactionIds
*
* @return Collection
*/
public function getTransactionsById(array $transactionIds): Collection;
/** /**
* @param TransactionJournal $journal * @param TransactionJournal $journal
* @param TransactionType $type * @param TransactionType $type

View File

@ -68,12 +68,19 @@ function storeReconcile() {
$.each($('.reconcile_checkbox:checked'), function (i, v) { $.each($('.reconcile_checkbox:checked'), function (i, v) {
ids.push($(v).data('id')); ids.push($(v).data('id'));
}); });
var cleared = [];
$.each($('input[class="cleared"]'), function (i, v) {
var obj = $(v);
cleared.push(obj.data('id'));
});
var variables = { var variables = {
startBalance: parseFloat($('input[name="start_balance"]').val()), startBalance: parseFloat($('input[name="start_balance"]').val()),
endBalance: parseFloat($('input[name="end_balance"]').val()), endBalance: parseFloat($('input[name="end_balance"]').val()),
startDate: $('input[name="start_date"]').val(), startDate: $('input[name="start_date"]').val(),
startEnd: $('input[name="end_date"]').val(), startEnd: $('input[name="end_date"]').val(),
transactions: ids transactions: ids,
cleared: cleared,
}; };
var uri = overviewUri.replace('%start%', $('input[name="start_date"]').val()).replace('%end%', $('input[name="end_date"]').val()); var uri = overviewUri.replace('%start%', $('input[name="start_date"]').val()).replace('%end%', $('input[name="end_date"]').val());
@ -126,11 +133,15 @@ function calculateBalanceDifference() {
var startBalance = parseFloat($('input[name="start_balance"]').val()); var startBalance = parseFloat($('input[name="start_balance"]').val());
var endBalance = parseFloat($('input[name="end_balance"]').val()); var endBalance = parseFloat($('input[name="end_balance"]').val());
balanceDifference = startBalance - endBalance; balanceDifference = startBalance - endBalance;
if (balanceDifference < 0) { //if (balanceDifference < 0) {
balanceDifference = balanceDifference * -1; // balanceDifference = balanceDifference * -1;
} //}
} }
/**
* Grab all transactions, update the URL and place the set of transactions in the box.
* This more or less resets the reconciliation.
*/
function getTransactionsForRange() { function getTransactionsForRange() {
// clear out the box: // clear out the box:
$('#transactions_holder').empty().append($('<p>').addClass('text-center').html('<i class="fa fa-fw fa-spin fa-spinner"></i>')); $('#transactions_holder').empty().append($('<p>').addClass('text-center').html('<i class="fa fa-fw fa-spin fa-spinner"></i>'));
@ -141,12 +152,31 @@ function getTransactionsForRange() {
$.getJSON(uri).done(placeTransactions); $.getJSON(uri).done(placeTransactions);
} }
/**
* Loop over all transactions that have already been cleared and add this to the selectedAmount.
*
*/
function includeClearedTransactions() {
$.each($('input[class="cleared"]'), function (i, v) {
var obj = $(v);
selectedAmount = selectedAmount - parseFloat(obj.val());
});
}
/**
* Place the HTML for the transactions within the date range and update the balance difference.
* @param data
*/
function placeTransactions(data) { function placeTransactions(data) {
$('#transactions_holder').empty().html(data.html); $('#transactions_holder').empty().html(data.html);
// as long as the dates are equal, changing the balance does not matter. // as long as the dates are equal, changing the balance does not matter.
calculateBalanceDifference(); calculateBalanceDifference();
// any already cleared transactions must be added to / removed from selectedAmount.
includeClearedTransactions();
difference = balanceDifference; difference = balanceDifference;
updateDifference(); updateDifference();

View File

@ -647,6 +647,18 @@ return [
'select_more_than_one_budget' => 'Please select more than one budget', 'select_more_than_one_budget' => 'Please select more than one budget',
'select_more_than_one_tag' => 'Please select more than one tag', 'select_more_than_one_tag' => 'Please select more than one tag',
'account_default_currency' => 'If you select another currency, new transactions from this account will have this currency pre-selected.', 'account_default_currency' => 'If you select another currency, new transactions from this account will have this currency pre-selected.',
'reconcile_has_more' => 'Your Firefly III ledger has more money in it than your bank claims you should have. There are several options. Please choose what to do. Then, press "Confirm reconciliation".',
'reconcile_has_less' => 'Your Firefly III ledger has less money in it than your bank claims you should have. There are several options. Please choose what to do. Then, press "Confirm reconciliation".',
'reconcile_is_equal' => 'Your Firefly III ledger and your bank statements match. There is nothing to do. Please press "Confirm reconciliation" to confirm your input.',
'create_pos_reconcile_transaction' => 'Clear the selected transactions, and create a correction adding :amount to this asset account.',
'create_neg_reconcile_transaction' => 'Clear the selected transactions, and create a correction removing :amount from this asset account.',
'reconcile_do_nothing' => 'Clear the selected transactions, but do not correct.',
'reconcile_go_back' => 'You can always edit or delete a correction later.',
'confirm_reconciliation' => 'Confirm reconciliation',
'submitted_start_balance' => 'Submitted start balance',
'selected_transactions' => 'Selected transactions (:count)',
'already_cleared_transactions' => 'Already cleared transactions (:count)',
'submitted_end_balance' => 'Submitted end balance',
// categories: // categories:
'new_category' => 'New category', 'new_category' => 'New category',

View File

@ -7,7 +7,7 @@
{% block content %} {% block content %}
<div class="row"> <div class="row">
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12"> <div class="col-lg-9 col-md-12 col-sm-12 col-xs-12">
<div class="box"> <div class="box">
<div class="box-header with-border"> <div class="box-header with-border">
<h3 class="box-title">{{ 'reconcile_range'|_ }}</h3> <h3 class="box-title">{{ 'reconcile_range'|_ }}</h3>
@ -89,32 +89,17 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12"> <div class="col-lg-3 col-md-12 col-sm-12 col-xs-12">
<div class="box"> <div class="box">
<div class="box-header with-border"> <div class="box-header with-border">
<h3 class="box-title">{{ 'reconcile_options'|_ }}</h3> <h3 class="box-title">{{ 'reconcile_options'|_ }}</h3>
</div> </div>
<div class="box-body no-padding"> <div class="box-body">
<table class="table table-hover"> <p class="lead" id="difference"></p>
<thead>
<tr> <div class="btn-group">
<th style="width:50%;">{{ 'difference'|_ }}</th> <button class="btn btn-default store_reconcile" disabled><i class="fa fa-fw fa-check"></i> {{ 'store_reconcile'|_ }}</button>
<th>{{ 'actions'|_ }}</th> </div>
</tr>
</thead>
<tbody>
<tr>
<td>
<p class="lead" id="difference"></p>
</td>
<td>
<div class="btn-group">
<button class="btn btn-default store_reconcile" disabled><i class="fa fa-fw fa-check"></i> {{ 'store_reconcile'|_ }}</button>
</div>
</td>
</tr>
</tbody>
</table>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,4 +1,4 @@
<div class="modal-dialog"> <div class="modal-dialog modal-lg">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span>&times;</span><span class="sr-only">{{ 'close'|_ }}</span> <button type="button" class="close" data-dismiss="modal"><span>&times;</span><span class="sr-only">{{ 'close'|_ }}</span>
@ -7,31 +7,90 @@
</h4> </h4>
</div> </div>
<form style="display: inline;" id="income" action="some route" method="POST"> <form style="display: inline;" class="form-horizontal" id="income" action="{{ route }}" method="POST">
<div class="modal-body"> <div class="modal-body">
<input type="hidden" name="_token" value="{{ csrf_token() }}"/> <input type="hidden" name="_token" value="{{ csrf_token() }}"/>
<input type="hidden" name="start" value="{{ start.format('Y-m-d') }}"/> <input type="hidden" name="start" value="{{ start.format('Y-m-d') }}"/>
<input type="hidden" name="end" value="{{ end.format('Y-m-d') }}"/> <input type="hidden" name="end" value="{{ end.format('Y-m-d') }}"/>
<input type="hidden" name="startBalance" value="{{ startBalance }}"/>
<input type="hidden" name="endBalance" value="{{ endBalance }}"/>
{% for id in transactionIds %}
<input type="hidden" name="transactions[]" value="{{ id }}" />
{% endfor %}
You have selected X transactions. Total value X. Difference is zero <table class="table table-striped table-bordered">
reconcile will go ahead. (so no popup actually) <tr>
<td>{{ 'submitted_start_balance'|_ }} (date)</td>
<td>{{ startBalance|formatAmount }}</td>
</tr>
<tr>
<td>{{ trans('firefly.selected_transactions', {count: transactionIds|length}) }}</td>
<td>{{ amount|formatAmount }}</td>
</tr>
<tr>
<td>{{ trans('firefly.already_cleared_transactions', {count: clearedIds|length}) }}</td>
<td>{{ clearedAmount|formatAmount }}</td>
</tr>
<tr>
<td>{{ 'submitted_end_balance'|_ }} (date)</td>
<td>{{ endBalance|formatAmount }}</td>
</tr>
{% set diff = (startBalance - endBalance) + clearedAmount + amount %}
<tr>
<td>{{ 'difference'|_ }}</td>
<td>{{ diff|formatAmount }}</td>
</tr>
</table>
<p>
{% if diff > 0 %}
{{ 'reconcile_has_more'|_ }}
{% endif %}
{% if diff < 0 %}
{{ 'reconcile_has_less'|_ }}
{% endif %}
</p>
{% if diff == 0 %}
<p>
{{ 'reconcile_is_equal'|_ }}
</p>
<input type="hidden" name="reconcile" value="nothing">
{% endif %}
{% if diff != 0 %}
<div class="form-group">
<div class="col-lg-12">
<div class="radio">
<label>
Difference is X or -X. X = you have too much money in Firefly III register. -X = you are missing amount X <input type="radio" name="reconcile" value="create">
in your Firefly III register. {% if diff > 0 %}
{{ trans('firefly.create_neg_reconcile_transaction', {amount: (diff*-1)|formatAmount})|raw }}
Please correct this first, or allow firefly to create a reconcilliation transaction. You can always edit or delete this later: {% endif %}
{% if diff < 0 %}
[X] Create a reconciling withdrawal for amount X {{ trans('firefly.create_pos_reconcile_transaction', {amount: (diff*-1)|formatAmount})|raw }}
[X] Create a reconciling deposit for amount X. {% endif %}
</label>
<div class="input-group"> </div>
<div class="input-group-addon">{{ getCurrencySymbol()|raw }}</div> </div>
<input step="any" class="form-control" id="amount" value="{{ available }}" autocomplete="off" name="amount" type="number"/>
</div> </div>
<div class="form-group">
<div class="col-lg-12">
<div class="radio">
<label>
<input type="radio" checked name="reconcile" value="nothing"> {{ 'reconcile_do_nothing'|_ }}
</label>
</div>
</div>
</div>
<p>
{{ 'reconcile_go_back'|_ }}
</p>
{% endif %}
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'close'|_ }}</button> <button type="button" class="btn btn-default" data-dismiss="modal">{{ 'close'|_ }}</button>
<button type="submit" class="btn btn-primary">{{ 'update_amount'|_ }}</button> <button type="submit" class="btn btn-primary">{{ 'confirm_reconciliation'|_ }}</button>
</div> </div>
</form> </form>
</div> </div>

View File

@ -84,6 +84,7 @@
<td> <td>
{% if transaction.reconciled %} {% if transaction.reconciled %}
{{ transaction|transactionReconciled }} {{ transaction|transactionReconciled }}
<input type="hidden" name="cleared[]" class="cleared" data-id="{{ transaction.id }}" value="{{ transaction.transaction_amount }}">
{% else %} {% else %}
<input type="checkbox" name="reconciled[]" <input type="checkbox" name="reconciled[]"
data-younger="{% if transaction.date > end %}true{% else %}false{% endif %}" data-younger="{% if transaction.date > end %}true{% else %}false{% endif %}"

View File

@ -18,11 +18,7 @@
{% for account in accounts %} {% for account in accounts %}
<tr> <tr>
<td class="hidden-sm hidden-xs"> <td class="hidden-sm hidden-xs">
<div class="btn-group btn-group-xs"> <div class="btn-group btn-group-xs edit_tr_buttons"><a class="btn btn-default btn-xs" title="{{ 'edit'|_ }}" href="{{ route('accounts.edit',account.id) }}"><i class="fa fa-fw fa-pencil"></i></a><a class="btn btn-default btn-xs" title="{{ 'reconcile'|_ }}" href="{{ route('accounts.reconcile',account.id) }}"><i class="fa fa-fw fa-check"></i></a><a class="btn btn-danger btn-xs" title="{{ 'delete'|_ }}" href="{{ route('accounts.delete',account.id) }}"><i class="fa fa-fw fa-trash-o"></i></a></div>
<a class="btn btn-default btn-xs" title="{{ 'edit'|_ }}" href="{{ route('accounts.edit',account.id) }}"><i class="fa fa-fw fa-pencil"></i></a>
<a class="btn btn-default btn-xs" title="{{ 'reconcile'|_ }}" href="{{ route('accounts.reconcile',account.id) }}"><i class="fa fa-fw fa-check"></i></a>
<a class="btn btn-danger btn-xs" title="{{ 'delete'|_ }}" href="{{ route('accounts.delete',account.id) }}"><i class="fa fa-fw fa-trash-o"></i></a>
</div>
</td> </td>
<td data-value="{{ account.name }}"><a href="{{ route('accounts.show',account.id) }}">{{ account.name }}</a></td> <td data-value="{{ account.name }}"><a href="{{ route('accounts.show',account.id) }}">{{ account.name }}</a></td>
{% if what == "asset" %} {% if what == "asset" %}