2013-01-13 09:50:15 +01:00
|
|
|
// Entry object
|
2013-01-13 12:27:42 +01:00
|
|
|
function entry(){
|
|
|
|
this.id=ko.observable();
|
2013-01-26 01:15:07 +01:00
|
|
|
this.pointed=ko.observable();
|
2013-01-13 12:27:42 +01:00
|
|
|
this.operation_date=ko.observable();
|
|
|
|
this.label=ko.observable();
|
|
|
|
this.value=ko.observable();
|
|
|
|
this.account_id=ko.observable();
|
|
|
|
this.sold=ko.observable();
|
|
|
|
this.pointedsold=ko.observable();
|
|
|
|
this.category=ko.observable();
|
2013-01-07 18:42:02 +01:00
|
|
|
}
|
|
|
|
|
2013-01-13 12:50:11 +01:00
|
|
|
// Account object
|
|
|
|
function account() {
|
|
|
|
this.id=ko.observable();
|
|
|
|
this.name=ko.observable();
|
2013-01-24 20:44:09 +01:00
|
|
|
this.authorized_overdraft=ko.observable();
|
2013-01-13 12:50:11 +01:00
|
|
|
this.future=ko.observable();
|
|
|
|
this.current=ko.observable();
|
|
|
|
this.pointed=ko.observable();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Month object
|
|
|
|
function month() {
|
|
|
|
this.year=ko.observable();
|
|
|
|
this.month=ko.observable();
|
|
|
|
}
|
|
|
|
|
2013-01-13 10:14:43 +01:00
|
|
|
// Util function to show a message in message placeholder.
|
2013-01-07 18:42:02 +01:00
|
|
|
function message(alertType, title, message) {
|
|
|
|
$(".alert").alert('close');
|
2013-01-13 20:57:48 +01:00
|
|
|
$("#message-placeholder").append('<div class="alert alert-' + alertType + '"><button type="button" class="close" data-dismiss="alert">×</button><h4>' + title + '</h4>' + message + '</div>');
|
2013-01-07 18:42:02 +01:00
|
|
|
}
|
|
|
|
|
2013-01-13 12:50:11 +01:00
|
|
|
// The ListViewModel used to instanciate viewmodel.
|
2013-01-07 18:42:02 +01:00
|
|
|
var ListViewModel = function() {
|
|
|
|
var self = this;
|
|
|
|
|
2013-01-13 10:14:43 +01:00
|
|
|
// Account store and selection
|
2013-01-07 18:42:02 +01:00
|
|
|
self.account = ko.observable();
|
|
|
|
self.accounts = ko.observableArray([]);
|
|
|
|
|
2013-01-14 00:33:32 +01:00
|
|
|
self.savedAccount = null;
|
|
|
|
self.editingAccount = ko.observable();
|
|
|
|
self.removedAccount = null;
|
|
|
|
|
2013-01-13 10:14:43 +01:00
|
|
|
// Month store and selection
|
2013-01-07 18:42:02 +01:00
|
|
|
self.months = ko.observableArray();
|
|
|
|
self.month = ko.observable();
|
|
|
|
|
2013-01-13 10:14:43 +01:00
|
|
|
// Entry store and selection
|
|
|
|
self.entries = ko.observableArray([]);
|
2013-01-07 18:42:02 +01:00
|
|
|
self.selectedItem = ko.observable();
|
2013-01-24 19:31:37 +01:00
|
|
|
self.newEntry = ko.observable(ko.mapping.fromJS({
|
|
|
|
id: null,
|
2013-01-26 01:15:07 +01:00
|
|
|
pointed: false,
|
2013-01-24 19:31:37 +01:00
|
|
|
operation_date: null,
|
|
|
|
label: null,
|
|
|
|
value: null,
|
|
|
|
sold: null,
|
|
|
|
pointedsold: null,
|
|
|
|
category: null,
|
|
|
|
account_id: null
|
|
|
|
}));
|
2013-01-13 12:00:44 +01:00
|
|
|
|
2013-01-13 10:14:43 +01:00
|
|
|
// Placeholder for saved value to cancel entry edition
|
2013-01-13 12:00:44 +01:00
|
|
|
self.savedItem = null;
|
|
|
|
|
2013-01-13 10:14:43 +01:00
|
|
|
// Placeholder for entry to remove to be available in modal function "yes" click callback
|
2013-01-14 00:33:32 +01:00
|
|
|
self.removedItem = null;
|
|
|
|
|
|
|
|
self.addAccount = function() {
|
|
|
|
self.editingAccount(ko.mapping.fromJS({
|
|
|
|
id: null,
|
2013-01-24 20:44:09 +01:00
|
|
|
name: null,
|
|
|
|
authorized_overdraft: null
|
2013-01-14 00:33:32 +01:00
|
|
|
}));
|
|
|
|
|
|
|
|
$("#edit-account").modal();
|
|
|
|
};
|
|
|
|
|
|
|
|
self.editAccount = function(account) {
|
|
|
|
self.editingAccount(account);
|
|
|
|
self.savedAccount = ko.toJS(account);
|
|
|
|
|
|
|
|
$("#edit-account").modal();
|
|
|
|
};
|
|
|
|
|
|
|
|
self.cancelEditAccount = function() {
|
|
|
|
if(self.editingAccount() && self.savedAccount) {
|
|
|
|
self.editingAccount().name(self.savedAccount.name);
|
2013-01-24 20:44:09 +01:00
|
|
|
self.editingAccount().authorized_overdraft(self.savedAccount.authorized_overdraft);
|
2013-01-14 00:33:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
self.editingAccount(null);
|
|
|
|
self.savedAccount = null;
|
|
|
|
|
|
|
|
$("#edit-account").modal('hide');
|
|
|
|
};
|
|
|
|
|
|
|
|
self.saveAccount = function() {
|
|
|
|
var account = self.editingAccount();
|
|
|
|
|
|
|
|
// Ajax call to save the entry.
|
|
|
|
var type;
|
2013-01-24 00:01:42 +01:00
|
|
|
var url = "api/accounts";
|
2013-01-14 00:33:32 +01:00
|
|
|
|
|
|
|
if(account.id()) {
|
2013-01-24 00:01:42 +01:00
|
|
|
url += "/" + account.id();
|
2013-01-14 00:33:32 +01:00
|
|
|
}
|
|
|
|
|
2013-01-24 00:01:42 +01:00
|
|
|
$.ajax({
|
|
|
|
url: url,
|
|
|
|
type: "PUT",
|
|
|
|
data: ko.toJSON(account),
|
|
|
|
dataType: "json",
|
|
|
|
contentType: "application/json"
|
|
|
|
}).success(function(data) {
|
2013-01-14 00:33:32 +01:00
|
|
|
message("success", "Save", data);
|
|
|
|
|
|
|
|
self.editingAccount(null);
|
|
|
|
self.savedAccount = null;
|
|
|
|
|
|
|
|
$("#edit-account").modal('hide');
|
|
|
|
|
|
|
|
// Reload accounts to update solds.
|
|
|
|
self.loadAccounts();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
self.removeAccount = function(account) {
|
|
|
|
// Cancel current editing.
|
|
|
|
self.removedAccount = account;
|
|
|
|
$('#remove-account-confirm').modal();
|
|
|
|
};
|
|
|
|
|
|
|
|
// Function to confirm the removal of an entry.
|
|
|
|
self.confirmAccountRemove = function() {
|
|
|
|
var account = self.removedAccount;
|
|
|
|
|
|
|
|
$.ajax("api/accounts/" + ko.utils.unwrapObservable(account.id), {type: "DELETE"}).success(function (data) {
|
|
|
|
message("success", "Save", data);
|
|
|
|
|
|
|
|
// Reload accounts to update solds.
|
|
|
|
self.loadAccounts();
|
|
|
|
}).complete(function (data) {
|
|
|
|
// Reset removed item to null and hide the modal dialog.
|
|
|
|
self.removedAccount = null;
|
|
|
|
$('#remove-account-confirm').modal('hide');
|
|
|
|
});
|
|
|
|
};
|
2013-01-07 18:42:02 +01:00
|
|
|
|
2013-01-25 00:13:03 +01:00
|
|
|
self.categories = ko.computed(function() {
|
|
|
|
var unwrap = ko.utils.unwrapObservable;
|
|
|
|
|
|
|
|
var entries=unwrap(self.entries);
|
|
|
|
|
|
|
|
var categories = ko.observableArray([]);
|
|
|
|
|
|
|
|
$.each(entries, function(index, entry) {
|
|
|
|
if(entry.category() && entry.category() != '' && categories.indexOf(entry.category()) == -1) {
|
|
|
|
categories.push(entry.category());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return categories();
|
|
|
|
});
|
|
|
|
|
2013-01-13 10:14:43 +01:00
|
|
|
// Returns the data for the categories by summing values with same category
|
2013-01-12 23:02:31 +01:00
|
|
|
self.expenseCategoriesChart = ko.computed(function() {
|
2013-01-13 13:32:23 +01:00
|
|
|
var unwrap = ko.utils.unwrapObservable;
|
|
|
|
|
|
|
|
var entries=unwrap(self.entries);
|
2013-01-12 22:11:48 +01:00
|
|
|
|
2013-01-13 10:14:43 +01:00
|
|
|
// First pass: get sum values for each category.
|
2013-01-12 22:11:48 +01:00
|
|
|
var chartValuesTmp = {};
|
|
|
|
$.each(entries, function(index, entry) {
|
2013-01-13 13:32:23 +01:00
|
|
|
var category = unwrap(entry.category);
|
|
|
|
var value = unwrap(entry.value) ? Number(unwrap(entry.value)) : null;
|
2013-01-12 22:11:48 +01:00
|
|
|
|
2013-01-13 10:14:43 +01:00
|
|
|
if(category && value && value < 0.0) {
|
2013-01-12 22:11:48 +01:00
|
|
|
var oldValue = 0.0;
|
|
|
|
|
|
|
|
if(chartValuesTmp[category]) {
|
|
|
|
oldValue = chartValuesTmp[category];
|
|
|
|
}
|
|
|
|
|
|
|
|
chartValuesTmp[category] = oldValue - value
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-01-13 10:14:43 +01:00
|
|
|
// Second pass: transform to an array readable by jqplot.
|
|
|
|
var chartValues = [];
|
2013-01-12 22:11:48 +01:00
|
|
|
$.each(chartValuesTmp, function(key, value) {
|
|
|
|
chartValues.push([key, value]);
|
|
|
|
});
|
|
|
|
|
|
|
|
return chartValues;
|
|
|
|
});
|
|
|
|
|
2013-01-13 10:14:43 +01:00
|
|
|
// Return the data for the sold chart.
|
2013-01-12 13:56:52 +01:00
|
|
|
self.entriesChart = ko.computed(function() {
|
2013-01-13 13:32:23 +01:00
|
|
|
var unwrap = ko.utils.unwrapObservable;
|
|
|
|
|
2013-01-13 10:14:43 +01:00
|
|
|
// We assume that entries are sorted by value date descending.
|
2013-01-13 13:32:23 +01:00
|
|
|
var entries = unwrap(self.entries).slice().reverse();
|
2013-01-12 13:56:52 +01:00
|
|
|
|
2013-01-24 01:04:01 +01:00
|
|
|
// Transform to an array readable by jqplot Line renderer.
|
2013-01-13 10:14:43 +01:00
|
|
|
var chartValues = [];
|
2013-01-24 01:04:01 +01:00
|
|
|
$.each(entries, function(index, entry) {
|
2013-01-26 01:15:07 +01:00
|
|
|
if(unwrap(entry.operation_date) && unwrap(entry.sold)) {
|
|
|
|
chartValues.push([unwrap(entry.operation_date), Number(unwrap(entry.sold))]);
|
2013-01-24 14:58:09 +01:00
|
|
|
}
|
2013-01-12 13:56:52 +01:00
|
|
|
});
|
|
|
|
|
2013-01-24 20:44:09 +01:00
|
|
|
return {account: self.account(), entries: chartValues};
|
2013-01-12 13:56:52 +01:00
|
|
|
}, self);
|
|
|
|
|
2013-01-13 10:14:43 +01:00
|
|
|
// Function to load entries from server for a specific account and month.
|
2013-01-10 13:10:41 +01:00
|
|
|
self.loadEntries = function(account, month) {
|
2013-01-14 00:33:32 +01:00
|
|
|
// An account may not have any month (new account)
|
|
|
|
if(month) {
|
|
|
|
$.get("api/entries/" + account.id() + "/" + month.year() + "/" + month.month()).success(function(data) {
|
2013-01-13 10:14:43 +01:00
|
|
|
// Clean up selected entry.
|
2013-01-07 18:42:02 +01:00
|
|
|
self.selectedItem(null);
|
|
|
|
|
2013-01-13 12:50:11 +01:00
|
|
|
// Update entries
|
2013-01-24 19:31:37 +01:00
|
|
|
self.clearNewEntry()
|
|
|
|
var entries = [self.newEntry()].concat(ko.utils.arrayMap($.parseJSON(data), ko.mapping.fromJS));
|
|
|
|
|
|
|
|
self.entries(entries);
|
2013-01-24 21:19:07 +01:00
|
|
|
|
|
|
|
// Initialize date picker for operation date column.
|
|
|
|
$("#new_operation_date").datepicker().on('changeDate', function(ev){
|
|
|
|
self.newEntry().operation_date(ev.date.format(ev.currentTarget.dataset.dateFormat));
|
|
|
|
});
|
2013-01-14 00:33:32 +01:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
// If no month, just remove all entries.
|
|
|
|
self.entries.removeAll();
|
|
|
|
}
|
2013-01-09 23:39:26 +01:00
|
|
|
};
|
|
|
|
|
2013-01-13 12:50:11 +01:00
|
|
|
// Function to load accounts
|
2013-01-09 23:39:26 +01:00
|
|
|
self.loadAccounts = function() {
|
2013-01-13 20:57:48 +01:00
|
|
|
$.get("api/accounts").success(function (data) {
|
2013-01-13 12:50:11 +01:00
|
|
|
// Update accounts
|
2013-01-13 20:57:48 +01:00
|
|
|
self.accounts(ko.utils.arrayMap($.parseJSON(data), ko.mapping.fromJS));
|
2013-01-09 23:39:26 +01:00
|
|
|
|
2013-01-24 20:44:09 +01:00
|
|
|
var accountToSelect = null
|
|
|
|
|
2013-01-13 12:50:11 +01:00
|
|
|
// Reset selected account to the new instance corresponding to the old one.
|
2013-01-12 13:56:52 +01:00
|
|
|
if(self.account()) {
|
2013-01-13 12:50:11 +01:00
|
|
|
// Find the new instance of the previously selected account.
|
2013-01-12 13:56:52 +01:00
|
|
|
$.each(self.accounts(), function(index, account) {
|
2013-01-24 20:44:09 +01:00
|
|
|
if(account.id() == self.account().id()) {
|
|
|
|
accountToSelect = account;
|
2013-01-12 13:56:52 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-01-13 12:50:11 +01:00
|
|
|
// Set selected account to first one if not yet selected
|
2013-01-24 20:44:09 +01:00
|
|
|
if(!accountToSelect && self.accounts().length > 0){
|
|
|
|
accountToSelect = self.accounts()[0];
|
2013-01-09 23:39:26 +01:00
|
|
|
}
|
2013-01-10 13:10:41 +01:00
|
|
|
|
2013-01-24 20:44:09 +01:00
|
|
|
// Reset to account to select
|
|
|
|
self.account(accountToSelect);
|
|
|
|
|
2013-01-14 00:33:32 +01:00
|
|
|
// Load months if there is any account, or remove months.
|
|
|
|
if(self.account()) {
|
|
|
|
self.loadMonths(self.account());
|
|
|
|
} else {
|
|
|
|
self.months.removeAll();
|
|
|
|
}
|
2013-01-09 23:39:26 +01:00
|
|
|
});
|
|
|
|
};
|
2013-01-07 18:42:02 +01:00
|
|
|
|
2013-01-13 12:50:11 +01:00
|
|
|
// Function to load months
|
2013-01-09 23:39:26 +01:00
|
|
|
self.loadMonths = function(account){
|
2013-01-13 20:57:48 +01:00
|
|
|
$.get("api/accounts/" + account.id() + "/months").success(function (data) {
|
2013-01-13 12:50:11 +01:00
|
|
|
// Update months
|
2013-01-13 20:57:48 +01:00
|
|
|
self.months(ko.utils.arrayMap($.parseJSON(data), ko.mapping.fromJS));
|
2013-01-09 23:39:26 +01:00
|
|
|
|
2013-01-24 20:44:09 +01:00
|
|
|
var monthToSelect = null;
|
|
|
|
|
2013-01-26 12:47:04 +01:00
|
|
|
var today = new Date();
|
|
|
|
var currentYear = today.getFullYear().toString();
|
|
|
|
var currentMonth = (today.getMonth() + 1 < 10 ? "0" : "") + (today.getMonth() + 1);
|
|
|
|
|
|
|
|
// Find the new instance of the previously selected month.
|
|
|
|
$.each(self.months(), function(index, month) {
|
|
|
|
// Reset selected month to the new instance corresponding to the old one
|
|
|
|
if(self.month()) {
|
2013-01-24 20:44:09 +01:00
|
|
|
if(month.year() === self.month().year() && month.month() === self.month().month()) {
|
|
|
|
monthToSelect = month;
|
2013-01-12 13:56:52 +01:00
|
|
|
}
|
2013-01-26 12:47:04 +01:00
|
|
|
} else {
|
|
|
|
if(month.year() === currentYear && month.month() === currentMonth) {
|
|
|
|
monthToSelect = month;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2013-01-12 13:56:52 +01:00
|
|
|
|
2013-01-13 12:50:11 +01:00
|
|
|
// Set selected month to the last one if not yet selected.
|
2013-01-24 20:44:09 +01:00
|
|
|
if(!monthToSelect && self.months().length > 0) {
|
|
|
|
monthToSelect = self.months()[self.months().length - 1];
|
2013-01-10 13:10:41 +01:00
|
|
|
}
|
|
|
|
|
2013-01-24 20:44:09 +01:00
|
|
|
// Reset to month to select
|
|
|
|
self.month(monthToSelect);
|
|
|
|
|
2013-01-14 00:33:32 +01:00
|
|
|
// Load entries if there is a month or remove entries.
|
|
|
|
if(self.month) {
|
|
|
|
self.loadEntries(self.account(), self.month());
|
|
|
|
} else {
|
|
|
|
self.entries.removeAll();
|
|
|
|
}
|
2013-01-07 18:42:02 +01:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2013-01-13 13:32:23 +01:00
|
|
|
// Function to select template in function of selected item.
|
2013-01-07 18:42:02 +01:00
|
|
|
self.templateToUse = function (item) {
|
2013-01-24 21:19:07 +01:00
|
|
|
return self.newEntry() === item ? 'newTmpl' : self.selectedItem() === item ? 'editTmpl' : 'itemsTmpl';
|
2013-01-07 18:42:02 +01:00
|
|
|
};
|
|
|
|
|
2013-01-13 13:32:23 +01:00
|
|
|
// Function to edit an item
|
2013-01-07 18:42:02 +01:00
|
|
|
self.edit = function(item) {
|
2013-01-13 13:32:23 +01:00
|
|
|
// Cancel previous editing.
|
2013-01-13 12:00:44 +01:00
|
|
|
if(self.savedItem) {
|
2013-01-07 18:42:02 +01:00
|
|
|
self.cancel();
|
|
|
|
}
|
|
|
|
|
2013-01-13 13:32:23 +01:00
|
|
|
// Save current item
|
2013-01-13 12:00:44 +01:00
|
|
|
self.savedItem=ko.toJS(item);
|
2013-01-07 18:42:02 +01:00
|
|
|
self.selectedItem(item);
|
|
|
|
|
2013-01-13 13:32:23 +01:00
|
|
|
// Initialize date picker for operation date column.
|
2013-01-13 12:00:44 +01:00
|
|
|
$("#operation_date").datepicker().on('changeDate', function(ev){
|
|
|
|
self.selectedItem().operation_date(ev.date.format(ev.currentTarget.dataset.dateFormat));
|
2013-01-08 18:03:25 +01:00
|
|
|
});
|
2013-01-07 18:42:02 +01:00
|
|
|
};
|
|
|
|
|
2013-01-24 19:31:37 +01:00
|
|
|
self.clearNewEntry = function() {
|
|
|
|
self.newEntry().id(null); // id should not change, but just in case...
|
2013-01-26 01:15:07 +01:00
|
|
|
self.newEntry().pointed(false)
|
2013-01-24 19:31:37 +01:00
|
|
|
self.newEntry().operation_date(null);
|
|
|
|
self.newEntry().label(null);
|
|
|
|
self.newEntry().value(null);
|
|
|
|
self.newEntry().account_id(self.account().id()); // account_id should not change, but just in case...
|
2013-01-25 23:40:47 +01:00
|
|
|
self.newEntry().category(null);
|
2013-01-24 19:31:37 +01:00
|
|
|
};
|
|
|
|
|
2013-01-13 13:32:23 +01:00
|
|
|
// Function to cancel current editing.
|
2013-01-24 19:31:37 +01:00
|
|
|
self.cancel = function(item) {
|
2013-01-13 13:32:23 +01:00
|
|
|
// Reset selected item fields to saved item ones.
|
2013-01-24 19:31:37 +01:00
|
|
|
if(item === self.selectedItem() && self.savedItem) {
|
2013-01-13 12:00:44 +01:00
|
|
|
self.selectedItem().id(self.savedItem.id); // id should not change, but just in case...
|
2013-01-26 01:15:07 +01:00
|
|
|
self.selectedItem().pointed(self.savedItem.pointed);
|
2013-01-13 12:00:44 +01:00
|
|
|
self.selectedItem().operation_date(self.savedItem.operation_date);
|
|
|
|
self.selectedItem().label(self.savedItem.label);
|
|
|
|
self.selectedItem().value(self.savedItem.value);
|
|
|
|
self.selectedItem().account_id(self.savedItem.account_id); // account_id should not change, but just in case...
|
2013-01-25 23:40:47 +01:00
|
|
|
self.selectedItem().category(self.savedItem.category);
|
2013-01-07 18:42:02 +01:00
|
|
|
}
|
|
|
|
|
2013-01-24 19:31:37 +01:00
|
|
|
// We are cancelling the new entry, just reset it.
|
|
|
|
if(item === self.newEntry()) {
|
|
|
|
self.clearNewEntry();
|
2013-01-07 18:42:02 +01:00
|
|
|
}
|
|
|
|
|
2013-01-13 13:32:23 +01:00
|
|
|
// Reset saved and selected items to null.
|
2013-01-13 12:00:44 +01:00
|
|
|
self.savedItem = null;
|
2013-01-07 18:42:02 +01:00
|
|
|
self.selectedItem(null);
|
|
|
|
};
|
|
|
|
|
2013-01-26 01:15:07 +01:00
|
|
|
self.pointEntry = function(entry) {
|
|
|
|
if(entry.pointed()) {
|
|
|
|
entry.pointed(false);
|
|
|
|
} else {
|
|
|
|
entry.pointed(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(entry != self.newEntry()) {
|
|
|
|
self.save(entry);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-01-13 13:32:23 +01:00
|
|
|
// Function to save the current selected entry.
|
2013-01-24 19:31:37 +01:00
|
|
|
self.save = function(item) {
|
|
|
|
//var item = self.selectedItem();
|
|
|
|
if(item === self.newEntry()) {
|
|
|
|
item.account_id(self.account().id());
|
|
|
|
}
|
2013-01-07 18:42:02 +01:00
|
|
|
|
2013-01-13 13:32:23 +01:00
|
|
|
// Ajax call to save the entry.
|
2013-01-13 20:57:48 +01:00
|
|
|
var type;
|
2013-01-24 00:01:42 +01:00
|
|
|
var url = "api/entries";
|
2013-01-13 20:57:48 +01:00
|
|
|
|
|
|
|
if(item.id()) {
|
2013-01-24 00:01:42 +01:00
|
|
|
url += "/" + item.id();
|
2013-01-13 20:57:48 +01:00
|
|
|
}
|
|
|
|
|
2013-01-24 00:01:42 +01:00
|
|
|
$.ajax({
|
|
|
|
url: url,
|
|
|
|
type: "PUT",
|
|
|
|
contentType: "application/json",
|
|
|
|
data:ko.toJSON(item),
|
|
|
|
dataType: "json"
|
|
|
|
}).success(function(data) {
|
2013-01-13 20:57:48 +01:00
|
|
|
message("success", "Save", data);
|
2013-01-07 18:42:02 +01:00
|
|
|
|
2013-01-24 19:31:37 +01:00
|
|
|
self.clearNewEntry();
|
2013-01-07 18:42:02 +01:00
|
|
|
self.selectedItem(null);
|
2013-01-13 13:32:23 +01:00
|
|
|
self.savedItem = null;
|
|
|
|
|
|
|
|
// Reload accounts to update solds.
|
2013-01-10 13:10:41 +01:00
|
|
|
self.loadAccounts();
|
2013-01-07 18:42:02 +01:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2013-01-13 13:32:23 +01:00
|
|
|
// Function to remove an entry.
|
2013-01-07 18:42:02 +01:00
|
|
|
self.remove = function (item) {
|
2013-01-13 13:32:23 +01:00
|
|
|
// Cancel current editing.
|
|
|
|
self.cancel();
|
|
|
|
|
2013-01-07 18:42:02 +01:00
|
|
|
if (item.id()) {
|
2013-01-13 13:32:23 +01:00
|
|
|
// This entry is saved in database, we show a modal dialog to confirm the removal.
|
|
|
|
self.removedItem = item;
|
2013-01-12 16:36:33 +01:00
|
|
|
$('#remove-confirm').modal();
|
2013-01-07 18:42:02 +01:00
|
|
|
} else {
|
2013-01-13 13:32:23 +01:00
|
|
|
// This entry was not saved in database yet, we just remove it from the list.
|
2013-01-07 18:42:02 +01:00
|
|
|
self.entries.remove(item);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-01-13 13:32:23 +01:00
|
|
|
// Function to confirm the removal of an entry.
|
2013-01-12 16:36:33 +01:00
|
|
|
self.confirmRemove = function() {
|
2013-01-13 13:32:23 +01:00
|
|
|
var item = self.removedItem;
|
2013-01-12 16:36:33 +01:00
|
|
|
|
2013-01-13 20:57:48 +01:00
|
|
|
$.ajax("api/entries/" + ko.utils.unwrapObservable(item.id), {type: "DELETE"}).success(function (result) {
|
2013-01-13 13:32:23 +01:00
|
|
|
// Reload accounts to update solds.
|
2013-01-12 16:36:33 +01:00
|
|
|
self.loadAccounts();
|
2013-01-24 00:01:42 +01:00
|
|
|
}).success(function (data) {
|
|
|
|
message("success", "Delete", data);
|
|
|
|
}).complete(function (data) {
|
2013-01-13 13:32:23 +01:00
|
|
|
// Reset removed item to null and hide the modal dialog.
|
|
|
|
self.removedItem = null;
|
2013-01-12 16:36:33 +01:00
|
|
|
$('#remove-confirm').modal('hide');
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2013-01-13 13:32:23 +01:00
|
|
|
// Callback function to select a new month.
|
2013-01-09 23:39:26 +01:00
|
|
|
self.selectMonth = function(month) {
|
2013-01-10 13:10:41 +01:00
|
|
|
if(month) {
|
|
|
|
self.month(month);
|
|
|
|
self.loadEntries(self.account(), month);
|
|
|
|
}
|
2013-01-08 18:50:47 +01:00
|
|
|
};
|
|
|
|
|
2013-01-13 13:32:23 +01:00
|
|
|
// Callback function to select a new account.
|
2013-01-09 23:39:26 +01:00
|
|
|
self.selectAccount = function(account) {
|
2013-01-10 13:10:41 +01:00
|
|
|
if(account) {
|
2013-01-24 19:31:37 +01:00
|
|
|
self.newEntry().account_id(account.id());
|
2013-01-10 13:10:41 +01:00
|
|
|
self.account(account);
|
|
|
|
self.loadMonths(account);
|
|
|
|
}
|
2013-01-07 18:42:02 +01:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2013-01-13 13:47:27 +01:00
|
|
|
// Function to draw the sold evolution chart.
|
2013-01-24 20:44:09 +01:00
|
|
|
drawChart = function(data, element) {
|
2013-01-13 13:47:27 +01:00
|
|
|
// Clear previous chart
|
2013-01-12 22:11:48 +01:00
|
|
|
$(element).html("");
|
|
|
|
|
2013-01-24 20:44:09 +01:00
|
|
|
var entries = data.entries;
|
|
|
|
|
2013-01-12 22:11:48 +01:00
|
|
|
if(entries && entries.length > 0) {
|
2013-01-13 13:47:27 +01:00
|
|
|
// Prepare for today vertical line.
|
2013-01-13 09:50:15 +01:00
|
|
|
var today = new Date();
|
|
|
|
today.setHours(0);
|
|
|
|
today.setMinutes(0);
|
|
|
|
|
2013-01-13 13:47:27 +01:00
|
|
|
// Find first and last days to set limits of the x axis.
|
2013-01-13 12:24:33 +01:00
|
|
|
var day = 24 * 60 * 60 * 1000;
|
|
|
|
var firstDate = new Date(Date.parse(entries[0][0]).valueOf() - day).format('yyyy-mm-dd');
|
|
|
|
var lastDate = new Date(Date.parse(entries[entries.length -1][0]).valueOf() + day).format('yyyy-mm-dd');
|
|
|
|
|
2013-01-13 13:47:27 +01:00
|
|
|
// Plot chart, and store it in a window parameter for resize callback (need to be done better than it...)
|
|
|
|
window.chart = $.jqplot(element.id, [entries], {
|
|
|
|
// Title of the chart
|
2013-01-12 23:02:31 +01:00
|
|
|
title: "Évolution du solde",
|
2013-01-12 22:11:48 +01:00
|
|
|
axes:{
|
2013-01-13 13:47:27 +01:00
|
|
|
// Parameters for the date axis
|
2013-01-12 22:11:48 +01:00
|
|
|
xaxis:{
|
2013-01-13 12:24:33 +01:00
|
|
|
autoscale: true,
|
2013-01-13 13:47:27 +01:00
|
|
|
|
|
|
|
// Date rendere for this axis
|
2013-01-12 22:11:48 +01:00
|
|
|
renderer:$.jqplot.DateAxisRenderer,
|
2013-01-13 13:47:27 +01:00
|
|
|
|
|
|
|
// Limits
|
2013-01-13 12:24:33 +01:00
|
|
|
min: firstDate,
|
|
|
|
max: lastDate,
|
2013-01-13 13:47:27 +01:00
|
|
|
|
|
|
|
// Tick options
|
|
|
|
tickOptions: {
|
|
|
|
// Format date for x axis.
|
|
|
|
formatString: "%F"
|
|
|
|
}
|
2013-01-12 22:11:48 +01:00
|
|
|
},
|
2013-01-13 13:47:27 +01:00
|
|
|
// Parameters for the value axis
|
2013-01-12 22:11:48 +01:00
|
|
|
yaxis: {
|
|
|
|
autoscale: true,
|
|
|
|
}
|
|
|
|
},
|
2013-01-13 13:47:27 +01:00
|
|
|
|
|
|
|
// Highlighter parameters
|
2013-01-12 22:11:48 +01:00
|
|
|
highlighter: {
|
2013-01-13 13:47:27 +01:00
|
|
|
show: true,
|
2013-01-24 01:04:01 +01:00
|
|
|
//yvalues: 4,
|
|
|
|
formatString: '<table class="jqplot-highlighter"><tr><td>date:</td><td>%s</td></tr><tr><td>sold:</td><td>%s</td></tr>'
|
2013-01-12 22:11:48 +01:00
|
|
|
},
|
|
|
|
|
2013-01-13 13:47:27 +01:00
|
|
|
// Series parameters
|
2013-01-12 22:11:48 +01:00
|
|
|
series: [{
|
2013-01-13 13:47:27 +01:00
|
|
|
// We use the OHLC (open, high, low, close) rendered.
|
2013-01-24 01:04:01 +01:00
|
|
|
//renderer:$.jqplot.OHLCRenderer,
|
2013-01-12 22:11:48 +01:00
|
|
|
color: "blue",
|
2013-01-24 01:04:01 +01:00
|
|
|
rendererOptions: {
|
|
|
|
smooth: true,
|
|
|
|
}
|
2013-01-13 01:14:58 +01:00
|
|
|
}],
|
2013-01-13 13:47:27 +01:00
|
|
|
|
|
|
|
// To display horizontal (0) and vertical (today) lines
|
2013-01-13 01:14:58 +01:00
|
|
|
canvasOverlay: {
|
|
|
|
show: true,
|
2013-01-13 13:47:27 +01:00
|
|
|
objects: [
|
2013-01-24 20:44:09 +01:00
|
|
|
// Orange horizontal line for 0 limit
|
2013-01-13 13:47:27 +01:00
|
|
|
{dashedHorizontalLine: {
|
2013-01-13 01:14:58 +01:00
|
|
|
name: "zero",
|
|
|
|
y: 0,
|
|
|
|
lineWidth: 1,
|
2013-01-24 20:44:09 +01:00
|
|
|
color: "orange",
|
|
|
|
shadow: false
|
|
|
|
}},
|
|
|
|
// Red horizontal line for authorized overdraft limit
|
|
|
|
{dashedHorizontalLine: {
|
|
|
|
name: "overdraft",
|
|
|
|
y: data.account.authorized_overdraft(),
|
|
|
|
lineWidth: 1,
|
2013-01-13 01:17:45 +01:00
|
|
|
color: "red",
|
|
|
|
shadow: false
|
2013-01-13 01:14:58 +01:00
|
|
|
}},
|
2013-01-13 13:47:27 +01:00
|
|
|
// Gray vertical line for today
|
2013-01-13 01:14:58 +01:00
|
|
|
{ dashedVerticalLine: {
|
|
|
|
name: "today",
|
2013-01-13 09:50:15 +01:00
|
|
|
x: today,
|
2013-01-13 01:14:58 +01:00
|
|
|
lineWidth: 1,
|
2013-01-13 01:17:45 +01:00
|
|
|
color: "gray",
|
|
|
|
shadow: false
|
2013-01-13 13:47:27 +01:00
|
|
|
}}
|
|
|
|
]
|
2013-01-13 01:14:58 +01:00
|
|
|
}
|
2013-01-12 22:11:48 +01:00
|
|
|
});
|
2013-01-12 22:14:42 +01:00
|
|
|
} else {
|
2013-01-13 13:47:27 +01:00
|
|
|
// Reset chart to null to avoid redraw of a non existing chart in resize callback.
|
2013-01-12 22:14:42 +01:00
|
|
|
window.chart = null;
|
2013-01-12 22:11:48 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-01-13 13:47:27 +01:00
|
|
|
// Function to draw the expense category pie chart.
|
2013-01-12 22:11:48 +01:00
|
|
|
drawPieChart = function(entries, element) {
|
2013-01-13 13:47:27 +01:00
|
|
|
// Clear previous chart
|
2013-01-12 22:11:48 +01:00
|
|
|
$(element).html("");
|
|
|
|
|
|
|
|
if(entries && entries.length > 0) {
|
2013-01-13 13:47:27 +01:00
|
|
|
// Plot chart, and store it in a window parameter for resize callback (need to be done better than it...)
|
|
|
|
window.pieChart = $.jqplot(element.id, [entries], {
|
|
|
|
// Title of the chart
|
2013-01-12 23:02:31 +01:00
|
|
|
title: "Dépenses",
|
2013-01-13 13:47:27 +01:00
|
|
|
|
|
|
|
// Series parameters.
|
2013-01-12 22:11:48 +01:00
|
|
|
seriesDefaults: {
|
2013-01-13 13:47:27 +01:00
|
|
|
// Pie chart renderer
|
2013-01-12 22:11:48 +01:00
|
|
|
renderer: $.jqplot.PieRenderer,
|
|
|
|
rendererOptions: {
|
2013-01-14 23:42:05 +01:00
|
|
|
showDataLabels: true,
|
2013-01-12 22:11:48 +01:00
|
|
|
}
|
|
|
|
},
|
2013-01-13 13:47:27 +01:00
|
|
|
|
|
|
|
// Legend parameters.
|
2013-01-12 22:11:48 +01:00
|
|
|
legend: {
|
|
|
|
show: true,
|
2013-01-14 23:42:05 +01:00
|
|
|
location: 'e',
|
|
|
|
rendererOptions: {
|
|
|
|
numberRows: 9,
|
|
|
|
numberColumns: 2
|
|
|
|
}
|
2013-01-12 22:11:48 +01:00
|
|
|
},
|
2013-01-13 13:47:27 +01:00
|
|
|
|
|
|
|
// Highlighter parameters;
|
2013-01-12 22:11:48 +01:00
|
|
|
highlighter: {
|
|
|
|
show: true,
|
|
|
|
formatString:'%s: %s',
|
|
|
|
tooltipLocation:'sw',
|
|
|
|
useAxesFormatters:false
|
|
|
|
}
|
|
|
|
});
|
2013-01-12 22:14:42 +01:00
|
|
|
} else {
|
2013-01-13 13:47:27 +01:00
|
|
|
// Reset chart to null to avoid redraw of a non existing chart in resize callback.
|
2013-01-12 22:14:42 +01:00
|
|
|
window.pieChart = null;
|
2013-01-12 22:11:48 +01:00
|
|
|
}
|
2013-01-12 19:10:43 +01:00
|
|
|
};
|
|
|
|
|
2013-01-13 13:47:27 +01:00
|
|
|
// Chart binding to redraw the chart on entries update.
|
2013-01-12 19:10:43 +01:00
|
|
|
ko.bindingHandlers.chart = {
|
|
|
|
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
|
|
|
|
// empty - left as placeholder if needed later
|
|
|
|
},
|
|
|
|
|
|
|
|
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
|
|
|
|
var unwrap = ko.utils.unwrapObservable;
|
|
|
|
var dataSource = valueAccessor();
|
|
|
|
|
2013-01-24 20:44:09 +01:00
|
|
|
var data = dataSource ? unwrap(dataSource) : null;
|
|
|
|
drawChart(data, element);
|
2013-01-12 13:56:52 +01:00
|
|
|
}
|
|
|
|
};
|
2013-01-07 18:42:02 +01:00
|
|
|
|
2013-01-13 13:47:27 +01:00
|
|
|
// Pie chart binding to redraw expense chart on entries update.
|
2013-01-12 22:11:48 +01:00
|
|
|
ko.bindingHandlers.pieChart = {
|
|
|
|
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
|
|
|
|
// empty - left as placeholder if needed later
|
|
|
|
},
|
|
|
|
|
|
|
|
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
|
|
|
|
var unwrap = ko.utils.unwrapObservable;
|
|
|
|
var dataSource = valueAccessor();
|
|
|
|
|
|
|
|
var entries = dataSource ? unwrap(dataSource) : null;
|
|
|
|
drawPieChart(entries, element);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-01-13 13:47:27 +01:00
|
|
|
// Default AJAX error handler.
|
2013-01-12 19:10:43 +01:00
|
|
|
$(document).ajaxError(function(event, xhr, settings) {
|
2013-01-13 20:57:48 +01:00
|
|
|
message("error", xhr.statusText, xhr.responseText);
|
2013-01-12 19:10:43 +01:00
|
|
|
});
|
2013-01-12 15:51:00 +01:00
|
|
|
|
2013-01-25 00:13:03 +01:00
|
|
|
// Bootstrap.Typeahead binding.
|
|
|
|
// Use like so: data-bind="typeahead: { source: namespaces }"
|
|
|
|
ko.bindingHandlers.typeahead = {
|
|
|
|
init: function(element, valueAccessor, allBindingsAccessor, viewModel) {
|
|
|
|
$(element).typeahead({
|
|
|
|
source: function() { return ko.utils.unwrapObservable(valueAccessor().source); },
|
|
|
|
onselect: function(value) { allBindingsAccessor().value(value); }
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
update: function(element, valueAccessor, allBindingsAccessor, viewModel) {
|
|
|
|
$(element).typeahead({
|
|
|
|
source: function() { ko.utils.unwrapObservable(valueAccessor().source); },
|
|
|
|
onselect: function(value) { allBindingsAccessor().value(value); }
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-01-13 13:47:27 +01:00
|
|
|
// Resize callback.
|
2013-01-12 22:11:48 +01:00
|
|
|
$(window).resize(function() {
|
|
|
|
if(window.chart) {
|
|
|
|
window.chart.replot({resetAxes: true});
|
|
|
|
}
|
|
|
|
|
2013-01-12 22:16:05 +01:00
|
|
|
if(window.pieChart) {
|
2013-01-12 22:11:48 +01:00
|
|
|
window.pieChart.replot({resetAxes: true});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-01-13 13:47:27 +01:00
|
|
|
// ViewModal instanciation.
|
2013-01-12 19:10:43 +01:00
|
|
|
var viewModel = new ListViewModel();
|
2013-01-12 15:51:00 +01:00
|
|
|
ko.applyBindings(viewModel);
|
|
|
|
|
2013-01-13 13:47:27 +01:00
|
|
|
// Load accounts after page initialization.
|
2013-01-12 15:51:00 +01:00
|
|
|
$(viewModel.loadAccounts);
|
2013-01-07 18:42:02 +01:00
|
|
|
|