-- added fancybox 1.3.1 to project, initially for showing screenshots. See documentat...
[Bookkeeping.git] / webkell / javascript / bkeeping.js
blob45993a4182e6bb77274a3285bd09e881ca9e0b8c
1 /* bkeeping */
3 if (!window.console)
5 var console = {};
6 console.log = alert;
9 var PROD_ENV = false;
10 var BLOCK_PARSE_REGEX = /\$\{(\w+)\}/igm;
11 // hack for mock data testing:
12 var _LAST_PARSED_DICT = null;
13 String.prototype.parse_vars = function(dataDict)
15 _LAST_PARSED_DICT = dataDict;
16 return this.replace(BLOCK_PARSE_REGEX, function(match, param, offset, orig)
18 if (!dataDict) { return ""; }
19 return (dataDict[param] || (dataDict[param] == 0)) ? (dataDict[param]) : ("");
20 });
23 /**
24 * The "bind()" function extension from Prototype.js, extracted for general use
26 * @author Richard Harrison, http://www.pluggable.co.uk
27 * @author Sam Stephenson (Modified from Prototype Javascript framework)
28 * @license MIT-style license @see http://www.prototypejs.org/
30 Function.prototype.bind = function()
32 var _$A = function(a)
34 return Array.prototype.slice.call(a);
36 if ((arguments.length < 2) && (typeof arguments[0] == "undefined"))
38 return this;
40 var __method = this, args = _$A(arguments), object = args.shift();
41 return function()
43 return __method.apply(object, args.concat(_$A(arguments)));
47 /* settings */
48 var DEBUG = false;
49 var MOCK_DATA = false;
50 var MOCK_DATA_FAILURES_MODE = false;
51 /* ******** */
53 /* commands */
54 var LOGIN_COMMAND = "login (user -username ${username} -password ${password});";
55 var REGISTER_COMMAND = "var someVariable = add ( " +
56 "(load (`/system[ @id='main.system']`)) " +
57 "<user xmlns='com/interrupt/bookkeeping/users' id='${username}' username='${username}' password='${password}'>" +
58 "<allowedActions id='${username}.allowedActions' xmlns='com/interrupt/bookkeeping/cc/bkell/aauth' >" +
59 "<command id='command.create' name='create' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
60 "<command id='command.add' name='add' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
61 "<command id='command.remove' name='remove' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
62 "<command id='command.reverse' name='reverse' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
63 "<command id='command.find' name='find' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
64 "<command id='command.load' name='load' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
65 "<command id='command.list' name='list' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
66 "<command id='command.print' name='print' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
67 "<command id='command.commit' name='commit' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
68 "<command id='command.login' name='login' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
69 "<command id='command.logout' name='logout' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
70 "<command id='command.exit' name='exit' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
71 "</allowedActions>" +
72 "<profileDetails xmlns='com/interrupt/bookkeeping/users' id='user.details'>" +
73 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='firstname' name='first.name' value='${firstname}'/>" +
74 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='lastname' name='last.name' value='${lastname}'/>" +
75 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='email' name='email' value='${email}'/>" +
76 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='country' name='country' value='${country}'/>" +
77 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='defaultCurrency' name='defaultCurrency' value='${currency}' />" +
78 "</profileDetails>" +
79 "</user> , user -returninput true" +
80 ") ; ";
81 var GETPROFILE_COMMAND = "load (`/system[@id='main.system']/aauthentication[@id='main.authentication']/users[@id='aauth.users']/user[@id='${username}']`);";
82 var UPDATEPROFILE_COMMAND = "var someVariable = update ( " +
83 "(`/system[ @id='main.system']/aauthentication[@id='main.authentication']/users[@id='aauth.users']/user[@id='${username}']/profileDetails[@id='user.details']`) " +
84 "<profileDetails xmlns='com/interrupt/bookkeeping/users' id='user.details'>" +
85 "<profileDetail name='first.name' value='${firstname}'/>" +
86 "<profileDetail name='last.name' value='${lastname}'/>" +
87 "<profileDetail name='email' value='${email}'/>" +
88 "<profileDetail name='country' value='${country}'/>" +
89 "</profileDetails>" +
90 ");commit ((`/system[ @id='main.system']/aauthentication[@id='main.authentication']/users[@id='aauth.users']/user[@id='${username}']/profileDetails[@id='user.details']`) @someVariable);";
92 var LISTACCOUNTS_COMMAND = "load ( `/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}.group\"]/bookkeeping[@id='main.bookkeeping']/accounts[@id='main.accounts']`);";
93 var LISTJOURNALS_COMMAND = "load ( `/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}.group\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals']`);";
94 var LISTENTRIES_COMMAND = "load ( `/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}.group\"]/bookkeeping[@id='main.bookkeeping']/journals[ @id='main.journals' ]/journal[@id='${journal}']/entries[@id='main.entries']`);";
95 var LISTENTRY_COMMAND = "load ( `/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}.group\"]/bookkeeping[@id='main.bookkeeping']/journals[ @id='main.journals' ]/journal[@id='${journal}']/entries[@id='main.entries']/entry[@id='${entry}']`);";
97 var LOADDEFAULTCURRENCY_COMMAND = "load ( `/system[ @id='main.system' ]/aauthentication[ @id='main.authentication' ]/users[ @id='aauth.users' ]/user[ @id='${username}' ]/profileDetails[ @id='user.details' ]/profileDetail[ @id='defaultCurrency' ] `);";
100 /****
101 * ACCOUNT Commands
103 var ADDACCOUNT_COMMAND = "var addedAccount = add((load(`/system[@id='main.system']/groups[@id='main.groups']/group[@id='${username}.group']/bookkeeping[@id='main.bookkeeping']/accounts[@id='main.accounts']`))" +
104 "<account xmlns='com/interrupt/bookkeeping/account' type=\"${type}\" id=\"${id}\" name=\"${name}\" counterWeight=\"${cw}\" currency='CDN' /> );"+
105 "commit((`/system[@id='main.system']/groups[@id='main.groups']/group[@id='${username}.group']/bookkeeping[@id='main.bookkeeping']/accounts[@id='main.accounts']`) @addedAccount);";
107 var REMOVEACCOUNT_COMMAND = "var removedAccount = remove ((`/system[@id='main.system']/groups[@id='main.groups']/group[@id='${username}.group']/bookkeeping[@id='main.bookkeeping']/accounts[@id='main.accounts']`) account -id ${aid});" +
108 "commit((`/system[@id='main.system']/groups[@id='main.groups']/group[@id='${username}.group']/bookkeeping[@id='main.bookkeeping']/accounts[@id='main.accounts']`) @removedAccount);";
109 var UPDATEACCOUNT_COMMAND = "var updatedAccount = update((`/system[@id='main.system']/groups[@id='main.groups']/group[@id='${username}.group']/bookkeeping[@id='main.bookkeeping']/accounts[@id='main.accounts']/account[@id='${id}']`)" +
110 "<account xmlns=\"com/interrupt/bookkeeping/account\" type=\"${type}\" id=\"${id}\" name=\"${name}\" counterWeight=\"${cw}\"/>);" +
111 "commit( (`/system[@id='main.system']/groups[@id='main.groups']/group[@id='${username}.group']/bookkeeping[@id='main.bookkeeping']/accounts[@id='main.accounts']/account[@id='${id}']`) @updatedAccount);";
113 /****
114 * JOURNAL Commands
116 var ADDJOURNAL_COMMAND = "var addedJournal = add((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals']/journal`)" +
117 "<journal:journal id='${jid}' name='${jname}' type='${jtype}' balance='${jbalance}'/>" +
118 "commit((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals']/journal`) @addedJournal);";
119 var REMOVEJOURNAL_COMMAND = "var removedJournal = remove ((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals']/journal`) journal -id ${jid});" +
120 "commit((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals']/journal`) @removedJournal);";
121 var UPDATEJOURNAL_COMMAND = "var updatedJournal = update(( load(`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}.group\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals']/journal[@id='${id}']`)) " +
122 "<journal xmlns=\"com/interrupt/bookkeeping/journal\" id='${id}' name='${name}' type='${jtype}' balance='${jbalance}'/> ); " +
123 "commit((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}.group\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals']/journal[@id='${id}']`) @updatedJournal);";
126 /****
127 * ENTRY Commands
129 var ADDENTRY_COMMAND = "var addedEntry = add((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}\"]/bookkeeping[@id='main.bookkeeping']/journals[ @id='main.journals' ]/journal[@id='${jid}']/entries[@id='main.entries']`)" +
130 "<entry xmlns='com/interrupt/bookkeeping/journal' id='${eid}' entrynum='' state='' journalid='${jid}' date='${date}' currency='${cc}'>" +
131 "${debitsANDcredits}" +
132 "</entry> ); " +
133 "commit((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}.group\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals' ]/journal[@id='${jid}']/entries[@id='main.entries']`) @addedEntry);";
135 var REMOVEENTRY_COMMAND = "var removedEntry = remove ((load(`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}.group\"]/bookkeeping[@id='main.bookkeeping']/journals[ @id='main.journals' ]/journal[@id='${jid}']/entries[@id='main.entries']`)) entry -id ${eid});" +
136 "commit((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}.group\"]/bookkeeping[@id='main.bookkeeping']/journals[ @id='main.journals' ]/journal[@id='${jid}']/entries[@id='main.entries']`) @removedEntry);";
138 var UPDATEENTRY_COMMAND = "var updatedEntry = update( (load(`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}.group\"]/bookkeeping[@id='main.bookkeeping']/journals[ @id='main.journals' ]/journal[@id='${jid}']/entries[@id='main.entries']/entry[@id='${id}']`))" +
139 "<entry xmlns=\"com/interrupt/bookkeeping/journal\" id='${id}' entrynum='' state='' journalid='${jid}' date='${date}' currency='${cc}'>" +
140 "${debitsANDcredits}" +
141 "</entry> );" +
142 "commit( (`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}.group\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals']/journal[@id='${jid}']/entries[@id='main.entries']/entry[@id='${id}']`) @updatedEntry);";
145 /* ******** */
147 /* templates */
148 // acl_rows
149 var ACCOUNT_ROW = '<div id="${rowid}" class="acl_row row">' +
150 '<div class="edit_column left_column"><a href="javascript:bkeeping.editAccount(\'${rowid}\');"><img border="0" src="images/editrow.jpg"/></a></div>' +
151 '<div class="name_column left_column" style="margin-left: 10px;" >${aname}</div>' +
152 '<div class="category_column left_column" style="margin-left: 40px; margin-right: 20px;" >${category}</div>' +
153 // '<div class="balance_column left_column">${balance}</div>' +
154 '<div class="delete_column left_column"><a href="javascript:bkeeping.deleteAccount(\'${rowid}\');"><img border="0" src="images/deleterow.jpg"/></a></div>' +
155 '</div>';
156 // js_rows
157 var JOURNAL_ROW = '<div id="${rowid}" class="js_row row">' +
158 '<div class="edit_column left_column"><a href="javascript:bkeeping.editJournal(\'${rowid}\');"><img border="0" src="images/editrow.jpg"/></a></div>' +
159 '<div class="large_name_column left_column"><a onclick="bkeeping.showJournal(\'${rowid}\')">${jname}</a></div>' +
160 '<div class="delete_column left_column"><a href="javascript: alert(\'Coming Soon\'); "><img border="0" src="images/deleterow.jpg"/></a></div>' +
161 '</div>';
163 // j_rows
164 var ENTRY_ROW = '<div id="${rowid}" class="j_row row">' +
166 '<div class="edit_column left_column"><a href="javascript:bkeeping.editEntry(\'${rowid}\');"><img border="0" src="images/editrow.jpg"/></a></div>' +
168 '<div class="date_column left_column" style="margin:0 50px;" >${date}</div>' +
169 '<div class="entry_column left_column" style="margin:0 50px;" >${entry}</div>' +
170 '<div class="balance_column left_column" style="margin:0 50px;" >${balance}</div>' +
172 '<div class="delete_column left_column"><a href="javascript:bkeeping.deleteThing(\'entry\', \'${rowid}\');"><img border="0" src="images/deleterow.jpg"/></a></div>' +
174 '</div>';
178 var TENTRY_ROW = '<div id="jentry_rows" class="j_rows rows">' +
180 ' <div class="edit_column left_column"><a href="javascript:bkeeping.addEditEntryPart(\'${rowid}\', \'${eid}\');"><img border="0" src="images/editrow.jpg"/></a></div>' +
182 ' <div style="float:left; width: 18%; margin-left: 90px; ">${dname}</div><div style="float:left; width: 20%; ">${damount}</div>' +
183 ' <div style="float:left; width: 20%; ">${cname}</div><div style="float:left; width: 20%; ">${camount}</div>' +
185 ' <div class="delete_column left_column"><a href="javascript: alert(\'Coming Soon. Please replace entry\');"><img border="0" src="images/deleterow.jpg"/></a></div>' +
187 '</div>';
190 var AE_ACCOUNT = ["Add/Edit Account", '<table class="formtable">' +
191 '<tr>' +
192 '<td>' +
193 'Name' +
194 '</td>' +
195 '<td>' +
196 '<input type="text" name="account_name" id="account_name" class="data_field" value="${aname}" />' +
197 '</td>' +
198 '</tr>' +
199 '<tr>' +
200 '<td>' +
201 'Type' +
202 '</td>' +
203 '<td>' +
204 '<select name="account_type" id="account_type" class="data_field">' +
205 '${accounts}'+
206 '</select>' +
207 '</td>' +
208 '</tr>' +
209 '</table>'];
211 var AE_JOURNAL = ["Add/Edit Journal", '<table class="formtable">' +
212 '<tr>' +
213 '<td>' +
214 'Name' +
215 '</td>' +
216 '<td>' +
217 '<input type="text" name="journal_name" id="journal_name" class="data_field" value="${name}" />' +
218 '</td>' +
219 '</tr>' +
220 /*'<tr>' +
221 '<td>' +
222 'Type' +
223 '</td>' +
224 '<td>' +
225 '<select name="journal_type" id="journal_type" class="data_field">' +
226 '<option>default</option>' +
227 '</select>' +
228 '</td>' +
229 '</tr>' +
230 '<tr>' +
231 '<td>' +
232 'Balance' +
233 '</td>' +
234 '<td>' +
235 '<input type="text" name="journal_balance" id="journal_balance" class="data_field" value="${balance}" />' +
236 '</td>' +
237 '</tr>' +
239 '</table>'];
241 var AE_ENTRY = ["Add/Edit Entry", '<table class="formtable">' +
242 '<input type="hidden" name="entryid" value="${entryid}" />' +
243 '<tr>' +
244 '<td>' +
246 '<span class="smallformtext">Currency:</span>&nbsp;' +
247 '<select name="entry_cc" id="entry_cc" class="data_field">' +
248 '<option value="CDN" ${curr_cdn} >Canadian Dollar</option>' +
249 '<option value="USD" ${curr_usd} >US Dollar</option>' +
250 '<option value="BP" ${curr_bp} >British Pound</option>' +
251 '<option value="EUR" ${curr_eur} >European Euro</option>' +
252 '<option value="JPN" ${curr_jpn} >Japanese Yen</option>' +
253 '</select>' +
254 '</td>' +
255 '</tr>' +
256 '<tr>' +
257 '<td>' +
258 '<span class="smallformtext">Amount:</span>&nbsp;<input type="text" size="6" name="entry_ecamount" id="entry_ecamount" class="data_field" value="${evalue}" >&nbsp;' +
259 '</td>' +
260 '</tr>' +
261 '<tr>' +
262 '<td>' +
263 '<span class="smallformtext">Account:</span>&nbsp;' +
264 '<select name="entry_caid" id="entry_caid" class="data_field">' +
265 '${accounts}' +
266 '</select>' +
267 '</td>' +
268 '</tr>' +
269 '<tr>' +
270 '<td>' +
271 '<span class="smallformtext">Type:</span>&nbsp;' +
272 '<select name="entry_ctype" id="entry_ctype" class="data_field">' +
273 '<option value="debit" ${entry_dtype} >debit</option><option value="credit" ${entry_ctype} >credit</option>' +
274 '</select>' +
275 '</td>' +
276 '</tr>' +
277 '</table>'];
280 /* ********* */
282 var DIALOG_HOLDER = "<div id='bkeeping_dialog' class='dialog'>Dialog default text.</div>";
283 var _DIALOG_FIRST = true;
285 // top namespace
286 var bkeeping = {
289 // session
290 ACTIVE_SESSION: null,
291 DEFAULT_CURRENCY: null,
292 ROOT_URL: null,
295 cache:
297 user: null,
298 accounts: [],
299 journals: [],
300 entries: []
303 /* logging */
304 debug: function(message)
306 if (DEBUG)
308 if (window.console)
310 //console.log(message);
312 else
314 alert(message);
319 log: function(message)
321 if (window.console)
323 //console.log(message);
325 else
327 // do nothing (yet)
331 raise: function(message)
333 alert(message);
335 /* ******* */
339 processLogin: function() {
341 bkeeping.login();
342 $('#loading-msg').animate({ left: 0 }, { duration: 750, easing: 'swing', queue: false });
344 processRegister: function() {
346 bkeeping.register();
347 $('#loading-msg').animate({ left: 0 }, { duration: 750, easing: 'swing', queue: false });
352 modalDialogScreen: function(showhide)
354 var m = $("#modal_screen");
355 if (showhide == "show")
357 m.css('display', 'block');
359 else
361 m.css('display', 'none');
365 showModalDialog: function()
367 bkeeping.modalDialogScreen("show");
368 bkeeping.showDialog.apply(this, arguments);
371 showDialog: function()
373 bkeeping.setDialogContent.apply(this, arguments);
374 bkeeping.openDialog();
377 setDialogContent: function()
379 var args = Array.prototype.slice.call(arguments);
380 var content = args.shift();
381 if (content instanceof Function)
383 $("#bkeeping_dialog").html(content.apply(this, args));
385 else
387 $("#bkeeping_dialog").html(content);
391 openDialog: function()
393 if (_DIALOG_FIRST)
395 _DIALOG_FIRST = false;
396 $("#bkeeping_dialog").dialog({ close: bkeeping.modalDialogScreen.bind(this, "hide") });
398 else
400 $("#bkeeping_dialog").dialog("open");
404 _a_open: true,
405 toggleAccountsOpen: function()
407 var newleft = "0px";
408 if (bkeeping._a_open)
410 bkeeping._a_open = false;
411 newleft = "-373px";
413 else
415 bkeeping._a_open = true;
418 $("#accounts_ui").animate({ left: newleft }, { duration: 750, easing: "swing", complete: function() {}, queue: false });
422 _j_open: true,
423 toggleJournalsOpen: function()
425 var newleft = "436px";
426 if (bkeeping._j_open)
428 bkeeping._j_open = false;
429 newleft = "809px";
431 else
433 bkeeping._j_open = true;
435 $("#journals_ui").animate({ left: newleft }, { duration: 750, easing: "swing", complete: function() {}, queue: false });
438 /* state */
439 /* cookie functions: http://techpatterns.com/downloads/javascript_cookies.php */
440 setCookie: function(name, value, expires, path, domain, secure)
442 // set time, it's in milliseconds
443 var today = new Date();
444 today.setTime(today.getTime());
446 // EdA: override this to default to base path instead of "" (easier for simplistic auth systems)
447 path = path || "/";
450 {expires} days
452 if (expires)
454 expires = expires * 1000 * 60 * 60 * 24;
456 var expires_date = new Date(today.getTime() + (expires));
457 document.cookie = name + "=" + escape(value) +
458 ((expires) ? ";expires=" + expires_date.toGMTString() : "") +
459 ((path) ? ";path=" + path : "") +
460 ((domain) ? ";domain=" + domain : "") +
461 ((secure) ? ";secure" : "" );
464 getCookie: function(name)
466 // first we'll split this cookie up into name/value pairs
467 // note: document.cookie only returns name=value, not the other components
468 var a_all_cookies = document.cookie.split(';');
469 var a_temp_cookie = '';
470 var cookie_name = '';
471 var cookie_value = '';
472 var b_cookie_found = false; // set boolean t/f default f
474 for (var i = 0; i < a_all_cookies.length; i++)
476 // now we'll split apart each name=value pair
477 a_temp_cookie = a_all_cookies[i].split('=');
479 // and trim left/right whitespace while we're at it
480 cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, '');
482 // if the extracted name matches passed name
483 if (cookie_name == name)
485 b_cookie_found = true;
486 // we need to handle case where cookie has no value but exists (no = sign, that is):
487 if (a_temp_cookie.length > 1)
489 cookie_value = unescape(a_temp_cookie[1].replace(/^\s+|\s+$/g, ''));
491 // note that in cases where cookie is initialized but no value, null is returned
492 return cookie_value;
493 break;
495 a_temp_cookie = null;
496 cookie_name = '';
498 if (!b_cookie_found)
500 return null;
504 deleteCookie: function(name, path, domain)
506 if (this.getCookie(name))
508 // EdA: override this to default to base path instead of "" (easier for simplistic auth systems)
509 path = path || "/";
510 document.cookie = name + "=" +
511 ((path) ? ";path=" + path : "") +
512 ((domain) ? ";domain=" + domain : "" ) +
513 ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
517 Session: function(sessionId, loggedInUserid, loggedInGroup)
519 this.cookieName = "_bk_session_js";
520 this.loaded = false;
521 this.sessionId = sessionId || null;
522 this.loggedInUserid = loggedInUserid || null;
523 this.loggedInGroup = loggedInGroup || null;
525 this.update = function(sessionId, loggedInUserid, loggedInGroup)
527 this.sessionId = sessionId || null;
528 this.loggedInUserid = loggedInUserid || null;
529 this.loggedInGroup = loggedInGroup || null;
530 return this;
533 this.clear = function()
535 this.loaded = false;
536 this.sessionId = null;
537 this.loggedInUserid = null;
538 this.loggedInGroup = null;
539 bkeeping.deleteCookie(this.cookieName);
540 return this;
543 function saveToSessionCookie()
545 bkeeping.setCookie(this.cookieName, this.sessionId + "," + this.loggedInUserid + "," + this.loggedInGroup);
546 return this;
548 this.save = saveToSessionCookie.bind(this);
550 function loadFromSessionCookie()
552 var cookievalues = bkeeping.getCookie(this.cookieName);
553 if (cookievalues)
555 var vals = cookievalues.split(',');
556 this.sessionId = vals[0];
557 this.loggedInUserid = vals[1];
558 this.loggedInGroup = vals[2];
559 return true;
561 return false;
564 if (!this.sessionId)
566 // try to load from cookie:
567 this.loaded = loadFromSessionCookie.call(this); // true if there was a session cookie, false otherwise
570 return this;
572 /* ***** */
574 /* data objects */
575 User: function(root)
577 this.root = root;
579 this.getProfileDetailsList = function()
581 return this.root["children"][0]["profileDetails"]["children"];
584 this.getUserid = function()
586 return this.root["id"];
589 this.getUsername = function()
591 return this.root["username"];
594 this.getPassword = function()
596 return this.root["password"];
599 this.getFirstname = function()
601 return this.root["children"][0]["profileDetails"]["children"][0]["profileDetail"].value;
604 this.getLastname = function()
606 return this.root["children"][0]["profileDetails"]["children"][1]["profileDetail"].value;
609 this.getEmail = function()
611 return this.root["children"][0]["profileDetails"]["children"][2]["profileDetail"].value;
614 this.getCountry = function()
616 return this.root["children"][0]["profileDetails"]["children"][3]["profileDetail"].value;
620 UserSession: function(root)
622 this.root = root;
623 this.getSessionId = function()
625 return this.root["id"];
628 this.getGroup = function()
630 return this.root["groupid"];
633 this.getUser = function()
635 return this.root["userid"];
638 return this;
641 DefaultCurrency: function(root)
643 this.root = root;
644 this.getDefaultCurrency = this.lister.bind(this, "profileDetail", bkeeping.DefaultCurrency);
645 this.getDefaultCurrencyRaw = this.rawlister.bind(this);
648 lister: function(nodename, _class)
650 if( this.root["children"] != null ) {
652 var c = this.root["children"];
653 if (c.length > 0)
655 var a = new Array();
656 for (var i = 0; i < c.length; i++) {
658 if( c[i][nodename] != undefined ) {
659 a.push(new _class(c[i][nodename]));
661 else if( c[i]["debit"] != undefined ) {
662 a.push( new bkeeping.Subentry( c[i]["debit"], "debit" ) );
664 else if( c[i]["credit"] != undefined ) {
665 a.push( new bkeeping.Subentry( c[i]["credit"], "credit" ) );
668 return a;
673 rawlister: function()
675 return this.root["children"];
678 Lister: function()
680 this.lister = bkeeping.lister;
681 this.rawlister = bkeeping.rawlister;
684 AccountList: function(root)
686 this.root = root;
687 this.getAccounts = this.lister.bind(this, "account", bkeeping.Account);
688 this.getAsList = this.getAccounts;
689 this.getAccountsRaw = this.rawlister.bind(this);
690 this.getAsRawList = this.getAccountsRaw;
693 Account: function(root)
695 this.root = root;
697 this.getType = function()
699 return this.root['type'];
702 this.getId = function()
704 return this.root['id'];
707 this.getName = function()
709 return this.root['name'];
712 this.getCounterWeight = function()
714 return this.root['counterWeight'];
717 this.getBalance = function()
719 // XXX balance not in account structure, HELP
720 return "xxxhelpxxx";
724 JournalList: function(root)
726 this.root = root;
727 this.getJournals = this.lister.bind(this, "journal", bkeeping.Journal);
728 this.getAsList = this.getJournals;
729 this.getJournalsRaw = this.rawlister.bind(this);
730 this.getAsRawList = this.getJournalsRaw;
733 Journal: function(root)
735 this.root = root;
737 this.getId = function()
739 return this.root["id"];
742 this.getName = function()
744 return this.root["name"];
747 this.getType = function()
749 return this.root["type"];
752 this.getBalance = function()
754 return this.root["balance"];
758 EntryList: function(root)
760 this.root = root;
761 this.getEntries = this.lister.bind(this, "entry", bkeeping.Entry);
762 this.getAsRawList = this.rawlister.bind(this);
765 Entry: function(root)
767 this.root = root;
769 this.getId = function()
771 return this.root["id"];
774 this.getEntrynum = function()
776 return this.root["entrynum"];
779 this.getState = function()
781 return this.root["state"];
784 this.getJournalid = function()
786 return this.root["journalid"];
789 this.getDate = function()
791 return this.root["date"];
794 this.getCurrency = function()
796 return this.root["currency"];
799 this.getSubentries = function(type, raw)
801 var d = this.root["children"];
802 var a = new Array();
803 for (var i = 0; i < d.length; i++)
805 if (d[i][type])
807 if (raw)
809 a.push(d[i]);
811 else
813 a.push(new bkeeping.Subentry(d[i], type));
817 return a;
820 this.getDebits = function()
822 return this.getSubentries("debit");
825 this.getCredits = function()
827 return this.getSubentries("credit");
830 this.getDebitsRaw = function()
832 return this.getSubentries("debit", true);
835 this.getCreditsRaw = function()
837 return this.getSubentries("credit", true);
841 Subentry: function(root, type)
843 this.root = root;
844 this.type = type;
846 this.getId = function()
848 return this.root["id"];
851 this.getAmount = function()
853 return this.root["amount"];
856 this.getEntryid = function()
858 return this.root["entryid"];
861 this.getAccountid = function()
863 return this.root["accountid"];
866 this.getAccount = function()
868 return this.root["account"];
871 this.getCurrency = function()
873 return this.root["currency"];
876 /* ************ */
878 /* commands */
879 login: function()
881 var u = $("#u").val();
882 var p = $("#p").val();
883 var ret = new this.LoginCommand(u, p).run();
886 logout: function()
889 var ret = new this.LogoutCommand().run();
893 register: function()
895 var username = $("#username").val();
896 var password = $("#password").val();
897 var firstname = $("#firstname").val();
898 var lastname = $("#lastname").val();
899 var email = $("#email").val();
900 var country = $("#country").val();
901 var currency = $("#currency").val();
902 var ret = new this.RegisterCommand(username, password, firstname, lastname, email, country, currency).run();
905 editprofile: function()
907 var username = $("#username").val();
908 var password = $("#password").val();
909 var firstname = $("#firstname").val();
910 var lastname = $("#lastname").val();
911 var email = $("#email").val();
912 var country = $("#country").val();
913 var ret = new this.RegisterCommand(username, password, firstname, lastname, email, country, currency, true).run();
916 deleteThing: function(thingtype, id)
918 var goAhead = function(id)
920 $('#bkeeping_dialog').dialog("close");
921 var ret = new this.RemoveCommand(thingtype, function(id) { alert(thingtype + " " + id + " removed"); }.bind(this, id), {'eid': id , 'jid': bkeeping._displayed_journal , 'username': bkeeping.ACTIVE_SESSION.loggedInUserid }).run();
922 }.bind(this, id);
923 bkeeping.showDialog("Are you sure you want to delete " + thingtype + " with id[" + id + "] ?");
924 $('#bkeeping_dialog').dialog('option', 'buttons', { "CANCEL": function() { $(this).dialog("close"); }, "DELETE": goAhead });
927 addedEdited: function(jsonData)
929 bkeeping.debug("addedEdited: returned jsonData:");
930 bkeeping.debug(jsonData);
931 alert('Recored added/edited. Reloading UI to display updates.');
932 var loc = document.location.href;
933 if (loc.indexOf('?') != -1)
935 loc = loc.substring(0, loc.indexOf('?'));
937 if (!DEBUG)
939 document.location.href = loc + '?timestamp=' + new Date().getTime();
943 //** closing entry window
944 $("#entry_ui").css("display", "none");
947 //** reloading journal window
948 bkeeping.showJournal(bkeeping._displayed_journal);
952 addedEditedJournal: function(jsonData)
954 bkeeping.debug("addedEditedJournal: returned jsonData:");
955 bkeeping.debug(jsonData);
956 alert('Recored added/edited. Reloading UI to display updates.');
958 //** reload the current page
959 setTimeout("document.location.href='"+ bkeeping.ROOT_URL +"';", 0);
963 wrapFormAsObj: function()
965 var pars = {};
966 var fields = $(".data_field");
967 for (var i = 0; i < fields.length; i++)
969 var field = fields[i];
970 var n = field.id.substring(field.id.lastIndexOf('_') + 1);
971 var v = $(field).val();
972 bkeeping.debug('wrapFormAsObj: found ' + n + '/' + v);
973 pars[n] = v;
975 return pars;
978 getAccountsAsOptionsList: function(atype)
980 // we have to assume they're cached, coz this is a synchronous call
981 var html = "";
982 var accounts = bkeeping.cache.accounts;
983 for (var i = 0; i < accounts.length; i++)
985 var id = accounts[i].account.id;
986 var nam = accounts[i].account.name;
987 var type = accounts[i].account.type;
988 if (id)
990 if( atype == type ) {
991 html += '<option value="' + id + '" selected="selected" >' + nam + '</option>';
993 else {
994 html += '<option value="' + id + '">' + nam + '</option>';
998 return html;
1000 getAccountTypesAsOptionsList: function(atype)
1002 // we have to assume they're cached, coz this is a synchronous call
1003 var html = "";
1004 var thingy = { asset: '<option ${selected}>asset</option>',
1005 expense: '<option ${selected}>expense</option>',
1006 liability: '<option ${selected}>liability</option>',
1007 revenue: '<option ${selected}>revenue</option>'
1010 for( property in thingy ) {
1012 if ( property == atype ) {
1014 html += thingy[property].parse_vars( { selected:"selected='selected'" } );
1016 else {
1018 html += thingy[property].parse_vars();
1021 return html;
1024 getCounterWeight: function(type)
1026 switch (type)
1028 case "asset":
1029 return "debit";
1030 case "expense":
1031 return "debit";
1032 case "liability":
1033 return "credit";
1034 case "revenue":
1035 return "credit";
1036 default:
1037 return "credit";
1041 getCurrentDate: function()
1043 var d = new Date();
1044 var day = d.getDate();
1045 var month = d.getMonth() + 1;
1046 var year = d.getFullYear();
1047 if (day < 10) { day = '0' + day; }
1048 if (month < 10) { month = '0' + month; }
1049 return day + '' + month + '' + year;
1052 getAccountNameForId: function(aid) {
1054 var accounts = bkeeping.cache.accounts;
1055 for (var i = 0; i < accounts.length; i++)
1057 var id = accounts[i].account.id;
1058 var nam = accounts[i].account.name;
1059 if (id == aid )
1061 return nam;
1064 return null;
1068 goJournalPart: function(thingtype, id)
1071 var type = id ? this.EditCommand : this.AddCommand;
1072 var pars = this.wrapFormAsObj();
1073 pars["username"] = bkeeping["ACTIVE_SESSION"]["loggedInUserid"];
1074 pars["id"] = id;
1077 $('#bkeeping_dialog').dialog("close");
1079 var ret = new type(thingtype, this.addedEditedJournal, pars).run();
1083 goAccountPart: function(thingtype, id)
1086 var type = id ? this.EditCommand : this.AddCommand;
1087 var pars = this.wrapFormAsObj();
1088 pars["username"] = bkeeping["ACTIVE_SESSION"]["loggedInUserid"];
1089 pars["id"] = id;
1092 if( (pars["type"] == "asset") || (pars["type"] == "expense") ) { //** if asset or expense - debit
1093 pars["cw"] = "debit";
1095 else { //** otherwise - credit
1096 pars["cw"] = "credit";
1099 $('#bkeeping_dialog').dialog("close");
1102 var ret = new type(thingtype, this.addedEdited, pars).run();
1105 goJournalEntryPart: function(thingtype, id)
1108 var type = id ? this.EditCommand : this.AddCommand;
1109 var pars = this.wrapFormAsObj();
1111 //** put in data mapping
1114 //** TODO - i) take ID and update entry or ii) put a new entry with ID of 'new'
1115 var entry = null;
1116 if( (id != null) && (id.length > 0) ) {
1118 entry = bkeeping.findEntry(id);
1120 else if( bkeeping.findEntry("new") != null ) {
1122 entry = bkeeping.findEntry("new");
1124 else {
1126 entry = new bkeeping.Entry();
1127 entry["children"] = [];
1129 entry["id"] = "new";
1130 entry["currency"] = pars["cc"];
1131 entry["date"] = "";
1132 entry["entrynum"] = null;
1133 entry["journalid"] = "";
1134 entry["state"] = null;
1135 entry["xmlns"] = "com/interrupt/bookkeeping/journal";
1137 //bkeeping.cache.entries[ bkeeping.cache.entries.length ] =
1138 bkeeping.cache.entries.push( { entry: entry } );
1142 //** set the entry currency
1143 if( pars["cc"] == null ) {
1144 if( entry != null ) {
1145 pars["cc"] = entry["currency"];
1149 var obj = null;
1150 if( pars["ctype"] == "debit" ) {
1152 var debit = {
1153 account: bkeeping.getAccountNameForId( pars["caid"] ),
1154 accountid: pars["caid"],
1155 amount: pars["ecamount"],
1156 currency: pars["cc"],
1157 entryid: "",
1158 id: ( id != null ) ? id : "" ,
1159 xmlns: "com/interrupt/bookkeeping/account"
1161 obj = {
1162 dname: bkeeping.getAccountNameForId( pars["caid"] ),
1163 daid: pars["caid"],
1164 damount: pars["ecamount"],
1165 cname: "&nbsp;",
1166 caid: "&nbsp;",
1167 camount: "&nbsp;"
1170 entry.children.push( { debit: debit } );
1173 else {
1175 var credit = {
1176 account: bkeeping.getAccountNameForId( pars["caid"] ),
1177 accountid: pars["caid"],
1178 amount: pars["ecamount"],
1179 currency: pars["cc"],
1180 entryid: "",
1181 id: ( id != null ) ? id : "" ,
1182 xmlns: "com/interrupt/bookkeeping/account"
1184 obj = {
1185 dname: "&nbsp;",
1186 daid: "&nbsp;",
1187 damount: "&nbsp;",
1188 cname: bkeeping.getAccountNameForId( pars["caid"] ),
1189 caid: pars["caid"],
1190 camount: pars["ecamount"]
1194 entry.children.push( { credit: credit } );
1199 var entryPart = TENTRY_ROW.parse_vars( obj );
1200 var thing = $("#jentry_rows");
1201 $("#jentry_rows").append( entryPart );
1203 $('#bkeeping_dialog').dialog("close");
1205 //var ret = new type(thingtype, this.addedEdited, pars).run();
1209 findJournalName: function(jid) {
1211 var resultName = null;
1212 for( var i = 0; i < bkeeping.cache.journals.length; i++ ) {
1214 var eachJ = bkeeping.cache.journals[i]["journal"];
1215 if( eachJ["id"] == jid ) {
1217 resultName = eachJ["name"];
1218 break;
1223 return resultName;
1226 findAccountName: function(aid) {
1228 var returnName = null;
1229 for( var i = 0; i < bkeeping.cache.accounts.length; i++ ) {
1231 var eachAccount = bkeeping.cache.accounts[i]["account"];
1232 if( eachAccount["id"] == aid ) {
1234 returnName = eachAccount["name"];
1235 break;
1239 return returnName;
1242 findAccountType: function(aid) {
1244 var returnType = null;
1245 for( var i = 0; i < bkeeping.cache.accounts.length; i++ ) {
1247 var eachAccount = bkeeping.cache.accounts[i]["account"];
1248 if( eachAccount["id"] == aid ) {
1250 returnType = eachAccount["type"];
1251 break;
1255 return returnType;
1258 findEntry: function(eid) {
1260 var entry = null;
1261 for( var i = 0; i < bkeeping.cache.entries.length; i++ ) {
1263 var eachEntry = bkeeping.cache.entries[i]["entry"];
1264 if( eachEntry["id"] == eid ) {
1266 entry = eachEntry;
1267 break;
1271 return entry;
1275 addEditJournal: function(thingtype, id, returnHandler) // id is null on adds
1278 // raise correct form
1279 var x = eval("AE_" + thingtype.toUpperCase());
1280 var form = x[1];
1282 var journalName = bkeeping.findJournalName(id);
1284 // could be vars:
1285 form = form.parse_vars( { 'name': journalName } );
1286 bkeeping.showModalDialog(form);
1287 $('#bkeeping_dialog').dialog('option', 'title', x[0]);
1289 $('#bkeeping_dialog').dialog('option', 'buttons', { "CANCEL": function() { $(this).dialog("close"); }, "SAVE": returnHandler });
1291 return true;
1294 addEditAccount: function(thingtype, id, returnHandler) // id is null on adds
1297 // raise correct form
1298 var x = eval("AE_" + thingtype.toUpperCase());
1299 var form = x[1];
1301 var accountName = bkeeping.findAccountName(id);
1302 var accountType = bkeeping.findAccountType(id);
1304 // could be vars:
1305 form = form.parse_vars( { accounts:bkeeping.getAccountTypesAsOptionsList(accountType), aname: accountName } );
1306 bkeeping.showModalDialog(form);
1307 $('#bkeeping_dialog').dialog('option', 'title', x[0]);
1309 $('#bkeeping_dialog').dialog('option', 'buttons', { "CANCEL": function() { $(this).dialog("close"); }, "SAVE": returnHandler });
1311 return true;
1314 addEditEntry: function(thingtype, id, eid, returnHandler) // id is null on adds
1317 // raise correct form
1318 var x = eval("AE_" + thingtype.toUpperCase());
1319 var form = x[1];
1320 var obj = {};
1321 if( (eid != undefined) && (eid != null) ) {
1322 obj["entryid"] = eid
1325 var entryF = bkeeping.findEntry(eid);
1327 // find entry part
1328 if( entryF != null ) {
1330 var entryPartF = null;
1331 for( var i=0; i < entryF.children.length; i++ ) {
1333 var eachC = entryF.children[i];
1334 var thing = null;
1336 if( eachC["credit"] || eachC["debit"] ) {
1338 if( eachC["credit"] ) {
1340 obj["entry_ctype"] = " selected='selected' ";
1341 thing = eachC["credit"];
1343 else if( eachC["debit"] ) {
1345 obj["entry_dtype"] = " selected='selected' ";
1346 thing = eachC["debit"];
1349 var accountType = bkeeping.findAccountType( thing["accountid"] );
1350 obj["accounts"] = bkeeping.getAccountsAsOptionsList(accountType);
1351 obj["evalue"] = thing["amount"];
1353 if( thing["currency"] == "CDN" ) {
1354 obj["curr_cdn"] = " selected='selected' ";
1356 else if( thing["currency"] == "USD" ) {
1357 obj["curr_usd"] = " selected='selected' ";
1359 else if( thing["currency"] == "BP" ) {
1360 obj["curr_bp"] = " selected='selected' ";
1362 else if( thing["currency"] == "EUR" ) {
1363 obj["curr_eur"] = " selected='selected' ";
1365 else if( thing["currency"] == "JPN" ) {
1366 obj["curr_jpn"] = " selected='selected' ";
1373 else {
1375 obj["accounts"] = bkeeping.getAccountsAsOptionsList(null);
1379 // could be vars:
1380 form = form.parse_vars( obj );
1382 bkeeping.showModalDialog(form);
1383 $('#bkeeping_dialog').dialog('option', 'title', x[0]);
1385 $('#bkeeping_dialog').dialog('option', 'buttons', { "CANCEL": function() { $(this).dialog("close"); }, "SAVE": returnHandler });
1387 return true;
1390 saveEntry: function(id)
1393 var type = id ? this.EditCommand : this.AddCommand;
1394 var pars = this.wrapFormAsObj();
1395 pars['date'] = bkeeping.getCurrentDate();
1396 pars['id'] = id;
1397 pars['jid'] = bkeeping._displayed_journal;
1398 pars['username'] = bkeeping.ACTIVE_SESSION["loggedInUserid"];
1401 //** TODO [done]
1402 //** 1. findEntry 'entryid' or 'new'
1403 var entry = null;
1404 if( id.length > 0 ) {
1406 entry = bkeeping.findEntry(id);
1408 else {
1410 entry = bkeeping.findEntry("new");
1411 entry["id"] = "";
1414 if( entry != null ) {
1415 pars["cc"] = entry["currency"];
1419 //** 2. ensure balances
1420 var dsumDEBIT = 0;
1421 var csumDEBIT = 0;
1423 var dsumCREDIT = 0;
1424 var csumCREDIT = 0;
1426 for( var i = 0; i < entry["children"].length; i++ ) { //** sum up debits & credits
1429 //** on debit side ( asset / expense )
1430 //** on credit side ( liability / revenue )
1431 if( entry["children"][i]["credit"] ) {
1433 var thing = entry["children"][i]["credit"];
1435 // check account type
1436 var atype = bkeeping.findAccountType( thing["accountid"] );
1437 if( atype == "asset" || atype == "expense" ) {
1439 csumDEBIT += parseFloat( thing["amount"] );
1441 else if( atype == "liability" || atype == "revenue" ) {
1443 csumCREDIT += parseFloat( thing["amount"] );
1447 else if( entry["children"][i]["debit"] ) {
1449 var thing = entry["children"][i]["debit"];
1451 // check account type
1452 var atype = bkeeping.findAccountType( thing["accountid"] );
1453 if( atype == "asset" || atype == "expense" ) {
1455 dsumDEBIT += parseFloat( thing["amount"] );
1457 else if( atype == "liability" || atype == "revenue" ) {
1459 dsumCREDIT += parseFloat( thing["amount"] );
1468 * check that debits and credots are balanced
1470 var lhs = csumDEBIT + dsumCREDIT;
1471 var rhs = csumCREDIT + dsumDEBIT;
1472 if( lhs != rhs ) {
1474 alert("debit and credits are not equal");
1475 return false;
1481 * 3. build debit and credit XML parts
1483 pars['debitsANDcredits'] = "";
1484 for( var i = 0; i < entry["children"].length; i++ ) { //** sum up debits & credits
1487 if( entry["children"][i]["credit"] ) {
1489 var cthing = entry["children"][i]["credit"];
1490 var cthingXML = "<credit xmlns='com/interrupt/bookkeeping/account' id='"+ cthing["id"] +"' amount='"+
1491 cthing["amount"] +"' entryid='"+ cthing["entryid"] +"' accountid='"+ cthing["accountid"] +"' account='"+
1492 cthing["account"] +"' currency='"+ cthing["currency"] +"'/>";
1494 pars['debitsANDcredits'] += cthingXML;
1497 else if( entry["children"][i]["debit"] ) {
1499 var dthing = entry["children"][i]["debit"];
1500 var dthingXML = "<debit xmlns='com/interrupt/bookkeeping/account' id='"+ dthing["id"] +"' amount='"+
1501 dthing["amount"] +"' entryid='"+ dthing["entryid"] +"' accountid='"+ dthing["accountid"] +"' account='"+
1502 dthing["account"] +"' currency='"+ dthing["currency"] +"'/>";
1504 pars['debitsANDcredits'] += dthingXML;
1511 //** 4. refresh the entries list at the end of 'run'
1512 var ret = new type("entry", this.addedEdited, pars).run();
1516 addAccount: function()
1518 var returnHandler = bkeeping.goAccountPart.bind(this, "account", null);
1519 bkeeping.addEditAccount("account", null, returnHandler);
1522 editAccount: function(aid)
1524 var returnHandler = bkeeping.goAccountPart.bind(this, "account", aid);
1525 bkeeping.addEditAccount("account", aid, returnHandler);
1528 deleteAccount: function()
1533 addJournal: function()
1535 var returnHandler = bkeeping.goJournalPart.bind(this, "journal", null);
1536 return bkeeping.addEditJournal("journal", null, returnHandler);
1539 editJournal: function(jid)
1542 var returnHandler = bkeeping.goJournalPart.bind(this, "journal", jid);
1544 bkeeping.addEditJournal("journal", jid, returnHandler);
1546 /*var a = new bkeeping.GetCommand("journal", jid, function(journalObj)
1548 $("#journal_name").val(journalObj.getName());
1549 //$("#journal_type").val(journalObj.getType());
1550 //$("#journal_balance").val(journalObj.getBalance());
1551 }).run();
1556 deleteJournal: function()
1561 addEntry: function()
1563 return bkeeping.showJournalEntry(null);
1566 addEditEntryPart: function(id, eid)
1568 var returnHandler = bkeeping.goJournalEntryPart.bind(this, "entry", null);
1569 bkeeping.addEditEntry("entry", id, eid, returnHandler);
1572 editEntry: function(eid)
1574 bkeeping.showJournalEntry( eid );
1575 var a = new bkeeping.GetCommand("entry", eid, function(entryObj)
1577 $("#entry_caid").val(entryObj.getCaid());
1578 $("#entry_daid").val(entryObj.getDaid());
1579 $("#entry_ecamount").val(entryObj.getEcamount());
1580 $("#entry_edamount").val(entryObj.getEdamount());
1581 $("#entry_cc").val(entryObj.getCurrency());
1582 }).run();
1585 deleteEntry: function()
1587 //var a = new bkeeping.RemoveCommand("entry", callback)...
1591 _displayed_journal: null,
1592 showJournal: function(id)
1595 //** clear previous content
1596 var jrows = document.getElementById( "j_rows" );
1597 jrows["innerHTML"] = "";
1600 //** show journal UI
1601 var journalUI = $("#journal_ui");
1602 journalUI.css("display", "block");
1605 bkeeping._displayed_journal = id;
1606 $("#_journal_id").html(id);
1608 new bkeeping.ListCommand("entries", bkeeping.displayEntries, id).run();
1611 showJournalEntry: function(id)
1614 //** clear previous content
1615 var erows = document.getElementById( "jentry_rows" );
1616 erows["innerHTML"] = "";
1619 var eui = $("#entry_ui");
1620 eui.css("display", "block");
1622 var eui2 = document.getElementById( "entry_ui" ); // trying to put the entryid in the template
1624 eui2["innerHTML"].parse_vars = String.prototype.parse_vars;
1625 eui2["innerHTML"] = eui2["innerHTML"].parse_vars( { entryid: id } );
1628 $("#_journalentry_id").html(id);
1630 if(id) {
1631 new bkeeping.ListCommand("entry", bkeeping.displayEntry, id).run();
1635 isAauthFailure: function( jsonData ) {
1637 var thing = jsonData["logs"]["children"][0]["log"]["children"][0]["logMessages"]["children"][0]["logMessage"]["value"];
1638 if( (thing != undefined) || (thing != null) ) {
1640 var containS = thing.indexOf("Authenticated");
1641 if( containS > 0 ) {
1642 return true;
1646 return false;
1649 getGenericFailureReason: function(jsonData)
1651 var r;
1652 try
1654 r = jsonData["logs"]["children"][0]["log"]["children"][0]["logMessages"]["children"][0]["logMessage"]["value"];
1656 catch (e)
1658 bkeeping.debug(e);
1659 r = "Unknown.";
1661 return r;
1664 loadProfile: function(data)
1666 for (var i = 0; i < data.length; i++)
1668 var o = data[i]["profileDetail"];
1669 $("#" + o.id).val(o.value);
1673 GetprofileCommand: function(callback)
1675 this.callback = callback;
1677 function isSuccess(jsonData)
1679 return (jsonData && jsonData["user"]);
1682 var getFailureReason = bkeeping.getGenericFailureReason;
1684 function handleResponse(jsonData, textStatus)
1686 if (isSuccess(jsonData))
1688 var u = new bkeeping.User(jsonData["user"]);
1689 setTimeout(callback.bind(this, u.getProfileDetailsList()), 1000);
1691 else
1693 var reason = getFailureReason(jsonData);
1694 alert("Failed to get profile. Reason: " + reason);
1698 this.run = function()
1700 bkeeping.send_bkexpr("getprofile", GETPROFILE_COMMAND.parse_vars({username:bkeeping.ACTIVE_SESSION.loggedInUserid}), handleResponse.bind(this));
1704 RegisterCommand: function(username, password, firstname, lastname, email, country, currency, updating)
1706 this.username = username;
1707 this.password = password;
1708 this.firstname = firstname;
1709 this.lastname = lastname;
1710 this.email = email;
1711 this.country = country;
1712 this.currency = currency;
1713 this.updating = updating;
1715 function isSuccess(jsonData)
1717 return (jsonData && jsonData["user"]);
1720 var getFailureReason = bkeeping.getGenericFailureReason;
1722 function handleResponse(jsonData, textStatus)
1725 // put back the 'Loading...' message
1726 $('#loading-msg').animate({ left: -100 }, { duration: 750, easing: 'swing', queue: false });
1729 // on successful register, user is redirected to the home page and messaged about his ability to login or that his changes were saved
1730 if (isSuccess(jsonData))
1732 var flag = "canlogin";
1733 if (this.updating)
1735 flag = "changessaved";
1737 setTimeout("document.location.href='accounts.html';", 0);
1739 else
1741 // show a UI for registration failure:
1742 var reason = getFailureReason(jsonData);
1743 var verb = "register";
1744 if (this.updating)
1746 verb = "save changes"
1748 alert("Failed to " + verb + ". Reason: " + reason);
1752 this.run = function()
1754 var command = REGISTER_COMMAND;
1755 var cname = "register";
1756 if (this.updating)
1758 command = UPDATEPROFILE_COMMAND;
1759 cname = "updateprofile";
1761 bkeeping.send_bkexpr(cname, command.parse_vars({username: this.username, password: this.password, firstname: this.firstname, lastname: this.lastname, email: this.email, country: this.country, currency: this.currency}), handleResponse.bind(this));
1765 LoginCommand: function(username, password)
1767 this.username = username;
1768 this.password = password;
1770 function isSuccess(jsonData)
1772 return (jsonData && jsonData["userSession"]);
1775 var getFailureReason = bkeeping.getGenericFailureReason;
1777 function handleResponse(jsonData, textStatus)
1780 // put back the 'Loading...' message
1781 $('#loading-msg').animate({ left: -100 }, { duration: 750, easing: 'swing', queue: false });
1784 // on successful login, user is redirected to the accounts page (accounts.html)
1785 if (isSuccess(jsonData))
1788 // set the session
1789 var us = new bkeeping.UserSession(jsonData["userSession"]);
1790 bkeeping.ACTIVE_SESSION.update(us.getSessionId(), us.getUser(), us.getGroup()).save();
1792 // go to the accounts page
1793 setTimeout("document.location.href='accounts.html';", 2000);
1795 else
1797 // show a UI for login failure:
1798 var reason = getFailureReason(jsonData);
1799 alert("Failed to login. Reason: " + reason);
1803 this.run = function()
1805 bkeeping.send_bkexpr("login", LOGIN_COMMAND.parse_vars({username: this.username, password: this.password}), handleResponse.bind(this));
1809 LogoutCommand: function()
1812 function isSuccess(jsonData)
1815 return (jsonData && !(jsonData["logs"]));
1818 var getFailureReason = bkeeping.getGenericFailureReason;
1820 function handleResponse(jsonData, textStatus)
1822 // on successful logout, user is redirected to the home page
1823 if (isSuccess(jsonData))
1825 // go to the accounts page
1826 bkeeping.ACTIVE_SESSION.clear();
1827 setTimeout('document.location.href = "'+ bkeeping.ROOT_URL +'";', 2000);
1830 else
1832 // show a UI for login failure:
1833 var reason = getFailureReason(jsonData);
1834 alert("Failed to logout. Reason: " + reason);
1838 this.run = function()
1840 bkeeping.send_bkexpr("logout", "logout;", handleResponse.bind(this));
1845 getEntryBalance: function( entryF ) {
1848 //** 2. ensure balances
1849 var dsumDEBIT = 0;
1850 var csumDEBIT = 0;
1852 var dsumCREDIT = 0;
1853 var csumCREDIT = 0;
1855 var entry = entryF["root"];
1857 for( var i = 0; i < entry["children"].length; i++ ) { //** sum up debits & credits
1860 //** on debit side ( asset / expense )
1861 //** on credit side ( liability / revenue )
1862 if( entry["children"][i]["credit"] ) {
1864 var thing = entry["children"][i]["credit"];
1866 // check account type
1867 var atype = bkeeping.findAccountType( thing["accountid"] );
1868 if( atype == "asset" || atype == "expense" ) {
1870 csumDEBIT += parseFloat( thing["amount"] );
1872 else if( atype == "liability" || atype == "revenue" ) {
1874 csumCREDIT += parseFloat( thing["amount"] );
1878 else if( entry["children"][i]["debit"] ) {
1880 var thing = entry["children"][i]["debit"];
1882 // check account type
1883 var atype = bkeeping.findAccountType( thing["accountid"] );
1884 if( atype == "asset" || atype == "expense" ) {
1886 dsumDEBIT += parseFloat( thing["amount"] );
1888 else if( atype == "liability" || atype == "revenue" ) {
1890 dsumCREDIT += parseFloat( thing["amount"] );
1898 * check that debits and credots are balanced
1900 var lhs = csumDEBIT + dsumCREDIT;
1901 var rhs = csumCREDIT + dsumDEBIT;
1902 if( lhs != rhs ) {
1904 alert("debit and credits are not equal");
1905 return false;
1908 return lhs;
1911 displayAccounts: function(accountListObj)
1913 var as = accountListObj.getAccounts();
1914 if( as ) {
1916 var lister = $("#acl_rows");
1917 var html = "";
1918 for (var i = 0; i < as.length; i++)
1920 var a = as[i];
1921 if( a["root"] ) {
1923 //var obj = { rowid: a.getId(), aname: a.getName(), category: a.getType(), balance: a.getBalance() };
1924 var obj = { rowid: a.getId(), aname: a.getName(), category: a.getType() };
1925 html += ACCOUNT_ROW.parse_vars(obj);
1928 lister.html(html);
1932 displayJournals: function(journalListObj)
1934 var js = journalListObj.getJournals();
1935 if( js ) {
1936 var lister = $("#js_rows");
1937 var html = "";
1938 for (var i = 0; i < js.length; i++)
1940 var j = js[i];
1941 if( j["root"] ) {
1942 var obj = { rowid: j.getId(), jname: j.getName() };
1943 html += JOURNAL_ROW.parse_vars(obj);
1946 lister.html(html);
1950 //** display all the entries in a journal
1951 displayEntries: function(entryListObj)
1953 var es = entryListObj.getEntries();
1954 var lister = $("#j_rows");
1955 var html = "";
1956 if(es) {
1957 for (var i = 0; i < es.length; i++)
1959 var e = es[i];
1961 // ??? xxx not sure what data to put here
1962 //var obj = { rowid: e.getId(), date: (e.getDate() || "<none>"), entry: ("..." + e.getId().substring(e.getId().length - 6) || "<none>"), balance: (e.getBalance() || "0.00") };
1964 if( e["root"] != undefined ) {
1966 var bal = bkeeping.getEntryBalance( e );
1968 var obj = {
1969 rowid: e.getId(),
1970 date: (e.getDate() || "<none>"),
1971 entry: ("..." + e.getId().substring(e.getId().length - 20) || "<none>"),
1972 balance: bal
1976 html += ENTRY_ROW.parse_vars(obj);
1981 lister.html(html);
1984 //** display the debit / credits in a single entry
1985 displayEntry: function(entryListObj)
1987 var es = entryListObj.getEntries();
1988 var lister = $("#jentry_rows");
1989 var html = "";
1990 if(es) {
1991 for (var i = 0; i < es.length; i++)
1993 var e = es[i];
1995 // ??? xxx not sure what data to put here
1996 //var obj = { rowid: e.getId(), date: (e.getDate() || "<none>"), entry: ("..." + e.getId().substring(e.getId().length - 6) || "<none>"), balance: (e.getBalance() || "0.00") };
1998 //var obj = { rowid: e.getId(), date: (e.getDate() || "<none>"), entry: ("..." + e.getId().substring(e.getId().length - 6) || "<none>"), balance: ( "0.00" ) };
2000 var obj = {};
2001 if( e["type"] == "debit" ) {
2002 obj = {
2003 dname: e.getAccount(),
2004 damount: e.getAmount(),
2005 cname: "&nbsp;",
2006 camount: "&nbsp;"
2009 else if( e["type"] == "credit" ) {
2010 obj = {
2011 dname: "&nbsp;",
2012 damount: "&nbsp;",
2013 cname: e.getAccount(),
2014 camount: e.getAmount()
2018 obj["rowid"] = e.getId();
2019 obj["eid"] = entryListObj["root"]["id"];
2021 html += TENTRY_ROW.parse_vars(obj);
2025 lister.html(html);
2028 objectIsOfType: function(obj, single_or_plural_type)
2030 var t = (single_or_plural_type.charAt(single_or_plural_type.length - 1) == 's') ? (single_or_plural_type.substring(0, single_or_plural_type.length - 1)) : single_or_plural_type;
2031 if (t == 'entrie') { t = "entry"; }
2032 if (obj && obj[t])
2034 return true;
2036 return false;
2039 addToCacheAsList: function(plural_type, listObj)
2041 var list = listObj.getAsRawList();
2042 bkeeping.debug('AddToCacheAsList: listing (' + plural_type + '/' + list.length + ')');
2043 bkeeping.debug(list);
2044 if (list && (list.length > 0))
2046 for (var i = 0; i < list.length; i++)
2048 if (bkeeping.objectIsOfType(list[i], plural_type))
2050 bkeeping.addToCache(plural_type, null, list[i]);
2056 addToCache: function(plural_type, id, jsonData)
2058 var found = false;
2059 var single_type = (plural_type == 'entries') ? 'entry' : plural_type.substring(0, plural_type.length - 1);
2060 for (var i = 0; i < bkeeping.cache[plural_type].length; i++)
2062 var item = bkeeping.cache[plural_type][i];
2063 bkeeping.debug('addToCache:iterating cache, found item ' + item[single_type].id + '/' + id);
2064 bkeeping.debug(item);
2065 if (item && (item[single_type].id == id))
2067 // item could be changed, so re-add
2068 found = true;
2069 bkeeping.cache[plural_type][i] = jsonData;
2072 if (!found)
2074 bkeeping.debug('addToCache: pushing jsonData onto cache');
2075 bkeeping.debug(jsonData);
2076 bkeeping.cache[plural_type].push(jsonData);
2080 getFromCache: function(plural_type, id)
2082 var single_type = (plural_type == 'entries') ? 'entry' : plural_type.substring(0, plural_type.length - 1);
2083 for (var i = 0; i < bkeeping.cache[plural_type].length; i++)
2085 var item = bkeeping.cache[plural_type][i];
2086 bkeeping.debug('getFromCache:iterating cache, found item ' + item[single_type].id + '/' + id);
2087 bkeeping.debug(item);
2088 if (item && (item[single_type].id == id))
2090 return item;
2095 deleteFromCache: function(single_type, id)
2097 var plural_type = (single_type == 'entry') ? 'entries' : (single_type + 's');
2098 for (var i = 0; i < bkeeping.cache[plural_type].length; i++)
2100 var item = bkeeping.cache[plural_type][i];
2101 bkeeping.debug('deleteFromCache:iterating cache, found item ' + item[single_type].id + '/' + id);
2102 bkeeping.debug(item);
2103 if (item && (item[single_type].id == id))
2105 delete bkeeping.cache[plural_type][i];
2106 break;
2111 GetCommand: function(calltype, id, callback)
2113 this.calltype = calltype;
2114 this.calltype_list = (this.calltype == 'entry') ? 'entries' : (this.calltype + 's');
2115 this.id = id;
2116 this.callback = callback;
2117 this.command = "";
2118 this.type = "";
2119 var u = bkeeping.ACTIVE_SESSION.loggedInUserid;
2120 var g = (u == 'root') ? "webkell" : (u + '.group');
2121 this.pars = {username: u, group: g, id: this.id};
2122 switch (this.calltype)
2124 case "account":
2125 this.command = GETACCOUNT_COMMAND;
2126 this.type = bkeeping.Account;
2127 break;
2128 case "journal":
2129 this.command = GETJOURNAL_COMMAND;
2130 this.type = bkeeping.Journal;
2131 break;
2132 case "entry":
2133 this.command = GETENTRY_COMMAND;
2134 this.type = bkeeping.Entry;
2135 this.pars['jid'] = bkeeping._displayed_journal;
2136 break;
2137 default:
2138 alert("Unknown calltype: " + this.calltype);
2139 break;
2142 function isSuccess(jsonData)
2144 return (jsonData && (jsonData[this.calltype]) || jsonData[this.calltype_list]);
2147 var getFailureReason = bkeeping.getGenericFailureReason;
2149 function handleResponse(jsonData, textStatus, cached)
2151 if (isSuccess.call(this, jsonData))
2153 if (!cached || (cached != 'true'))
2155 bkeeping.addToCache(this.calltype_list, this.id, jsonData);
2157 return this.callback(new this.type(jsonData[this.calltype]));
2159 else
2161 // show a UI for login failure:
2162 var reason = getFailureReason(jsonData);
2163 alert("Failed to get list " + this.calltype + ". Reason: " + reason);
2167 this.run = function()
2169 var item = bkeeping.getFromCache(this.calltype_list, this.id);
2170 if (item)
2172 bkeeping.debug('handling call type ' + this.calltype + '/' + this.id + ' from cache');
2173 handleResponse.call(this, item, 'success', 'true');
2175 else
2177 bkeeping.debug('handling call type ' + this.calltype + ' from ajax');
2178 var t = "get" + this.calltype;
2179 bkeeping.send_bkexpr(t, this.command.parse_vars(this.pars), handleResponse.bind(this));
2185 setDefaultCurrency: function(profileDetailObj)
2188 bkeeping.DEFAULT_CURRENCY = profileDetailObj["root"]["value"];
2192 ListCommand: function(listtype, callback, optJournalid)
2194 this.listtype = listtype;
2195 this.callback = callback;
2196 this.command = "";
2197 this.type = "";
2198 this.pars = { username: bkeeping.ACTIVE_SESSION.loggedInUserid };
2199 switch (this.listtype)
2201 case "accounts":
2202 this.command = LISTACCOUNTS_COMMAND;
2203 this.type = bkeeping.AccountList;
2204 break;
2205 case "journals":
2206 this.command = LISTJOURNALS_COMMAND;
2207 this.type = bkeeping.JournalList;
2208 this.pars["journal"] = optJournalid;
2209 break;
2210 case "entries":
2211 this.command = LISTENTRIES_COMMAND;
2212 this.type = bkeeping.EntryList;
2213 this.pars["journal"] = bkeeping["_displayed_journal"];
2214 break;
2215 case "entry":
2216 this.command = LISTENTRY_COMMAND;
2217 this.type = bkeeping.EntryList;
2218 this.pars["journal"] = bkeeping["_displayed_journal"];
2219 this.pars["entry"] = optJournalid;
2220 break;
2221 default:
2222 alert("Unknown list: " + this.listtype);
2223 break;
2226 function isSuccess(jsonData)
2228 return (jsonData && jsonData[this.listtype]);
2231 var getFailureReason = bkeeping.getGenericFailureReason;
2234 function handleResponse(jsonData, textStatus, cached)
2237 if (cached || isSuccess.call(this, jsonData))
2239 var r;
2240 if (cached && (cached == 'true'))
2242 r = new this.type({ "children" : jsonData });
2244 else
2246 r = new this.type(jsonData[this.listtype]);
2247 bkeeping.debug("Add to cache as list in ListCommand: " + this.listtype);
2248 bkeeping.debug(r);
2249 bkeeping.addToCacheAsList(this.listtype, r);
2251 return this.callback(r);
2253 else if( bkeeping.isAauthFailure(jsonData) ) {
2255 var msg = jsonData["logs"]["children"][0]["log"]["children"][0]["logMessages"]["children"][0]["logMessage"]["value"];
2256 //alert(msg);
2258 var rootURL = document.URL.substring( 0, document.URL.lastIndexOf("accounts.html") );
2260 setTimeout("document.location.href='"+ rootURL +"';", 0);
2263 else
2265 // show a UI for login failure:
2266 var reason = getFailureReason(jsonData);
2267 alert("Failed to get list " + this.listtype + ". Reason: " + reason);
2271 this.run = function()
2273 if ( (bkeeping.cache[this.listtype] != undefined) && (bkeeping.cache[this.listtype].length > 0) )
2275 bkeeping.debug('handling list type ' + this.listtype + ' from CACHE');
2276 handleResponse.call(this, bkeeping.cache[this.listtype], 'success', 'true');
2278 else
2280 bkeeping.debug('handling list type ' + this.listtype + ' from AJAX');
2281 var t = "list" + this.listtype;
2282 bkeeping.send_bkexpr(t, this.command.parse_vars(this.pars), handleResponse.bind(this));
2288 LoadCommand: function(listtype, callback, optJournalid)
2290 this.listtype = listtype;
2291 this.callback = callback;
2292 this.command = "";
2293 this.type = "";
2294 this.pars = {username: bkeeping.ACTIVE_SESSION.loggedInUserid};
2295 switch (this.listtype)
2297 case "profileDetail":
2298 this.command = LOADDEFAULTCURRENCY_COMMAND;
2299 this.type = bkeeping.DefaultCurrency;
2300 break;
2301 default:
2302 alert("Unknown list: " + this.listtype);
2303 break;
2306 function isSuccess(jsonData)
2308 return (jsonData && jsonData[this.listtype]);
2311 var getFailureReason = bkeeping.getGenericFailureReason;
2313 function handleResponse(jsonData, textStatus)
2315 if (isSuccess.call(this, jsonData))
2317 this.callback(new this.type(jsonData[this.listtype]));
2319 else
2321 // show a UI for login failure:
2322 var reason = getFailureReason(jsonData);
2323 alert("Failed to get list " + this.listtype + ". Reason: " + reason);
2327 this.run = function()
2329 var t = "list" + this.listtype;
2330 bkeeping.send_bkexpr(t, this.command.parse_vars(this.pars), handleResponse.bind(this));
2335 /* pars is an object containing the data to be added */
2336 AddCommand: function(type, callback, pars)
2338 // pars for account: ${aid} ${atype} ${aname} ${acw} ${username}
2339 // pars for journal: ${jid} ${jname} ${jtype} ${jbalance} ${username}
2340 // pars for entry: ${eid} ${cc} ${edid} ${edamount} ${aid} ${ecid} ${ecamount} ${username} ${jid}
2341 this.type = type;
2342 this.callback = callback;
2343 this.command = "";
2344 this.pars = pars;
2345 switch (this.type)
2347 case "account":
2348 this.command = ADDACCOUNT_COMMAND;
2350 this.type = "accounts"; // special case where what's returned will be an 'accounts'
2351 break;
2352 case "journals":
2353 this.command = ADDJOURNAL_COMMAND;
2354 break;
2355 case "entry":
2356 this.command = ADDENTRY_COMMAND;
2357 break;
2358 default:
2359 alert("Unknown add: " + this.type);
2360 break;
2363 function isSuccess(jsonData)
2366 return (jsonData && ( jsonData["logMessages"] == undefined ) );
2370 var getFailureReason = bkeeping.getGenericFailureReason;
2372 function handleResponse(jsonData, textStatus)
2374 if (isSuccess.call(this, jsonData))
2376 return this.callback(jsonData);
2378 else if( bkeeping.isAauthFailure(jsonData) ) {
2380 var msg = jsonData["logs"]["children"][0]["log"]["children"][0]["logMessages"]["children"][0]["logMessage"]["value"];
2381 //alert(msg);
2383 var rootURL = document.URL.substring( 0, document.URL.lastIndexOf("accounts.html") );
2385 setTimeout("document.location.href='"+ rootURL +"';", 0);
2388 else
2390 // show a UI for login failure:
2391 var reason = getFailureReason(jsonData);
2392 alert("Failed to add " + this.type + ". Reason: " + reason);
2396 this.run = function()
2398 bkeeping.send_bkexpr("add" + this.type, this.command.parse_vars(pars), handleResponse.bind(this));
2402 RemoveCommand: function(type, callback, pars)
2404 // pars for account: ${aid}
2405 // pars for journal: ${jid}
2406 // pars for entry: ${eid}
2407 this.type = type;
2408 this.callback = callback;
2409 this.command = "";
2410 switch (this.type)
2412 case "account":
2413 this.command = REMOVEACCOUNT_COMMAND;
2414 break;
2415 case "journals":
2416 this.command = REMOVEJOURNAL_COMMAND;
2417 break;
2418 case "entry":
2419 this.command = REMOVEENTRY_COMMAND;
2420 break;
2421 default:
2422 alert("Unknown remove: " + this.type);
2423 break;
2426 function isSuccess(jsonData)
2428 return (jsonData && jsonData[this.type]);
2431 var getFailureReason = bkeeping.getGenericFailureReason;
2433 function handleResponse(jsonData, textStatus)
2435 if (isSuccess.call(this, jsonData))
2437 return this.callback(jsonData);
2439 else
2441 // show a UI for login failure:
2442 var reason = getFailureReason(jsonData);
2443 alert("Failed to add " + this.type + ". Reason: " + reason);
2447 this.run = function()
2449 bkeeping.send_bkexpr("remove" + this.type, this.command.parse_vars( pars ), handleResponse.bind(this) );
2453 /* pars is an object containing the data to be added */
2454 EditCommand: function(type, callback, pars)
2456 // pars for account: ${aid} ${atype} ${aname} ${acw} ${username}
2457 // pars for journal: ${jid} ${jname} ${jtype} ${jbalance} ${username}
2458 // pars for entry: ${eid} ${cc} ${edid} ${edamount} ${aid} ${ecid} ${ecamount} ${username} ${jid}
2459 this.type = type;
2460 this.callback = callback;
2461 this.command = "";
2462 this.pars = pars;
2463 switch (this.type)
2465 case "account":
2466 this.command = UPDATEACCOUNT_COMMAND;
2467 break;
2468 case "journal":
2469 this.command = UPDATEJOURNAL_COMMAND;
2470 break;
2471 case "entry":
2472 this.command = UPDATEENTRY_COMMAND;
2473 break;
2474 default:
2475 alert("Unknown edit: " + this.type);
2476 break;
2479 function isSuccess(jsonData)
2481 return (jsonData && ( jsonData["logMessages"] == undefined ) );
2484 var getFailureReason = bkeeping.getGenericFailureReason;
2486 function handleResponse(jsonData, textStatus)
2488 if (isSuccess.call(this, jsonData))
2490 return this.callback(jsonData);
2492 else if( bkeeping.isAauthFailure(jsonData) ) {
2494 var msg = jsonData["logs"]["children"][0]["log"]["children"][0]["logMessages"]["children"][0]["logMessage"]["value"];
2495 //alert(msg);
2497 var rootURL = document.URL.substring( 0, document.URL.lastIndexOf("accounts.html") );
2499 setTimeout("document.location.href='"+ rootURL +"';", 0);
2502 else
2504 // show a UI for login failure:
2505 var reason = getFailureReason(jsonData);
2506 alert("Failed to add " + this.type + ". Reason: " + reason);
2510 this.run = function()
2512 bkeeping.send_bkexpr("edit" + this.type, this.command.parse_vars(pars), handleResponse.bind(this));
2515 /* ******** */
2517 /* ajax */
2518 error: function(xmlHttpRequest, textStatus, errorThrown)
2520 bkeeping.raise('There was a problem with the server request: ' + textStatus + '/' + errorThrown);
2523 success: function(data, textStatus)
2525 bkeeping.log('genericSuccess');
2528 complete: function(xmlHttRequest, textStatus)
2530 bkeeping.log('genericComplete');
2533 // caveat: this function does NOT handle xml with textnodes mixed in with other element children
2534 xmlRecurse: function(node, context)
2536 // squeeze the attributes and add to the context
2537 var attributes = node.attributes;
2538 if (attributes)
2540 for (var i = 0; i < attributes.length; i++)
2542 var n = attributes[i].name;
2543 var v = attributes[i].nodeValue;
2544 context[n] = v;
2547 // recurse:
2548 var children = node.childNodes;
2549 if (children)
2551 if (children.length > 0)
2553 if ((children.length == 1) && (children[0].nodeType == 3)) // 3 is text node
2555 context["value"] = children[0].textContent;
2557 else
2559 if (!context["children"])
2561 context["children"] = [];
2563 for (var j = 0; j < children.length; j++)
2565 var p = {};
2566 p[children[j].nodeName] = {};
2567 context["children"].push(p);
2568 this.xmlRecurse(children[j], p[children[j].nodeName]);
2573 return context;
2576 // caveat: this function does NOT handle xml with textnodes mixed in with other element children
2577 xmlToJson: function(data, type)
2579 if (!data)
2581 return "";
2583 var xmlDoc;
2584 var json = {};
2585 try //Internet Explorer
2587 xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
2588 xmlDoc.async = "false";
2589 xmlDoc.loadXML(data);
2591 catch(e)
2593 var parser = new DOMParser();
2594 //xmlDoc = parser.parseFromString(data, "text/xml");
2595 xmlDoc = data;
2598 // convert to json (simplified greatly)
2599 // we'll take advantage of the fact that all the data being passed around is linked to attributes
2600 // and thus we'll go ahead and make our json like this:
2601 // <name1 attr1=val attr2=val...>
2602 // <name2 attr3=val attr4=val...>
2603 // <name2 attr5=val attr6=val...>
2604 // <name3 attr7=val attr8=val...>
2605 // </name1>
2607 // becomes this:
2608 // {
2609 // "name1":
2610 // {
2611 // "attr1": val,
2612 // "attr2": val,
2613 // "value": null, // special value -- the text node child of this ele, exclusive with children
2614 // "children":
2615 // [
2616 // {
2617 // "name2":
2618 // {
2619 // "attr3": val,
2620 // "attr4": val
2621 // }
2622 // },
2623 // {
2624 // "name2":
2625 // {
2626 // "attr5": val,
2627 // "attr6": val
2628 // }
2629 // },
2630 // {
2631 // "name3":
2632 // {
2633 // "attr7": val,
2634 // "attr8": val
2635 // }
2636 // }
2637 // ]
2638 // }
2639 // }
2640 var root = xmlDoc.firstChild;
2641 json[root.nodeName] = {};
2642 //json[root.nodeName] = this.xmlRecurse(root, json[root.nodeName]);
2643 json[root.nodeName] = bkeeping.xmlRecurse(root, json[root.nodeName]);
2644 return json;
2647 zzz: function (xhr, ajaxOptions, thrownError) {
2648 alert(xhr.status);
2649 alert(thrownError);
2651 send_bkexpr: function(bkexprname, expr, callback, optErrorCallback, optCompleteCallback)
2653 if ( MOCK_DATA == "true" || MOCK_DATA == true )
2655 var r;
2656 if (MOCK_DATA_FAILURES_MODE)
2658 r = MOCK_DATA["failures"][bkexprname];
2660 else
2662 r = MOCK_DATA["successes"][bkexprname];
2664 var response = r.response.parse_vars(_LAST_PARSED_DICT);
2665 var type = r.type;
2666 var textStatus = r.textStatus;
2667 var sanitizedData = this.xmlToJson(response, type);
2668 callback(sanitizedData, textStatus);
2670 else
2672 var url = (PROD_ENV) ? "/webkell" : "/webkell/webkell";
2673 if (bkexprname == "register")
2675 url = (PROD_ENV) ? "/authenticate" : "/webkell/authenticate";
2677 $.ajax(
2679 success: callback || bkeeping.success,
2680 error: optErrorCallback || bkeeping.error,
2681 complete: optCompleteCallback || bkeeping.complete,
2682 data: {"bkexpr": expr},
2683 dataFilter: this.xmlToJson,
2684 dataType: "xml",
2685 type: "POST",
2686 url: url
2691 /* **** */
2693 /* initialization */
2694 displayLoginBlurb: function()
2696 $("#loggedinnote").css('display', "block");
2697 var template = $("#loggedinnote").html();
2698 $("#loggedinnote").html(template.parse_vars({"name": bkeeping.ACTIVE_SESSION.loggedInUserid}));
2701 $("#loginholder").css("visibility", "hidden");
2703 catch(e)
2705 // element not there
2709 page_loader_init: function()
2711 if (window.page_loader)
2713 page_loader(); // defined in e.g. accounts.html, or any other UI page
2717 page_init: function()
2719 var path = document.location.href.match(/^http:\/\/[^\/]+\/(.*)$/);
2720 if (path)
2722 path = path[1];
2723 // cut off querystring and hash
2724 if (path.indexOf("?") > -1)
2726 path = path.substring(0, path.indexOf("?"));
2728 if (path.indexOf("#") > -1)
2730 path = path.substring(0, path.indexOf("#"));
2732 // if it's under webkell, remove that:
2733 if (path.indexOf("webkell") == 0)
2735 path = path.substring(8);
2738 else
2740 path = "home";
2743 if ((path == "") || (path == "/") || (path == "index.html"))
2745 path = "home";
2748 switch (path)
2750 case "home":
2751 // this is not exactly efficient, but we'll do this til we're on a proper server-side template engine
2752 var mode = document.location.href.substring(document.location.href.indexOf("mode=") + 5).match(/^(\w+)(.*)$/);
2753 if (!mode)
2755 mode = "home";
2757 else
2759 mode = mode[1];
2761 $("#contentbox").load(mode + ".html");
2762 if (mode == "userprofile")
2764 var u = new bkeeping.GetprofileCommand(bkeeping.loadProfile).run();
2766 break;;
2767 case "accounts.html":
2768 // do nothing, this UI is generated dynamically
2769 break;;
2770 default:
2771 bkeeping.debug("Not a known page: " + path);
2772 break;;
2774 if (bkeeping.ACTIVE_SESSION.loaded)
2776 bkeeping.displayLoginBlurb();
2779 var flag = document.location.href.indexOf("flag=");
2780 if (flag != -1)
2782 var value = document.location.href.substring(flag);
2783 if (value.indexOf("canlogin") != -1)
2785 bkeeping.showDialog("You have registered successfully. You can now log in.")
2787 if (value.indexOf("changessaved") != -1)
2789 bkeeping.showDialog("Your changes have been saved.");
2794 init: function()
2796 bkeeping.ACTIVE_SESSION = new bkeeping.Session(); // load from cookie if present
2797 if (!$("#bkeeping_dialog").length)
2799 $("body").append(DIALOG_HOLDER);
2801 //bkeeping.page_init(); // initializations related to the page we're on
2802 //bkeeping.page_loader_init(); // initializations defined on the page we're on
2805 unload: function()
2809 /* ************ */
2812 /* prototypes */
2813 bkeeping.AccountList.prototype = new bkeeping.Lister();
2814 bkeeping.JournalList.prototype = new bkeeping.Lister();
2815 bkeeping.EntryList.prototype = new bkeeping.Lister();
2817 bkeeping.DefaultCurrency.prototype = new bkeeping.Lister();
2820 /* ********** */
2822 $(document).ready(bkeeping.init);
2823 $(window).unload(bkeeping.unload);
2825 var MOCK_DATA =
2827 successes:
2829 login:
2831 response: "<userSession xmlns='com/interrupt/bookkeeping/users' id='c4e2e5344b91c6c1-1c82792712270d305b2-7ffd' groupid='${username}' userid='${username}' />",
2832 type: "xml",
2833 textStatus: "OK"
2836 register:
2838 response: "<user xmlns='com/interrupt/bookkeeping/users' id='${username}' username='${username}' password='${password}' logintimeout='600000' accountLevel='FREE' defaultGroup='webkell' authenticated=''>" +
2839 "<profileDetails xmlns='com/interrupt/bookkeeping/users' id='user.details'>" +
2840 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='firstname' name='first.name' value='${firstname}'/>" +
2841 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='lastname' name='last.name' value='${lastname}'/>" +
2842 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='email' name='email' value='${email}'/>" +
2843 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='country' name='country' value='${country}'/>" +
2844 "</profileDetails>" +
2845 "</user>",
2846 type: "xml",
2847 textStatus: "OK"
2850 getprofile:
2852 response: "<user xmlns='com/interrupt/bookkeeping/users' id='${username}' username='${username}' password='${password}' logintimeout='600000' accountLevel='FREE' defaultGroup='webkell' authenticated=''>" +
2853 "<profileDetails xmlns='com/interrupt/bookkeeping/users' id='user.details'>" +
2854 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='username' name='user.name' value='ed'/>" +
2855 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='password' name='password' value='foo'/>" +
2856 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='firstname' name='first.name' value='Eddie'/>" +
2857 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='lastname' name='last.name' value='Abrams'/>" +
2858 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='email' name='email' value='ed@abra.ms'/>" +
2859 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='country' name='country' value='Canada'/>" +
2860 "</profileDetails>" +
2861 "</user>",
2862 type: "xml",
2863 textStatus: "OK"
2866 updateprofile:
2868 response: "<user xmlns='com/interrupt/bookkeeping/users' id='${username}' username='${username}' password='${password}' logintimeout='600000' accountLevel='FREE' defaultGroup='webkell' authenticated=''>" +
2869 "<profileDetails xmlns='com/interrupt/bookkeeping/users' id='user.details'>" +
2870 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='username' name='user.name' value='${username}'/>" +
2871 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='password' name='password' value='${password}'/>" +
2872 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='firstname' name='first.name' value='${firstname}'/>" +
2873 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='lastname' name='last.name' value='${lastname}'/>" +
2874 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='email' name='email' value='${email}'/>" +
2875 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='country' name='country' value='${country}'/>" +
2876 "</profileDetails>" +
2877 "</user>",
2878 type: "xml",
2879 textStatus: "OK"
2882 listaccounts:
2884 response: "<accounts id='main.accounts'>" +
2885 "<account id='05' name='bankaccount' type='asset' counterWeight='debit' currency=''/>" +
2886 "<account id='06' name='creditcard' type='liability' counterWeight='credit' currency=''/>" +
2887 "</accounts>",
2888 type: "xml",
2889 textStatus: "OK"
2892 listjournals:
2894 response: "<journals xmlns:journal='com/interrupt/bookkeeping/journal' id='main.journals'>" +
2895 "<journal id='generalledger' name='generalledger' type='' balance=''>" +
2896 "<entries id='main.entries'>" +
2897 "<entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
2898 "<debit xmlns:account='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
2899 "<credit xmlns:account='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
2900 "</entry>" +
2901 "</entries>" +
2902 "</journal>" +
2903 "<journal id='otherledger' name='otherledger' type='' balance=''>" +
2904 "<entries id='main.entries'>" +
2905 "<entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
2906 "<debit xmlns:account='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
2907 "<credit xmlns:account='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
2908 "</entry>" +
2909 "</entries>" +
2910 "</journal>" +
2911 "</journals>",
2912 type: "xml",
2913 textStatus: "OK"
2916 listentries:
2918 response: "<entries id='main.entries'>" +
2919 "<entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
2920 "<debit xmlns:account='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
2921 "<credit xmlns:account='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
2922 "</entry>" +
2923 "</entries>",
2924 type: "xml",
2925 textStatus: "OK"
2928 addaccount:
2930 response: "<account id='${aid}' name='${aname}' type='${atype}' counterWeight='${acn}' currency='${cc}'/>",
2931 type: "xml",
2932 textStatus: "OK"
2935 removeaccount:
2937 response: "",
2938 type: "xml",
2939 textStatus: "OK"
2942 editaccount:
2944 response: "<account id='${aid}' name='${aname}' type='${atype}' counterWeight='${acw}' currency='${cc}'/>",
2945 type: "xml",
2946 textStatus: "OK"
2949 addjournal:
2951 response: "<journal id='${jid}' name='${jname}' type='${jtype}' balance='${jbalance}'>" +
2952 // "<entries id='main.entries'>" +
2953 // "<entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
2954 // "<debit xmlns:account='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
2955 // "<credit xmlns:account='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
2956 // "</entry>" +
2957 // "</entries>" +
2958 "</journal>",
2959 type: "xml",
2960 textStatus: "OK"
2963 removejournal:
2965 response: "",
2966 type: "xml",
2967 textStatus: "OK"
2970 editjournal:
2972 response: "<journal id='${jid}' name='${jname}' type='${jtype}' balance='${jbalance}'>" +
2973 // "<entries id='main.entries'>" +
2974 // "<entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
2975 // "<debit xmlns:account='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
2976 // "<credit xmlns:account='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
2977 // "</entry>" +
2978 // "</entries>" +
2979 "</journal>",
2980 type: "xml",
2981 textStatus: "OK"
2984 addentry:
2986 response: "<entry id='${eid}' entrynum='' state='' journalid='${jid}' date='' currency='${cc}'>" +
2987 "<debit xmlns:account='com/interrupt/bookkeeping/account' id='${edid}' amount='${edamount}' entryid='${eid}' accountid='${aid}' account='' currency='${cc}'/>" +
2988 "<credit xmlns:account='com/interrupt/bookkeeping/account' id='${ecid}' amount='${ecamount}' entryid='${eid}' accountid='${aid}' account='' currency='${cc}'/>" +
2989 "</entry>",
2990 type: "xml",
2991 textStatus: "OK"
2994 removeentry:
2996 response: "",
2997 type: "xml",
2998 textStatus: "OK"
3001 editentry:
3003 response: "<entry id='${eid}' entrynum='' state='' journalid='${jid}' date='' currency='${cc}'>" +
3004 "<debit xmlns:account='com/interrupt/bookkeeping/account' id='${edid}' amount='${edamount}' entryid='${eid}' accountid='${aid}' account='' currency='${cc}'/>" +
3005 "<credit xmlns:account='com/interrupt/bookkeeping/account' id='${ecid}' amount='${ecamount}' entryid='${eid}' accountid='${aid}' account='' currency='${cc}'/>" +
3006 "</entry>",
3007 type: "xml",
3008 textStatus: "OK"
3011 failures:
3013 login:
3015 response: "<logs>" +
3016 "<log level=''>" +
3017 "<logMessages>" +
3018 "<logMessage>Failed to login.</logMessage>" +
3019 "</logMessages>" +
3020 "</log>" +
3021 "</logs>",
3022 type: "xml",
3023 textStatus: "OK"
3026 register:
3028 response: "<logs>" +
3029 "<log level=''>" +
3030 "<logMessages>" +
3031 "<logMessage>Failed to register.</logMessage>" +
3032 "</logMessages>" +
3033 "</log>" +
3034 "</logs>",
3035 type: "xml",
3036 textStatus: "OK"
3039 getprofile:
3041 response: "<logs>" +
3042 "<log level=''>" +
3043 "<logMessages>" +
3044 "<logMessage>Failed to get user profile.</logMessage>" +
3045 "</logMessages>" +
3046 "</log>" +
3047 "</logs>",
3048 type: "xml",
3049 textStatus: "OK"
3052 updateprofile:
3054 response: "<logs>" +
3055 "<log level=''>" +
3056 "<logMessages>" +
3057 "<logMessage>Failed to update user profile.</logMessage>" +
3058 "</logMessages>" +
3059 "</log>" +
3060 "</logs>",
3061 type: "xml",
3062 textStatus: "OK"
3065 listaccounts:
3067 response: "<logs>" +
3068 "<log level=''>" +
3069 "<logMessages>" +
3070 "<logMessage>Failed to get accounts list.</logMessage>" +
3071 "</logMessages>" +
3072 "</log>" +
3073 "</logs>",
3074 type: "xml",
3075 textStatus: "OK"
3078 listjournals:
3080 response: "<logs>" +
3081 "<log level=''>" +
3082 "<logMessages>" +
3083 "<logMessage>Failed to get journals list.</logMessage>" +
3084 "</logMessages>" +
3085 "</log>" +
3086 "</logs>",
3087 type: "xml",
3088 textStatus: "OK"
3091 listentries:
3093 response: "<logs>" +
3094 "<log level=''>" +
3095 "<logMessages>" +
3096 "<logMessage>Failed to get entries list.</logMessage>" +
3097 "</logMessages>" +
3098 "</log>" +
3099 "</logs>",
3100 type: "xml",
3101 textStatus: "OK"
3104 addaccount:
3106 response: "<logs>" +
3107 "<log level=''>" +
3108 "<logMessages>" +
3109 "<logMessage>Failed to add account.</logMessage>" +
3110 "</logMessages>" +
3111 "</log>" +
3112 "</logs>",
3113 type: "xml",
3114 textStatus: "OK"
3117 removeaccount:
3119 response: "<logs>" +
3120 "<log level=''>" +
3121 "<logMessages>" +
3122 "<logMessage>Failed to remove account.</logMessage>" +
3123 "</logMessages>" +
3124 "</log>" +
3125 "</logs>",
3126 type: "xml",
3127 textStatus: "OK"
3130 editaccount:
3132 response: "<logs>" +
3133 "<log level=''>" +
3134 "<logMessages>" +
3135 "<logMessage>Failed to edit account.</logMessage>" +
3136 "</logMessages>" +
3137 "</log>" +
3138 "</logs>",
3139 type: "xml",
3140 textStatus: "OK"
3143 addjournal:
3145 response: "<logs>" +
3146 "<log level=''>" +
3147 "<logMessages>" +
3148 "<logMessage>Failed to add journal.</logMessage>" +
3149 "</logMessages>" +
3150 "</log>" +
3151 "</logs>",
3152 type: "xml",
3153 textStatus: "OK"
3156 removejournal:
3158 response: "<logs>" +
3159 "<log level=''>" +
3160 "<logMessages>" +
3161 "<logMessage>Failed to remove journal.</logMessage>" +
3162 "</logMessages>" +
3163 "</log>" +
3164 "</logs>",
3165 type: "xml",
3166 textStatus: "OK"
3169 editjournal:
3171 response: "<logs>" +
3172 "<log level=''>" +
3173 "<logMessages>" +
3174 "<logMessage>Failed to edit journal.</logMessage>" +
3175 "</logMessages>" +
3176 "</log>" +
3177 "</logs>",
3178 type: "xml",
3179 textStatus: "OK"
3182 addentry:
3184 response: "<logs>" +
3185 "<log level=''>" +
3186 "<logMessages>" +
3187 "<logMessage>Failed to add entry.</logMessage>" +
3188 "</logMessages>" +
3189 "</log>" +
3190 "</logs>",
3191 type: "xml",
3192 textStatus: "OK"
3195 removeentry:
3197 response: "<logs>" +
3198 "<log level=''>" +
3199 "<logMessages>" +
3200 "<logMessage>Failed to remove entry.</logMessage>" +
3201 "</logMessages>" +
3202 "</log>" +
3203 "</logs>",
3204 type: "xml",
3205 textStatus: "OK"
3208 editentry:
3210 response: "<logs>" +
3211 "<log level=''>" +
3212 "<logMessages>" +
3213 "<logMessage>Failed to edit entry.</logMessage>" +
3214 "</logMessages>" +
3215 "</log>" +
3216 "</logs>",
3217 type: "xml",
3218 textStatus: "OK"
3223 var _TEST_AC = "<accounts id='main.accounts'>" +
3224 "<account id='05' name='bankaccount' type='asset' counterWeight='debit' currency=''/>" +
3225 "<account id='06' name='creditcard' type='liability' counterWeight='credit' currency=''/>" +
3226 "</accounts>";
3227 var _TEST_XML = "<bookkeeping xmlns='com/interrupt/bookkeeping' xmlns:account='com/interrupt/bookkeeping/account' xmlns:journal='com/interrupt/bookkeeping/journal' xmlns:currency='com/interrupt/bookkeeping/currency' id='main.bookkeeping'>" +
3228 "<currency:currencies id='main.currencies' default='CDN'>" +
3229 "<currency:currency id='CDN' name='Canadian Dollar'/>" +
3230 "<currency:currency id='USD' name='US Dollar'/>" +
3231 "<currency:currency id='BP' name='British Pound'/>" +
3232 "<currency:currency id='EUR' name='Euoropean Euro'/>" +
3233 "<currency:currency id='JPN' name='Japanese Yen'/>" +
3234 "</currency:currencies>" +
3235 "<accounts xmlns='com/interrupt/bookkeeping/account' id='main.accounts'>" +
3236 "<account xmlns='com/interrupt/bookkeeping/account' type='asset' id='' name='' counterWeight='debit'/>" +
3237 "</accounts>" +
3238 "<journal:journals id='main.journals'>" +
3239 "<journal:journal id='generalledger' name='generalledger' type='' balance=''>" +
3240 "<journal:entries id='main.entries'>" +
3241 "<journal:entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
3242 "<debit xmlns='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
3243 "<credit xmlns='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
3244 "</journal:entry>" +
3245 "</journal:entries>" +
3246 "</journal:journal>" +
3247 "</journal:journals>" +
3248 "</bookkeeping>";