more bug fixes
[Bookkeeping.git] / webkell / javascript / bkeeping.js
blob5322f648e1a58dd6b605b089b63f124ca76a4ec0
1 /* bkeeping */
3 if (!window.console)
5 var console = {};
6 console.log = alert;
9 var BLOCK_PARSE_REGEX = /\$\{(\w+)\}/igm;
10 // hack for mock data testing:
11 var _LAST_PARSED_DICT = null;
12 String.prototype.parse_vars = function(dataDict)
14 _LAST_PARSED_DICT = dataDict;
15 return this.replace(BLOCK_PARSE_REGEX, function(match, param, offset, orig)
17 if (!dataDict) { return ""; }
18 return (dataDict[param] || (dataDict[param] == 0)) ? (dataDict[param]) : ("");
19 });
22 /**
23 * The "bind()" function extension from Prototype.js, extracted for general use
25 * @author Richard Harrison, http://www.pluggable.co.uk
26 * @author Sam Stephenson (Modified from Prototype Javascript framework)
27 * @license MIT-style license @see http://www.prototypejs.org/
29 Function.prototype.bind = function()
31 var _$A = function(a)
33 return Array.prototype.slice.call(a);
35 if ((arguments.length < 2) && (typeof arguments[0] == "undefined"))
37 return this;
39 var __method = this, args = _$A(arguments), object = args.shift();
40 return function()
42 return __method.apply(object, args.concat(_$A(arguments)));
46 /* settings */
47 var DEBUG = false;
48 var MOCK_DATA = false;
49 var MOCK_DATA_FAILURES_MODE = false;
50 /* ******** */
52 /* commands */
53 var LOGIN_COMMAND = "login (user -username ${username} -password ${password});";
54 var REGISTER_COMMAND = "var someVariable = add ( " +
55 "(load (`/system[ @id='main.system']`)) " +
56 "<user xmlns='com/interrupt/bookkeeping/users' id='${username}' username='${username}' password='${password}'>" +
57 "<allowedActions id='${username}.allowedActions' xmlns='com/interrupt/bookkeeping/cc/bkell/aauth' >" +
58 "<command id='command.create' name='create' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
59 "<command id='command.add' name='add' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
60 "<command id='command.remove' name='remove' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
61 "<command id='command.reverse' name='reverse' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
62 "<command id='command.find' name='find' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
63 "<command id='command.load' name='load' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
64 "<command id='command.list' name='list' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
65 "<command id='command.print' name='print' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
66 "<command id='command.commit' name='commit' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
67 "<command id='command.login' name='login' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
68 "<command id='command.logout' name='logout' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
69 "<command id='command.exit' name='exit' xmlns='com/interrupt/bookkeeping/cc/bkell/command' />" +
70 "</allowedActions>" +
71 "<profileDetails xmlns='com/interrupt/bookkeeping/users' id='user.details'>" +
72 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='firstname' name='first.name' value='${firstname}'/>" +
73 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='lastname' name='last.name' value='${lastname}'/>" +
74 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='email' name='email' value='${email}'/>" +
75 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='country' name='country' value='${country}'/>" +
76 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='defaultCurrency' name='defaultCurrency' value='${currency}' />" +
77 "</profileDetails>" +
78 "</user> , user -returninput true" +
79 ") ; ";
80 var GETPROFILE_COMMAND = "load (`/system[@id='main.system']/aauthentication[@id='main.authentication']/users[@id='aauth.users']/user[@id='${username}']`);";
81 var UPDATEPROFILE_COMMAND = "var someVariable = update ( " +
82 "(`/system[ @id='main.system']/aauthentication[@id='main.authentication']/users[@id='aauth.users']/user[@id='${username}']/profileDetails[@id='user.details']`) " +
83 "<profileDetails xmlns='com/interrupt/bookkeeping/users' id='user.details'>" +
84 "<profileDetail name='first.name' value='${firstname}'/>" +
85 "<profileDetail name='last.name' value='${lastname}'/>" +
86 "<profileDetail name='email' value='${email}'/>" +
87 "<profileDetail name='country' value='${country}'/>" +
88 "</profileDetails>" +
89 ");commit ((`/system[ @id='main.system']/aauthentication[@id='main.authentication']/users[@id='aauth.users']/user[@id='${username}']/profileDetails[@id='user.details']`) @someVariable);";
91 var LISTACCOUNTS_COMMAND = "load ( `/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}.group\"]/bookkeeping[@id='main.bookkeeping']/accounts[@id='main.accounts']`);";
92 var LISTJOURNALS_COMMAND = "load ( `/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}.group\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals']`);";
93 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']`);";
94 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}']`);";
96 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' ] `);";
99 /****
100 * ACCOUNT Commands
102 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']`))" +
103 "<account xmlns='com/interrupt/bookkeeping/account' type=\"${type}\" id=\"${id}\" name=\"${name}\" counterWeight=\"${cw}\" currency='CDN' /> );"+
104 "commit((`/system[@id='main.system']/groups[@id='main.groups']/group[@id='${username}.group']/bookkeeping[@id='main.bookkeeping']/accounts[@id='main.accounts']`) @addedAccount);";
106 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});" +
107 "commit((`/system[@id='main.system']/groups[@id='main.groups']/group[@id='${username}.group']/bookkeeping[@id='main.bookkeeping']/accounts[@id='main.accounts']`) @removedAccount);";
108 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}']`)" +
109 "<account xmlns=\"com/interrupt/bookkeeping/account\" type=\"${type}\" id=\"${id}\" name=\"${name}\" counterWeight=\"${cw}\"/>);" +
110 "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);";
112 /****
113 * JOURNAL Commands
115 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`)" +
116 "<journal:journal id='${jid}' name='${jname}' type='${jtype}' balance='${jbalance}'/>" +
117 "commit((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals']/journal`) @addedJournal);";
118 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});" +
119 "commit((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals']/journal`) @removedJournal);";
120 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}']`)) " +
121 "<journal xmlns=\"com/interrupt/bookkeeping/journal\" id='${id}' name='${name}' type='${jtype}' balance='${jbalance}'/> ); " +
122 "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);";
125 /****
126 * ENTRY Commands
128 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']`)" +
129 "<entry xmlns='com/interrupt/bookkeeping/journal' id='${eid}' entrynum='' state='' journalid='${jid}' date='${date}' currency='${cc}'>" +
130 "${debitsANDcredits}" +
131 "</entry> ); " +
132 "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);";
134 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});" +
135 "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);";
137 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}']`))" +
138 "<entry xmlns=\"com/interrupt/bookkeeping/journal\" id='${id}' entrynum='' state='' journalid='${jid}' date='${date}' currency='${cc}'>" +
139 "${debitsANDcredits}" +
140 "</entry> );" +
141 "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);";
144 /* ******** */
146 /* templates */
147 // acl_rows
148 var ACCOUNT_ROW = '<div id="${rowid}" class="acl_row row">' +
149 '<div class="edit_column left_column"><a href="javascript:bkeeping.editAccount(\'${rowid}\');"><img border="0" src="images/editrow.jpg"/></a></div>' +
150 '<div class="name_column left_column" style="margin-left: 10px;" >${aname}</div>' +
151 '<div class="category_column left_column" style="margin-left: 40px; margin-right: 20px;" >${category}</div>' +
152 // '<div class="balance_column left_column">${balance}</div>' +
153 '<div class="delete_column left_column"><a href="javascript:bkeeping.deleteAccount(\'${rowid}\');"><img border="0" src="images/deleterow.jpg"/></a></div>' +
154 '</div>';
155 // js_rows
156 var JOURNAL_ROW = '<div id="${rowid}" class="js_row row">' +
157 '<div class="edit_column left_column"><a href="javascript:bkeeping.editJournal(\'${rowid}\');"><img border="0" src="images/editrow.jpg"/></a></div>' +
158 '<div class="large_name_column left_column"><a onclick="bkeeping.showJournal(\'${rowid}\')">${jname}</a></div>' +
159 '<div class="delete_column left_column"><a href="javascript: alert(\'Coming Soon\'); "><img border="0" src="images/deleterow.jpg"/></a></div>' +
160 '</div>';
162 // j_rows
163 var ENTRY_ROW = '<div id="${rowid}" class="j_row row">' +
165 '<div class="edit_column left_column"><a href="javascript:bkeeping.editEntry(\'${rowid}\');"><img border="0" src="images/editrow.jpg"/></a></div>' +
167 '<div class="date_column left_column" style="margin:0 50px;" >${date}</div>' +
168 '<div class="entry_column left_column" style="margin:0 50px;" >${entry}</div>' +
169 '<div class="balance_column left_column" style="margin:0 50px;" >${balance}</div>' +
171 '<div class="delete_column left_column"><a href="javascript:bkeeping.deleteThing(\'entry\', \'${rowid}\');"><img border="0" src="images/deleterow.jpg"/></a></div>' +
173 '</div>';
177 var TENTRY_ROW = '<div id="jentry_rows" class="j_rows rows">' +
179 ' <div class="edit_column left_column"><a href="javascript:bkeeping.addEditEntryPart(\'${rowid}\', \'${eid}\');"><img border="0" src="images/editrow.jpg"/></a></div>' +
181 ' <div style="float:left; width: 18%; margin-left: 90px; ">${dname}</div><div style="float:left; width: 20%; ">${damount}</div>' +
182 ' <div style="float:left; width: 20%; ">${cname}</div><div style="float:left; width: 20%; ">${camount}</div>' +
184 ' <div class="delete_column left_column"><a href="javascript: alert(\'Coming Soon. Please replace entry\');"><img border="0" src="images/deleterow.jpg"/></a></div>' +
186 '</div>';
189 var AE_ACCOUNT = ["Add/Edit Account", '<table class="formtable">' +
190 '<tr>' +
191 '<td>' +
192 'Name' +
193 '</td>' +
194 '<td>' +
195 '<input type="text" name="account_name" id="account_name" class="data_field" value="${aname}" />' +
196 '</td>' +
197 '</tr>' +
198 '<tr>' +
199 '<td>' +
200 'Type' +
201 '</td>' +
202 '<td>' +
203 '<select name="account_type" id="account_type" class="data_field">' +
204 '${accounts}'+
205 '</select>' +
206 '</td>' +
207 '</tr>' +
208 '</table>'];
210 var AE_JOURNAL = ["Add/Edit Journal", '<table class="formtable">' +
211 '<tr>' +
212 '<td>' +
213 'Name' +
214 '</td>' +
215 '<td>' +
216 '<input type="text" name="journal_name" id="journal_name" class="data_field" value="${name}" />' +
217 '</td>' +
218 '</tr>' +
219 /*'<tr>' +
220 '<td>' +
221 'Type' +
222 '</td>' +
223 '<td>' +
224 '<select name="journal_type" id="journal_type" class="data_field">' +
225 '<option>default</option>' +
226 '</select>' +
227 '</td>' +
228 '</tr>' +
229 '<tr>' +
230 '<td>' +
231 'Balance' +
232 '</td>' +
233 '<td>' +
234 '<input type="text" name="journal_balance" id="journal_balance" class="data_field" value="${balance}" />' +
235 '</td>' +
236 '</tr>' +
238 '</table>'];
240 var AE_ENTRY = ["Add/Edit Entry", '<table class="formtable">' +
241 '<input type="hidden" name="entryid" value="${entryid}" />' +
242 '<tr>' +
243 '<td>' +
245 '<span class="smallformtext">Currency:</span>&nbsp;' +
246 '<select name="entry_cc" id="entry_cc" class="data_field">' +
247 '<option value="CDN" ${curr_cdn} >Canadian Dollar</option>' +
248 '<option value="USD" ${curr_usd} >US Dollar</option>' +
249 '<option value="BP" ${curr_bp} >British Pound</option>' +
250 '<option value="EUR" ${curr_eur} >European Euro</option>' +
251 '<option value="JPN" ${curr_jpn} >Japanese Yen</option>' +
252 '</select>' +
253 '</td>' +
254 '</tr>' +
255 '<tr>' +
256 '<td>' +
257 '<span class="smallformtext">Amount:</span>&nbsp;<input type="text" size="6" name="entry_ecamount" id="entry_ecamount" class="data_field" value="${evalue}" >&nbsp;' +
258 '</td>' +
259 '</tr>' +
260 '<tr>' +
261 '<td>' +
262 '<span class="smallformtext">Account:</span>&nbsp;' +
263 '<select name="entry_caid" id="entry_caid" class="data_field">' +
264 '${accounts}' +
265 '</select>' +
266 '</td>' +
267 '</tr>' +
268 '<tr>' +
269 '<td>' +
270 '<span class="smallformtext">Type:</span>&nbsp;' +
271 '<select name="entry_ctype" id="entry_ctype" class="data_field">' +
272 '<option value="debit" ${entry_dtype} >debit</option><option value="credit" ${entry_ctype} >credit</option>' +
273 '</select>' +
274 '</td>' +
275 '</tr>' +
276 '</table>'];
279 /* ********* */
281 var DIALOG_HOLDER = "<div id='bkeeping_dialog' class='dialog'>Dialog default text.</div>";
282 var _DIALOG_FIRST = true;
284 // top namespace
285 var bkeeping = {
288 // session
289 ACTIVE_SESSION: null,
290 DEFAULT_CURRENCY: null,
291 ROOT_URL: null,
294 cache:
296 user: null,
297 accounts: [],
298 journals: [],
299 entries: []
302 /* logging */
303 debug: function(message)
305 if (DEBUG)
307 if (window.console)
309 console.log(message);
311 else
313 alert(message);
318 log: function(message)
320 if (window.console)
322 console.log(message);
324 else
326 // do nothing (yet)
330 raise: function(message)
332 alert(message);
334 /* ******* */
336 modalDialogScreen: function(showhide)
338 var m = $("#modal_screen");
339 if (showhide == "show")
341 m.css('display', 'block');
343 else
345 m.css('display', 'none');
349 showModalDialog: function()
351 bkeeping.modalDialogScreen("show");
352 bkeeping.showDialog.apply(this, arguments);
355 showDialog: function()
357 bkeeping.setDialogContent.apply(this, arguments);
358 bkeeping.openDialog();
361 setDialogContent: function()
363 var args = Array.prototype.slice.call(arguments);
364 var content = args.shift();
365 if (content instanceof Function)
367 $("#bkeeping_dialog").html(content.apply(this, args));
369 else
371 $("#bkeeping_dialog").html(content);
375 openDialog: function()
377 if (_DIALOG_FIRST)
379 _DIALOG_FIRST = false;
380 $("#bkeeping_dialog").dialog({ close: bkeeping.modalDialogScreen.bind(this, "hide") });
382 else
384 $("#bkeeping_dialog").dialog("open");
388 _a_open: true,
389 toggleAccountsOpen: function()
391 var newleft = "0px";
392 if (bkeeping._a_open)
394 bkeeping._a_open = false;
395 newleft = "-373px";
397 else
399 bkeeping._a_open = true;
402 $("#accounts_ui").animate({ left: newleft }, { duration: 750, easing: "swing", complete: function() {}, queue: false });
406 _j_open: true,
407 toggleJournalsOpen: function()
409 var newleft = "436px";
410 if (bkeeping._j_open)
412 bkeeping._j_open = false;
413 newleft = "809px";
415 else
417 bkeeping._j_open = true;
419 $("#journals_ui").animate({ left: newleft }, { duration: 750, easing: "swing", complete: function() {}, queue: false });
422 /* state */
423 /* cookie functions: http://techpatterns.com/downloads/javascript_cookies.php */
424 setCookie: function(name, value, expires, path, domain, secure)
426 // set time, it's in milliseconds
427 var today = new Date();
428 today.setTime(today.getTime());
430 // EdA: override this to default to base path instead of "" (easier for simplistic auth systems)
431 path = path || "/";
434 {expires} days
436 if (expires)
438 expires = expires * 1000 * 60 * 60 * 24;
440 var expires_date = new Date(today.getTime() + (expires));
441 document.cookie = name + "=" + escape(value) +
442 ((expires) ? ";expires=" + expires_date.toGMTString() : "") +
443 ((path) ? ";path=" + path : "") +
444 ((domain) ? ";domain=" + domain : "") +
445 ((secure) ? ";secure" : "" );
448 getCookie: function(name)
450 // first we'll split this cookie up into name/value pairs
451 // note: document.cookie only returns name=value, not the other components
452 var a_all_cookies = document.cookie.split(';');
453 var a_temp_cookie = '';
454 var cookie_name = '';
455 var cookie_value = '';
456 var b_cookie_found = false; // set boolean t/f default f
458 for (var i = 0; i < a_all_cookies.length; i++)
460 // now we'll split apart each name=value pair
461 a_temp_cookie = a_all_cookies[i].split('=');
463 // and trim left/right whitespace while we're at it
464 cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, '');
466 // if the extracted name matches passed name
467 if (cookie_name == name)
469 b_cookie_found = true;
470 // we need to handle case where cookie has no value but exists (no = sign, that is):
471 if (a_temp_cookie.length > 1)
473 cookie_value = unescape(a_temp_cookie[1].replace(/^\s+|\s+$/g, ''));
475 // note that in cases where cookie is initialized but no value, null is returned
476 return cookie_value;
477 break;
479 a_temp_cookie = null;
480 cookie_name = '';
482 if (!b_cookie_found)
484 return null;
488 deleteCookie: function(name, path, domain)
490 if (this.getCookie(name))
492 // EdA: override this to default to base path instead of "" (easier for simplistic auth systems)
493 path = path || "/";
494 document.cookie = name + "=" +
495 ((path) ? ";path=" + path : "") +
496 ((domain) ? ";domain=" + domain : "" ) +
497 ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
501 Session: function(sessionId, loggedInUserid, loggedInGroup)
503 this.cookieName = "_bk_session_js";
504 this.loaded = false;
505 this.sessionId = sessionId || null;
506 this.loggedInUserid = loggedInUserid || null;
507 this.loggedInGroup = loggedInGroup || null;
509 this.update = function(sessionId, loggedInUserid, loggedInGroup)
511 this.sessionId = sessionId || null;
512 this.loggedInUserid = loggedInUserid || null;
513 this.loggedInGroup = loggedInGroup || null;
514 return this;
517 this.clear = function()
519 this.loaded = false;
520 this.sessionId = null;
521 this.loggedInUserid = null;
522 this.loggedInGroup = null;
523 bkeeping.deleteCookie(this.cookieName);
524 return this;
527 function saveToSessionCookie()
529 bkeeping.setCookie(this.cookieName, this.sessionId + "," + this.loggedInUserid + "," + this.loggedInGroup);
530 return this;
532 this.save = saveToSessionCookie.bind(this);
534 function loadFromSessionCookie()
536 var cookievalues = bkeeping.getCookie(this.cookieName);
537 if (cookievalues)
539 var vals = cookievalues.split(',');
540 this.sessionId = vals[0];
541 this.loggedInUserid = vals[1];
542 this.loggedInGroup = vals[2];
543 return true;
545 return false;
548 if (!this.sessionId)
550 // try to load from cookie:
551 this.loaded = loadFromSessionCookie.call(this); // true if there was a session cookie, false otherwise
554 return this;
556 /* ***** */
558 /* data objects */
559 User: function(root)
561 this.root = root;
563 this.getProfileDetailsList = function()
565 return this.root["children"][0]["profileDetails"]["children"];
568 this.getUserid = function()
570 return this.root["id"];
573 this.getUsername = function()
575 return this.root["username"];
578 this.getPassword = function()
580 return this.root["password"];
583 this.getFirstname = function()
585 return this.root["children"][0]["profileDetails"]["children"][0]["profileDetail"].value;
588 this.getLastname = function()
590 return this.root["children"][0]["profileDetails"]["children"][1]["profileDetail"].value;
593 this.getEmail = function()
595 return this.root["children"][0]["profileDetails"]["children"][2]["profileDetail"].value;
598 this.getCountry = function()
600 return this.root["children"][0]["profileDetails"]["children"][3]["profileDetail"].value;
604 UserSession: function(root)
606 this.root = root;
607 this.getSessionId = function()
609 return this.root["id"];
612 this.getGroup = function()
614 return this.root["groupid"];
617 this.getUser = function()
619 return this.root["userid"];
622 return this;
625 DefaultCurrency: function(root)
627 this.root = root;
628 this.getDefaultCurrency = this.lister.bind(this, "profileDetail", bkeeping.DefaultCurrency);
629 this.getDefaultCurrencyRaw = this.rawlister.bind(this);
632 lister: function(nodename, _class)
634 if( this.root["children"] != null ) {
636 var c = this.root["children"];
637 if (c.length > 0)
639 var a = new Array();
640 for (var i = 0; i < c.length; i++) {
642 if( c[i][nodename] != undefined ) {
643 a.push(new _class(c[i][nodename]));
645 else if( c[i]["debit"] != undefined ) {
646 a.push( new bkeeping.Subentry( c[i]["debit"], "debit" ) );
648 else if( c[i]["credit"] != undefined ) {
649 a.push( new bkeeping.Subentry( c[i]["credit"], "credit" ) );
652 return a;
657 rawlister: function()
659 return this.root["children"];
662 Lister: function()
664 this.lister = bkeeping.lister;
665 this.rawlister = bkeeping.rawlister;
668 AccountList: function(root)
670 this.root = root;
671 this.getAccounts = this.lister.bind(this, "account", bkeeping.Account);
672 this.getAsList = this.getAccounts;
673 this.getAccountsRaw = this.rawlister.bind(this);
674 this.getAsRawList = this.getAccountsRaw;
677 Account: function(root)
679 this.root = root;
681 this.getType = function()
683 return this.root['type'];
686 this.getId = function()
688 return this.root['id'];
691 this.getName = function()
693 return this.root['name'];
696 this.getCounterWeight = function()
698 return this.root['counterWeight'];
701 this.getBalance = function()
703 // XXX balance not in account structure, HELP
704 return "xxxhelpxxx";
708 JournalList: function(root)
710 this.root = root;
711 this.getJournals = this.lister.bind(this, "journal", bkeeping.Journal);
712 this.getAsList = this.getJournals;
713 this.getJournalsRaw = this.rawlister.bind(this);
714 this.getAsRawList = this.getJournalsRaw;
717 Journal: function(root)
719 this.root = root;
721 this.getId = function()
723 return this.root["id"];
726 this.getName = function()
728 return this.root["name"];
731 this.getType = function()
733 return this.root["type"];
736 this.getBalance = function()
738 return this.root["balance"];
742 EntryList: function(root)
744 this.root = root;
745 this.getEntries = this.lister.bind(this, "entry", bkeeping.Entry);
746 this.getAsRawList = this.rawlister.bind(this);
749 Entry: function(root)
751 this.root = root;
753 this.getId = function()
755 return this.root["id"];
758 this.getEntrynum = function()
760 return this.root["entrynum"];
763 this.getState = function()
765 return this.root["state"];
768 this.getJournalid = function()
770 return this.root["journalid"];
773 this.getDate = function()
775 return this.root["date"];
778 this.getCurrency = function()
780 return this.root["currency"];
783 this.getSubentries = function(type, raw)
785 var d = this.root["children"];
786 var a = new Array();
787 for (var i = 0; i < d.length; i++)
789 if (d[i][type])
791 if (raw)
793 a.push(d[i]);
795 else
797 a.push(new bkeeping.Subentry(d[i], type));
801 return a;
804 this.getDebits = function()
806 return this.getSubentries("debit");
809 this.getCredits = function()
811 return this.getSubentries("credit");
814 this.getDebitsRaw = function()
816 return this.getSubentries("debit", true);
819 this.getCreditsRaw = function()
821 return this.getSubentries("credit", true);
825 Subentry: function(root, type)
827 this.root = root;
828 this.type = type;
830 this.getId = function()
832 return this.root["id"];
835 this.getAmount = function()
837 return this.root["amount"];
840 this.getEntryid = function()
842 return this.root["entryid"];
845 this.getAccountid = function()
847 return this.root["accountid"];
850 this.getAccount = function()
852 return this.root["account"];
855 this.getCurrency = function()
857 return this.root["currency"];
860 /* ************ */
862 /* commands */
863 login: function()
865 var u = $("#u").val();
866 var p = $("#p").val();
867 var ret = new this.LoginCommand(u, p).run();
870 logout: function()
873 var ret = new this.LogoutCommand().run();
877 register: function()
879 var username = $("#username").val();
880 var password = $("#password").val();
881 var firstname = $("#firstname").val();
882 var lastname = $("#lastname").val();
883 var email = $("#email").val();
884 var country = $("#country").val();
885 var currency = $("#currency").val();
886 var ret = new this.RegisterCommand(username, password, firstname, lastname, email, country, currency).run();
889 editprofile: function()
891 var username = $("#username").val();
892 var password = $("#password").val();
893 var firstname = $("#firstname").val();
894 var lastname = $("#lastname").val();
895 var email = $("#email").val();
896 var country = $("#country").val();
897 var ret = new this.RegisterCommand(username, password, firstname, lastname, email, country, currency, true).run();
900 deleteThing: function(thingtype, id)
902 var goAhead = function(id)
904 $('#bkeeping_dialog').dialog("close");
905 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();
906 }.bind(this, id);
907 bkeeping.showDialog("Are you sure you want to delete " + thingtype + " with id[" + id + "] ?");
908 $('#bkeeping_dialog').dialog('option', 'buttons', { "CANCEL": function() { $(this).dialog("close"); }, "DELETE": goAhead });
911 addedEdited: function(jsonData)
913 bkeeping.debug("addedEdited: returned jsonData:");
914 bkeeping.debug(jsonData);
915 alert('Recored added/edited. Reloading UI to display updates.');
916 var loc = document.location.href;
917 if (loc.indexOf('?') != -1)
919 loc = loc.substring(0, loc.indexOf('?'));
921 if (!DEBUG)
923 document.location.href = loc + '?timestamp=' + new Date().getTime();
927 //** closing entry window
928 $("#entry_ui").css("display", "none");
931 //** reloading journal window
932 bkeeping.showJournal(bkeeping._displayed_journal);
936 addedEditedJournal: function(jsonData)
938 bkeeping.debug("addedEditedJournal: returned jsonData:");
939 bkeeping.debug(jsonData);
940 alert('Recored added/edited. Reloading UI to display updates.');
942 //** reload the current page
943 setTimeout("document.location.href='"+ bkeeping.ROOT_URL +"';", 0);
947 wrapFormAsObj: function()
949 var pars = {};
950 var fields = $(".data_field");
951 for (var i = 0; i < fields.length; i++)
953 var field = fields[i];
954 var n = field.id.substring(field.id.lastIndexOf('_') + 1);
955 var v = $(field).val();
956 bkeeping.debug('wrapFormAsObj: found ' + n + '/' + v);
957 pars[n] = v;
959 return pars;
962 getAccountsAsOptionsList: function(atype)
964 // we have to assume they're cached, coz this is a synchronous call
965 var html = "";
966 var accounts = bkeeping.cache.accounts;
967 for (var i = 0; i < accounts.length; i++)
969 var id = accounts[i].account.id;
970 var nam = accounts[i].account.name;
971 var type = accounts[i].account.type;
972 if (id)
974 if( atype == type ) {
975 html += '<option value="' + id + '" selected="selected" >' + nam + '</option>';
977 else {
978 html += '<option value="' + id + '">' + nam + '</option>';
982 return html;
984 getAccountTypesAsOptionsList: function(atype)
986 // we have to assume they're cached, coz this is a synchronous call
987 var html = "";
988 var thingy = { asset: '<option ${selected}>asset</option>',
989 expense: '<option ${selected}>expense</option>',
990 liability: '<option ${selected}>liability</option>',
991 revenue: '<option ${selected}>revenue</option>'
994 for( property in thingy ) {
996 if ( property == atype ) {
998 html += thingy[property].parse_vars( { selected:"selected='selected'" } );
1000 else {
1002 html += thingy[property].parse_vars();
1005 return html;
1008 getCounterWeight: function(type)
1010 switch (type)
1012 case "asset":
1013 return "debit";
1014 case "expense":
1015 return "debit";
1016 case "liability":
1017 return "credit";
1018 case "revenue":
1019 return "credit";
1020 default:
1021 return "credit";
1025 getCurrentDate: function()
1027 var d = new Date();
1028 var day = d.getDate();
1029 var month = d.getMonth() + 1;
1030 var year = d.getFullYear();
1031 if (day < 10) { day = '0' + day; }
1032 if (month < 10) { month = '0' + month; }
1033 return day + '' + month + '' + year;
1036 getAccountNameForId: function(aid) {
1038 var accounts = bkeeping.cache.accounts;
1039 for (var i = 0; i < accounts.length; i++)
1041 var id = accounts[i].account.id;
1042 var nam = accounts[i].account.name;
1043 if (id == aid )
1045 return nam;
1048 return null;
1052 goJournalPart: function(thingtype, id)
1055 var type = id ? this.EditCommand : this.AddCommand;
1056 var pars = this.wrapFormAsObj();
1057 pars["username"] = bkeeping["ACTIVE_SESSION"]["loggedInUserid"];
1058 pars["id"] = id;
1061 $('#bkeeping_dialog').dialog("close");
1063 var ret = new type(thingtype, this.addedEditedJournal, pars).run();
1067 goAccountPart: function(thingtype, id)
1070 var type = id ? this.EditCommand : this.AddCommand;
1071 var pars = this.wrapFormAsObj();
1072 pars["username"] = bkeeping["ACTIVE_SESSION"]["loggedInUserid"];
1073 pars["id"] = id;
1076 if( (pars["type"] == "asset") || (pars["type"] == "expense") ) { //** if asset or expense - debit
1077 pars["cw"] = "debit";
1079 else { //** otherwise - credit
1080 pars["cw"] = "credit";
1083 $('#bkeeping_dialog').dialog("close");
1086 var ret = new type(thingtype, this.addedEdited, pars).run();
1089 goJournalEntryPart: function(thingtype, id)
1092 var type = id ? this.EditCommand : this.AddCommand;
1093 var pars = this.wrapFormAsObj();
1095 //** put in data mapping
1098 //** TODO - i) take ID and update entry or ii) put a new entry with ID of 'new'
1099 var entry = null;
1100 if( (id != null) && (id.length > 0) ) {
1102 entry = bkeeping.findEntry(id);
1104 else if( bkeeping.findEntry("new") != null ) {
1106 entry = bkeeping.findEntry("new");
1108 else {
1110 entry = new bkeeping.Entry();
1111 entry["children"] = [];
1113 entry["id"] = "new";
1114 entry["currency"] = pars["cc"];
1115 entry["date"] = "";
1116 entry["entrynum"] = null;
1117 entry["journalid"] = "";
1118 entry["state"] = null;
1119 entry["xmlns"] = "com/interrupt/bookkeeping/journal";
1121 //bkeeping.cache.entries[ bkeeping.cache.entries.length ] =
1122 bkeeping.cache.entries.push( { entry: entry } );
1126 //** set the entry currency
1127 if( pars["cc"] == null ) {
1128 if( entry != null ) {
1129 pars["cc"] = entry["currency"];
1133 var obj = null;
1134 if( pars["ctype"] == "debit" ) {
1136 var debit = {
1137 account: bkeeping.getAccountNameForId( pars["caid"] ),
1138 accountid: pars["caid"],
1139 amount: pars["ecamount"],
1140 currency: pars["cc"],
1141 entryid: "",
1142 id: ( id != null ) ? id : "" ,
1143 xmlns: "com/interrupt/bookkeeping/account"
1145 obj = {
1146 dname: bkeeping.getAccountNameForId( pars["caid"] ),
1147 daid: pars["caid"],
1148 damount: pars["ecamount"],
1149 cname: "&nbsp;",
1150 caid: "&nbsp;",
1151 camount: "&nbsp;"
1154 entry.children.push( { debit: debit } );
1157 else {
1159 var credit = {
1160 account: bkeeping.getAccountNameForId( pars["caid"] ),
1161 accountid: pars["caid"],
1162 amount: pars["ecamount"],
1163 currency: pars["cc"],
1164 entryid: "",
1165 id: ( id != null ) ? id : "" ,
1166 xmlns: "com/interrupt/bookkeeping/account"
1168 obj = {
1169 dname: "&nbsp;",
1170 daid: "&nbsp;",
1171 damount: "&nbsp;",
1172 cname: bkeeping.getAccountNameForId( pars["caid"] ),
1173 caid: pars["caid"],
1174 camount: pars["ecamount"]
1178 entry.children.push( { credit: credit } );
1183 var entryPart = TENTRY_ROW.parse_vars( obj );
1184 var thing = $("#jentry_rows");
1185 $("#jentry_rows").append( entryPart );
1187 $('#bkeeping_dialog').dialog("close");
1189 //var ret = new type(thingtype, this.addedEdited, pars).run();
1193 findJournalName: function(jid) {
1195 var resultName = null;
1196 for( var i = 0; i < bkeeping.cache.journals.length; i++ ) {
1198 var eachJ = bkeeping.cache.journals[i]["journal"];
1199 if( eachJ["id"] == jid ) {
1201 resultName = eachJ["name"];
1202 break;
1207 return resultName;
1210 findAccountName: function(aid) {
1212 var returnName = null;
1213 for( var i = 0; i < bkeeping.cache.accounts.length; i++ ) {
1215 var eachAccount = bkeeping.cache.accounts[i]["account"];
1216 if( eachAccount["id"] == aid ) {
1218 returnName = eachAccount["name"];
1219 break;
1223 return returnName;
1226 findAccountType: function(aid) {
1228 var returnType = 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 returnType = eachAccount["type"];
1235 break;
1239 return returnType;
1242 findEntry: function(eid) {
1244 var entry = null;
1245 for( var i = 0; i < bkeeping.cache.entries.length; i++ ) {
1247 var eachEntry = bkeeping.cache.entries[i]["entry"];
1248 if( eachEntry["id"] == eid ) {
1250 entry = eachEntry;
1251 break;
1255 return entry;
1259 addEditJournal: function(thingtype, id, returnHandler) // id is null on adds
1262 // raise correct form
1263 var x = eval("AE_" + thingtype.toUpperCase());
1264 var form = x[1];
1266 var journalName = bkeeping.findJournalName(id);
1268 // could be vars:
1269 form = form.parse_vars( { 'name': journalName } );
1270 bkeeping.showModalDialog(form);
1271 $('#bkeeping_dialog').dialog('option', 'title', x[0]);
1273 $('#bkeeping_dialog').dialog('option', 'buttons', { "CANCEL": function() { $(this).dialog("close"); }, "SAVE": returnHandler });
1275 return true;
1278 addEditAccount: function(thingtype, id, returnHandler) // id is null on adds
1281 // raise correct form
1282 var x = eval("AE_" + thingtype.toUpperCase());
1283 var form = x[1];
1285 var accountName = bkeeping.findAccountName(id);
1286 var accountType = bkeeping.findAccountType(id);
1288 // could be vars:
1289 form = form.parse_vars( { accounts:bkeeping.getAccountTypesAsOptionsList(accountType), aname: accountName } );
1290 bkeeping.showModalDialog(form);
1291 $('#bkeeping_dialog').dialog('option', 'title', x[0]);
1293 $('#bkeeping_dialog').dialog('option', 'buttons', { "CANCEL": function() { $(this).dialog("close"); }, "SAVE": returnHandler });
1295 return true;
1298 addEditEntry: function(thingtype, id, eid, returnHandler) // id is null on adds
1301 // raise correct form
1302 var x = eval("AE_" + thingtype.toUpperCase());
1303 var form = x[1];
1304 var obj = {};
1305 if( (eid != undefined) && (eid != null) ) {
1306 obj["entryid"] = eid
1309 var entryF = bkeeping.findEntry(eid);
1311 // find entry part
1312 if( entryF != null ) {
1314 var entryPartF = null;
1315 for( var i=0; i < entryF.children.length; i++ ) {
1317 var eachC = entryF.children[i];
1318 var thing = null;
1320 if( eachC["credit"] || eachC["debit"] ) {
1322 if( eachC["credit"] ) {
1324 obj["entry_ctype"] = " selected='selected' ";
1325 thing = eachC["credit"];
1327 else if( eachC["debit"] ) {
1329 obj["entry_dtype"] = " selected='selected' ";
1330 thing = eachC["debit"];
1333 var accountType = bkeeping.findAccountType( thing["accountid"] );
1334 obj["accounts"] = bkeeping.getAccountsAsOptionsList(accountType);
1335 obj["evalue"] = thing["amount"];
1337 if( thing["currency"] == "CDN" ) {
1338 obj["curr_cdn"] = " selected='selected' ";
1340 else if( thing["currency"] == "USD" ) {
1341 obj["curr_usd"] = " selected='selected' ";
1343 else if( thing["currency"] == "BP" ) {
1344 obj["curr_bp"] = " selected='selected' ";
1346 else if( thing["currency"] == "EUR" ) {
1347 obj["curr_eur"] = " selected='selected' ";
1349 else if( thing["currency"] == "JPN" ) {
1350 obj["curr_jpn"] = " selected='selected' ";
1357 else {
1359 obj["accounts"] = bkeeping.getAccountsAsOptionsList(null);
1363 // could be vars:
1364 form = form.parse_vars( obj );
1366 bkeeping.showModalDialog(form);
1367 $('#bkeeping_dialog').dialog('option', 'title', x[0]);
1369 $('#bkeeping_dialog').dialog('option', 'buttons', { "CANCEL": function() { $(this).dialog("close"); }, "SAVE": returnHandler });
1371 return true;
1374 saveEntry: function(id)
1377 var type = id ? this.EditCommand : this.AddCommand;
1378 var pars = this.wrapFormAsObj();
1379 pars['date'] = bkeeping.getCurrentDate();
1380 pars['id'] = id;
1381 pars['jid'] = bkeeping._displayed_journal;
1382 pars['username'] = bkeeping.ACTIVE_SESSION["loggedInUserid"];
1385 //** TODO [done]
1386 //** 1. findEntry 'entryid' or 'new'
1387 var entry = null;
1388 if( id.length > 0 ) {
1390 entry = bkeeping.findEntry(id);
1392 else {
1394 entry = bkeeping.findEntry("new");
1395 entry["id"] = "";
1398 if( entry != null ) {
1399 pars["cc"] = entry["currency"];
1403 //** 2. ensure balances
1404 var dsumDEBIT = 0;
1405 var csumDEBIT = 0;
1407 var dsumCREDIT = 0;
1408 var csumCREDIT = 0;
1410 for( var i = 0; i < entry["children"].length; i++ ) { //** sum up debits & credits
1413 //** on debit side ( asset / expense )
1414 //** on credit side ( liability / revenue )
1415 if( entry["children"][i]["credit"] ) {
1417 var thing = entry["children"][i]["credit"];
1419 // check account type
1420 var atype = bkeeping.findAccountType( thing["accountid"] );
1421 if( atype == "asset" || atype == "expense" ) {
1423 csumDEBIT += parseFloat( thing["amount"] );
1425 else if( atype == "liability" || atype == "revenue" ) {
1427 csumCREDIT += parseFloat( thing["amount"] );
1431 else if( entry["children"][i]["debit"] ) {
1433 var thing = entry["children"][i]["debit"];
1435 // check account type
1436 var atype = bkeeping.findAccountType( thing["accountid"] );
1437 if( atype == "asset" || atype == "expense" ) {
1439 dsumDEBIT += parseFloat( thing["amount"] );
1441 else if( atype == "liability" || atype == "revenue" ) {
1443 dsumCREDIT += parseFloat( thing["amount"] );
1452 * check that debits and credots are balanced
1454 var lhs = csumDEBIT + dsumCREDIT;
1455 var rhs = csumCREDIT + dsumDEBIT;
1456 if( lhs != rhs ) {
1458 alert("debit and credits are not equal");
1459 return false;
1465 * 3. build debit and credit XML parts
1467 pars['debitsANDcredits'] = "";
1468 for( var i = 0; i < entry["children"].length; i++ ) { //** sum up debits & credits
1471 if( entry["children"][i]["credit"] ) {
1473 var cthing = entry["children"][i]["credit"];
1474 var cthingXML = "<credit xmlns='com/interrupt/bookkeeping/account' id='"+ cthing["id"] +"' amount='"+
1475 cthing["amount"] +"' entryid='"+ cthing["entryid"] +"' accountid='"+ cthing["accountid"] +"' account='"+
1476 cthing["account"] +"' currency='"+ cthing["currency"] +"'/>";
1478 pars['debitsANDcredits'] += cthingXML;
1481 else if( entry["children"][i]["debit"] ) {
1483 var dthing = entry["children"][i]["debit"];
1484 var dthingXML = "<debit xmlns='com/interrupt/bookkeeping/account' id='"+ dthing["id"] +"' amount='"+
1485 dthing["amount"] +"' entryid='"+ dthing["entryid"] +"' accountid='"+ dthing["accountid"] +"' account='"+
1486 dthing["account"] +"' currency='"+ dthing["currency"] +"'/>";
1488 pars['debitsANDcredits'] += dthingXML;
1495 //** 4. refresh the entries list at the end of 'run'
1496 var ret = new type("entry", this.addedEdited, pars).run();
1500 addAccount: function()
1502 var returnHandler = bkeeping.goAccountPart.bind(this, "account", null);
1503 bkeeping.addEditAccount("account", null, returnHandler);
1506 editAccount: function(aid)
1508 var returnHandler = bkeeping.goAccountPart.bind(this, "account", aid);
1509 bkeeping.addEditAccount("account", aid, returnHandler);
1512 deleteAccount: function()
1517 addJournal: function()
1519 var returnHandler = bkeeping.goJournalPart.bind(this, "journal", null);
1520 return bkeeping.addEditJournal("journal", null, returnHandler);
1523 editJournal: function(jid)
1526 var returnHandler = bkeeping.goJournalPart.bind(this, "journal", jid);
1528 bkeeping.addEditJournal("journal", jid, returnHandler);
1530 /*var a = new bkeeping.GetCommand("journal", jid, function(journalObj)
1532 $("#journal_name").val(journalObj.getName());
1533 //$("#journal_type").val(journalObj.getType());
1534 //$("#journal_balance").val(journalObj.getBalance());
1535 }).run();
1540 deleteJournal: function()
1545 addEntry: function()
1547 return bkeeping.showJournalEntry(null);
1550 addEditEntryPart: function(id, eid)
1552 var returnHandler = bkeeping.goJournalEntryPart.bind(this, "entry", null);
1553 bkeeping.addEditEntry("entry", id, eid, returnHandler);
1556 editEntry: function(eid)
1558 bkeeping.showJournalEntry( eid );
1559 var a = new bkeeping.GetCommand("entry", eid, function(entryObj)
1561 $("#entry_caid").val(entryObj.getCaid());
1562 $("#entry_daid").val(entryObj.getDaid());
1563 $("#entry_ecamount").val(entryObj.getEcamount());
1564 $("#entry_edamount").val(entryObj.getEdamount());
1565 $("#entry_cc").val(entryObj.getCurrency());
1566 }).run();
1569 deleteEntry: function()
1571 //var a = new bkeeping.RemoveCommand("entry", callback)...
1575 _displayed_journal: null,
1576 showJournal: function(id)
1579 //** clear previous content
1580 var jrows = document.getElementById( "j_rows" );
1581 jrows["innerHTML"] = "";
1584 //** show journal UI
1585 var journalUI = $("#journal_ui");
1586 journalUI.css("display", "block");
1589 bkeeping._displayed_journal = id;
1590 $("#_journal_id").html(id);
1592 new bkeeping.ListCommand("entries", bkeeping.displayEntries, id).run();
1595 showJournalEntry: function(id)
1598 //** clear previous content
1599 var erows = document.getElementById( "jentry_rows" );
1600 erows["innerHTML"] = "";
1603 var eui = $("#entry_ui");
1604 eui.css("display", "block");
1606 var eui2 = document.getElementById( "entry_ui" ); // trying to put the entryid in the template
1608 eui2["innerHTML"].parse_vars = String.prototype.parse_vars;
1609 eui2["innerHTML"] = eui2["innerHTML"].parse_vars( { entryid: id } );
1612 $("#_journalentry_id").html(id);
1614 if(id) {
1615 new bkeeping.ListCommand("entry", bkeeping.displayEntry, id).run();
1619 isAauthFailure: function( jsonData ) {
1621 var thing = jsonData["logs"]["children"][0]["log"]["children"][0]["logMessages"]["children"][0]["logMessage"]["value"];
1622 if( (thing != undefined) || (thing != null) ) {
1624 var containS = thing.indexOf("Authenticated");
1625 if( containS > 0 ) {
1626 return true;
1630 return false;
1633 getGenericFailureReason: function(jsonData)
1635 var r;
1636 try
1638 r = jsonData["logs"]["children"][0]["log"]["children"][0]["logMessages"]["children"][0]["logMessage"]["value"];
1640 catch (e)
1642 bkeeping.debug(e);
1643 r = "Unknown.";
1645 return r;
1648 loadProfile: function(data)
1650 for (var i = 0; i < data.length; i++)
1652 var o = data[i]["profileDetail"];
1653 $("#" + o.id).val(o.value);
1657 GetprofileCommand: function(callback)
1659 this.callback = callback;
1661 function isSuccess(jsonData)
1663 return (jsonData && jsonData["user"]);
1666 var getFailureReason = bkeeping.getGenericFailureReason;
1668 function handleResponse(jsonData, textStatus)
1670 if (isSuccess(jsonData))
1672 var u = new bkeeping.User(jsonData["user"]);
1673 setTimeout(callback.bind(this, u.getProfileDetailsList()), 1000);
1675 else
1677 var reason = getFailureReason(jsonData);
1678 alert("Failed to get profile. Reason: " + reason);
1682 this.run = function()
1684 bkeeping.send_bkexpr("getprofile", GETPROFILE_COMMAND.parse_vars({username:bkeeping.ACTIVE_SESSION.loggedInUserid}), handleResponse.bind(this));
1688 RegisterCommand: function(username, password, firstname, lastname, email, country, currency, updating)
1690 this.username = username;
1691 this.password = password;
1692 this.firstname = firstname;
1693 this.lastname = lastname;
1694 this.email = email;
1695 this.country = country;
1696 this.currency = currency;
1697 this.updating = updating;
1699 function isSuccess(jsonData)
1701 return (jsonData && jsonData["user"]);
1704 var getFailureReason = bkeeping.getGenericFailureReason;
1706 function handleResponse(jsonData, textStatus)
1708 // on successful register, user is redirected to the home page and messaged about his ability to login or that his changes were saved
1709 if (isSuccess(jsonData))
1711 var flag = "canlogin";
1712 if (this.updating)
1714 flag = "changessaved";
1716 setTimeout("document.location.href='accounts.html';", 0);
1718 else
1720 // show a UI for registration failure:
1721 var reason = getFailureReason(jsonData);
1722 var verb = "register";
1723 if (this.updating)
1725 verb = "save changes"
1727 alert("Failed to " + verb + ". Reason: " + reason);
1731 this.run = function()
1733 var command = REGISTER_COMMAND;
1734 var cname = "register";
1735 if (this.updating)
1737 command = UPDATEPROFILE_COMMAND;
1738 cname = "updateprofile";
1740 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));
1744 LoginCommand: function(username, password)
1746 this.username = username;
1747 this.password = password;
1749 function isSuccess(jsonData)
1751 return (jsonData && jsonData["userSession"]);
1754 var getFailureReason = bkeeping.getGenericFailureReason;
1756 function handleResponse(jsonData, textStatus)
1758 // on successful login, user is redirected to the accounts page (accounts.html)
1759 if (isSuccess(jsonData))
1762 // set the session
1763 var us = new bkeeping.UserSession(jsonData["userSession"]);
1764 bkeeping.ACTIVE_SESSION.update(us.getSessionId(), us.getUser(), us.getGroup()).save();
1766 // go to the accounts page
1767 setTimeout("document.location.href='accounts.html';", 2000);
1769 else
1771 // show a UI for login failure:
1772 var reason = getFailureReason(jsonData);
1773 alert("Failed to login. Reason: " + reason);
1777 this.run = function()
1779 bkeeping.send_bkexpr("login", LOGIN_COMMAND.parse_vars({username: this.username, password: this.password}), handleResponse.bind(this));
1783 LogoutCommand: function()
1786 function isSuccess(jsonData)
1789 return (jsonData && jsonData["profileDetail"]);
1792 var getFailureReason = bkeeping.getGenericFailureReason;
1794 function handleResponse(jsonData, textStatus)
1796 // on successful logout, user is redirected to the home page
1797 if (isSuccess(jsonData))
1799 // go to the accounts page
1800 bkeeping.ACTIVE_SESSION.clear();
1801 setTimeout('document.location.href = "'+ bkeeping.ROOT_URL +'";', 2000);
1804 else
1806 // show a UI for login failure:
1807 var reason = getFailureReason(jsonData);
1808 alert("Failed to logout. Reason: " + reason);
1812 this.run = function()
1814 bkeeping.send_bkexpr("logout", "logout;", handleResponse.bind(this));
1819 getEntryBalance: function( entryF ) {
1822 //** 2. ensure balances
1823 var dsumDEBIT = 0;
1824 var csumDEBIT = 0;
1826 var dsumCREDIT = 0;
1827 var csumCREDIT = 0;
1829 var entry = entryF["root"];
1831 for( var i = 0; i < entry["children"].length; i++ ) { //** sum up debits & credits
1834 //** on debit side ( asset / expense )
1835 //** on credit side ( liability / revenue )
1836 if( entry["children"][i]["credit"] ) {
1838 var thing = entry["children"][i]["credit"];
1840 // check account type
1841 var atype = bkeeping.findAccountType( thing["accountid"] );
1842 if( atype == "asset" || atype == "expense" ) {
1844 csumDEBIT += parseFloat( thing["amount"] );
1846 else if( atype == "liability" || atype == "revenue" ) {
1848 csumCREDIT += parseFloat( thing["amount"] );
1852 else if( entry["children"][i]["debit"] ) {
1854 var thing = entry["children"][i]["debit"];
1856 // check account type
1857 var atype = bkeeping.findAccountType( thing["accountid"] );
1858 if( atype == "asset" || atype == "expense" ) {
1860 dsumDEBIT += parseFloat( thing["amount"] );
1862 else if( atype == "liability" || atype == "revenue" ) {
1864 dsumCREDIT += parseFloat( thing["amount"] );
1872 * check that debits and credots are balanced
1874 var lhs = csumDEBIT + dsumCREDIT;
1875 var rhs = csumCREDIT + dsumDEBIT;
1876 if( lhs != rhs ) {
1878 alert("debit and credits are not equal");
1879 return false;
1882 return lhs;
1885 displayAccounts: function(accountListObj)
1887 var as = accountListObj.getAccounts();
1888 if( as ) {
1890 var lister = $("#acl_rows");
1891 var html = "";
1892 for (var i = 0; i < as.length; i++)
1894 var a = as[i];
1895 if( a["root"] ) {
1897 //var obj = { rowid: a.getId(), aname: a.getName(), category: a.getType(), balance: a.getBalance() };
1898 var obj = { rowid: a.getId(), aname: a.getName(), category: a.getType() };
1899 html += ACCOUNT_ROW.parse_vars(obj);
1902 lister.html(html);
1906 displayJournals: function(journalListObj)
1908 var js = journalListObj.getJournals();
1909 if( js ) {
1910 var lister = $("#js_rows");
1911 var html = "";
1912 for (var i = 0; i < js.length; i++)
1914 var j = js[i];
1915 if( j["root"] ) {
1916 var obj = { rowid: j.getId(), jname: j.getName() };
1917 html += JOURNAL_ROW.parse_vars(obj);
1920 lister.html(html);
1924 //** display all the entries in a journal
1925 displayEntries: function(entryListObj)
1927 var es = entryListObj.getEntries();
1928 var lister = $("#j_rows");
1929 var html = "";
1930 if(es) {
1931 for (var i = 0; i < es.length; i++)
1933 var e = es[i];
1935 // ??? xxx not sure what data to put here
1936 //var obj = { rowid: e.getId(), date: (e.getDate() || "<none>"), entry: ("..." + e.getId().substring(e.getId().length - 6) || "<none>"), balance: (e.getBalance() || "0.00") };
1938 if( e["root"] != undefined ) {
1940 var bal = bkeeping.getEntryBalance( e );
1942 var obj = {
1943 rowid: e.getId(),
1944 date: (e.getDate() || "<none>"),
1945 entry: ("..." + e.getId().substring(e.getId().length - 20) || "<none>"),
1946 balance: bal
1950 html += ENTRY_ROW.parse_vars(obj);
1955 lister.html(html);
1958 //** display the debit / credits in a single entry
1959 displayEntry: function(entryListObj)
1961 var es = entryListObj.getEntries();
1962 var lister = $("#jentry_rows");
1963 var html = "";
1964 if(es) {
1965 for (var i = 0; i < es.length; i++)
1967 var e = es[i];
1969 // ??? xxx not sure what data to put here
1970 //var obj = { rowid: e.getId(), date: (e.getDate() || "<none>"), entry: ("..." + e.getId().substring(e.getId().length - 6) || "<none>"), balance: (e.getBalance() || "0.00") };
1972 //var obj = { rowid: e.getId(), date: (e.getDate() || "<none>"), entry: ("..." + e.getId().substring(e.getId().length - 6) || "<none>"), balance: ( "0.00" ) };
1974 var obj = {};
1975 if( e["type"] == "debit" ) {
1976 obj = {
1977 dname: e.getAccount(),
1978 damount: e.getAmount(),
1979 cname: "&nbsp;",
1980 camount: "&nbsp;"
1983 else if( e["type"] == "credit" ) {
1984 obj = {
1985 dname: "&nbsp;",
1986 damount: "&nbsp;",
1987 cname: e.getAccount(),
1988 camount: e.getAmount()
1992 obj["rowid"] = e.getId();
1993 obj["eid"] = entryListObj["root"]["id"];
1995 html += TENTRY_ROW.parse_vars(obj);
1999 lister.html(html);
2002 objectIsOfType: function(obj, single_or_plural_type)
2004 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;
2005 if (t == 'entrie') { t = "entry"; }
2006 if (obj && obj[t])
2008 return true;
2010 return false;
2013 addToCacheAsList: function(plural_type, listObj)
2015 var list = listObj.getAsRawList();
2016 bkeeping.debug('AddToCacheAsList: listing (' + plural_type + '/' + list.length + ')');
2017 bkeeping.debug(list);
2018 if (list && (list.length > 0))
2020 for (var i = 0; i < list.length; i++)
2022 if (bkeeping.objectIsOfType(list[i], plural_type))
2024 bkeeping.addToCache(plural_type, null, list[i]);
2030 addToCache: function(plural_type, id, jsonData)
2032 var found = false;
2033 var single_type = (plural_type == 'entries') ? 'entry' : plural_type.substring(0, plural_type.length - 1);
2034 for (var i = 0; i < bkeeping.cache[plural_type].length; i++)
2036 var item = bkeeping.cache[plural_type][i];
2037 bkeeping.debug('addToCache:iterating cache, found item ' + item[single_type].id + '/' + id);
2038 bkeeping.debug(item);
2039 if (item && (item[single_type].id == id))
2041 // item could be changed, so re-add
2042 found = true;
2043 bkeeping.cache[plural_type][i] = jsonData;
2046 if (!found)
2048 bkeeping.debug('addToCache: pushing jsonData onto cache');
2049 bkeeping.debug(jsonData);
2050 bkeeping.cache[plural_type].push(jsonData);
2054 getFromCache: function(plural_type, id)
2056 var single_type = (plural_type == 'entries') ? 'entry' : plural_type.substring(0, plural_type.length - 1);
2057 for (var i = 0; i < bkeeping.cache[plural_type].length; i++)
2059 var item = bkeeping.cache[plural_type][i];
2060 bkeeping.debug('getFromCache:iterating cache, found item ' + item[single_type].id + '/' + id);
2061 bkeeping.debug(item);
2062 if (item && (item[single_type].id == id))
2064 return item;
2069 deleteFromCache: function(single_type, id)
2071 var plural_type = (single_type == 'entry') ? 'entries' : (single_type + 's');
2072 for (var i = 0; i < bkeeping.cache[plural_type].length; i++)
2074 var item = bkeeping.cache[plural_type][i];
2075 bkeeping.debug('deleteFromCache:iterating cache, found item ' + item[single_type].id + '/' + id);
2076 bkeeping.debug(item);
2077 if (item && (item[single_type].id == id))
2079 delete bkeeping.cache[plural_type][i];
2080 break;
2085 GetCommand: function(calltype, id, callback)
2087 this.calltype = calltype;
2088 this.calltype_list = (this.calltype == 'entry') ? 'entries' : (this.calltype + 's');
2089 this.id = id;
2090 this.callback = callback;
2091 this.command = "";
2092 this.type = "";
2093 var u = bkeeping.ACTIVE_SESSION.loggedInUserid;
2094 var g = (u == 'root') ? "webkell" : (u + '.group');
2095 this.pars = {username: u, group: g, id: this.id};
2096 switch (this.calltype)
2098 case "account":
2099 this.command = GETACCOUNT_COMMAND;
2100 this.type = bkeeping.Account;
2101 break;
2102 case "journal":
2103 this.command = GETJOURNAL_COMMAND;
2104 this.type = bkeeping.Journal;
2105 break;
2106 case "entry":
2107 this.command = GETENTRY_COMMAND;
2108 this.type = bkeeping.Entry;
2109 this.pars['jid'] = bkeeping._displayed_journal;
2110 break;
2111 default:
2112 alert("Unknown calltype: " + this.calltype);
2113 break;
2116 function isSuccess(jsonData)
2118 return (jsonData && (jsonData[this.calltype]) || jsonData[this.calltype_list]);
2121 var getFailureReason = bkeeping.getGenericFailureReason;
2123 function handleResponse(jsonData, textStatus, cached)
2125 if (isSuccess.call(this, jsonData))
2127 if (!cached || (cached != 'true'))
2129 bkeeping.addToCache(this.calltype_list, this.id, jsonData);
2131 return this.callback(new this.type(jsonData[this.calltype]));
2133 else
2135 // show a UI for login failure:
2136 var reason = getFailureReason(jsonData);
2137 alert("Failed to get list " + this.calltype + ". Reason: " + reason);
2141 this.run = function()
2143 var item = bkeeping.getFromCache(this.calltype_list, this.id);
2144 if (item)
2146 bkeeping.debug('handling call type ' + this.calltype + '/' + this.id + ' from cache');
2147 handleResponse.call(this, item, 'success', 'true');
2149 else
2151 bkeeping.debug('handling call type ' + this.calltype + ' from ajax');
2152 var t = "get" + this.calltype;
2153 bkeeping.send_bkexpr(t, this.command.parse_vars(this.pars), handleResponse.bind(this));
2159 setDefaultCurrency: function(profileDetailObj)
2162 bkeeping.DEFAULT_CURRENCY = profileDetailObj["root"]["value"];
2166 ListCommand: function(listtype, callback, optJournalid)
2168 this.listtype = listtype;
2169 this.callback = callback;
2170 this.command = "";
2171 this.type = "";
2172 this.pars = { username: bkeeping.ACTIVE_SESSION.loggedInUserid };
2173 switch (this.listtype)
2175 case "accounts":
2176 this.command = LISTACCOUNTS_COMMAND;
2177 this.type = bkeeping.AccountList;
2178 break;
2179 case "journals":
2180 this.command = LISTJOURNALS_COMMAND;
2181 this.type = bkeeping.JournalList;
2182 this.pars["journal"] = optJournalid;
2183 break;
2184 case "entries":
2185 this.command = LISTENTRIES_COMMAND;
2186 this.type = bkeeping.EntryList;
2187 this.pars["journal"] = bkeeping["_displayed_journal"];
2188 break;
2189 case "entry":
2190 this.command = LISTENTRY_COMMAND;
2191 this.type = bkeeping.EntryList;
2192 this.pars["journal"] = bkeeping["_displayed_journal"];
2193 this.pars["entry"] = optJournalid;
2194 break;
2195 default:
2196 alert("Unknown list: " + this.listtype);
2197 break;
2200 function isSuccess(jsonData)
2202 return (jsonData && jsonData[this.listtype]);
2205 var getFailureReason = bkeeping.getGenericFailureReason;
2208 function handleResponse(jsonData, textStatus, cached)
2211 if (cached || isSuccess.call(this, jsonData))
2213 var r;
2214 if (cached && (cached == 'true'))
2216 r = new this.type({ "children" : jsonData });
2218 else
2220 r = new this.type(jsonData[this.listtype]);
2221 bkeeping.debug("Add to cache as list in ListCommand: " + this.listtype);
2222 bkeeping.debug(r);
2223 bkeeping.addToCacheAsList(this.listtype, r);
2225 return this.callback(r);
2227 else if( bkeeping.isAauthFailure(jsonData) ) {
2229 var msg = jsonData["logs"]["children"][0]["log"]["children"][0]["logMessages"]["children"][0]["logMessage"]["value"];
2230 //alert(msg);
2232 var rootURL = document.URL.substring( 0, document.URL.lastIndexOf("accounts.html") );
2234 setTimeout("document.location.href='"+ rootURL +"';", 0);
2237 else
2239 // show a UI for login failure:
2240 var reason = getFailureReason(jsonData);
2241 alert("Failed to get list " + this.listtype + ". Reason: " + reason);
2245 this.run = function()
2247 if ( (bkeeping.cache[this.listtype] != undefined) && (bkeeping.cache[this.listtype].length > 0) )
2249 bkeeping.debug('handling list type ' + this.listtype + ' from CACHE');
2250 handleResponse.call(this, bkeeping.cache[this.listtype], 'success', 'true');
2252 else
2254 bkeeping.debug('handling list type ' + this.listtype + ' from AJAX');
2255 var t = "list" + this.listtype;
2256 bkeeping.send_bkexpr(t, this.command.parse_vars(this.pars), handleResponse.bind(this));
2262 LoadCommand: function(listtype, callback, optJournalid)
2264 this.listtype = listtype;
2265 this.callback = callback;
2266 this.command = "";
2267 this.type = "";
2268 this.pars = {username: bkeeping.ACTIVE_SESSION.loggedInUserid};
2269 switch (this.listtype)
2271 case "profileDetail":
2272 this.command = LOADDEFAULTCURRENCY_COMMAND;
2273 this.type = bkeeping.DefaultCurrency;
2274 break;
2275 default:
2276 alert("Unknown list: " + this.listtype);
2277 break;
2280 function isSuccess(jsonData)
2282 return (jsonData && jsonData[this.listtype]);
2285 var getFailureReason = bkeeping.getGenericFailureReason;
2287 function handleResponse(jsonData, textStatus)
2289 if (isSuccess.call(this, jsonData))
2291 this.callback(new this.type(jsonData[this.listtype]));
2293 else
2295 // show a UI for login failure:
2296 var reason = getFailureReason(jsonData);
2297 alert("Failed to get list " + this.listtype + ". Reason: " + reason);
2301 this.run = function()
2303 var t = "list" + this.listtype;
2304 bkeeping.send_bkexpr(t, this.command.parse_vars(this.pars), handleResponse.bind(this));
2309 /* pars is an object containing the data to be added */
2310 AddCommand: function(type, callback, pars)
2312 // pars for account: ${aid} ${atype} ${aname} ${acw} ${username}
2313 // pars for journal: ${jid} ${jname} ${jtype} ${jbalance} ${username}
2314 // pars for entry: ${eid} ${cc} ${edid} ${edamount} ${aid} ${ecid} ${ecamount} ${username} ${jid}
2315 this.type = type;
2316 this.callback = callback;
2317 this.command = "";
2318 this.pars = pars;
2319 switch (this.type)
2321 case "account":
2322 this.command = ADDACCOUNT_COMMAND;
2324 this.type = "accounts"; // special case where what's returned will be an 'accounts'
2325 break;
2326 case "journals":
2327 this.command = ADDJOURNAL_COMMAND;
2328 break;
2329 case "entry":
2330 this.command = ADDENTRY_COMMAND;
2331 break;
2332 default:
2333 alert("Unknown add: " + this.type);
2334 break;
2337 function isSuccess(jsonData)
2340 return (jsonData && ( jsonData["logMessages"] == undefined ) );
2344 var getFailureReason = bkeeping.getGenericFailureReason;
2346 function handleResponse(jsonData, textStatus)
2348 if (isSuccess.call(this, jsonData))
2350 return this.callback(jsonData);
2352 else if( bkeeping.isAauthFailure(jsonData) ) {
2354 var msg = jsonData["logs"]["children"][0]["log"]["children"][0]["logMessages"]["children"][0]["logMessage"]["value"];
2355 //alert(msg);
2357 var rootURL = document.URL.substring( 0, document.URL.lastIndexOf("accounts.html") );
2359 setTimeout("document.location.href='"+ rootURL +"';", 0);
2362 else
2364 // show a UI for login failure:
2365 var reason = getFailureReason(jsonData);
2366 alert("Failed to add " + this.type + ". Reason: " + reason);
2370 this.run = function()
2372 bkeeping.send_bkexpr("add" + this.type, this.command.parse_vars(pars), handleResponse.bind(this));
2376 RemoveCommand: function(type, callback, pars)
2378 // pars for account: ${aid}
2379 // pars for journal: ${jid}
2380 // pars for entry: ${eid}
2381 this.type = type;
2382 this.callback = callback;
2383 this.command = "";
2384 switch (this.type)
2386 case "account":
2387 this.command = REMOVEACCOUNT_COMMAND;
2388 break;
2389 case "journals":
2390 this.command = REMOVEJOURNAL_COMMAND;
2391 break;
2392 case "entry":
2393 this.command = REMOVEENTRY_COMMAND;
2394 break;
2395 default:
2396 alert("Unknown remove: " + this.type);
2397 break;
2400 function isSuccess(jsonData)
2402 return (jsonData && jsonData[this.type]);
2405 var getFailureReason = bkeeping.getGenericFailureReason;
2407 function handleResponse(jsonData, textStatus)
2409 if (isSuccess.call(this, jsonData))
2411 return this.callback(jsonData);
2413 else
2415 // show a UI for login failure:
2416 var reason = getFailureReason(jsonData);
2417 alert("Failed to add " + this.type + ". Reason: " + reason);
2421 this.run = function()
2423 bkeeping.send_bkexpr("remove" + this.type, this.command.parse_vars( pars ), handleResponse.bind(this) );
2427 /* pars is an object containing the data to be added */
2428 EditCommand: function(type, callback, pars)
2430 // pars for account: ${aid} ${atype} ${aname} ${acw} ${username}
2431 // pars for journal: ${jid} ${jname} ${jtype} ${jbalance} ${username}
2432 // pars for entry: ${eid} ${cc} ${edid} ${edamount} ${aid} ${ecid} ${ecamount} ${username} ${jid}
2433 this.type = type;
2434 this.callback = callback;
2435 this.command = "";
2436 this.pars = pars;
2437 switch (this.type)
2439 case "account":
2440 this.command = UPDATEACCOUNT_COMMAND;
2441 break;
2442 case "journal":
2443 this.command = UPDATEJOURNAL_COMMAND;
2444 break;
2445 case "entry":
2446 this.command = UPDATEENTRY_COMMAND;
2447 break;
2448 default:
2449 alert("Unknown edit: " + this.type);
2450 break;
2453 function isSuccess(jsonData)
2455 return (jsonData && ( jsonData["logMessages"] == undefined ) );
2458 var getFailureReason = bkeeping.getGenericFailureReason;
2460 function handleResponse(jsonData, textStatus)
2462 if (isSuccess.call(this, jsonData))
2464 return this.callback(jsonData);
2466 else if( bkeeping.isAauthFailure(jsonData) ) {
2468 var msg = jsonData["logs"]["children"][0]["log"]["children"][0]["logMessages"]["children"][0]["logMessage"]["value"];
2469 //alert(msg);
2471 var rootURL = document.URL.substring( 0, document.URL.lastIndexOf("accounts.html") );
2473 setTimeout("document.location.href='"+ rootURL +"';", 0);
2476 else
2478 // show a UI for login failure:
2479 var reason = getFailureReason(jsonData);
2480 alert("Failed to add " + this.type + ". Reason: " + reason);
2484 this.run = function()
2486 bkeeping.send_bkexpr("edit" + this.type, this.command.parse_vars(pars), handleResponse.bind(this));
2489 /* ******** */
2491 /* ajax */
2492 error: function(xmlHttpRequest, textStatus, errorThrown)
2494 bkeeping.raise('There was a problem with the server request: ' + textStatus + '/' + errorThrown);
2497 success: function(data, textStatus)
2499 bkeeping.log('genericSuccess');
2502 complete: function(xmlHttRequest, textStatus)
2504 bkeeping.log('genericComplete');
2507 // caveat: this function does NOT handle xml with textnodes mixed in with other element children
2508 xmlRecurse: function(node, context)
2510 // squeeze the attributes and add to the context
2511 var attributes = node.attributes;
2512 if (attributes)
2514 for (var i = 0; i < attributes.length; i++)
2516 var n = attributes[i].name;
2517 var v = attributes[i].nodeValue;
2518 context[n] = v;
2521 // recurse:
2522 var children = node.childNodes;
2523 if (children)
2525 if (children.length > 0)
2527 if ((children.length == 1) && (children[0].nodeType == 3)) // 3 is text node
2529 context["value"] = children[0].textContent;
2531 else
2533 if (!context["children"])
2535 context["children"] = [];
2537 for (var j = 0; j < children.length; j++)
2539 var p = {};
2540 p[children[j].nodeName] = {};
2541 context["children"].push(p);
2542 this.xmlRecurse(children[j], p[children[j].nodeName]);
2547 return context;
2550 // caveat: this function does NOT handle xml with textnodes mixed in with other element children
2551 xmlToJson: function(data, type)
2553 if (!data)
2555 return "";
2557 var xmlDoc;
2558 var json = {};
2559 try //Internet Explorer
2561 xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
2562 xmlDoc.async = "false";
2563 xmlDoc.loadXML(data);
2565 catch(e)
2567 var parser = new DOMParser();
2568 //xmlDoc = parser.parseFromString(data, "text/xml");
2569 xmlDoc = data;
2572 // convert to json (simplified greatly)
2573 // we'll take advantage of the fact that all the data being passed around is linked to attributes
2574 // and thus we'll go ahead and make our json like this:
2575 // <name1 attr1=val attr2=val...>
2576 // <name2 attr3=val attr4=val...>
2577 // <name2 attr5=val attr6=val...>
2578 // <name3 attr7=val attr8=val...>
2579 // </name1>
2581 // becomes this:
2582 // {
2583 // "name1":
2584 // {
2585 // "attr1": val,
2586 // "attr2": val,
2587 // "value": null, // special value -- the text node child of this ele, exclusive with children
2588 // "children":
2589 // [
2590 // {
2591 // "name2":
2592 // {
2593 // "attr3": val,
2594 // "attr4": val
2595 // }
2596 // },
2597 // {
2598 // "name2":
2599 // {
2600 // "attr5": val,
2601 // "attr6": val
2602 // }
2603 // },
2604 // {
2605 // "name3":
2606 // {
2607 // "attr7": val,
2608 // "attr8": val
2609 // }
2610 // }
2611 // ]
2612 // }
2613 // }
2614 var root = xmlDoc.firstChild;
2615 json[root.nodeName] = {};
2616 //json[root.nodeName] = this.xmlRecurse(root, json[root.nodeName]);
2617 json[root.nodeName] = bkeeping.xmlRecurse(root, json[root.nodeName]);
2618 return json;
2621 zzz: function (xhr, ajaxOptions, thrownError) {
2622 alert(xhr.status);
2623 alert(thrownError);
2625 send_bkexpr: function(bkexprname, expr, callback, optErrorCallback, optCompleteCallback)
2627 if ( MOCK_DATA == "true" || MOCK_DATA == true )
2629 var r;
2630 if (MOCK_DATA_FAILURES_MODE)
2632 r = MOCK_DATA["failures"][bkexprname];
2634 else
2636 r = MOCK_DATA["successes"][bkexprname];
2638 var response = r.response.parse_vars(_LAST_PARSED_DICT);
2639 var type = r.type;
2640 var textStatus = r.textStatus;
2641 var sanitizedData = this.xmlToJson(response, type);
2642 callback(sanitizedData, textStatus);
2644 else
2646 var url = "/webkell/webkell";
2647 if (bkexprname == "register")
2649 url = "/webkell/authenticate";
2651 $.ajax(
2653 success: callback || bkeeping.success,
2654 error: optErrorCallback || bkeeping.error,
2655 complete: optCompleteCallback || bkeeping.complete,
2656 data: {"bkexpr": expr},
2657 dataFilter: this.xmlToJson,
2658 dataType: "xml",
2659 type: "POST",
2660 url: url
2665 /* **** */
2667 /* initialization */
2668 displayLoginBlurb: function()
2670 $("#loggedinnote").css('display', "block");
2671 var template = $("#loggedinnote").html();
2672 $("#loggedinnote").html(template.parse_vars({"name": bkeeping.ACTIVE_SESSION.loggedInUserid}));
2675 $("#loginholder").css("visibility", "hidden");
2677 catch(e)
2679 // element not there
2683 page_loader_init: function()
2685 if (window.page_loader)
2687 page_loader(); // defined in e.g. accounts.html, or any other UI page
2691 page_init: function()
2693 var path = document.location.href.match(/^http:\/\/[^\/]+\/(.*)$/);
2694 if (path)
2696 path = path[1];
2697 // cut off querystring and hash
2698 if (path.indexOf("?") > -1)
2700 path = path.substring(0, path.indexOf("?"));
2702 if (path.indexOf("#") > -1)
2704 path = path.substring(0, path.indexOf("#"));
2706 // if it's under webkell, remove that:
2707 if (path.indexOf("webkell") == 0)
2709 path = path.substring(8);
2712 else
2714 path = "home";
2717 if ((path == "") || (path == "/") || (path == "index.html"))
2719 path = "home";
2722 switch (path)
2724 case "home":
2725 // this is not exactly efficient, but we'll do this til we're on a proper server-side template engine
2726 var mode = document.location.href.substring(document.location.href.indexOf("mode=") + 5).match(/^(\w+)(.*)$/);
2727 if (!mode)
2729 mode = "home";
2731 else
2733 mode = mode[1];
2735 $("#contentbox").load(mode + ".html");
2736 if (mode == "userprofile")
2738 var u = new bkeeping.GetprofileCommand(bkeeping.loadProfile).run();
2740 break;;
2741 case "accounts.html":
2742 // do nothing, this UI is generated dynamically
2743 break;;
2744 default:
2745 bkeeping.debug("Not a known page: " + path);
2746 break;;
2748 if (bkeeping.ACTIVE_SESSION.loaded)
2750 bkeeping.displayLoginBlurb();
2753 var flag = document.location.href.indexOf("flag=");
2754 if (flag != -1)
2756 var value = document.location.href.substring(flag);
2757 if (value.indexOf("canlogin") != -1)
2759 bkeeping.showDialog("You have registered successfully. You can now log in.")
2761 if (value.indexOf("changessaved") != -1)
2763 bkeeping.showDialog("Your changes have been saved.");
2768 init: function()
2770 bkeeping.ACTIVE_SESSION = new bkeeping.Session(); // load from cookie if present
2771 if (!$("#bkeeping_dialog").length)
2773 $("body").append(DIALOG_HOLDER);
2775 //bkeeping.page_init(); // initializations related to the page we're on
2776 //bkeeping.page_loader_init(); // initializations defined on the page we're on
2779 unload: function()
2783 /* ************ */
2786 /* prototypes */
2787 bkeeping.AccountList.prototype = new bkeeping.Lister();
2788 bkeeping.JournalList.prototype = new bkeeping.Lister();
2789 bkeeping.EntryList.prototype = new bkeeping.Lister();
2791 bkeeping.DefaultCurrency.prototype = new bkeeping.Lister();
2794 /* ********** */
2796 $(document).ready(bkeeping.init);
2797 $(window).unload(bkeeping.unload);
2799 var MOCK_DATA =
2801 successes:
2803 login:
2805 response: "<userSession xmlns='com/interrupt/bookkeeping/users' id='c4e2e5344b91c6c1-1c82792712270d305b2-7ffd' groupid='${username}' userid='${username}' />",
2806 type: "xml",
2807 textStatus: "OK"
2810 register:
2812 response: "<user xmlns='com/interrupt/bookkeeping/users' id='${username}' username='${username}' password='${password}' logintimeout='600000' accountLevel='FREE' defaultGroup='webkell' authenticated=''>" +
2813 "<profileDetails xmlns='com/interrupt/bookkeeping/users' id='user.details'>" +
2814 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='firstname' name='first.name' value='${firstname}'/>" +
2815 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='lastname' name='last.name' value='${lastname}'/>" +
2816 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='email' name='email' value='${email}'/>" +
2817 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='country' name='country' value='${country}'/>" +
2818 "</profileDetails>" +
2819 "</user>",
2820 type: "xml",
2821 textStatus: "OK"
2824 getprofile:
2826 response: "<user xmlns='com/interrupt/bookkeeping/users' id='${username}' username='${username}' password='${password}' logintimeout='600000' accountLevel='FREE' defaultGroup='webkell' authenticated=''>" +
2827 "<profileDetails xmlns='com/interrupt/bookkeeping/users' id='user.details'>" +
2828 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='username' name='user.name' value='ed'/>" +
2829 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='password' name='password' value='foo'/>" +
2830 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='firstname' name='first.name' value='Eddie'/>" +
2831 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='lastname' name='last.name' value='Abrams'/>" +
2832 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='email' name='email' value='ed@abra.ms'/>" +
2833 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='country' name='country' value='Canada'/>" +
2834 "</profileDetails>" +
2835 "</user>",
2836 type: "xml",
2837 textStatus: "OK"
2840 updateprofile:
2842 response: "<user xmlns='com/interrupt/bookkeeping/users' id='${username}' username='${username}' password='${password}' logintimeout='600000' accountLevel='FREE' defaultGroup='webkell' authenticated=''>" +
2843 "<profileDetails xmlns='com/interrupt/bookkeeping/users' id='user.details'>" +
2844 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='username' name='user.name' value='${username}'/>" +
2845 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='password' name='password' value='${password}'/>" +
2846 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='firstname' name='first.name' value='${firstname}'/>" +
2847 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='lastname' name='last.name' value='${lastname}'/>" +
2848 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='email' name='email' value='${email}'/>" +
2849 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='country' name='country' value='${country}'/>" +
2850 "</profileDetails>" +
2851 "</user>",
2852 type: "xml",
2853 textStatus: "OK"
2856 listaccounts:
2858 response: "<accounts id='main.accounts'>" +
2859 "<account id='05' name='bankaccount' type='asset' counterWeight='debit' currency=''/>" +
2860 "<account id='06' name='creditcard' type='liability' counterWeight='credit' currency=''/>" +
2861 "</accounts>",
2862 type: "xml",
2863 textStatus: "OK"
2866 listjournals:
2868 response: "<journals xmlns:journal='com/interrupt/bookkeeping/journal' id='main.journals'>" +
2869 "<journal id='generalledger' name='generalledger' type='' balance=''>" +
2870 "<entries id='main.entries'>" +
2871 "<entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
2872 "<debit xmlns:account='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
2873 "<credit xmlns:account='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
2874 "</entry>" +
2875 "</entries>" +
2876 "</journal>" +
2877 "<journal id='otherledger' name='otherledger' type='' balance=''>" +
2878 "<entries id='main.entries'>" +
2879 "<entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
2880 "<debit xmlns:account='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
2881 "<credit xmlns:account='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
2882 "</entry>" +
2883 "</entries>" +
2884 "</journal>" +
2885 "</journals>",
2886 type: "xml",
2887 textStatus: "OK"
2890 listentries:
2892 response: "<entries id='main.entries'>" +
2893 "<entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
2894 "<debit xmlns:account='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
2895 "<credit xmlns:account='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
2896 "</entry>" +
2897 "</entries>",
2898 type: "xml",
2899 textStatus: "OK"
2902 addaccount:
2904 response: "<account id='${aid}' name='${aname}' type='${atype}' counterWeight='${acn}' currency='${cc}'/>",
2905 type: "xml",
2906 textStatus: "OK"
2909 removeaccount:
2911 response: "",
2912 type: "xml",
2913 textStatus: "OK"
2916 editaccount:
2918 response: "<account id='${aid}' name='${aname}' type='${atype}' counterWeight='${acw}' currency='${cc}'/>",
2919 type: "xml",
2920 textStatus: "OK"
2923 addjournal:
2925 response: "<journal id='${jid}' name='${jname}' type='${jtype}' balance='${jbalance}'>" +
2926 // "<entries id='main.entries'>" +
2927 // "<entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
2928 // "<debit xmlns:account='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
2929 // "<credit xmlns:account='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
2930 // "</entry>" +
2931 // "</entries>" +
2932 "</journal>",
2933 type: "xml",
2934 textStatus: "OK"
2937 removejournal:
2939 response: "",
2940 type: "xml",
2941 textStatus: "OK"
2944 editjournal:
2946 response: "<journal id='${jid}' name='${jname}' type='${jtype}' balance='${jbalance}'>" +
2947 // "<entries id='main.entries'>" +
2948 // "<entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
2949 // "<debit xmlns:account='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
2950 // "<credit xmlns:account='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
2951 // "</entry>" +
2952 // "</entries>" +
2953 "</journal>",
2954 type: "xml",
2955 textStatus: "OK"
2958 addentry:
2960 response: "<entry id='${eid}' entrynum='' state='' journalid='${jid}' date='' currency='${cc}'>" +
2961 "<debit xmlns:account='com/interrupt/bookkeeping/account' id='${edid}' amount='${edamount}' entryid='${eid}' accountid='${aid}' account='' currency='${cc}'/>" +
2962 "<credit xmlns:account='com/interrupt/bookkeeping/account' id='${ecid}' amount='${ecamount}' entryid='${eid}' accountid='${aid}' account='' currency='${cc}'/>" +
2963 "</entry>",
2964 type: "xml",
2965 textStatus: "OK"
2968 removeentry:
2970 response: "",
2971 type: "xml",
2972 textStatus: "OK"
2975 editentry:
2977 response: "<entry id='${eid}' entrynum='' state='' journalid='${jid}' date='' currency='${cc}'>" +
2978 "<debit xmlns:account='com/interrupt/bookkeeping/account' id='${edid}' amount='${edamount}' entryid='${eid}' accountid='${aid}' account='' currency='${cc}'/>" +
2979 "<credit xmlns:account='com/interrupt/bookkeeping/account' id='${ecid}' amount='${ecamount}' entryid='${eid}' accountid='${aid}' account='' currency='${cc}'/>" +
2980 "</entry>",
2981 type: "xml",
2982 textStatus: "OK"
2985 failures:
2987 login:
2989 response: "<logs>" +
2990 "<log level=''>" +
2991 "<logMessages>" +
2992 "<logMessage>Failed to login.</logMessage>" +
2993 "</logMessages>" +
2994 "</log>" +
2995 "</logs>",
2996 type: "xml",
2997 textStatus: "OK"
3000 register:
3002 response: "<logs>" +
3003 "<log level=''>" +
3004 "<logMessages>" +
3005 "<logMessage>Failed to register.</logMessage>" +
3006 "</logMessages>" +
3007 "</log>" +
3008 "</logs>",
3009 type: "xml",
3010 textStatus: "OK"
3013 getprofile:
3015 response: "<logs>" +
3016 "<log level=''>" +
3017 "<logMessages>" +
3018 "<logMessage>Failed to get user profile.</logMessage>" +
3019 "</logMessages>" +
3020 "</log>" +
3021 "</logs>",
3022 type: "xml",
3023 textStatus: "OK"
3026 updateprofile:
3028 response: "<logs>" +
3029 "<log level=''>" +
3030 "<logMessages>" +
3031 "<logMessage>Failed to update user profile.</logMessage>" +
3032 "</logMessages>" +
3033 "</log>" +
3034 "</logs>",
3035 type: "xml",
3036 textStatus: "OK"
3039 listaccounts:
3041 response: "<logs>" +
3042 "<log level=''>" +
3043 "<logMessages>" +
3044 "<logMessage>Failed to get accounts list.</logMessage>" +
3045 "</logMessages>" +
3046 "</log>" +
3047 "</logs>",
3048 type: "xml",
3049 textStatus: "OK"
3052 listjournals:
3054 response: "<logs>" +
3055 "<log level=''>" +
3056 "<logMessages>" +
3057 "<logMessage>Failed to get journals list.</logMessage>" +
3058 "</logMessages>" +
3059 "</log>" +
3060 "</logs>",
3061 type: "xml",
3062 textStatus: "OK"
3065 listentries:
3067 response: "<logs>" +
3068 "<log level=''>" +
3069 "<logMessages>" +
3070 "<logMessage>Failed to get entries list.</logMessage>" +
3071 "</logMessages>" +
3072 "</log>" +
3073 "</logs>",
3074 type: "xml",
3075 textStatus: "OK"
3078 addaccount:
3080 response: "<logs>" +
3081 "<log level=''>" +
3082 "<logMessages>" +
3083 "<logMessage>Failed to add account.</logMessage>" +
3084 "</logMessages>" +
3085 "</log>" +
3086 "</logs>",
3087 type: "xml",
3088 textStatus: "OK"
3091 removeaccount:
3093 response: "<logs>" +
3094 "<log level=''>" +
3095 "<logMessages>" +
3096 "<logMessage>Failed to remove account.</logMessage>" +
3097 "</logMessages>" +
3098 "</log>" +
3099 "</logs>",
3100 type: "xml",
3101 textStatus: "OK"
3104 editaccount:
3106 response: "<logs>" +
3107 "<log level=''>" +
3108 "<logMessages>" +
3109 "<logMessage>Failed to edit account.</logMessage>" +
3110 "</logMessages>" +
3111 "</log>" +
3112 "</logs>",
3113 type: "xml",
3114 textStatus: "OK"
3117 addjournal:
3119 response: "<logs>" +
3120 "<log level=''>" +
3121 "<logMessages>" +
3122 "<logMessage>Failed to add journal.</logMessage>" +
3123 "</logMessages>" +
3124 "</log>" +
3125 "</logs>",
3126 type: "xml",
3127 textStatus: "OK"
3130 removejournal:
3132 response: "<logs>" +
3133 "<log level=''>" +
3134 "<logMessages>" +
3135 "<logMessage>Failed to remove journal.</logMessage>" +
3136 "</logMessages>" +
3137 "</log>" +
3138 "</logs>",
3139 type: "xml",
3140 textStatus: "OK"
3143 editjournal:
3145 response: "<logs>" +
3146 "<log level=''>" +
3147 "<logMessages>" +
3148 "<logMessage>Failed to edit journal.</logMessage>" +
3149 "</logMessages>" +
3150 "</log>" +
3151 "</logs>",
3152 type: "xml",
3153 textStatus: "OK"
3156 addentry:
3158 response: "<logs>" +
3159 "<log level=''>" +
3160 "<logMessages>" +
3161 "<logMessage>Failed to add entry.</logMessage>" +
3162 "</logMessages>" +
3163 "</log>" +
3164 "</logs>",
3165 type: "xml",
3166 textStatus: "OK"
3169 removeentry:
3171 response: "<logs>" +
3172 "<log level=''>" +
3173 "<logMessages>" +
3174 "<logMessage>Failed to remove entry.</logMessage>" +
3175 "</logMessages>" +
3176 "</log>" +
3177 "</logs>",
3178 type: "xml",
3179 textStatus: "OK"
3182 editentry:
3184 response: "<logs>" +
3185 "<log level=''>" +
3186 "<logMessages>" +
3187 "<logMessage>Failed to edit entry.</logMessage>" +
3188 "</logMessages>" +
3189 "</log>" +
3190 "</logs>",
3191 type: "xml",
3192 textStatus: "OK"
3197 var _TEST_AC = "<accounts id='main.accounts'>" +
3198 "<account id='05' name='bankaccount' type='asset' counterWeight='debit' currency=''/>" +
3199 "<account id='06' name='creditcard' type='liability' counterWeight='credit' currency=''/>" +
3200 "</accounts>";
3201 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'>" +
3202 "<currency:currencies id='main.currencies' default='CDN'>" +
3203 "<currency:currency id='CDN' name='Canadian Dollar'/>" +
3204 "<currency:currency id='USD' name='US Dollar'/>" +
3205 "<currency:currency id='BP' name='British Pound'/>" +
3206 "<currency:currency id='EUR' name='Euoropean Euro'/>" +
3207 "<currency:currency id='JPN' name='Japanese Yen'/>" +
3208 "</currency:currencies>" +
3209 "<accounts xmlns='com/interrupt/bookkeeping/account' id='main.accounts'>" +
3210 "<account xmlns='com/interrupt/bookkeeping/account' type='asset' id='' name='' counterWeight='debit'/>" +
3211 "</accounts>" +
3212 "<journal:journals id='main.journals'>" +
3213 "<journal:journal id='generalledger' name='generalledger' type='' balance=''>" +
3214 "<journal:entries id='main.entries'>" +
3215 "<journal:entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
3216 "<debit xmlns='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
3217 "<credit xmlns='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
3218 "</journal:entry>" +
3219 "</journal:entries>" +
3220 "</journal:journal>" +
3221 "</journal:journals>" +
3222 "</bookkeeping>";