Feature: Keyboard navigation in ledger with Remove and Undo.
This commit is contained in:
parent
4e85d97e47
commit
248a841695
|
@ -25,8 +25,9 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- <script src="/js/mousetrap.min.js"></script> -->
|
<script src="/js/mousetrap.min.js"></script>
|
||||||
<script src="/js/jquery-2.1.0.min.js"></script>
|
<script src="/js/jquery-2.1.0.min.js"></script>
|
||||||
|
<script src="/js/jquery.scrolltoview.js"></script>
|
||||||
<script src="/js/lodash.min.js"></script>
|
<script src="/js/lodash.min.js"></script>
|
||||||
<script src="/js/bootstrap.min.js"></script>
|
<script src="/js/bootstrap.min.js"></script>
|
||||||
<script src="/js/ace/ace.js"></script>
|
<script src="/js/ace/ace.js"></script>
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
/*!
|
||||||
|
* jQuery scrollintoview() plugin and :scrollable selector filter
|
||||||
|
*
|
||||||
|
* Version 1.8 (14 Jul 2011)
|
||||||
|
* Requires jQuery 1.4 or newer
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011 Robert Koritnik
|
||||||
|
* Licensed under the terms of the MIT license
|
||||||
|
* http://www.opensource.org/licenses/mit-license.php
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function ($) {
|
||||||
|
var settings = {
|
||||||
|
topBar: 51 // hack for brewman with bootstrap v3.1.1
|
||||||
|
};
|
||||||
|
|
||||||
|
var rootrx = /^(?:html)$/i;
|
||||||
|
|
||||||
|
// gets border dimensions
|
||||||
|
var borders = function (domElement, styles) {
|
||||||
|
styles = styles || (document.defaultView && document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(domElement, null) : domElement.currentStyle);
|
||||||
|
var px = document.defaultView && document.defaultView.getComputedStyle ? true : false;
|
||||||
|
var b = {
|
||||||
|
top: (parseFloat(px ? styles.borderTopWidth : $.css(domElement, "borderTopWidth")) || 0),
|
||||||
|
left: (parseFloat(px ? styles.borderLeftWidth : $.css(domElement, "borderLeftWidth")) || 0),
|
||||||
|
bottom: (parseFloat(px ? styles.borderBottomWidth : $.css(domElement, "borderBottomWidth")) || 0),
|
||||||
|
right: (parseFloat(px ? styles.borderRightWidth : $.css(domElement, "borderRightWidth")) || 0)
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
top: b.top,
|
||||||
|
left: b.left,
|
||||||
|
bottom: b.bottom,
|
||||||
|
right: b.right,
|
||||||
|
vertical: b.top + b.bottom,
|
||||||
|
horizontal: b.left + b.right
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var dimensions = function ($element) {
|
||||||
|
var win = $(window);
|
||||||
|
var isRoot = rootrx.test($element[0].nodeName);
|
||||||
|
return {
|
||||||
|
border: isRoot ? { top: 0, left: 0, bottom: 0, right: 0} : borders($element[0]),
|
||||||
|
scroll: {
|
||||||
|
top: (isRoot ? win : $element).scrollTop(),
|
||||||
|
left: (isRoot ? win : $element).scrollLeft()
|
||||||
|
},
|
||||||
|
scrollbar: {
|
||||||
|
right: isRoot ? 0 : $element.innerWidth() - $element[0].clientWidth,
|
||||||
|
bottom: isRoot ? 0 : $element.innerHeight() - $element[0].clientHeight
|
||||||
|
},
|
||||||
|
rect: (function () {
|
||||||
|
var r = $element[0].getBoundingClientRect();
|
||||||
|
return {
|
||||||
|
top: isRoot ? 0 : r.top,
|
||||||
|
left: isRoot ? 0 : r.left,
|
||||||
|
bottom: isRoot ? $element[0].clientHeight : r.bottom,
|
||||||
|
right: isRoot ? $element[0].clientWidth : r.right
|
||||||
|
};
|
||||||
|
})()
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
$.fn.extend({
|
||||||
|
scrollintoview: function (options) {
|
||||||
|
/// <summary>Scrolls the first element in the set into view by scrolling its closest scrollable parent.</summary>
|
||||||
|
/// <param name="options" type="Object">Additional options that can configure scrolling:
|
||||||
|
/// </param>
|
||||||
|
/// <return type="jQuery">Returns the same jQuery set that this function was run on.</return>
|
||||||
|
|
||||||
|
options = $.extend({}, settings, options);
|
||||||
|
|
||||||
|
var el = this.eq(0);
|
||||||
|
var scroller = $("html,body");
|
||||||
|
|
||||||
|
// check if there's anything to scroll in the first place
|
||||||
|
var dim = {
|
||||||
|
e: dimensions(el),
|
||||||
|
s: dimensions(scroller)
|
||||||
|
};
|
||||||
|
|
||||||
|
var rel = {
|
||||||
|
top: dim.e.rect.top,
|
||||||
|
bottom: dim.s.rect.bottom - dim.s.scrollbar.bottom - dim.e.rect.bottom
|
||||||
|
};
|
||||||
|
|
||||||
|
var scrollTop = null;
|
||||||
|
|
||||||
|
// scroll
|
||||||
|
if (rel.top < options.topBar) {
|
||||||
|
scrollTop = dim.s.scroll.top + rel.top - options.topBar;
|
||||||
|
}
|
||||||
|
else if (rel.top > 0 && rel.bottom < 0) {
|
||||||
|
scrollTop = dim.s.scroll.top + Math.min(rel.top, -rel.bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
// scroll if needed
|
||||||
|
if (scrollTop !== null) {
|
||||||
|
scroller.scrollTop(scrollTop);
|
||||||
|
}
|
||||||
|
// return set back
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})(jQuery);
|
|
@ -38,7 +38,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table id="gvGrid" class="table table-condensed table-bordered table-striped">
|
<table id="gvGrid" class="table table-condensed table-bordered table-striped" keypress="shortcuts">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Date</th>
|
<th>Date</th>
|
||||||
|
@ -51,7 +51,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="item in info.Body" ng-class="{danger:!item.Posted, warning:$index==selected}"
|
<tr id="{{$index}}" ng-repeat="item in display.Body" ng-class="{danger:!item.Posted, warning:$index==selected}"
|
||||||
ng-click="setSelected($index)">
|
ng-click="setSelected($index)">
|
||||||
<td class="no-wrap">{{item.Date}}</td>
|
<td class="no-wrap">{{item.Date}}</td>
|
||||||
<td><a href="{{item.Url}}">{{item.Name}}</a></td>
|
<td><a href="{{item.Url}}">{{item.Name}}</a></td>
|
||||||
|
@ -64,13 +64,13 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="no-wrap">{{info.Footer.Date}}</td>
|
<td class="no-wrap">{{display.Footer.Date}}</td>
|
||||||
<td>{{info.Footer.Name}}</td>
|
<td>{{display.Footer.Name}}</td>
|
||||||
<td>{{info.Footer.Type}}</td>
|
<td>{{display.Footer.Type}}</td>
|
||||||
<td>{{info.Footer.Narration}}</td>
|
<td>{{display.Footer.Narration}}</td>
|
||||||
<td class="text-right no-wrap">{{info.Footer.Debit | currency}}</td>
|
<td class="text-right no-wrap">{{display.Footer.Debit | currency}}</td>
|
||||||
<td class="text-right no-wrap">{{info.Footer.Credit | currency}}</td>
|
<td class="text-right no-wrap">{{display.Footer.Credit | currency}}</td>
|
||||||
<td class="text-right no-wrap">{{info.Footer.Running | accounting}}</td>
|
<td class="text-right no-wrap">{{display.Footer.Running | accounting}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -125,13 +125,15 @@ overlord_directive.directive('keypress', [function () {
|
||||||
var attribute = scope.$eval(attrs.keypress || '{}');
|
var attribute = scope.$eval(attrs.keypress || '{}');
|
||||||
for (var k in attribute) {
|
for (var k in attribute) {
|
||||||
(function (k) {
|
(function (k) {
|
||||||
Mousetrap.bind(k, function () {
|
Mousetrap.bind(k, attribute[k]);
|
||||||
return attribute[k](scope, element);
|
|
||||||
});
|
|
||||||
})(k);
|
})(k);
|
||||||
}
|
}
|
||||||
element.on('$destroy', function () {
|
element.on('$destroy', function () {
|
||||||
$interval.cancel(timeoutId);
|
for (var k in attribute) {
|
||||||
|
(function (k) {
|
||||||
|
Mousetrap.unbind(k);
|
||||||
|
})(k);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
var LedgerCtrl = ['$scope', '$routeParams', '$location', 'dateFilter', 'ledger', 'Ledger', 'Account', function ($scope, $routeParams, $location, dateFilter, ledger, Ledger, Account) {
|
var LedgerCtrl = ['$scope', '$routeParams', '$location', 'dateFilter', 'ledger', 'Ledger', 'Account', function ($scope, $routeParams, $location, dateFilter, ledger, Ledger, Account) {
|
||||||
$scope.info = ledger;
|
$scope.info = ledger;
|
||||||
|
$scope.hidden = [];
|
||||||
$scope.show = function () {
|
$scope.show = function () {
|
||||||
var id = $scope.info.Ledger.LedgerID;
|
var id = $scope.info.Ledger.LedgerID;
|
||||||
var start_date = angular.isDate($scope.info.StartDate) ? dateFilter($scope.info.StartDate, 'dd-MMM-yyyy') : $scope.info.StartDate;
|
var start_date = angular.isDate($scope.info.StartDate) ? dateFilter($scope.info.StartDate, 'dd-MMM-yyyy') : $scope.info.StartDate;
|
||||||
|
@ -14,6 +15,7 @@ var LedgerCtrl = ['$scope', '$routeParams', '$location', 'dateFilter', 'ledger',
|
||||||
$location.path('/Ledger/' + id).search('StartDate', start_date).search('FinishDate', finish_date);
|
$location.path('/Ledger/' + id).search('StartDate', start_date).search('FinishDate', finish_date);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.downloadTable = function () {
|
$scope.downloadTable = function () {
|
||||||
var table = $('#gvGrid'),
|
var table = $('#gvGrid'),
|
||||||
html = table.clone().wrap('<div></div>').parent().html();
|
html = table.clone().wrap('<div></div>').parent().html();
|
||||||
|
@ -32,32 +34,85 @@ var LedgerCtrl = ['$scope', '$routeParams', '$location', 'dateFilter', 'ledger',
|
||||||
$scope.selected = index;
|
$scope.selected = index;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.$watch('hidden', function () {
|
||||||
|
$scope.display = doFilter($scope.info, $scope.hidden);
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
var doFilter = function (input, hidden) {
|
||||||
|
var ledger = angular.copy(input),
|
||||||
|
data = ledger.Body,
|
||||||
|
footer = ledger.Footer,
|
||||||
|
debit = 0, credit = 0, running = 0;
|
||||||
|
|
||||||
|
if (hidden.length !== 0) {
|
||||||
|
data = _.filter(data, function (item) {
|
||||||
|
return !_.any(hidden, function (hi) {
|
||||||
|
if (!item.hasOwnProperty('Url') && !hi.hasOwnProperty('Url')) {
|
||||||
|
return true;
|
||||||
|
} else if (item.hasOwnProperty('Url') !== hi.hasOwnProperty('Url')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return item.Url === hi.Url;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_.forEach(data, function (item) {
|
||||||
|
debit += item.Debit;
|
||||||
|
credit += item.Credit;
|
||||||
|
running = debit - credit;
|
||||||
|
item.Running = running;
|
||||||
|
});
|
||||||
|
footer.Debit = debit;
|
||||||
|
footer.Credit = credit;
|
||||||
|
footer.Running = running;
|
||||||
|
return {Body: data, Footer: footer};
|
||||||
|
};
|
||||||
|
|
||||||
$scope.shortcuts = {
|
$scope.shortcuts = {
|
||||||
'up': function () {
|
'up': function (e) {
|
||||||
if ($scope.selected > 0) {
|
if ($scope.selected > 0) {
|
||||||
$scope.$apply(function () {
|
$scope.$apply(function () {
|
||||||
$scope.selected = $scope.selected -= 1;
|
$scope.selected = $scope.selected -= 1;
|
||||||
});
|
});
|
||||||
|
$("#" + $scope.selected).scrollintoview({duration: 'fast'});
|
||||||
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'down': function () {
|
'down': function (e) {
|
||||||
if ($scope.selected < $scope.info.Body.length - 1) {
|
if ($scope.selected < $scope.info.Body.length - 1) {
|
||||||
$scope.$apply(function () {
|
$scope.$apply(function () {
|
||||||
$scope.selected = $scope.selected += 1;
|
$scope.selected = $scope.selected += 1;
|
||||||
});
|
});
|
||||||
|
$("#" + $scope.selected).scrollintoview({duration: 'fast'});
|
||||||
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'alt+r': function () {
|
'alt+r': function (e) {
|
||||||
console.log('alt+r');
|
if ($scope.selected > -1) {
|
||||||
|
$scope.$apply(function () {
|
||||||
|
var max = $scope.display.Body.length;
|
||||||
|
$scope.hidden.push(angular.copy($scope.display.Body[$scope.selected]));
|
||||||
|
if ($scope.selected === max -1) {
|
||||||
|
$scope.selected = max - 2;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
},
|
},
|
||||||
'enter': function () {
|
'enter': function (e) {
|
||||||
var path = $scope.info.Body[$scope.selected].Url.replace(/^(?:\/\/|[^\/]+)*/, "");
|
var path = $scope.info.Body[$scope.selected].Url.replace(/^(?:\/\/|[^\/]+)*/, "");
|
||||||
$scope.$apply(function () {
|
$scope.$apply(function () {
|
||||||
$location.path(path).search('StartDate', null).search('FinishDate', null);
|
$location.path(path).search('StartDate', null).search('FinishDate', null);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'alt+u': function () {
|
'alt+u': function (e) {
|
||||||
console.log('alt+u');
|
if ($scope.hidden.length !== 0) {
|
||||||
|
$scope.$apply(function () {
|
||||||
|
$scope.hidden.pop();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -71,11 +71,11 @@ def build_report(request, info):
|
||||||
if journal.debit == 1:
|
if journal.debit == 1:
|
||||||
debit = journal.amount
|
debit = journal.amount
|
||||||
total_debit += journal.amount
|
total_debit += journal.amount
|
||||||
credit = ""
|
credit = 0
|
||||||
else:
|
else:
|
||||||
credit = journal.amount
|
credit = journal.amount
|
||||||
total_credit += journal.amount
|
total_credit += journal.amount
|
||||||
debit = ""
|
debit = 0
|
||||||
for journal in voucher.journals:
|
for journal in voucher.journals:
|
||||||
if journal.debit != journal_debit:
|
if journal.debit != journal_debit:
|
||||||
name += "{0} / ".format(journal.ledger.name)
|
name += "{0} / ".format(journal.ledger.name)
|
||||||
|
|
Loading…
Reference in New Issue