mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
New code for #736
This commit is contained in:
parent
d413615943
commit
5530347bb2
173
app/Http/Controllers/Account/ReconcileController.php
Normal file
173
app/Http/Controllers/Account/ReconcileController.php
Normal file
@ -0,0 +1,173 @@
|
||||
<?php
|
||||
/**
|
||||
* ReconcileController.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\Account;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Navigation;
|
||||
use Preferences;
|
||||
use Response;
|
||||
use View;
|
||||
|
||||
/**
|
||||
* Class ReconcileController
|
||||
*
|
||||
* @package FireflyIII\Http\Controllers\Account
|
||||
*/
|
||||
class ReconcileController extends Controller
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
// translations:
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
View::share('mainTitleIcon', 'fa-credit-card');
|
||||
View::share('title', trans('firefly.accounts'));
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Account $account
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function overview(Request $request, Account $account, Carbon $start, Carbon $end)
|
||||
{
|
||||
$startBalance = $request->get('startBalance');
|
||||
$endBalance = $request->get('endBalance');
|
||||
$transactions = $request->get('transactions');
|
||||
|
||||
$return = [
|
||||
'is_zero' => false,
|
||||
'post_uri' => route('accounts.reconcile.submit', [$account->id, $start->format('Ymd'), $end->format('Ymd')]),
|
||||
'html' => '',
|
||||
];
|
||||
$return['html'] = view('accounts.reconcile.overview', compact('account', 'start', 'end'))->render();
|
||||
|
||||
return Response::json($return);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param Carbon|null $start
|
||||
* @param Carbon|null $end
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function reconcile(Account $account, Carbon $start = null, Carbon $end = null)
|
||||
{
|
||||
if ($account->accountType->type === AccountType::INITIAL_BALANCE) {
|
||||
return $this->redirectToOriginalAccount($account);
|
||||
}
|
||||
/** @var CurrencyRepositoryInterface $currencyRepos */
|
||||
$currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$currencyId = intval($account->getMeta('currency_id'));
|
||||
$currency = $currencyRepos->find($currencyId);
|
||||
if ($currencyId === 0) {
|
||||
$currency = app('amount')->getDefaultCurrency();
|
||||
}
|
||||
|
||||
// no start or end:
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
|
||||
// get start and end
|
||||
if (is_null($start) && is_null($end)) {
|
||||
$start = clone session('start', Navigation::startOfPeriod(new Carbon, $range));
|
||||
$end = clone session('end', Navigation::endOfPeriod(new Carbon, $range));
|
||||
}
|
||||
if (is_null($end)) {
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
}
|
||||
|
||||
$startDate = clone $start;
|
||||
$startDate->subDays(1);
|
||||
$startBalance = round(app('steam')->balance($account, $startDate), $currency->decimal_places);
|
||||
$endBalance = round(app('steam')->balance($account, $end), $currency->decimal_places);
|
||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
|
||||
$subTitle = trans('firefly.reconcile_account', ['account' => $account->name]);
|
||||
|
||||
// various links
|
||||
$transactionsUri = route('accounts.reconcile.transactions', [$account->id, '%start%', '%end%']);
|
||||
$overviewUri = route('accounts.reconcile.overview', [$account->id, '%start%', '%end%']);
|
||||
$indexUri = route('accounts.reconcile', [$account->id, '%start%', '%end%']);
|
||||
|
||||
return view(
|
||||
'accounts.reconcile.index', compact(
|
||||
'account', 'currency', 'subTitleIcon', 'start', 'end', 'subTitle', 'startBalance', 'endBalance', 'transactionsUri',
|
||||
'selectionStart', 'selectionEnd', 'overviewUri', 'indexUri'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function transactions(Account $account, Carbon $start, Carbon $end)
|
||||
{
|
||||
if ($account->accountType->type === AccountType::INITIAL_BALANCE) {
|
||||
return $this->redirectToOriginalAccount($account);
|
||||
}
|
||||
|
||||
// get the transactions
|
||||
$selectionStart = clone $start;
|
||||
$selectionStart->subDays(3);
|
||||
$selectionEnd = clone $end;
|
||||
$selectionEnd->addDays(3);
|
||||
|
||||
|
||||
// grab transactions:
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAccounts(new Collection([$account]))
|
||||
->setRange($selectionStart, $selectionEnd)->withBudgetInformation()->withOpposingAccount()->withCategoryInformation();
|
||||
$transactions = $collector->getJournals();
|
||||
$html = view('accounts.reconcile.transactions', compact('account', 'transactions', 'start', 'end', 'selectionStart', 'selectionEnd'))->render();
|
||||
|
||||
return Response::json(['html' => $html]);
|
||||
}
|
||||
|
||||
}
|
@ -250,74 +250,6 @@ class AccountController extends Controller
|
||||
return view('accounts.index', compact('what', 'subTitleIcon', 'subTitle', 'accounts'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Account $account
|
||||
* @param string $moment
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function reconcile(Request $request, Account $account, Carbon $start = null, Carbon $end = null)
|
||||
{
|
||||
if ($account->accountType->type === AccountType::INITIAL_BALANCE) {
|
||||
return $this->redirectToOriginalAccount($account);
|
||||
}
|
||||
/** @var CurrencyRepositoryInterface $currencyRepos */
|
||||
$currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$currencyId = intval($account->getMeta('currency_id'));
|
||||
$currency = $currencyRepos->find($currencyId);
|
||||
if ($currencyId === 0) {
|
||||
$currency = app('amount')->getDefaultCurrency();
|
||||
}
|
||||
|
||||
// no start or end:
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
|
||||
// get start and end
|
||||
if(is_null($start) && is_null($end)) {
|
||||
$start = clone session('start', Navigation::startOfPeriod(new Carbon, $range));
|
||||
$end = clone session('end', Navigation::endOfPeriod(new Carbon, $range));
|
||||
}
|
||||
if(is_null($end)) {
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
}
|
||||
|
||||
$startDate = clone $start;
|
||||
$startDate->subDays(1);
|
||||
$startBalance = round(app('steam')->balance($account, $startDate), $currency->decimal_places);
|
||||
$endBalance = round(app('steam')->balance($account, $end), $currency->decimal_places);
|
||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
|
||||
$subTitle = trans('firefly.reconcile_account', ['account' => $account->name]);
|
||||
|
||||
// get the transactions
|
||||
$selectionStart = clone $start;
|
||||
$selectionStart->subDays(7);
|
||||
$selectionEnd = clone $end;
|
||||
$selectionEnd->addDays(5);
|
||||
|
||||
// grab transactions:
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAccounts(new Collection([$account]))
|
||||
->setRange($selectionStart, $selectionEnd)->withBudgetInformation()->withOpposingAccount()->withCategoryInformation();
|
||||
$transactions = $collector->getJournals();
|
||||
|
||||
return view('accounts.reconcile', compact('account', 'currency', 'subTitleIcon', 'start', 'end', 'subTitle', 'startBalance', 'endBalance','transactions','selectionStart','selectionEnd'));
|
||||
|
||||
// prep for "specific date" view.
|
||||
if (strlen($moment) > 0 && $moment !== 'all') {
|
||||
$start = new Carbon($moment);
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return view(
|
||||
'accounts.show',
|
||||
compact('account', 'currency', 'moment', 'periods', 'subTitleIcon', 'transactions', 'subTitle', 'start', 'end', 'chartUri')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show an account.
|
||||
*
|
||||
|
174
public/js/ff/accounts/reconcile.js
vendored
174
public/js/ff/accounts/reconcile.js
vendored
@ -18,20 +18,178 @@
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var balanceDifference = 0;
|
||||
var difference = 0;
|
||||
var selectedAmount = 0;
|
||||
var reconcileStarted = false;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
$(function () {
|
||||
"use strict";
|
||||
$('input[type="date"]').on('change', showUpdateButton);
|
||||
$('.update_view').on('click', updateView);
|
||||
|
||||
/*
|
||||
Respond to changes in balance statements.
|
||||
*/
|
||||
$('input[type="number"]').on('change', function () {
|
||||
if (reconcileStarted) {
|
||||
calculateBalanceDifference();
|
||||
difference = balanceDifference - selectedAmount;
|
||||
updateDifference();
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
Respond to changes in the date range.
|
||||
*/
|
||||
$('input[type="date"]').on('change', function () {
|
||||
if (reconcileStarted) {
|
||||
// hide original instructions.
|
||||
$('.select_transactions_instruction').hide();
|
||||
|
||||
// show date-change warning
|
||||
$('.date_change_warning').show();
|
||||
|
||||
// show update button
|
||||
$('.change_date_button').show();
|
||||
}
|
||||
});
|
||||
|
||||
$('.change_date_button').click(startReconcile);
|
||||
$('.start_reconcile').click(startReconcile);
|
||||
$('.store_reconcile').click(storeReconcile);
|
||||
|
||||
});
|
||||
|
||||
function showUpdateButton() {
|
||||
$('.update_date_button').show();
|
||||
function storeReconcile() {
|
||||
// get modal HTML:
|
||||
var ids = [];
|
||||
$.each($('.reconcile_checkbox:checked'), function (i, v) {
|
||||
ids.push($(v).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
|
||||
};
|
||||
var uri = overviewUri.replace('%start%', $('input[name="start_date"]').val()).replace('%end%', $('input[name="end_date"]').val());
|
||||
|
||||
|
||||
$.getJSON(uri, variables).done(function (data) {
|
||||
if (data.is_zero === false) {
|
||||
$('#defaultModal').empty().html(data.html).modal('show');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateView() {
|
||||
var startDate = $('input[name="start_date"]').val();
|
||||
var endDate = $('input[name="end_date"]').val();
|
||||
window.location = '/accounts/reconcile/2/' + startDate + '/' + endDate;
|
||||
/**
|
||||
* What happens when you check a checkbox:
|
||||
* @param e
|
||||
*/
|
||||
function checkReconciledBox(e) {
|
||||
var el = $(e.target);
|
||||
var amount = parseFloat(el.val());
|
||||
console.log('Amount is ' + amount);
|
||||
// if checked, add to selected amount
|
||||
if (el.prop('checked') === true && el.data('younger') === false) {
|
||||
console.log("Sum is: " + selectedAmount + " - " + amount + " = " + (selectedAmount - amount));
|
||||
selectedAmount = selectedAmount - amount;
|
||||
}
|
||||
if (el.prop('checked') === false && el.data('younger') === false) {
|
||||
console.log("Sum is: " + selectedAmount + " + " + amount + " = " + (selectedAmount + amount));
|
||||
selectedAmount = selectedAmount + amount;
|
||||
}
|
||||
difference = balanceDifference - selectedAmount;
|
||||
updateDifference();
|
||||
allowReconcile();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function allowReconcile() {
|
||||
var count = $('.reconcile_checkbox:checked').length;
|
||||
console.log('Count checkboxes is ' + count);
|
||||
if (count > 0) {
|
||||
$('.store_reconcile').prop('disabled', false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the difference between given start and end balance
|
||||
* and put it in balanceDifference.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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>'));
|
||||
var uri = transactionsUri.replace('%start%', $('input[name="start_date"]').val()).replace('%end%', $('input[name="end_date"]').val());
|
||||
var index = indexUri.replace('%start%', $('input[name="start_date"]').val()).replace('%end%', $('input[name="end_date"]').val());
|
||||
window.history.pushState('object or string', "Reconcile account", index);
|
||||
|
||||
$.getJSON(uri).done(placeTransactions);
|
||||
}
|
||||
|
||||
function placeTransactions(data) {
|
||||
$('#transactions_holder').empty().html(data.html);
|
||||
|
||||
|
||||
// as long as the dates are equal, changing the balance does not matter.
|
||||
calculateBalanceDifference();
|
||||
difference = balanceDifference;
|
||||
updateDifference();
|
||||
|
||||
// enable the check buttons:
|
||||
$('.reconcile_checkbox').prop('disabled', false).unbind('change').change(checkReconciledBox);
|
||||
|
||||
// show the other instruction:
|
||||
$('.select_transactions_instruction').show();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function startReconcile() {
|
||||
|
||||
reconcileStarted = true;
|
||||
|
||||
// hide the start button.
|
||||
$('.start_reconcile').hide();
|
||||
|
||||
// hide the start instructions:
|
||||
$('.update_balance_instruction').hide();
|
||||
|
||||
// hide date-change warning
|
||||
$('.date_change_warning').hide();
|
||||
|
||||
// hide update button
|
||||
$('.change_date_button').hide();
|
||||
|
||||
getTransactionsForRange();
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function updateDifference() {
|
||||
var addClass = 'text-info';
|
||||
if (difference > 0) {
|
||||
addClass = 'text-success';
|
||||
}
|
||||
if (difference < 0) {
|
||||
addClass = 'text-danger';
|
||||
}
|
||||
$('#difference').addClass(addClass).text(accounting.formatMoney(difference));
|
||||
}
|
@ -619,8 +619,19 @@ return [
|
||||
'cash_accounts' => 'Cash accounts',
|
||||
'Cash account' => 'Cash account',
|
||||
'reconcile_account' => 'Reconcile account ":account"',
|
||||
'end_of_reconcile_period' => 'End of reconcile period: :period',
|
||||
'start_of_reconcile_period' => 'Start of reconcile period: :period',
|
||||
'end_of_reconcile_period' => 'End of reconcile period: :period',
|
||||
'start_of_reconcile_period' => 'Start of reconcile period: :period',
|
||||
'start_balance' => 'Start balance',
|
||||
'end_balance' => 'End balance',
|
||||
'update_balance_dates_instruction' => 'Match the amounts and dates above to your bank statement, and press "Start reconciling"',
|
||||
'select_transactions_instruction' => 'Select the transactions that appear on your bank statement.',
|
||||
'select_range_and_balance' => 'First verify the date-range and balances. Then press "Start reconciling"',
|
||||
'date_change_instruction' => 'If you change the date range now, any progress will be lost.',
|
||||
'update_selection' => 'Update selection',
|
||||
'store_reconcile' => 'Store reconciliation',
|
||||
'reconcile_options' => 'Reconciliation options',
|
||||
'reconcile_range' => 'Reconciliation range',
|
||||
'start_reconcile' => 'Start reconciling',
|
||||
'cash' => 'cash',
|
||||
'account_type' => 'Account type',
|
||||
'save_transactions_by_moving' => 'Save these transaction(s) by moving them to another account:',
|
||||
|
@ -75,9 +75,13 @@
|
||||
<div class="select_transactions_instruction" style="display:none;">
|
||||
{{ 'select_transactions_instruction'|_ }}
|
||||
</div>
|
||||
<div class="date_change_warning text-danger" style="display:none;">
|
||||
{{ 'date_change_instruction'|_ }}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a href="#" class="btn btn-default start_reconcile">{{ 'start_reconcile'|_ }}</a>
|
||||
<a href="#" class="btn btn-default change_date_button" style="display: none;">{{ 'update_selection'|_ }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
@ -124,120 +128,9 @@
|
||||
<h3 class="box-title">{{ 'transactions'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<table class="table table-striped table-condensed">
|
||||
<thead>
|
||||
<tr class="ignore">
|
||||
<th class="hidden-xs" colspan="2"> </th>
|
||||
<th>{{ trans('list.description') }}</th>
|
||||
<th style="text-align:right;">{{ trans('list.amount') }}</th>
|
||||
<th class="hidden-xs hidden-sm hidden-md">{{ trans('list.reconcile') }}</th>
|
||||
<th class="hidden-xs hidden-sm">{{ trans('list.date') }}</th>
|
||||
<th class="hidden-xs hidden-sm hidden-md">{{ trans('list.from') }}</th>
|
||||
<th class="hidden-xs hidden-sm hidden-md">{{ trans('list.to') }}</th>
|
||||
|
||||
<th class="hidden-xs"><i class="fa fa-tasks fa-fw" title="{{ trans('list.budget') }}"></i></th>
|
||||
<th class="hidden-xs"><i class="fa fa-bar-chart fa-fw" title="{{ trans('list.category') }}"></i></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{# data for previous/next markers #}
|
||||
{% set endSet = false %}
|
||||
{% set startSet = false %}
|
||||
{% for transaction in transactions %}
|
||||
{# start marker #}
|
||||
{% if transaction.date < start and startSet == false %}
|
||||
<tr>
|
||||
<td colspan="5">
|
||||
|
||||
</td>
|
||||
<td colspan="3">
|
||||
<span class="label label-default">
|
||||
{{ trans('firefly.start_of_reconcile_period', {period: start.formatLocalized(monthAndDayFormat) }) }}
|
||||
</span>
|
||||
</td>
|
||||
<td colspan="2">
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
{% set startSet = true %}
|
||||
{% endif %}
|
||||
|
||||
{# end marker #}
|
||||
{% if transaction.date <= end and endSet == false %}
|
||||
<tr>
|
||||
<td colspan="5">
|
||||
|
||||
</td>
|
||||
<td colspan="3">
|
||||
<span class="label label-default">
|
||||
{{ trans('firefly.end_of_reconcile_period', {period: end.formatLocalized(monthAndDayFormat) }) }}
|
||||
</span>
|
||||
</td>
|
||||
<td colspan="2">
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
{% set endSet = true %}
|
||||
{% endif %}
|
||||
|
||||
<tr data-date="{{ transaction.date.format('Y-m-d') }}" data-id="{{ transaction.journal_id }}"
|
||||
data-transaction-id="{{ transaction.id }}">
|
||||
<td class="hidden-xs">
|
||||
<div class="btn-group btn-group-xs">
|
||||
<a href="{{ route('transactions.edit',transaction.journal_id) }}" class="btn btn-xs btn-default"><i
|
||||
class="fa fa-fw fa-pencil"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
{# icon #}
|
||||
<td class="hidden-xs">
|
||||
{{ transaction|transactionIcon }}
|
||||
</td>
|
||||
|
||||
{# description #}
|
||||
<td>
|
||||
<a href="{{ route('transactions.show',transaction.journal_id) }}">
|
||||
{{ transaction|transactionDescription }}
|
||||
</a>
|
||||
{# is a split journal #}
|
||||
{{ transaction|transactionIsSplit }}
|
||||
|
||||
{# count attachments #}
|
||||
{{ transaction|transactionHasAtt }}
|
||||
|
||||
|
||||
</td>
|
||||
<td style="text-align: right;"><span style="margin-right:5px;">{{ transaction|transactionAmount }}</span></td>
|
||||
<td>
|
||||
{% if transaction.reconciled %}
|
||||
{{ transaction|transactionReconciled }}
|
||||
{% else %}
|
||||
<input type="checkbox" name="reconciled[]"
|
||||
data-younger="{% if transaction.date > end %}true{% else %}false{% endif %}"
|
||||
value="{{ transaction.transaction_amount }}" data-id="{{ transaction.id }}" disabled class="reconcile_checkbox">
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="hidden-sm hidden-xs">
|
||||
{{ transaction.date.formatLocalized(monthAndDayFormat) }}
|
||||
</td>
|
||||
<td class="hidden-xs hidden-sm hidden-md">
|
||||
{# all source accounts #}
|
||||
{{ transaction|transactionSourceAccount }}
|
||||
</td>
|
||||
<td class="hidden-xs hidden-sm hidden-md">
|
||||
{# all destination accounts #}
|
||||
{{ transaction|transactionDestinationAccount }}
|
||||
</td>
|
||||
<td class="hidden-xs">
|
||||
{{ transaction|transactionBudgets }}
|
||||
</td>
|
||||
<td class="hidden-xs">
|
||||
{{ transaction|transactionCategories }}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="transactions_holder">
|
||||
<p class="text-center lead">{{ 'select_range_and_balance'|_ }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -253,6 +146,9 @@
|
||||
var accountID = {{ account.id }};
|
||||
var startBalance = {{ startBalance }};
|
||||
var endBalance = {{ endBalance }};
|
||||
var transactionsUri = '{{ transactionsUri }}';
|
||||
var overviewUri = '{{ overviewUri }}';
|
||||
var indexUri = '{{ indexUri }}';
|
||||
</script>
|
||||
<script src="js/ff/accounts/reconcile.js?v={{ FF_VERSION }}" type="text/javascript"></script>
|
||||
{% endblock %}
|
||||
|
39
resources/views/accounts/reconcile/overview.twig
Normal file
39
resources/views/accounts/reconcile/overview.twig
Normal file
@ -0,0 +1,39 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"><span>×</span><span class="sr-only">{{ 'close'|_ }}</span>
|
||||
</button>
|
||||
<h4 class="modal-title">Overview of reconciliation
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<form style="display: inline;" id="income" action="some 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') }}"/>
|
||||
|
||||
You have selected X transactions. Total value X. Difference is zero
|
||||
reconcile will go ahead. (so no popup actually)
|
||||
|
||||
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"/>
|
||||
</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">{{ 'update_amount'|_ }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
131
resources/views/accounts/reconcile/transactions.twig
Normal file
131
resources/views/accounts/reconcile/transactions.twig
Normal file
@ -0,0 +1,131 @@
|
||||
<table class="table table-striped table-condensed">
|
||||
<thead>
|
||||
<tr class="ignore">
|
||||
<th class="hidden-xs" colspan="2"> </th>
|
||||
<th>{{ trans('list.description') }}</th>
|
||||
<th style="text-align:right;">{{ trans('list.amount') }}</th>
|
||||
<th class="hidden-xs hidden-sm hidden-md">{{ trans('list.reconcile') }}</th>
|
||||
<th class="hidden-xs hidden-sm">{{ trans('list.date') }}</th>
|
||||
<th class="hidden-xs hidden-sm hidden-md">{{ trans('list.from') }}</th>
|
||||
<th class="hidden-xs hidden-sm hidden-md">{{ trans('list.to') }}</th>
|
||||
|
||||
<th class="hidden-xs"><i class="fa fa-tasks fa-fw" title="{{ trans('list.budget') }}"></i></th>
|
||||
<th class="hidden-xs"><i class="fa fa-bar-chart fa-fw" title="{{ trans('list.category') }}"></i></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{# data for previous/next markers #}
|
||||
{% set endSet = false %}
|
||||
{% set startSet = false %}
|
||||
{% for transaction in transactions %}
|
||||
{# start marker #}
|
||||
{% if transaction.date < start and startSet == false %}
|
||||
<tr>
|
||||
<td colspan="5">
|
||||
|
||||
</td>
|
||||
<td colspan="3">
|
||||
<span class="label label-default">
|
||||
{{ trans('firefly.start_of_reconcile_period', {period: start.formatLocalized(monthAndDayFormat) }) }}
|
||||
</span>
|
||||
</td>
|
||||
<td colspan="2">
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
{% set startSet = true %}
|
||||
{% endif %}
|
||||
|
||||
{# end marker #}
|
||||
{% if transaction.date <= end and endSet == false %}
|
||||
<tr>
|
||||
<td colspan="5">
|
||||
|
||||
</td>
|
||||
<td colspan="3">
|
||||
<span class="label label-default">
|
||||
{{ trans('firefly.end_of_reconcile_period', {period: end.formatLocalized(monthAndDayFormat) }) }}
|
||||
</span>
|
||||
</td>
|
||||
<td colspan="2">
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
{% set endSet = true %}
|
||||
{% endif %}
|
||||
|
||||
<tr data-date="{{ transaction.date.format('Y-m-d') }}" data-id="{{ transaction.journal_id }}"
|
||||
data-transaction-id="{{ transaction.id }}">
|
||||
<td class="hidden-xs">
|
||||
<div class="btn-group btn-group-xs">
|
||||
<a href="{{ route('transactions.edit',transaction.journal_id) }}" class="btn btn-xs btn-default"><i
|
||||
class="fa fa-fw fa-pencil"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
{# icon #}
|
||||
<td class="hidden-xs">
|
||||
{{ transaction|transactionIcon }}
|
||||
</td>
|
||||
|
||||
{# description #}
|
||||
<td>
|
||||
<a href="{{ route('transactions.show',transaction.journal_id) }}">
|
||||
{{ transaction|transactionDescription }}
|
||||
</a>
|
||||
{# is a split journal #}
|
||||
{{ transaction|transactionIsSplit }}
|
||||
|
||||
{# count attachments #}
|
||||
{{ transaction|transactionHasAtt }}
|
||||
|
||||
|
||||
</td>
|
||||
<td style="text-align: right;"><span style="margin-right:5px;">{{ transaction|transactionAmount }}</span></td>
|
||||
<td>
|
||||
{% if transaction.reconciled %}
|
||||
{{ transaction|transactionReconciled }}
|
||||
{% else %}
|
||||
<input type="checkbox" name="reconciled[]"
|
||||
data-younger="{% if transaction.date > end %}true{% else %}false{% endif %}"
|
||||
value="{{ transaction.transaction_amount }}" data-id="{{ transaction.id }}" disabled class="reconcile_checkbox">
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="hidden-sm hidden-xs">
|
||||
{{ transaction.date.formatLocalized(monthAndDayFormat) }}
|
||||
</td>
|
||||
<td class="hidden-xs hidden-sm hidden-md">
|
||||
{# all source accounts #}
|
||||
{{ transaction|transactionSourceAccount }}
|
||||
</td>
|
||||
<td class="hidden-xs hidden-sm hidden-md">
|
||||
{# all destination accounts #}
|
||||
{{ transaction|transactionDestinationAccount }}
|
||||
</td>
|
||||
<td class="hidden-xs">
|
||||
{{ transaction|transactionBudgets }}
|
||||
</td>
|
||||
<td class="hidden-xs">
|
||||
{{ transaction|transactionCategories }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
{# if the start marker has not been generated yet, do it now, at the end of the loop. #}
|
||||
{% if startSet == false %}
|
||||
<tr>
|
||||
<td colspan="5">
|
||||
|
||||
</td>
|
||||
<td colspan="3">
|
||||
<span class="label label-default">
|
||||
{{ trans('firefly.start_of_reconcile_period', {period: start.formatLocalized(monthAndDayFormat) }) }}
|
||||
</span>
|
||||
</td>
|
||||
<td colspan="2">
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
{% set startSet = true %}
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
@ -21,7 +21,7 @@
|
||||
|
||||
{# description #}
|
||||
<td>
|
||||
{# count attachments #}
|
||||
{# is reconciled? #}
|
||||
{{ transaction|transactionReconciled }}
|
||||
|
||||
<a href="{{ route('transactions.show',transaction.journal_id) }}">
|
||||
|
@ -90,10 +90,15 @@ Route::group(
|
||||
Route::get('{what}', ['uses' => 'AccountController@index', 'as' => 'index'])->where('what', 'revenue|asset|expense');
|
||||
Route::get('create/{what}', ['uses' => 'AccountController@create', 'as' => 'create'])->where('what', 'revenue|asset|expense');
|
||||
Route::get('edit/{account}', ['uses' => 'AccountController@edit', 'as' => 'edit']);
|
||||
Route::get('reconcile/{account}/{start_date?}/{end_date?}', ['uses' => 'AccountController@reconcile', 'as' => 'reconcile']);
|
||||
Route::get('delete/{account}', ['uses' => 'AccountController@delete', 'as' => 'delete']);
|
||||
Route::get('show/{account}/{moment?}', ['uses' => 'AccountController@show', 'as' => 'show']);
|
||||
|
||||
// reconcile routes:
|
||||
Route::get('reconcile/{account}/index/{start_date?}/{end_date?}', ['uses' => 'Account\ReconcileController@reconcile', 'as' => 'reconcile']);
|
||||
Route::get('reconcile/{account}/transactions/{start_date?}/{end_date?}', ['uses' => 'Account\ReconcileController@transactions', 'as' => 'reconcile.transactions']);
|
||||
Route::get('reconcile/{account}/overview/{start_date?}/{end_date?}', ['uses' => 'Account\ReconcileController@overview', 'as' => 'reconcile.overview']);
|
||||
Route::post('reconcile/{account}/submit/{start_date?}/{end_date?}', ['uses' => 'Account\ReconcileController@submit', 'as' => 'reconcile.submit']);
|
||||
|
||||
Route::post('store', ['uses' => 'AccountController@store', 'as' => 'store']);
|
||||
Route::post('update/{account}', ['uses' => 'AccountController@update', 'as' => 'update']);
|
||||
Route::post('destroy/{account}', ['uses' => 'AccountController@destroy', 'as' => 'destroy']);
|
||||
|
Loading…
Reference in New Issue
Block a user