Improve entries app using resources and events.
This commit is contained in:
parent
714cd28742
commit
38e5154536
@ -98,7 +98,7 @@ class EntryResource(Resource):
|
|||||||
|
|
||||||
@session_aware
|
@session_aware
|
||||||
@marshal_with_field(Object(resource_fields))
|
@marshal_with_field(Object(resource_fields))
|
||||||
def put(self, entry_id, session):
|
def post(self, entry_id, session):
|
||||||
kwargs = parser.parse_args()
|
kwargs = parser.parse_args()
|
||||||
|
|
||||||
assert (id not in kwargs or kwargs.id is None
|
assert (id not in kwargs or kwargs.id is None
|
||||||
|
@ -14,13 +14,29 @@
|
|||||||
You should have received a copy of the GNU Affero General Public License
|
You should have received a copy of the GNU Affero General Public License
|
||||||
along with Accountant. If not, see <http://www.gnu.org/licenses/>.
|
along with Accountant. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
accountantApp.controller(
|
accountantApp
|
||||||
"EntryController", function($scope, $http, $rootScope, $filter) {
|
|
||||||
|
.factory("Entries", [ "$resource", function($resource) {
|
||||||
|
return $resource(
|
||||||
|
"/api/entries/:id", {
|
||||||
|
id: "@id"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}])
|
||||||
|
|
||||||
|
.controller(
|
||||||
|
"EntryController", [
|
||||||
|
"$scope", "$http", "$rootScope", "$filter", "Entries",
|
||||||
|
function($scope, $http, $rootScope, $filter, Entries) {
|
||||||
|
// Range for entries.
|
||||||
|
$scope.begin = moment.utc().startOf('month');
|
||||||
|
$scope.end = moment.utc().endOf('month');
|
||||||
|
|
||||||
// Entry store and selection
|
// Entry store and selection
|
||||||
$scope.entries = [];
|
$scope.entries = [];
|
||||||
|
|
||||||
$scope.categories = [];
|
$scope.categories = [];
|
||||||
$scope.chartValues = [];
|
|
||||||
$scope.pieChartValues = [];
|
|
||||||
$scope.selectedItem = null;
|
$scope.selectedItem = null;
|
||||||
|
|
||||||
$scope.account = null;
|
$scope.account = null;
|
||||||
@ -28,9 +44,11 @@ accountantApp.controller(
|
|||||||
// Placeholder for saved value to cancel entry edition
|
// Placeholder for saved value to cancel entry edition
|
||||||
$scope.savedItem = null;
|
$scope.savedItem = null;
|
||||||
|
|
||||||
// Range for entries.
|
// Function to reset the new entry.
|
||||||
$scope.begin = moment.utc().startOf('month');
|
$scope.resetNewEntry = function() {
|
||||||
$scope.end = moment.utc().endOf('month');
|
// The new entry.
|
||||||
|
$scope.newEntry = new Entries({});
|
||||||
|
};
|
||||||
|
|
||||||
$scope.setExtremes = function(e) {
|
$scope.setExtremes = function(e) {
|
||||||
begin = moment.utc(e.min);
|
begin = moment.utc(e.min);
|
||||||
@ -58,6 +76,9 @@ accountantApp.controller(
|
|||||||
plotOptions: {
|
plotOptions: {
|
||||||
pie: {
|
pie: {
|
||||||
startAngle: -90
|
startAngle: -90
|
||||||
|
},
|
||||||
|
series: {
|
||||||
|
allowPointSelect: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
@ -183,6 +204,8 @@ accountantApp.controller(
|
|||||||
|
|
||||||
// Load categories, mainly to populate the pie chart.
|
// Load categories, mainly to populate the pie chart.
|
||||||
$scope.loadCategories = function() {
|
$scope.loadCategories = function() {
|
||||||
|
$scope.categoriesChartConfig.loading = true;
|
||||||
|
|
||||||
$http.get("/api/categories", {
|
$http.get("/api/categories", {
|
||||||
params: {
|
params: {
|
||||||
account: $scope.account.id,
|
account: $scope.account.id,
|
||||||
@ -214,14 +237,14 @@ accountantApp.controller(
|
|||||||
// Note: expenses and revenues must be in the same order than in series[0].
|
// Note: expenses and revenues must be in the same order than in series[0].
|
||||||
config.series[1].data = revenues.concat(expenses);
|
config.series[1].data = revenues.concat(expenses);
|
||||||
|
|
||||||
$scope.categoriesChartConfig.loaded = true;
|
$scope.categoriesChartConfig.loading = false;
|
||||||
|
|
||||||
$scope.loadSolds();
|
$scope.loadSolds();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.loadSolds = function() {
|
$scope.loadSolds = function() {
|
||||||
$scope.soldChartConfig.loaded = true;
|
$scope.soldChartConfig.loading = true;
|
||||||
|
|
||||||
$http.get("/api/solds", {
|
$http.get("/api/solds", {
|
||||||
params: {
|
params: {
|
||||||
@ -239,10 +262,15 @@ accountantApp.controller(
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$scope.soldChartConfig.loading = false;
|
||||||
|
|
||||||
$scope.loadEntries();
|
$scope.loadEntries();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hook on account selected event to display account status.
|
||||||
|
*/
|
||||||
$rootScope.$on("accountSelectedEvent", function(event, args) {
|
$rootScope.$on("accountSelectedEvent", function(event, args) {
|
||||||
$scope.account = args.account;
|
$scope.account = args.account;
|
||||||
|
|
||||||
@ -253,43 +281,45 @@ accountantApp.controller(
|
|||||||
$scope.getAccountStatus();
|
$scope.getAccountStatus();
|
||||||
});
|
});
|
||||||
|
|
||||||
$rootScope.$on("entriesLoadedEvent", function(event, args) {
|
|
||||||
//$scope.loadCategories();
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.getAccountStatus = function() {
|
$scope.getAccountStatus = function() {
|
||||||
$scope.categoriesChartConfig.loaded = true;
|
$scope.categoriesChartConfig.loading = true;
|
||||||
|
|
||||||
$http.get("/api/accounts/" + $scope.account.id, {
|
$http.get("/api/accounts/" + $scope.account.id, {
|
||||||
params: {
|
params: {
|
||||||
begin: $scope.begin.format('YYYY-MM-DD'),
|
begin: $scope.begin.format('YYYY-MM-DD'),
|
||||||
end: $scope.end.format('YYYY-MM-DD')
|
end: $scope.end.format('YYYY-MM-DD')
|
||||||
}
|
}
|
||||||
}).success(function(status) {
|
}).success(function(account) {
|
||||||
|
$scope.categoriesChartConfig.loading = false;
|
||||||
|
|
||||||
|
$scope.$emit("accountLoadedEvent", account);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$rootScope.$on("accountLoadedEvent", $scope.loadCategories);
|
||||||
|
|
||||||
|
$rootScope.$on("accountLoadedEvent", function(e, account) {
|
||||||
var colors = Highcharts.getOptions().colors;
|
var colors = Highcharts.getOptions().colors;
|
||||||
|
|
||||||
var config = $scope.categoriesChartConfig;
|
var config = $scope.categoriesChartConfig;
|
||||||
|
|
||||||
// Update pie chart subtitle with Balance.
|
// Update pie chart subtitle with Balance.
|
||||||
config.subtitle = {
|
config.subtitle = {
|
||||||
text: "Balance: " + status.balance
|
text: "Balance: " + account.balance
|
||||||
};
|
};
|
||||||
|
|
||||||
config.series[0].data = [{
|
config.series[0].data = [{
|
||||||
name: "Revenues",
|
name: "Revenues",
|
||||||
y: status.revenues,
|
y: account.revenues,
|
||||||
color: colors[2]
|
color: colors[2]
|
||||||
}, {
|
}, {
|
||||||
name: "Expenses",
|
name: "Expenses",
|
||||||
y: -status.expenses,
|
y: -account.expenses,
|
||||||
color: colors[3],
|
color: colors[3],
|
||||||
}];
|
}];
|
||||||
|
|
||||||
$scope.soldChartConfig.yAxis.plotLines[1].value = status.authorized_overdraft;
|
$scope.soldChartConfig.yAxis.plotLines[1].value = account.authorized_overdraft;
|
||||||
|
|
||||||
$scope.loadCategories();
|
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
// Function to load entries from server for a specific account and month.
|
// Function to load entries from server for a specific account and month.
|
||||||
$scope.loadEntries = function() {
|
$scope.loadEntries = function() {
|
||||||
@ -297,49 +327,13 @@ accountantApp.controller(
|
|||||||
$scope.selectedItem = null;
|
$scope.selectedItem = null;
|
||||||
$scope.savedItem = null;
|
$scope.savedItem = null;
|
||||||
|
|
||||||
if(!$scope.entriesLoading) {
|
$scope.entries = Entries.query({
|
||||||
$scope.entriesLoading = true;
|
|
||||||
|
|
||||||
$http.get("/api/entries", {
|
|
||||||
params: {
|
|
||||||
account: $scope.account.id,
|
account: $scope.account.id,
|
||||||
begin: $scope.begin.format('YYYY-MM-DD'),
|
begin: $scope.begin.format('YYYY-MM-DD'),
|
||||||
end: $scope.end.format('YYYY-MM-DD')
|
end: $scope.end.format('YYYY-MM-DD')
|
||||||
}
|
}, function(data) {
|
||||||
}).success(function(data) {
|
$scope.$emit("entriesLoadedEvent", {entries: data});
|
||||||
var entries = [{
|
|
||||||
id: null,
|
|
||||||
pointed: false,
|
|
||||||
operation_date: null,
|
|
||||||
label: null,
|
|
||||||
value: null,
|
|
||||||
sold: null,
|
|
||||||
pointedsold: null,
|
|
||||||
category: null,
|
|
||||||
account_id: null,
|
|
||||||
state: 'new',
|
|
||||||
canceled: false,
|
|
||||||
scheduled_operation_id: null
|
|
||||||
}];
|
|
||||||
|
|
||||||
if(data) {
|
|
||||||
entries = entries.concat(angular.fromJson(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.entries = entries;
|
|
||||||
|
|
||||||
$scope.entriesLoading = false;
|
|
||||||
|
|
||||||
$scope.$emit("entriesLoadedEvent", {entries: entries});
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Returns the CSS class for a pointed entry.
|
|
||||||
$scope.pointedEntryClass = function(entry) {
|
|
||||||
if(entry.pointed) {
|
|
||||||
return "active";
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns the CSS class for an entry row.
|
// Returns the CSS class for an entry row.
|
||||||
@ -380,21 +374,18 @@ accountantApp.controller(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save current entry values.
|
// Save current entry values.
|
||||||
|
if(!entry.id) {
|
||||||
$scope.savedItem = angular.copy(entry);
|
$scope.savedItem = angular.copy(entry);
|
||||||
$scope.selectedItem = entry;
|
$scope.selectedItem = entry;
|
||||||
|
}
|
||||||
|
|
||||||
// Enter edit state.
|
// Enter edit state.
|
||||||
entry.state='edit';
|
entry.state='edit';
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns true if the entry is a new one.
|
|
||||||
$scope.isNew = function(entry) {
|
|
||||||
return !$scope.isSaved(entry) && (entry.state === 'edit' || entry.state === 'new');
|
|
||||||
};
|
|
||||||
|
|
||||||
// Returns true if the entry is in editing state.
|
// Returns true if the entry is in editing state.
|
||||||
$scope.isEditing = function(entry) {
|
$scope.isEditing = function(entry) {
|
||||||
return entry.state === 'edit' || entry.state === 'new';
|
return entry.state === 'edit';
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns true if the entry is in displaying state.
|
// Returns true if the entry is in displaying state.
|
||||||
@ -407,38 +398,10 @@ accountantApp.controller(
|
|||||||
return entry.id !== null;
|
return entry.id !== null;
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.iconSaveClass = function(entry) {
|
|
||||||
if(!$scope.isSaved(entry)) {
|
|
||||||
return "fa fa-plus";
|
|
||||||
} else if ($scope.isEditing(entry)) {
|
|
||||||
return "fa fa-floppy-o";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.iconCancelClass = function(entry) {
|
|
||||||
if($scope.isNew(entry)) {
|
|
||||||
return "fa fa-times";
|
|
||||||
} else if ($scope.isEditing(entry)) {
|
|
||||||
return "fa fa-ban";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Cancel current editing entry or clears field if a new one.
|
// Cancel current editing entry or clears field if a new one.
|
||||||
$scope.cancelEditEntry = function(entry) {
|
$scope.cancelEditEntry = function(entry) {
|
||||||
if ($scope.savedItem === null) {
|
if (entry.id) {
|
||||||
// We are cancelling the new entry, just reset it.
|
entry.$get();
|
||||||
entry.id = null; // id should not change, but just in case...
|
|
||||||
entry.pointed = false;
|
|
||||||
entry.operation_date = null;
|
|
||||||
entry.label = null;
|
|
||||||
entry.value = null;
|
|
||||||
entry.account_id = $scope.account.id; // account_id should not change, but just in case...
|
|
||||||
entry.category = null;
|
|
||||||
entry.canceled = false;
|
|
||||||
entry.scheduled_operation_id = null;
|
|
||||||
|
|
||||||
// Reset state to new.
|
|
||||||
entry.state = 'new';
|
|
||||||
} else {
|
} else {
|
||||||
// Reset selected item fields to saved item ones.
|
// Reset selected item fields to saved item ones.
|
||||||
entry.id = $scope.savedItem.id; // id should not change, but just in case...
|
entry.id = $scope.savedItem.id; // id should not change, but just in case...
|
||||||
@ -462,48 +425,55 @@ accountantApp.controller(
|
|||||||
|
|
||||||
// Points an entry.
|
// Points an entry.
|
||||||
$scope.pointEntry = function(entry) {
|
$scope.pointEntry = function(entry) {
|
||||||
if(entry.pointed) {
|
entry.pointed = !entry.pointed;
|
||||||
entry.pointed = false;
|
|
||||||
} else {
|
|
||||||
entry.pointed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the entry if not new.
|
|
||||||
if(!$scope.isNew(entry)) {
|
|
||||||
$scope.saveEntry(entry);
|
$scope.saveEntry(entry);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Saves an entry.
|
// Create an new entry.
|
||||||
|
$scope.createEntry = function(entry) {
|
||||||
|
entry.account_id = $scope.account.id;
|
||||||
|
|
||||||
|
// Ajax call to create an entry
|
||||||
|
$scope.newEntry.$save(function(data) {
|
||||||
|
$scope.resetNewEntry();
|
||||||
|
|
||||||
|
// Send the "entry saved" event.
|
||||||
|
$scope.$emit("entryCreatedEvent", entry);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$rootScope.$on("entryCreatedEvent", function(e, entry) {
|
||||||
|
new PNotify({
|
||||||
|
type: "success",
|
||||||
|
title: "Save",
|
||||||
|
text: "Entry #" + entry.id + " created."
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// Saves an existing entry.
|
||||||
$scope.saveEntry = function(entry) {
|
$scope.saveEntry = function(entry) {
|
||||||
// Affects the account ID if not (should never happen)
|
|
||||||
if(!entry.account_id) {
|
if(!entry.account_id) {
|
||||||
entry.account_id = $scope.account.id;
|
entry.account_id = $scope.account.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the Ajax call to save the entry.
|
|
||||||
var type;
|
|
||||||
var url = "/api/entries";
|
|
||||||
|
|
||||||
if($scope.isSaved(entry)) {
|
|
||||||
url += "/" + entry.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ajax call to save an entry
|
// Ajax call to save an entry
|
||||||
$http.put(url, angular.toJson(entry)).success(function(data) {
|
entry.$save(function(data) {
|
||||||
$.pnotify({
|
|
||||||
type: "success",
|
|
||||||
title: "Save",
|
|
||||||
text: data
|
|
||||||
});
|
|
||||||
|
|
||||||
// $scope.savedItem = null;
|
|
||||||
|
|
||||||
// Send the "entry saved" event.
|
// Send the "entry saved" event.
|
||||||
$scope.$emit("entrySavedEvent", entry);
|
$scope.$emit("entrySavedEvent", entry);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$rootScope.$on("entrySavedEvent", function(e, entry) {
|
||||||
|
new PNotify({
|
||||||
|
type: "success",
|
||||||
|
title: "Save",
|
||||||
|
text: "Entry #" + entry.id + " saved."
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
$scope.removeEntry = function(entry) {
|
$scope.removeEntry = function(entry) {
|
||||||
$scope.removingEntry = entry;
|
$scope.removingEntry = entry;
|
||||||
$("#remove_entry").modal({
|
$("#remove_entry").modal({
|
||||||
@ -520,13 +490,7 @@ accountantApp.controller(
|
|||||||
$scope.confirmRemoveEntry = function() {
|
$scope.confirmRemoveEntry = function() {
|
||||||
// Cancel current editing.
|
// Cancel current editing.
|
||||||
if ($scope.removingEntry) {
|
if ($scope.removingEntry) {
|
||||||
$http.delete("/api/entries/" + $scope.removingEntry.id).success(function (result) {
|
$scope.removingEntry.$delete(function (result) {
|
||||||
$.pnotify({
|
|
||||||
type: "success",
|
|
||||||
title: "Delete",
|
|
||||||
text: result
|
|
||||||
});
|
|
||||||
|
|
||||||
// Send the "entry removed" event.
|
// Send the "entry removed" event.
|
||||||
$scope.$emit("entryRemovedEvent", $scope.removingEntry);
|
$scope.$emit("entryRemovedEvent", $scope.removingEntry);
|
||||||
|
|
||||||
@ -535,10 +499,18 @@ accountantApp.controller(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$rootScope.$on("entryRemovedEvent", function(e, entry) {
|
||||||
|
new PNotify({
|
||||||
|
type: "success",
|
||||||
|
title: "Delete",
|
||||||
|
text: "Entry #" + entry.id + " deleted."
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
$scope.closeModal = function(modalScope) {
|
$scope.closeModal = function(modalScope) {
|
||||||
// Close the modal dialog
|
// Close the modal dialog
|
||||||
if(modalScope && modalScope.dismiss) {
|
if(modalScope && modalScope.dismiss) {
|
||||||
modalScope.dismiss();
|
modalScope.dismiss();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
}]);
|
||||||
|
Loading…
Reference in New Issue
Block a user