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\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Navigation;
@ -68,16 +70,38 @@ class ReconcileController extends Controller
*/
public function overview(Request $request, Account $account, Carbon $start, Carbon $end)
{
$startBalance = $request->get('startBalance');
$endBalance = $request->get('endBalance');
$transactions = $request->get('transactions');
$startBalance = $request->get('startBalance');
$endBalance = $request->get('endBalance');
$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 = [
'is_zero' => false,
'post_uri' => route('accounts.reconcile.submit', [$account->id, $start->format('Ymd'), $end->format('Ymd')]),
'post_uri' => $route,
'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);
}
@ -175,4 +199,13 @@ class ReconcileController extends Controller
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));
}
$number = strval(number_format(round(floatval($value), 12), 12));
$number = strval(number_format(round(floatval($value), 12), 12,'',''));
return $number;
}
}

View File

@ -177,6 +177,23 @@ class JournalRepository implements JournalRepositoryInterface
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
*

View File

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

View File

@ -68,12 +68,19 @@ function storeReconcile() {
$.each($('.reconcile_checkbox:checked'), function (i, v) {
ids.push($(v).data('id'));
});
var cleared = [];
$.each($('input[class="cleared"]'), function (i, v) {
var obj = $(v);
cleared.push(obj.data('id'));
});
var variables = {
startBalance: parseFloat($('input[name="start_balance"]').val()),
endBalance: parseFloat($('input[name="end_balance"]').val()),
startDate: $('input[name="start_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());
@ -126,11 +133,15 @@ function calculateBalanceDifference() {
var startBalance = parseFloat($('input[name="start_balance"]').val());
var endBalance = parseFloat($('input[name="end_balance"]').val());
balanceDifference = startBalance - endBalance;
if (balanceDifference < 0) {
balanceDifference = balanceDifference * -1;
}
//if (balanceDifference < 0) {
// 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() {
// clear out the box:
$('#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);
}
/**
* 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) {
$('#transactions_holder').empty().html(data.html);
// as long as the dates are equal, changing the balance does not matter.
calculateBalanceDifference();
// any already cleared transactions must be added to / removed from selectedAmount.
includeClearedTransactions();
difference = balanceDifference;
updateDifference();

View File

@ -647,6 +647,18 @@ return [
'select_more_than_one_budget' => 'Please select more than one budget',
'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.',
'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:
'new_category' => 'New category',

View File

@ -7,7 +7,7 @@
{% block content %}
<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-header with-border">
<h3 class="box-title">{{ 'reconcile_range'|_ }}</h3>
@ -89,32 +89,17 @@
</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-header with-border">
<h3 class="box-title">{{ 'reconcile_options'|_ }}</h3>
</div>
<div class="box-body no-padding">
<table class="table table-hover">
<thead>
<tr>
<th style="width:50%;">{{ 'difference'|_ }}</th>
<th>{{ 'actions'|_ }}</th>
</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 class="box-body">
<p class="lead" id="difference"></p>
<div class="btn-group">
<button class="btn btn-default store_reconcile" disabled><i class="fa fa-fw fa-check"></i> {{ 'store_reconcile'|_ }}</button>
</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-header">
<button type="button" class="close" data-dismiss="modal"><span>&times;</span><span class="sr-only">{{ 'close'|_ }}</span>
@ -7,31 +7,90 @@
</h4>
</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">
<input type="hidden" name="_token" value="{{ csrf_token() }}"/>
<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="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
reconcile will go ahead. (so no popup actually)
<table class="table table-striped table-bordered">
<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
in your Firefly III register.
Please correct this first, or allow firefly to create a reconcilliation transaction. You can always edit or delete this later:
[X] Create a reconciling withdrawal for amount X
[X] Create a reconciling deposit for amount X.
<div class="input-group">
<div class="input-group-addon">{{ getCurrencySymbol()|raw }}</div>
<input step="any" class="form-control" id="amount" value="{{ available }}" autocomplete="off" name="amount" type="number"/>
<input type="radio" name="reconcile" value="create">
{% if diff > 0 %}
{{ trans('firefly.create_neg_reconcile_transaction', {amount: (diff*-1)|formatAmount})|raw }}
{% endif %}
{% if diff < 0 %}
{{ trans('firefly.create_pos_reconcile_transaction', {amount: (diff*-1)|formatAmount})|raw }}
{% endif %}
</label>
</div>
</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 class="modal-footer">
<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>
</form>
</div>

View File

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

View File

@ -18,11 +18,7 @@
{% for account in accounts %}
<tr>
<td class="hidden-sm hidden-xs">
<div class="btn-group btn-group-xs">
<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>
<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>
</td>
<td data-value="{{ account.name }}"><a href="{{ route('accounts.show',account.id) }}">{{ account.name }}</a></td>
{% if what == "asset" %}