From d7789642c1aecbeb0584c502390e70795f8e7862 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 23 Aug 2012 13:09:01 +0530 Subject: [PATCH] Journal virtually complete using AngularJS. Footer / update amount input on add / delete / update with remaining amount left. --- .../static/scripts/angular_directive.js | 55 ++++++++ .../brewman/static/scripts/angular_filter.js | 19 +++ .../brewman/static/scripts/angular_service.js | 41 ++++++ .../brewman/static/scripts/autocomplete.js | 126 ++++++++---------- brewman/brewman/static/scripts/journal.js | 40 +++++- brewman/brewman/templates/base.mako | 15 ++- .../templates/transaction/journal.mako | 41 ++++-- brewman/brewman/views/services/voucher.py | 2 +- brewman/brewman/views/transactions/journal.py | 12 +- 9 files changed, 255 insertions(+), 96 deletions(-) create mode 100644 brewman/brewman/static/scripts/angular_directive.js create mode 100644 brewman/brewman/static/scripts/angular_filter.js create mode 100644 brewman/brewman/static/scripts/angular_service.js diff --git a/brewman/brewman/static/scripts/angular_directive.js b/brewman/brewman/static/scripts/angular_directive.js new file mode 100644 index 00000000..6376a691 --- /dev/null +++ b/brewman/brewman/static/scripts/angular_directive.js @@ -0,0 +1,55 @@ +var overlord_directive = angular.module('overlord.directive', []); +overlord_directive.directive('autocomplete', function () { + var directiveDefinitionObject = { + restrict:'E', + replace:true, + transclude:true, + template:'', + link:function (scope, element, attrs) { + element.autocomplete({ + source:function (request, response) { + $.ajax({ + type:"POST", + url:attrs.url, + contentType:"application/json; charset=utf-8", + dataType:"json", + data:JSON.stringify({prefixText:request.term, count:20}), + success:function (data) { + response(data); + }, + error:function (XMLHttpRequest, textStatus, errorThrown) { + showError(textStatus.responseText); + } + }); + }, + minLength:1, + autoFocus:true, + select:function (event, ui) { + scope[attrs.selname] = ui.item.label; + scope[attrs.selid] = ui.item.id; + scope.$apply(); + } + }); + } + }; + return directiveDefinitionObject; +}); + +overlord_directive.directive('datepicker', function () { + var directiveDefinitionObject = { + restrict:'E', + replace:true, + transclude:true, + template:'', + link:function (scope, element, attrs) { + element.datepicker({ + dateFormat:'dd-M-yy', + onSelect:function (dateText, inst) { + scope[attrs.model][attrs.prop] = dateText; + scope.$apply(); + } + }); + } + }; + return directiveDefinitionObject; +}); diff --git a/brewman/brewman/static/scripts/angular_filter.js b/brewman/brewman/static/scripts/angular_filter.js new file mode 100644 index 00000000..9d534ec6 --- /dev/null +++ b/brewman/brewman/static/scripts/angular_filter.js @@ -0,0 +1,19 @@ +var overlord_filter = angular.module('overlord.filter', []); +overlord_filter.filter('debit', function () { + return function (input) { + if (input == -1) { + return 'Cr'; + } else if (input == 1) { + return 'Dr'; + } else { + return ''; + } + ; + }; +}); + +overlord_filter.filter('posted', function () { + return function (input) { + return input == true ? 'Posted' : 'Post'; + }; +}); diff --git a/brewman/brewman/static/scripts/angular_service.js b/brewman/brewman/static/scripts/angular_service.js new file mode 100644 index 00000000..f3809012 --- /dev/null +++ b/brewman/brewman/static/scripts/angular_service.js @@ -0,0 +1,41 @@ +var overlord_service = angular.module('overlord.service', []); + +overlord_service.factory('save_voucher', ['$http', function ($http) { + return function (scope) { + $http.post('', scope.voucher). + success(function (data, status) { + scope.voucher = data; + scope.toasts.push({Type:'Success', Message:data.Code}); + }). + error(function (data, status) { + scope.toasts.push({Type:'Error', Message:data}); + }); + }; +}]); + +overlord_service.factory('delete_voucher', ['$http', function ($http) { + return function (scope) { + $http.post(delete_url, {}). + success(function (data, status) { + scope.voucher = {}; + scope.toasts.push({Type:'Success', Message:data.Code}); + }). + error(function (data, status) { + scope.toasts.push({Type:'Error', Message:data}); + }); + }; +}]); + +overlord_service.factory('post_voucher', ['$http', function ($http) { + return function (scope) { + $http.post(post_url, {}). + success(function (data, status) { + scope.voucher.Posted = true; + scope.voucher.Poster = data.name; + scope.toasts.push({Type:'Success', Message:data.Code}); + }). + error(function (data, status) { + scope.toasts.push({Type:'Error', Message:data}); + }); + }; +}]); diff --git a/brewman/brewman/static/scripts/autocomplete.js b/brewman/brewman/static/scripts/autocomplete.js index 10506d46..02d809cf 100644 --- a/brewman/brewman/static/scripts/autocomplete.js +++ b/brewman/brewman/static/scripts/autocomplete.js @@ -1,73 +1,6 @@ -var myApp = angular.module('myApp', ['journalFilters']); -myApp.directive('autocomplete', function () { - var directiveDefinitionObject = { - restrict:'E', - replace:true, - transclude:true, - template:'', - link:function (scope, element, attrs) { - element.autocomplete({ - source:function (request, response) { - $.ajax({ - type:"POST", - url:attrs.url, - contentType:"application/json; charset=utf-8", - dataType:"json", - data:JSON.stringify({prefixText:request.term, count:20}), - success:function (data) { - response(data); - }, - error:function (XMLHttpRequest, textStatus, errorThrown) { - alert("Due to unexpected errors we were unable to load data\n\r" + textStatus); - } - }); - }, - minLength:1, - autoFocus:true, - select:function (event, ui) { - scope[attrs.selname] = ui.item.label; - scope[attrs.selid] = ui.item.id; - scope.$apply(); - } - }); - } - }; - return directiveDefinitionObject; -}); +var overlord = angular.module('overlord', ['overlord.directive', 'overlord.filter', 'overlord.service']); -myApp.directive('datepicker', function () { - var directiveDefinitionObject = { - restrict:'E', - replace:true, - transclude:true, - template:'', - link:function (scope, element, attrs) { - element.datepicker({ - dateFormat:'dd-M-yy', - onSelect:function (dateText, inst) { - scope[attrs.model][attrs.prop] = dateText; - scope.$apply(); - } - }); - } - }; - return directiveDefinitionObject; -}); - - -angular.module('journalFilters', []).filter('debit', function() { - return function(input) { - if (input == -1) { - return 'Cr'; - } else if (input == 1) { - return 'Dr'; - } else { - return ''; - }; - }; -}); - function AutoComplete(matchFieldName, lookupURL) { $(matchFieldName).autocomplete({ source:function (request, response) { @@ -82,11 +15,62 @@ function AutoComplete(matchFieldName, lookupURL) { response(data); }, error:function (XMLHttpRequest, textStatus, errorThrown) { - alert("Due to unexpected errors we were unable to load data\n\r" + textStatus); + showError(textStatus.responseText); } }); }, minLength:1, - autoFocus:true + autoFocus:true }); -} \ No newline at end of file +} + + +//http://jsfiddle.net/tadchristiansen/gt92r/ + +// +// Example Confirm +// + +//function PopupCtrl($scope, PopupService) +//{ +// $scope.cancel = function () { +// PopupService.close(); +// }; +// +// $scope.doIt = function () { +// alert("you did it"); +// PopupService.close(); +// }; +// +// $scope.okay = function () { +// alert("you clicked okay"); +// PopupService.close(); +// }; +//} + +//directivesModule.directive('ngConfirm', function(PopupService) { +// return { +// restrict: 'E', +// link: function postLink(scope, element, attrs) { +// // Could have custom or boostrap modal options here +// var popupOptions = {}; +// element.bind("click", function() +// { +// PopupService.confirm(attrs["title"], attrs["actionText"], +// attrs["actionButtonText"], attrs["actionFunction"], +// attrs["cancelButtonText"], attrs["cancelFunction"], +// scope, popupOptions); +// }); +// } +// }; +// +//}); + + +//overlord.factory('auth', function ($rootScope) { +// $rootScope.auth = { +// isUserInRole:function (role) { +// return $rootScope.auth.perms.indexOf(role) !== -1; +// } +// }; +//}); diff --git a/brewman/brewman/static/scripts/journal.js b/brewman/brewman/static/scripts/journal.js index 10974a23..7651169b 100644 --- a/brewman/brewman/static/scripts/journal.js +++ b/brewman/brewman/static/scripts/journal.js @@ -1,15 +1,41 @@ -function JournalCtrl($scope) { -// $scope.voucher = { -// code:'(Auto)', -// Journals:[], -// date:'' -// }; +function JournalCtrl($scope, save_voucher, delete_voucher, post_voucher) { $scope.debit = 1; $scope.addJournal = function () { - this.voucher.Journals.push({Debit:$scope.debit, Amount:$scope.amount, Ledger:{LedgerID:$scope.id, Name:$scope.name}}); + for (var i = 0, l = this.voucher.Journals.length; i < l; i++) { + if (this.voucher.Journals[i].Ledger.LedgerID === $scope.id) { + var amount = (this.voucher.Journals[i].Debit * this.voucher.Journals[i].Amount) + (parseInt($scope.debit) * Number($scope.amount)); + if (amount < 0) { + this.voucher.Journals[i].Debit = -1; + this.voucher.Journals[i].Amount = amount * -1; + } else { + this.voucher.Journals[i].Debit = 1; + this.voucher.Journals[i].Amount = amount; + } + break; + } + } + if (i >= l) { + this.voucher.Journals.push({Debit:$scope.debit, Amount:$scope.amount, Ledger:{LedgerID:$scope.id, Name:$scope.name}}); + } + delete $scope.amount; + delete $scope.id; + delete $scope.name; + $('#txtLedger').focus(); }; $scope.removeJournal = function (journal) { var index = this.voucher.Journals.indexOf(journal); this.voucher.Journals.splice(index, 1); }; + + $scope.save = function () { + save_voucher($scope); + }; + + $scope.delete = function () { + delete_voucher($scope); + }; + + $scope.post = function () { + post_voucher($scope); + }; } diff --git a/brewman/brewman/templates/base.mako b/brewman/brewman/templates/base.mako index 92d86713..8c7ee4bc 100644 --- a/brewman/brewman/templates/base.mako +++ b/brewman/brewman/templates/base.mako @@ -1,5 +1,5 @@ - + ${title} @@ -33,9 +33,12 @@ ${h.ScriptLink(request, 'autocomplete.js')} - ${h.ScriptLink(request, 'session.js')} + ${h.ScriptLink(request, 'angular_directive.js')} + ${h.ScriptLink(request, 'angular_filter.js')} + ${h.ScriptLink(request, 'angular_service.js')} + ${h.ScriptLink(request, 'session.js')} - <%block name="header" /> + <%block name="header" /> @@ -76,7 +79,11 @@
-
+
+
+ {{toast.Type}}! {{toast.Message}} +
+
<%block name="content"/> diff --git a/brewman/brewman/templates/transaction/journal.mako b/brewman/brewman/templates/transaction/journal.mako index e658dc18..9119927d 100644 --- a/brewman/brewman/templates/transaction/journal.mako +++ b/brewman/brewman/templates/transaction/journal.mako @@ -2,19 +2,25 @@ <%inherit file="../base.mako"/> <%block name="header"> ${h.ScriptLink(request, 'journal.js')} @@ -44,13 +50,14 @@ - +
- +
@@ -86,14 +93,28 @@
+ class="non-search-box" ng-model="voucher.Narration">
- ${h.SubmitButton('btnSave', 'Save', 'btn btn-primary')} - ${h.SubmitButton('btnUpdate', 'Update', 'btn-primary')} - ${h.SubmitButton('btnPost', 'Post', 'btn btn-inverse')} - ${h.SubmitButton('btnDelete', 'Delete', 'btn btn-danger')} + + + + +
+
+ Created on {{voucher.CreationDate}} and Last Edited on {{voucher.LastEditDate}} by {{voucher.User}}. Posted + by {{voucher.Poster}}
\ No newline at end of file diff --git a/brewman/brewman/views/services/voucher.py b/brewman/brewman/views/services/voucher.py index 10e17a12..7021f638 100644 --- a/brewman/brewman/views/services/voucher.py +++ b/brewman/brewman/views/services/voucher.py @@ -50,7 +50,7 @@ def voucher_post(request): voucher.posted = True voucher.poster_id = user.id transaction.commit() - return {} + return {'id': str(user.id), 'name': user.name} @view_config(request_method='POST', route_name='delete_voucher', renderer='json', xhr=True) def voucher_delete(request): diff --git a/brewman/brewman/views/transactions/journal.py b/brewman/brewman/views/transactions/journal.py index 2a7bfcc1..6e1aa290 100644 --- a/brewman/brewman/views/transactions/journal.py +++ b/brewman/brewman/views/transactions/journal.py @@ -24,6 +24,7 @@ from brewman.views.transactions import session_current_date permission='Journal Create') def journal_get(request): id = request.matchdict.get('id', None) + perms = Literal(json.dumps(request.session['perms'])) if id is None: json_voucher = Literal( json.dumps({'Code': '(Auto)', 'Date': session_current_date(request), 'Posted': True, 'Narration': "", @@ -33,12 +34,17 @@ def journal_get(request): json_voucher = Literal(json.dumps(voucher_info(voucher))) return {'title': 'Hops n Grains - Journal Entry', 'id': id, - 'json_voucher': json_voucher} + 'json_voucher': json_voucher, + 'perms': perms} def voucher_info(voucher): json_voucher = {'VoucherID': str(voucher.id), 'Date': voucher.date.strftime('%d-%b-%Y'), 'Posted': voucher.posted, - 'Code': voucher.code, 'Narration': voucher.narration, 'Journals': []} + 'Code': voucher.code, 'Narration': voucher.narration, 'Journals': [], + 'CreationDate': voucher.creation_date.strftime('%d-%b-%Y %H:%M'), + 'LastEditDate': voucher.last_edit_date.strftime('%d-%b-%Y %H:%M'), 'User': voucher.user.name, + 'Poster': voucher.poster.name if voucher.posted else ''} + for item in voucher.journals: json_voucher['Journals'].append({'JournalID': str(item.id), 'Debit': item.debit, 'Amount': str(item.amount), 'Ledger': {'LedgerID': str(item.ledger.id), 'Name': item.ledger.name}}) @@ -95,7 +101,7 @@ def update_voucher(id, json, user): found = False for i in range(len(newJournals), 0, -1): j = newJournals[i - 1] - if item.id == uuid.UUID(j['JournalID']): + if 'JournalID' in j and item.id == uuid.UUID(j['JournalID']): ledger = LedgerBase.by_id(uuid.UUID(j['Ledger']['LedgerID'])) found = True item.debit = int(j['Debit'])