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]) : ("");
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()
33 return Array.prototype.slice.call(a);
35 if ((arguments.length < 2) && (typeof arguments[0] == "undefined"))
39 var __method = this, args = _$A(arguments), object = args.shift();
42 return __method.apply(object, args.concat(_$A(arguments)));
48 var MOCK_DATA = false;
49 var MOCK_DATA_FAILURES_MODE = false;
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' />" +
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}' />" +
78 "</user> , user -returninput true" +
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}'/>" +
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' ] `);";
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);";
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((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals']/journal[@id='${jid}]`)" +
121 "<journal xmlns=\"com/interrupt/bookkeeping/account\" id='${jid}' name='${jname}' type='${jtype}' balance='${jbalance}'/>" +
122 "commit((`/system[@id='main.system']/groups[@id=\"main.groups\"]/group[@id=\"${username}\"]/bookkeeping[@id='main.bookkeeping']/journals[@id='main.journals']/journal[@id='${jid}']`) @updatedJournal);";
126 * JOURNAL 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='' currency='${cc}'>" +
130 "${debitsANDcredits}" +
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 ((`/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']`) entry -id ${eid});" +
135 "commit((`/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']`) @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}" +
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);";
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">${aname}</div>' +
151 '<div class="category_column left_column">${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>' +
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:bkeeping.deleteJournal(\'${rowid}\');"><img border="0" src="images/deleterow.jpg"/></a></div>' +
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.deleteEntry(\'${rowid}\');"><img border="0" src="images/deleterow.jpg"/></a></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:bkeeping.zzz(\'${rowid}\');"><img border="0" src="images/deleterow.jpg"/></a></div>' +
189 var AE_ACCOUNT = ["Add/Edit Account", '<table class="formtable">' +
195 '<input type="text" name="account_name" id="account_name" class="data_field" value="${aname}" />' +
203 '<select name="account_type" id="account_type" class="data_field">' +
210 var AE_JOURNAL = ["Add/Edit Journal", '<table class="formtable">' +
216 '<input type="text" name="journal_name" id="journal_name" class="data_field" />' +
224 '<select name="journal_type" id="journal_type" class="data_field">' +
225 '<option>default</option>' +
234 '<input type="text" name="journal_balance" id="journal_balance" class="data_field" />' +
239 var AE_ENTRY = ["Add/Edit Entry", '<table class="formtable">' +
240 '<input type="hidden" name="entryid" value="${entryid}" />' +
244 '<span class="smallformtext">Currency:</span> ' +
245 '<select name="entry_cc" id="entry_cc" class="data_field">' +
246 '<option value="CDN" ${curr_cdn} >Canadian Dollar</option>' +
247 '<option value="USD" ${curr_usd} >US Dollar</option>' +
248 '<option value="BP" ${curr_bp} >British Pound</option>' +
249 '<option value="EUR" ${curr_eur} >European Euro</option>' +
250 '<option value="JPN" ${curr_jpn} >Japanese Yen</option>' +
256 '<span class="smallformtext">Amount:</span> <input type="text" size="6" name="entry_ecamount" id="entry_ecamount" class="data_field" value="${evalue}" > ' +
261 '<span class="smallformtext">Account:</span> ' +
262 '<select name="entry_caid" id="entry_caid" class="data_field">' +
269 '<span class="smallformtext">Type:</span> ' +
270 '<select name="entry_ctype" id="entry_ctype" class="data_field">' +
271 '<option value="debit" ${entry_dtype} >debit</option><option value="credit" ${entry_ctype} >credit</option>' +
280 var DIALOG_HOLDER = "<div id='bkeeping_dialog' class='dialog'>Dialog default text.</div>";
281 var _DIALOG_FIRST = true;
288 ACTIVE_SESSION: null,
289 DEFAULT_CURRENCY: null,
292 //** initialise entry UI with debit & credit lists
305 debug: function(message)
311 console.log(message);
320 log: function(message)
324 console.log(message);
332 raise: function(message)
338 modalDialogScreen: function(showhide)
340 var m = $("#modal_screen");
341 if (showhide == "show")
343 m.css('display', 'block');
347 m.css('display', 'none');
351 showModalDialog: function()
353 bkeeping.modalDialogScreen("show");
354 bkeeping.showDialog.apply(this, arguments);
357 showDialog: function()
359 bkeeping.setDialogContent.apply(this, arguments);
360 bkeeping.openDialog();
363 setDialogContent: function()
365 var args = Array.prototype.slice.call(arguments);
366 var content = args.shift();
367 if (content instanceof Function)
369 $("#bkeeping_dialog").html(content.apply(this, args));
373 $("#bkeeping_dialog").html(content);
377 openDialog: function()
381 _DIALOG_FIRST = false;
382 $("#bkeeping_dialog").dialog({ close: bkeeping.modalDialogScreen.bind(this, "hide") });
386 $("#bkeeping_dialog").dialog("open");
391 toggleAccountsOpen: function()
394 if (bkeeping._a_open)
396 bkeeping._a_open = false;
401 bkeeping._a_open = true;
404 $("#accounts_ui").animate({ left: newleft }, { duration: 750, easing: "swing", complete: function() {}, queue: false });
409 toggleJournalsOpen: function()
411 var newleft = "436px";
412 if (bkeeping._j_open)
414 bkeeping._j_open = false;
419 bkeeping._j_open = true;
421 $("#journals_ui").animate({ left: newleft }, { duration: 750, easing: "swing", complete: function() {}, queue: false });
425 /* cookie functions: http://techpatterns.com/downloads/javascript_cookies.php */
426 setCookie: function(name, value, expires, path, domain, secure)
428 // set time, it's in milliseconds
429 var today = new Date();
430 today.setTime(today.getTime());
432 // EdA: override this to default to base path instead of "" (easier for simplistic auth systems)
440 expires = expires * 1000 * 60 * 60 * 24;
442 var expires_date = new Date(today.getTime() + (expires));
443 document.cookie = name + "=" + escape(value) +
444 ((expires) ? ";expires=" + expires_date.toGMTString() : "") +
445 ((path) ? ";path=" + path : "") +
446 ((domain) ? ";domain=" + domain : "") +
447 ((secure) ? ";secure" : "" );
450 getCookie: function(name)
452 // first we'll split this cookie up into name/value pairs
453 // note: document.cookie only returns name=value, not the other components
454 var a_all_cookies = document.cookie.split(';');
455 var a_temp_cookie = '';
456 var cookie_name = '';
457 var cookie_value = '';
458 var b_cookie_found = false; // set boolean t/f default f
460 for (var i = 0; i < a_all_cookies.length; i++)
462 // now we'll split apart each name=value pair
463 a_temp_cookie = a_all_cookies[i].split('=');
465 // and trim left/right whitespace while we're at it
466 cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, '');
468 // if the extracted name matches passed name
469 if (cookie_name == name)
471 b_cookie_found = true;
472 // we need to handle case where cookie has no value but exists (no = sign, that is):
473 if (a_temp_cookie.length > 1)
475 cookie_value = unescape(a_temp_cookie[1].replace(/^\s+|\s+$/g, ''));
477 // note that in cases where cookie is initialized but no value, null is returned
481 a_temp_cookie = null;
490 deleteCookie: function(name, path, domain)
492 if (this.getCookie(name))
494 // EdA: override this to default to base path instead of "" (easier for simplistic auth systems)
496 document.cookie = name + "=" +
497 ((path) ? ";path=" + path : "") +
498 ((domain) ? ";domain=" + domain : "" ) +
499 ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
503 Session: function(sessionId, loggedInUserid, loggedInGroup)
505 this.cookieName = "_bk_session_js";
507 this.sessionId = sessionId || null;
508 this.loggedInUserid = loggedInUserid || null;
509 this.loggedInGroup = loggedInGroup || null;
511 this.update = function(sessionId, loggedInUserid, loggedInGroup)
513 this.sessionId = sessionId || null;
514 this.loggedInUserid = loggedInUserid || null;
515 this.loggedInGroup = loggedInGroup || null;
519 this.clear = function()
522 this.sessionId = null;
523 this.loggedInUserid = null;
524 this.loggedInGroup = null;
525 bkeeping.deleteCookie(this.cookieName);
529 function saveToSessionCookie()
531 bkeeping.setCookie(this.cookieName, this.sessionId + "," + this.loggedInUserid + "," + this.loggedInGroup);
534 this.save = saveToSessionCookie.bind(this);
536 function loadFromSessionCookie()
538 var cookievalues = bkeeping.getCookie(this.cookieName);
541 var vals = cookievalues.split(',');
542 this.sessionId = vals[0];
543 this.loggedInUserid = vals[1];
544 this.loggedInGroup = vals[2];
552 // try to load from cookie:
553 this.loaded = loadFromSessionCookie.call(this); // true if there was a session cookie, false otherwise
565 this.getProfileDetailsList = function()
567 return this.root["children"][0]["profileDetails"]["children"];
570 this.getUserid = function()
572 return this.root["id"];
575 this.getUsername = function()
577 return this.root["username"];
580 this.getPassword = function()
582 return this.root["password"];
585 this.getFirstname = function()
587 return this.root["children"][0]["profileDetails"]["children"][0]["profileDetail"].value;
590 this.getLastname = function()
592 return this.root["children"][0]["profileDetails"]["children"][1]["profileDetail"].value;
595 this.getEmail = function()
597 return this.root["children"][0]["profileDetails"]["children"][2]["profileDetail"].value;
600 this.getCountry = function()
602 return this.root["children"][0]["profileDetails"]["children"][3]["profileDetail"].value;
606 UserSession: function(root)
609 this.getSessionId = function()
611 return this.root["id"];
614 this.getGroup = function()
616 return this.root["groupid"];
619 this.getUser = function()
621 return this.root["userid"];
627 DefaultCurrency: function(root)
630 this.getDefaultCurrency = this.lister.bind(this, "profileDetail", bkeeping.DefaultCurrency);
631 this.getDefaultCurrencyRaw = this.rawlister.bind(this);
634 lister: function(nodename, _class)
636 if( this.root["children"] != null ) {
638 var c = this.root["children"];
642 for (var i = 0; i < c.length; i++) {
644 if( c[i][nodename] != undefined ) {
645 a.push(new _class(c[i][nodename]));
647 else if( c[i]["debit"] != undefined ) {
648 a.push( new bkeeping.Subentry( c[i]["debit"], "debit" ) );
650 else if( c[i]["credit"] != undefined ) {
651 a.push( new bkeeping.Subentry( c[i]["credit"], "credit" ) );
659 rawlister: function()
661 return this.root["children"];
666 this.lister = bkeeping.lister;
667 this.rawlister = bkeeping.rawlister;
670 AccountList: function(root)
673 this.getAccounts = this.lister.bind(this, "account", bkeeping.Account);
674 this.getAsList = this.getAccounts;
675 this.getAccountsRaw = this.rawlister.bind(this);
676 this.getAsRawList = this.getAccountsRaw;
679 Account: function(root)
683 this.getType = function()
685 return this.root['type'];
688 this.getId = function()
690 return this.root['id'];
693 this.getName = function()
695 return this.root['name'];
698 this.getCounterWeight = function()
700 return this.root['counterWeight'];
703 this.getBalance = function()
705 // XXX balance not in account structure, HELP
710 JournalList: function(root)
713 this.getJournals = this.lister.bind(this, "journal", bkeeping.Journal);
714 this.getAsList = this.getJournals;
715 this.getJournalsRaw = this.rawlister.bind(this);
716 this.getAsRawList = this.getJournalsRaw;
719 Journal: function(root)
723 this.getId = function()
725 return this.root["id"];
728 this.getName = function()
730 return this.root["name"];
733 this.getType = function()
735 return this.root["type"];
738 this.getBalance = function()
740 return this.root["balance"];
744 EntryList: function(root)
747 this.getEntries = this.lister.bind(this, "entry", bkeeping.Entry);
748 this.getAsRawList = this.rawlister.bind(this);
751 Entry: function(root)
755 this.getId = function()
757 return this.root["id"];
760 this.getEntrynum = function()
762 return this.root["entrynum"];
765 this.getState = function()
767 return this.root["state"];
770 this.getJournalid = function()
772 return this.root["journalid"];
775 this.getDate = function()
777 return this.root["date"];
780 this.getCurrency = function()
782 return this.root["currency"];
785 this.getSubentries = function(type, raw)
787 var d = this.root["children"];
789 for (var i = 0; i < d.length; i++)
799 a.push(new bkeeping.Subentry(d[i], type));
806 this.getDebits = function()
808 return this.getSubentries("debit");
811 this.getCredits = function()
813 return this.getSubentries("credit");
816 this.getDebitsRaw = function()
818 return this.getSubentries("debit", true);
821 this.getCreditsRaw = function()
823 return this.getSubentries("credit", true);
827 Subentry: function(root, type)
832 this.getId = function()
834 return this.root["id"];
837 this.getAmount = function()
839 return this.root["amount"];
842 this.getEntryid = function()
844 return this.root["entryid"];
847 this.getAccountid = function()
849 return this.root["accountid"];
852 this.getAccount = function()
854 return this.root["account"];
857 this.getCurrency = function()
859 return this.root["currency"];
867 var u = $("#u").val();
868 var p = $("#p").val();
869 var ret = new this.LoginCommand(u, p).run();
874 this.ACTIVE_SESSION.clear();
875 setTimeout('document.location.href = "index.html";', 2000);
880 var username = $("#username").val();
881 var password = $("#password").val();
882 var firstname = $("#firstname").val();
883 var lastname = $("#lastname").val();
884 var email = $("#email").val();
885 var country = $("#country").val();
886 var currency = $("#currency").val();
887 var ret = new this.RegisterCommand(username, password, firstname, lastname, email, country, currency).run();
890 editprofile: function()
892 var username = $("#username").val();
893 var password = $("#password").val();
894 var firstname = $("#firstname").val();
895 var lastname = $("#lastname").val();
896 var email = $("#email").val();
897 var country = $("#country").val();
898 var ret = new this.RegisterCommand(username, password, firstname, lastname, email, country, currency, true).run();
901 deleteThing: function(thingtype, id)
903 var goAhead = function(id)
905 $('#bkeeping_dialog').dialog("close");
906 var ret = new this.RemoveCommand(thingtype, function(id) { alert(thingtype + " " + id + " removed"); }.bind(this, id), {'id': id}).run();
908 bkeeping.showDialog("Are you sure you want to delete " + thingtype + " " + id + "?");
909 $('#bkeeping_dialog').dialog('option', 'buttons', { "CANCEL": function() { $(this).dialog("close"); }, "DELETE": goAhead });
913 addedEdited: function(jsonData)
915 bkeeping.debug("addedEdited: returned jsonData:");
916 bkeeping.debug(jsonData);
917 alert('Recored added/edited. Reloading UI to display updates.');
918 var loc = document.location.href;
919 if (loc.indexOf('?') != -1)
921 loc = loc.substring(0, loc.indexOf('?'));
925 document.location.href = loc + '?timestamp=' + new Date().getTime();
929 //** closing entry window
930 $("#entry_ui").css("display", "none");
933 //** reloading journal window
934 bkeeping.showJournal(bkeeping._displayed_journal);
938 wrapFormAsObj: function()
941 var fields = $(".data_field");
942 for (var i = 0; i < fields.length; i++)
944 var field = fields[i];
945 var n = field.id.substring(field.id.lastIndexOf('_') + 1);
946 var v = $(field).val();
947 bkeeping.debug('wrapFormAsObj: found ' + n + '/' + v);
953 getAccountsAsOptionsList: function(atype)
955 // we have to assume they're cached, coz this is a synchronous call
957 var accounts = bkeeping.cache.accounts;
958 for (var i = 0; i < accounts.length; i++)
960 var id = accounts[i].account.id;
961 var nam = accounts[i].account.name;
962 var type = accounts[i].account.type;
965 if( atype == type ) {
966 html += '<option value="' + id + '" selected="selected" >' + nam + '</option>';
969 html += '<option value="' + id + '">' + nam + '</option>';
975 getAccountTypesAsOptionsList: function(atype)
977 // we have to assume they're cached, coz this is a synchronous call
979 var thingy = { asset: '<option ${selected}>asset</option>',
980 expense: '<option ${selected}>expense</option>',
981 liability: '<option ${selected}>liability</option>',
982 revenue: '<option ${selected}>revenue</option>'
985 for( property in thingy ) {
987 if ( property == atype ) {
989 html += thingy[property].parse_vars( { selected:"selected='selected'" } );
993 html += thingy[property].parse_vars();
999 getCounterWeight: function(type)
1016 getCurrentDate: function()
1019 var day = d.getDate();
1020 var month = d.getMonth() + 1;
1021 var year = d.getFullYear();
1022 if (day < 10) { day = '0' + day; }
1023 if (month < 10) { month = '0' + month; }
1024 return day + '' + month + '' + year;
1027 getAccountNameForId: function(aid) {
1029 var accounts = bkeeping.cache.accounts;
1030 for (var i = 0; i < accounts.length; i++)
1032 var id = accounts[i].account.id;
1033 var nam = accounts[i].account.name;
1043 goAccountPart: function(thingtype, id)
1046 var type = id ? this.EditCommand : this.AddCommand;
1047 var pars = this.wrapFormAsObj();
1048 pars["username"] = bkeeping["ACTIVE_SESSION"]["loggedInUserid"];
1052 if( (pars["type"] == "asset") || (pars["type"] == "expense") ) { //** if asset or expense - debit
1053 pars["cw"] = "debit";
1055 else { //** otherwise - credit
1056 pars["cw"] = "credit";
1059 $('#bkeeping_dialog').dialog("close");
1062 var ret = new type(thingtype, this.addedEdited, pars).run();
1065 goJournalEntryPart: function(thingtype, id)
1068 var type = id ? this.EditCommand : this.AddCommand;
1069 var pars = this.wrapFormAsObj();
1071 //** put in data mapping
1074 //** TODO - i) take ID and update entry or ii) put a new entry with ID of 'new'
1076 if( (id != null) && (id.length > 0) ) {
1078 entry = bkeeping.findEntry(id);
1080 else if( bkeeping.findEntry("new") != null ) {
1082 entry = bkeeping.findEntry("new");
1086 entry = new bkeeping.Entry();
1087 entry["children"] = [];
1089 entry["id"] = "new";
1090 entry["currency"] = pars["cc"];
1092 entry["entrynum"] = null;
1093 entry["journalid"] = "";
1094 entry["state"] = null;
1095 entry["xmlns"] = "com/interrupt/bookkeeping/journal";
1097 //bkeeping.cache.entries[ bkeeping.cache.entries.length ] =
1098 bkeeping.cache.entries.push( { entry: entry } );
1102 //** set the entry currency
1103 if( pars["cc"] == null ) {
1104 if( entry != null ) {
1105 pars["cc"] = entry["currency"];
1110 if( pars["ctype"] == "debit" ) {
1113 account: bkeeping.getAccountNameForId( pars["caid"] ),
1114 accountid: pars["caid"],
1115 amount: pars["ecamount"],
1116 currency: pars["cc"],
1119 xmlns: "com/interrupt/bookkeeping/account"
1122 dname: bkeeping.getAccountNameForId( pars["caid"] ),
1124 damount: pars["ecamount"],
1130 entry.children.push( { debit: debit } );
1136 account: bkeeping.getAccountNameForId( pars["caid"] ),
1137 accountid: pars["caid"],
1138 amount: pars["ecamount"],
1139 currency: pars["cc"],
1142 xmlns: "com/interrupt/bookkeeping/account"
1148 cname: bkeeping.getAccountNameForId( pars["caid"] ),
1150 camount: pars["ecamount"]
1154 entry.children.push( { credit: credit } );
1159 var entryPart = TENTRY_ROW.parse_vars( obj );
1160 var thing = $("#jentry_rows");
1161 $("#jentry_rows").append( entryPart );
1163 $('#bkeeping_dialog').dialog("close");
1165 //var ret = new type(thingtype, this.addedEdited, pars).run();
1169 findAccountName: function(aid) {
1171 var returnName = null;
1172 for( var i = 0; i < bkeeping.cache.accounts.length; i++ ) {
1174 var eachAccount = bkeeping.cache.accounts[i]["account"];
1175 if( eachAccount["id"] == aid ) {
1177 returnName = eachAccount["name"];
1185 findAccountType: function(aid) {
1187 var returnType = null;
1188 for( var i = 0; i < bkeeping.cache.accounts.length; i++ ) {
1190 var eachAccount = bkeeping.cache.accounts[i]["account"];
1191 if( eachAccount["id"] == aid ) {
1193 returnType = eachAccount["type"];
1201 findEntry: function(eid) {
1204 for( var i = 0; i < bkeeping.cache.entries.length; i++ ) {
1206 var eachEntry = bkeeping.cache.entries[i]["entry"];
1207 if( eachEntry["id"] == eid ) {
1218 addEditJournal: function(thingtype, id, returnHandler) // id is null on adds
1221 // raise correct form
1222 var x = eval("AE_" + thingtype.toUpperCase());
1225 var accountName = bkeeping.findAccountName(id);
1226 var accountType = bkeeping.findAccountType(id);
1229 form = form.parse_vars( { accounts:bkeeping.getAccountTypesAsOptionsList(accountType), aname: accountName } );
1230 bkeeping.showModalDialog(form);
1231 $('#bkeeping_dialog').dialog('option', 'title', x[0]);
1233 $('#bkeeping_dialog').dialog('option', 'buttons', { "CANCEL": function() { $(this).dialog("close"); }, "SAVE": returnHandler });
1238 addEditAccount: function(thingtype, id, returnHandler) // id is null on adds
1241 // raise correct form
1242 var x = eval("AE_" + thingtype.toUpperCase());
1245 var accountName = bkeeping.findAccountName(id);
1246 var accountType = bkeeping.findAccountType(id);
1249 form = form.parse_vars( { accounts:bkeeping.getAccountTypesAsOptionsList(accountType), aname: accountName } );
1250 bkeeping.showModalDialog(form);
1251 $('#bkeeping_dialog').dialog('option', 'title', x[0]);
1253 $('#bkeeping_dialog').dialog('option', 'buttons', { "CANCEL": function() { $(this).dialog("close"); }, "SAVE": returnHandler });
1258 addEditEntry: function(thingtype, id, eid, returnHandler) // id is null on adds
1261 // raise correct form
1262 var x = eval("AE_" + thingtype.toUpperCase());
1265 if( (eid != undefined) && (eid != null) ) {
1266 obj["entryid"] = eid
1269 var entryF = bkeeping.findEntry(eid);
1272 if( entryF != null ) {
1274 var entryPartF = null;
1275 for( var i=0; i < entryF.children.length; i++ ) {
1277 var eachC = entryF.children[i];
1280 if( eachC["credit"] || eachC["debit"] ) {
1282 if( eachC["credit"] ) {
1284 obj["entry_ctype"] = " selected='selected' ";
1285 thing = eachC["credit"];
1287 else if( eachC["debit"] ) {
1289 obj["entry_dtype"] = " selected='selected' ";
1290 thing = eachC["debit"];
1293 var accountType = bkeeping.findAccountType( thing["accountid"] );
1294 obj["accounts"] = bkeeping.getAccountsAsOptionsList(accountType);
1295 obj["evalue"] = thing["amount"];
1297 if( thing["currency"] == "CDN" ) {
1298 obj["curr_cdn"] = " selected='selected' ";
1300 else if( thing["currency"] == "USD" ) {
1301 obj["curr_usd"] = " selected='selected' ";
1303 else if( thing["currency"] == "BP" ) {
1304 obj["curr_bp"] = " selected='selected' ";
1306 else if( thing["currency"] == "EUR" ) {
1307 obj["curr_eur"] = " selected='selected' ";
1309 else if( thing["currency"] == "JPN" ) {
1310 obj["curr_jpn"] = " selected='selected' ";
1319 obj["accounts"] = bkeeping.getAccountsAsOptionsList(null);
1324 form = form.parse_vars( obj );
1326 bkeeping.showModalDialog(form);
1327 $('#bkeeping_dialog').dialog('option', 'title', x[0]);
1329 $('#bkeeping_dialog').dialog('option', 'buttons', { "CANCEL": function() { $(this).dialog("close"); }, "SAVE": returnHandler });
1334 saveEntry: function(id)
1337 var type = id ? this.EditCommand : this.AddCommand;
1338 var pars = this.wrapFormAsObj();
1339 pars['date'] = bkeeping.getCurrentDate();
1341 pars['jid'] = bkeeping._displayed_journal;
1342 pars['username'] = bkeeping.ACTIVE_SESSION["loggedInUserid"];
1346 //** 1. findEntry 'entryid' or 'new'
1348 if( id.length > 0 ) {
1350 entry = bkeeping.findEntry(id);
1354 entry = bkeeping.findEntry("new");
1358 if( entry != null ) {
1359 pars["cc"] = entry["currency"];
1363 //** 2. ensure balances
1370 for( var i = 0; i < entry["children"].length; i++ ) { //** sum up debits & credits
1373 //** on debit side ( asset / expense )
1374 //** on credit side ( liability / revenue )
1375 if( entry["children"][i]["credit"] ) {
1377 var thing = entry["children"][i]["credit"];
1379 // check account type
1380 var atype = bkeeping.findAccountType( thing["accountid"] );
1381 if( atype == "asset" || atype == "expense" ) {
1383 csumDEBIT += parseFloat( thing["amount"] );
1385 else if( atype == "liability" || atype == "revenue" ) {
1387 csumCREDIT += parseFloat( thing["amount"] );
1391 else if( entry["children"][i]["debit"] ) {
1393 var thing = entry["children"][i]["debit"];
1395 // check account type
1396 var atype = bkeeping.findAccountType( thing["accountid"] );
1397 if( atype == "asset" || atype == "expense" ) {
1399 dsumDEBIT += parseFloat( thing["amount"] );
1401 else if( atype == "liability" || atype == "revenue" ) {
1403 dsumCREDIT += parseFloat( thing["amount"] );
1412 * check that debits and credots are balanced
1414 var lhs = csumDEBIT + dsumCREDIT;
1415 var rhs = csumCREDIT + dsumDEBIT;
1418 alert("debit and credits are not equal");
1425 * 3. build debit and credit XML parts
1427 pars['debitsANDcredits'] = "";
1428 for( var i = 0; i < entry["children"].length; i++ ) { //** sum up debits & credits
1431 if( entry["children"][i]["credit"] ) {
1433 var cthing = entry["children"][i]["credit"];
1434 var cthingXML = "<credit xmlns='com/interrupt/bookkeeping/account' id='"+ cthing["id"] +"' amount='"+
1435 cthing["amount"] +"' entryid='"+ cthing["entryid"] +"' accountid='"+ cthing["accountid"] +"' account='"+
1436 cthing["account"] +"' currency='"+ cthing["currency"] +"'/>";
1438 pars['debitsANDcredits'] += cthingXML;
1441 else if( entry["children"][i]["debit"] ) {
1443 var dthing = entry["children"][i]["debit"];
1444 var dthingXML = "<debit xmlns='com/interrupt/bookkeeping/account' id='"+ dthing["id"] +"' amount='"+
1445 dthing["amount"] +"' entryid='"+ dthing["entryid"] +"' accountid='"+ dthing["accountid"] +"' account='"+
1446 dthing["account"] +"' currency='"+ dthing["currency"] +"'/>";
1448 pars['debitsANDcredits'] += dthingXML;
1455 //** 4. refresh the entries list at the end of 'run'
1456 var ret = new type("entry", this.addedEdited, pars).run();
1460 addAccount: function()
1462 var returnHandler = bkeeping.goAccountPart.bind(this, "account", null);
1463 bkeeping.addEditAccount("account", null, returnHandler);
1466 editAccount: function(aid)
1468 var returnHandler = bkeeping.goAccountPart.bind(this, "account", aid);
1469 bkeeping.addEditAccount("account", aid, returnHandler);
1472 deleteAccount: function()
1477 addJournal: function()
1479 var returnHandler = bkeeping.goJournalEntryPart.bind(this, "journal", null);
1480 return bkeeping.addEditJournal("journal", null, returnHandler);
1483 editJournal: function(jid)
1486 var returnHandler = bkeeping.goJournalEntryPart.bind(this, "journal", jid);
1487 bkeeping.addEditJournal("journal", jid, returnHandler);
1489 var a = new bkeeping.GetCommand("journal", jid, function(journalObj)
1491 $("#journal_name").val(journalObj.getName());
1492 $("#journal_type").val(journalObj.getType());
1493 $("#journal_balance").val(journalObj.getBalance());
1497 deleteJournal: function()
1502 addEntry: function()
1504 return bkeeping.showJournalEntry(null);
1507 addEditEntryPart: function(id, eid)
1509 var returnHandler = bkeeping.goJournalEntryPart.bind(this, "entry", null);
1510 bkeeping.addEditEntry("entry", id, eid, returnHandler);
1513 editEntry: function(eid)
1515 bkeeping.showJournalEntry( eid );
1516 var a = new bkeeping.GetCommand("entry", eid, function(entryObj)
1518 $("#entry_caid").val(entryObj.getCaid());
1519 $("#entry_daid").val(entryObj.getDaid());
1520 $("#entry_ecamount").val(entryObj.getEcamount());
1521 $("#entry_edamount").val(entryObj.getEdamount());
1522 $("#entry_cc").val(entryObj.getCurrency());
1526 deleteEntry: function()
1531 _displayed_journal: null,
1532 showJournal: function(id)
1535 //** clear previous content
1536 var jrows = document.getElementById( "j_rows" );
1537 jrows["innerHTML"] = "";
1540 //** show journal UI
1541 var journalUI = $("#journal_ui");
1542 journalUI.css("display", "block");
1545 bkeeping._displayed_journal = id;
1546 $("#_journal_id").html(id);
1548 new bkeeping.ListCommand("entries", bkeeping.displayEntries, id).run();
1551 showJournalEntry: function(id)
1554 //** clear previous content
1555 var erows = document.getElementById( "jentry_rows" );
1556 erows["innerHTML"] = "";
1559 var eui = $("#entry_ui");
1560 eui.css("display", "block");
1562 var eui2 = document.getElementById( "entry_ui" ); // trying to put the entryid in the template
1564 eui2["innerHTML"].parse_vars = String.prototype.parse_vars;
1565 eui2["innerHTML"] = eui2["innerHTML"].parse_vars( { entryid: id } );
1568 $("#_journalentry_id").html(id);
1571 new bkeeping.ListCommand("entry", bkeeping.displayEntry, id).run();
1575 getGenericFailureReason: function(jsonData)
1580 r = jsonData["logs"]["children"][0]["log"]["children"][0]["logMessages"]["children"][0]["logMessage"]["value"];
1590 loadProfile: function(data)
1592 for (var i = 0; i < data.length; i++)
1594 var o = data[i]["profileDetail"];
1595 $("#" + o.id).val(o.value);
1599 GetprofileCommand: function(callback)
1601 this.callback = callback;
1603 function isSuccess(jsonData)
1605 return (jsonData && jsonData["user"]);
1608 var getFailureReason = bkeeping.getGenericFailureReason;
1610 function handleResponse(jsonData, textStatus)
1612 if (isSuccess(jsonData))
1614 var u = new bkeeping.User(jsonData["user"]);
1615 setTimeout(callback.bind(this, u.getProfileDetailsList()), 1000);
1619 var reason = getFailureReason(jsonData);
1620 alert("Failed to get profile. Reason: " + reason);
1624 this.run = function()
1626 bkeeping.send_bkexpr("getprofile", GETPROFILE_COMMAND.parse_vars({username:bkeeping.ACTIVE_SESSION.loggedInUserid}), handleResponse.bind(this));
1630 RegisterCommand: function(username, password, firstname, lastname, email, country, currency, updating)
1632 this.username = username;
1633 this.password = password;
1634 this.firstname = firstname;
1635 this.lastname = lastname;
1637 this.country = country;
1638 this.currency = currency;
1639 this.updating = updating;
1641 function isSuccess(jsonData)
1643 return (jsonData && jsonData["user"]);
1646 var getFailureReason = bkeeping.getGenericFailureReason;
1648 function handleResponse(jsonData, textStatus)
1650 // on successful register, user is redirected to the home page and messaged about his ability to login or that his changes were saved
1651 if (isSuccess(jsonData))
1653 var flag = "canlogin";
1656 flag = "changessaved";
1658 setTimeout("document.location.href='accounts.html';", 0);
1662 // show a UI for registration failure:
1663 var reason = getFailureReason(jsonData);
1664 var verb = "register";
1667 verb = "save changes"
1669 alert("Failed to " + verb + ". Reason: " + reason);
1673 this.run = function()
1675 var command = REGISTER_COMMAND;
1676 var cname = "register";
1679 command = UPDATEPROFILE_COMMAND;
1680 cname = "updateprofile";
1682 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));
1686 LoginCommand: function(username, password)
1688 this.username = username;
1689 this.password = password;
1691 function isSuccess(jsonData)
1693 return (jsonData && jsonData["userSession"]);
1696 var getFailureReason = bkeeping.getGenericFailureReason;
1698 function handleResponse(jsonData, textStatus)
1700 // on successful login, user is redirected to the accounts page (accounts.html)
1701 if (isSuccess(jsonData))
1704 var us = new bkeeping.UserSession(jsonData["userSession"]);
1705 bkeeping.ACTIVE_SESSION.update(us.getSessionId(), us.getUser(), us.getGroup()).save();
1706 // go to the accounts page
1707 setTimeout("document.location.href='accounts.html';", 2000);
1711 // show a UI for login failure:
1712 var reason = getFailureReason(jsonData);
1713 alert("Failed to login. Reason: " + reason);
1717 this.run = function()
1719 bkeeping.send_bkexpr("login", LOGIN_COMMAND.parse_vars({username: this.username, password: this.password}), handleResponse.bind(this));
1723 displayAccounts: function(accountListObj)
1725 var as = accountListObj.getAccounts();
1728 var lister = $("#acl_rows");
1730 for (var i = 0; i < as.length; i++)
1734 var obj = { rowid: a.getId(), aname: a.getName(), category: a.getType(), balance: a.getBalance() };
1735 html += ACCOUNT_ROW.parse_vars(obj);
1742 displayJournals: function(journalListObj)
1744 var js = journalListObj.getJournals();
1746 var lister = $("#js_rows");
1748 for (var i = 0; i < js.length; i++)
1752 var obj = { rowid: j.getId(), jname: j.getName() };
1753 html += JOURNAL_ROW.parse_vars(obj);
1760 //** display all the entries in a journal
1761 displayEntries: function(entryListObj)
1763 var es = entryListObj.getEntries();
1764 var lister = $("#j_rows");
1767 for (var i = 0; i < es.length; i++)
1771 // ??? xxx not sure what data to put here
1772 //var obj = { rowid: e.getId(), date: (e.getDate() || "<none>"), entry: ("..." + e.getId().substring(e.getId().length - 6) || "<none>"), balance: (e.getBalance() || "0.00") };
1774 if( e["root"] != undefined ) {
1778 date: (e.getDate() || "<none>"),
1779 entry: ("..." + e.getId().substring(e.getId().length - 6) || "<none>"),
1784 html += ENTRY_ROW.parse_vars(obj);
1792 //** display the debit / credits in a single entry
1793 displayEntry: function(entryListObj)
1795 var es = entryListObj.getEntries();
1796 var lister = $("#jentry_rows");
1799 for (var i = 0; i < es.length; i++)
1803 // ??? xxx not sure what data to put here
1804 //var obj = { rowid: e.getId(), date: (e.getDate() || "<none>"), entry: ("..." + e.getId().substring(e.getId().length - 6) || "<none>"), balance: (e.getBalance() || "0.00") };
1806 //var obj = { rowid: e.getId(), date: (e.getDate() || "<none>"), entry: ("..." + e.getId().substring(e.getId().length - 6) || "<none>"), balance: ( "0.00" ) };
1809 if( e["type"] == "debit" ) {
1811 dname: e.getAccount(),
1812 damount: e.getAmount(),
1817 else if( e["type"] == "credit" ) {
1821 cname: e.getAccount(),
1822 camount: e.getAmount()
1826 obj["rowid"] = e.getId();
1827 obj["eid"] = entryListObj["root"]["id"];
1829 html += TENTRY_ROW.parse_vars(obj);
1836 objectIsOfType: function(obj, single_or_plural_type)
1838 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;
1839 if (t == 'entrie') { t = "entry"; }
1847 addToCacheAsList: function(plural_type, listObj)
1849 var list = listObj.getAsRawList();
1850 bkeeping.debug('AddToCacheAsList: listing (' + plural_type + '/' + list.length + ')');
1851 bkeeping.debug(list);
1852 if (list && (list.length > 0))
1854 for (var i = 0; i < list.length; i++)
1856 if (bkeeping.objectIsOfType(list[i], plural_type))
1858 bkeeping.addToCache(plural_type, null, list[i]);
1864 addToCache: function(plural_type, id, jsonData)
1867 var single_type = (plural_type == 'entries') ? 'entry' : plural_type.substring(0, plural_type.length - 1);
1868 for (var i = 0; i < bkeeping.cache[plural_type].length; i++)
1870 var item = bkeeping.cache[plural_type][i];
1871 bkeeping.debug('addToCache:iterating cache, found item ' + item[single_type].id + '/' + id);
1872 bkeeping.debug(item);
1873 if (item && (item[single_type].id == id))
1875 // item could be changed, so re-add
1877 bkeeping.cache[plural_type][i] = jsonData;
1882 bkeeping.debug('addToCache: pushing jsonData onto cache');
1883 bkeeping.debug(jsonData);
1884 bkeeping.cache[plural_type].push(jsonData);
1888 getFromCache: function(plural_type, id)
1890 var single_type = (plural_type == 'entries') ? 'entry' : plural_type.substring(0, plural_type.length - 1);
1891 for (var i = 0; i < bkeeping.cache[plural_type].length; i++)
1893 var item = bkeeping.cache[plural_type][i];
1894 bkeeping.debug('getFromCache:iterating cache, found item ' + item[single_type].id + '/' + id);
1895 bkeeping.debug(item);
1896 if (item && (item[single_type].id == id))
1903 deleteFromCache: function(single_type, id)
1905 var plural_type = (single_type == 'entry') ? 'entries' : (single_type + 's');
1906 for (var i = 0; i < bkeeping.cache[plural_type].length; i++)
1908 var item = bkeeping.cache[plural_type][i];
1909 bkeeping.debug('deleteFromCache:iterating cache, found item ' + item[single_type].id + '/' + id);
1910 bkeeping.debug(item);
1911 if (item && (item[single_type].id == id))
1913 delete bkeeping.cache[plural_type][i];
1919 GetCommand: function(calltype, id, callback)
1921 this.calltype = calltype;
1922 this.calltype_list = (this.calltype == 'entry') ? 'entries' : (this.calltype + 's');
1924 this.callback = callback;
1927 var u = bkeeping.ACTIVE_SESSION.loggedInUserid;
1928 var g = (u == 'root') ? "webkell" : (u + '.group');
1929 this.pars = {username: u, group: g, id: this.id};
1930 switch (this.calltype)
1933 this.command = GETACCOUNT_COMMAND;
1934 this.type = bkeeping.Account;
1937 this.command = GETJOURNAL_COMMAND;
1938 this.type = bkeeping.Journal;
1941 this.command = GETENTRY_COMMAND;
1942 this.type = bkeeping.Entry;
1943 this.pars['jid'] = bkeeping._displayed_journal;
1946 alert("Unknown calltype: " + this.calltype);
1950 function isSuccess(jsonData)
1952 return (jsonData && (jsonData[this.calltype]) || jsonData[this.calltype_list]);
1955 var getFailureReason = bkeeping.getGenericFailureReason;
1957 function handleResponse(jsonData, textStatus, cached)
1959 if (isSuccess.call(this, jsonData))
1961 if (!cached || (cached != 'true'))
1963 bkeeping.addToCache(this.calltype_list, this.id, jsonData);
1965 return this.callback(new this.type(jsonData[this.calltype]));
1969 // show a UI for login failure:
1970 var reason = getFailureReason(jsonData);
1971 alert("Failed to get list " + this.calltype + ". Reason: " + reason);
1975 this.run = function()
1977 var item = bkeeping.getFromCache(this.calltype_list, this.id);
1980 bkeeping.debug('handling call type ' + this.calltype + '/' + this.id + ' from cache');
1981 handleResponse.call(this, item, 'success', 'true');
1985 bkeeping.debug('handling call type ' + this.calltype + ' from ajax');
1986 var t = "get" + this.calltype;
1987 bkeeping.send_bkexpr(t, this.command.parse_vars(this.pars), handleResponse.bind(this));
1993 setDefaultCurrency: function(profileDetailObj)
1996 bkeeping.DEFAULT_CURRENCY = profileDetailObj["root"]["value"];
2000 ListCommand: function(listtype, callback, optJournalid)
2002 this.listtype = listtype;
2003 this.callback = callback;
2006 this.pars = { username: bkeeping.ACTIVE_SESSION.loggedInUserid };
2007 switch (this.listtype)
2010 this.command = LISTACCOUNTS_COMMAND;
2011 this.type = bkeeping.AccountList;
2014 this.command = LISTJOURNALS_COMMAND;
2015 this.type = bkeeping.JournalList;
2016 this.pars["journal"] = optJournalid;
2019 this.command = LISTENTRIES_COMMAND;
2020 this.type = bkeeping.EntryList;
2021 this.pars["journal"] = bkeeping["_displayed_journal"];
2024 this.command = LISTENTRY_COMMAND;
2025 this.type = bkeeping.EntryList;
2026 this.pars["journal"] = bkeeping["_displayed_journal"];
2027 this.pars["entry"] = optJournalid;
2030 alert("Unknown list: " + this.listtype);
2034 function isSuccess(jsonData)
2036 return (jsonData && jsonData[this.listtype]);
2039 var getFailureReason = bkeeping.getGenericFailureReason;
2042 function handleResponse(jsonData, textStatus, cached)
2044 if (cached || isSuccess.call(this, jsonData))
2047 if (cached && (cached == 'true'))
2049 r = new this.type({ "children" : jsonData });
2053 r = new this.type(jsonData[this.listtype]);
2054 bkeeping.debug("Add to cache as list in ListCommand: " + this.listtype);
2056 bkeeping.addToCacheAsList(this.listtype, r);
2058 return this.callback(r);
2062 // show a UI for login failure:
2063 var reason = getFailureReason(jsonData);
2064 alert("Failed to get list " + this.listtype + ". Reason: " + reason);
2068 this.run = function()
2070 if ( (bkeeping.cache[this.listtype] != undefined) && (bkeeping.cache[this.listtype].length > 0) )
2072 bkeeping.debug('handling list type ' + this.listtype + ' from CACHE');
2073 handleResponse.call(this, bkeeping.cache[this.listtype], 'success', 'true');
2077 bkeeping.debug('handling list type ' + this.listtype + ' from AJAX');
2078 var t = "list" + this.listtype;
2079 bkeeping.send_bkexpr(t, this.command.parse_vars(this.pars), handleResponse.bind(this));
2085 LoadCommand: function(listtype, callback, optJournalid)
2087 this.listtype = listtype;
2088 this.callback = callback;
2091 this.pars = {username: bkeeping.ACTIVE_SESSION.loggedInUserid};
2092 switch (this.listtype)
2094 case "profileDetail":
2095 this.command = LOADDEFAULTCURRENCY_COMMAND;
2096 this.type = bkeeping.DefaultCurrency;
2099 alert("Unknown list: " + this.listtype);
2103 function isSuccess(jsonData)
2105 return (jsonData && jsonData[this.listtype]);
2108 var getFailureReason = bkeeping.getGenericFailureReason;
2110 function handleResponse(jsonData, textStatus)
2112 if (isSuccess.call(this, jsonData))
2114 this.callback(new this.type(jsonData[this.listtype]));
2118 // show a UI for login failure:
2119 var reason = getFailureReason(jsonData);
2120 alert("Failed to get list " + this.listtype + ". Reason: " + reason);
2124 this.run = function()
2126 var t = "list" + this.listtype;
2127 bkeeping.send_bkexpr(t, this.command.parse_vars(this.pars), handleResponse.bind(this));
2132 /* pars is an object containing the data to be added */
2133 AddCommand: function(type, callback, pars)
2135 // pars for account: ${aid} ${atype} ${aname} ${acw} ${username}
2136 // pars for journal: ${jid} ${jname} ${jtype} ${jbalance} ${username}
2137 // pars for entry: ${eid} ${cc} ${edid} ${edamount} ${aid} ${ecid} ${ecamount} ${username} ${jid}
2139 this.callback = callback;
2145 this.command = ADDACCOUNT_COMMAND;
2147 this.type = "accounts"; // special case where what's returned will be an 'accounts'
2150 this.command = ADDJOURNAL_COMMAND;
2153 this.command = ADDENTRY_COMMAND;
2156 alert("Unknown add: " + this.type);
2160 function isSuccess(jsonData)
2162 return (jsonData && jsonData[this.type]);
2165 var getFailureReason = bkeeping.getGenericFailureReason;
2167 function handleResponse(jsonData, textStatus)
2169 if (isSuccess.call(this, jsonData))
2171 return this.callback(jsonData);
2175 // show a UI for login failure:
2176 var reason = getFailureReason(jsonData);
2177 alert("Failed to add " + this.type + ". Reason: " + reason);
2181 this.run = function()
2183 bkeeping.send_bkexpr("add" + this.type, this.command.parse_vars(pars), handleResponse.bind(this));
2187 RemoveCommand: function(type, callback)
2189 // pars for account: ${aid}
2190 // pars for journal: ${jid}
2191 // pars for entry: ${eid}
2193 this.callback = callback;
2198 this.command = REMOVEACCOUNT_COMMAND;
2201 this.command = REMOVEJOURNAL_COMMAND;
2204 this.command = REMOVEENTRY_COMMAND;
2207 alert("Unknown remove: " + this.type);
2211 function isSuccess(jsonData)
2213 return (jsonData && jsonData[this.type]);
2216 var getFailureReason = bkeeping.getGenericFailureReason;
2218 function handleResponse(jsonData, textStatus)
2220 if (isSuccess.call(this, jsonData))
2222 return this.callback(jsonData);
2226 // show a UI for login failure:
2227 var reason = getFailureReason(jsonData);
2228 alert("Failed to add " + this.type + ". Reason: " + reason);
2232 this.run = function()
2234 bkeeping.send_bkexpr("remove" + this.type, this.command.parse_vars({}), handleResponse.bind(this));
2238 /* pars is an object containing the data to be added */
2239 EditCommand: function(type, callback, pars)
2241 // pars for account: ${aid} ${atype} ${aname} ${acw} ${username}
2242 // pars for journal: ${jid} ${jname} ${jtype} ${jbalance} ${username}
2243 // pars for entry: ${eid} ${cc} ${edid} ${edamount} ${aid} ${ecid} ${ecamount} ${username} ${jid}
2245 this.callback = callback;
2251 this.command = UPDATEACCOUNT_COMMAND;
2254 this.command = EDITJOURNAL_COMMAND;
2257 this.command = UPDATEENTRY_COMMAND;
2260 alert("Unknown edit: " + this.type);
2264 function isSuccess(jsonData)
2266 return (jsonData && jsonData[this.type]);
2269 var getFailureReason = bkeeping.getGenericFailureReason;
2271 function handleResponse(jsonData, textStatus)
2273 if (isSuccess.call(this, jsonData))
2275 return this.callback(jsonData);
2279 // show a UI for login failure:
2280 var reason = getFailureReason(jsonData);
2281 alert("Failed to add " + this.type + ". Reason: " + reason);
2285 this.run = function()
2287 bkeeping.send_bkexpr("edit" + this.type, this.command.parse_vars(pars), handleResponse.bind(this));
2293 error: function(xmlHttpRequest, textStatus, errorThrown)
2295 bkeeping.raise('There was a problem with the server request: ' + textStatus + '/' + errorThrown);
2298 success: function(data, textStatus)
2300 bkeeping.log('genericSuccess');
2303 complete: function(xmlHttRequest, textStatus)
2305 bkeeping.log('genericComplete');
2308 // caveat: this function does NOT handle xml with textnodes mixed in with other element children
2309 xmlRecurse: function(node, context)
2311 // squeeze the attributes and add to the context
2312 var attributes = node.attributes;
2315 for (var i = 0; i < attributes.length; i++)
2317 var n = attributes[i].name;
2318 var v = attributes[i].nodeValue;
2323 var children = node.childNodes;
2326 if (children.length > 0)
2328 if ((children.length == 1) && (children[0].nodeType == 3)) // 3 is text node
2330 context["value"] = children[0].textContent;
2334 if (!context["children"])
2336 context["children"] = [];
2338 for (var j = 0; j < children.length; j++)
2341 p[children[j].nodeName] = {};
2342 context["children"].push(p);
2343 this.xmlRecurse(children[j], p[children[j].nodeName]);
2351 // caveat: this function does NOT handle xml with textnodes mixed in with other element children
2352 xmlToJson: function(data, type)
2360 try //Internet Explorer
2362 xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
2363 xmlDoc.async = "false";
2364 xmlDoc.loadXML(data);
2368 var parser = new DOMParser();
2369 //xmlDoc = parser.parseFromString(data, "text/xml");
2373 // convert to json (simplified greatly)
2374 // we'll take advantage of the fact that all the data being passed around is linked to attributes
2375 // and thus we'll go ahead and make our json like this:
2376 // <name1 attr1=val attr2=val...>
2377 // <name2 attr3=val attr4=val...>
2378 // <name2 attr5=val attr6=val...>
2379 // <name3 attr7=val attr8=val...>
2388 // "value": null, // special value -- the text node child of this ele, exclusive with children
2415 var root = xmlDoc.firstChild;
2416 json[root.nodeName] = {};
2417 //json[root.nodeName] = this.xmlRecurse(root, json[root.nodeName]);
2418 json[root.nodeName] = bkeeping.xmlRecurse(root, json[root.nodeName]);
2422 zzz: function (xhr, ajaxOptions, thrownError) {
2426 send_bkexpr: function(bkexprname, expr, callback, optErrorCallback, optCompleteCallback)
2428 if ( MOCK_DATA == "true" || MOCK_DATA == true )
2431 if (MOCK_DATA_FAILURES_MODE)
2433 r = MOCK_DATA["failures"][bkexprname];
2437 r = MOCK_DATA["successes"][bkexprname];
2439 var response = r.response.parse_vars(_LAST_PARSED_DICT);
2441 var textStatus = r.textStatus;
2442 var sanitizedData = this.xmlToJson(response, type);
2443 callback(sanitizedData, textStatus);
2447 var url = "/webkell/webkell";
2448 if (bkexprname == "register")
2450 url = "/webkell/authenticate";
2454 success: callback || bkeeping.success,
2455 error: optErrorCallback || bkeeping.error,
2456 complete: optCompleteCallback || bkeeping.complete,
2457 data: {"bkexpr": expr},
2458 dataFilter: this.xmlToJson,
2468 /* initialization */
2469 displayLoginBlurb: function()
2471 $("#loggedinnote").css('display', "block");
2472 var template = $("#loggedinnote").html();
2473 $("#loggedinnote").html(template.parse_vars({"name": bkeeping.ACTIVE_SESSION.loggedInUserid}));
2476 $("#loginholder").css("visibility", "hidden");
2480 // element not there
2484 page_loader_init: function()
2486 if (window.page_loader)
2488 page_loader(); // defined in e.g. accounts.html, or any other UI page
2492 page_init: function()
2494 var path = document.location.href.match(/^http:\/\/[^\/]+\/(.*)$/);
2498 // cut off querystring and hash
2499 if (path.indexOf("?") > -1)
2501 path = path.substring(0, path.indexOf("?"));
2503 if (path.indexOf("#") > -1)
2505 path = path.substring(0, path.indexOf("#"));
2507 // if it's under webkell, remove that:
2508 if (path.indexOf("webkell") == 0)
2510 path = path.substring(8);
2518 if ((path == "") || (path == "/") || (path == "index.html"))
2526 // this is not exactly efficient, but we'll do this til we're on a proper server-side template engine
2527 var mode = document.location.href.substring(document.location.href.indexOf("mode=") + 5).match(/^(\w+)(.*)$/);
2536 $("#contentbox").load(mode + ".html");
2537 if (mode == "userprofile")
2539 var u = new bkeeping.GetprofileCommand(bkeeping.loadProfile).run();
2542 case "accounts.html":
2543 // do nothing, this UI is generated dynamically
2546 bkeeping.debug("Not a known page: " + path);
2549 if (bkeeping.ACTIVE_SESSION.loaded)
2551 bkeeping.displayLoginBlurb();
2554 var flag = document.location.href.indexOf("flag=");
2557 var value = document.location.href.substring(flag);
2558 if (value.indexOf("canlogin") != -1)
2560 bkeeping.showDialog("You have registered successfully. You can now log in.")
2562 if (value.indexOf("changessaved") != -1)
2564 bkeeping.showDialog("Your changes have been saved.");
2571 bkeeping.ACTIVE_SESSION = new bkeeping.Session(); // load from cookie if present
2572 if (!$("#bkeeping_dialog").length)
2574 $("body").append(DIALOG_HOLDER);
2576 //bkeeping.page_init(); // initializations related to the page we're on
2577 //bkeeping.page_loader_init(); // initializations defined on the page we're on
2588 bkeeping.AccountList.prototype = new bkeeping.Lister();
2589 bkeeping.JournalList.prototype = new bkeeping.Lister();
2590 bkeeping.EntryList.prototype = new bkeeping.Lister();
2592 bkeeping.DefaultCurrency.prototype = new bkeeping.Lister();
2597 $(document).ready(bkeeping.init);
2598 $(window).unload(bkeeping.unload);
2606 response: "<userSession xmlns='com/interrupt/bookkeeping/users' id='c4e2e5344b91c6c1-1c82792712270d305b2-7ffd' groupid='${username}' userid='${username}' />",
2613 response: "<user xmlns='com/interrupt/bookkeeping/users' id='${username}' username='${username}' password='${password}' logintimeout='600000' accountLevel='FREE' defaultGroup='webkell' authenticated=''>" +
2614 "<profileDetails xmlns='com/interrupt/bookkeeping/users' id='user.details'>" +
2615 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='firstname' name='first.name' value='${firstname}'/>" +
2616 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='lastname' name='last.name' value='${lastname}'/>" +
2617 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='email' name='email' value='${email}'/>" +
2618 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='country' name='country' value='${country}'/>" +
2619 "</profileDetails>" +
2627 response: "<user xmlns='com/interrupt/bookkeeping/users' id='${username}' username='${username}' password='${password}' logintimeout='600000' accountLevel='FREE' defaultGroup='webkell' authenticated=''>" +
2628 "<profileDetails xmlns='com/interrupt/bookkeeping/users' id='user.details'>" +
2629 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='username' name='user.name' value='ed'/>" +
2630 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='password' name='password' value='foo'/>" +
2631 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='firstname' name='first.name' value='Eddie'/>" +
2632 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='lastname' name='last.name' value='Abrams'/>" +
2633 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='email' name='email' value='ed@abra.ms'/>" +
2634 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='country' name='country' value='Canada'/>" +
2635 "</profileDetails>" +
2643 response: "<user xmlns='com/interrupt/bookkeeping/users' id='${username}' username='${username}' password='${password}' logintimeout='600000' accountLevel='FREE' defaultGroup='webkell' authenticated=''>" +
2644 "<profileDetails xmlns='com/interrupt/bookkeeping/users' id='user.details'>" +
2645 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='username' name='user.name' value='${username}'/>" +
2646 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='password' name='password' value='${password}'/>" +
2647 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='firstname' name='first.name' value='${firstname}'/>" +
2648 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='lastname' name='last.name' value='${lastname}'/>" +
2649 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='email' name='email' value='${email}'/>" +
2650 "<profileDetail xmlns='com/interrupt/bookkeeping/users' id='country' name='country' value='${country}'/>" +
2651 "</profileDetails>" +
2659 response: "<accounts id='main.accounts'>" +
2660 "<account id='05' name='bankaccount' type='asset' counterWeight='debit' currency=''/>" +
2661 "<account id='06' name='creditcard' type='liability' counterWeight='credit' currency=''/>" +
2669 response: "<journals xmlns:journal='com/interrupt/bookkeeping/journal' id='main.journals'>" +
2670 "<journal id='generalledger' name='generalledger' type='' balance=''>" +
2671 "<entries id='main.entries'>" +
2672 "<entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
2673 "<debit xmlns:account='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
2674 "<credit xmlns:account='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
2678 "<journal id='otherledger' name='otherledger' type='' balance=''>" +
2679 "<entries id='main.entries'>" +
2680 "<entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
2681 "<debit xmlns:account='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
2682 "<credit xmlns:account='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
2693 response: "<entries id='main.entries'>" +
2694 "<entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
2695 "<debit xmlns:account='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
2696 "<credit xmlns:account='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
2705 response: "<account id='${aid}' name='${aname}' type='${atype}' counterWeight='${acn}' currency='${cc}'/>",
2719 response: "<account id='${aid}' name='${aname}' type='${atype}' counterWeight='${acw}' currency='${cc}'/>",
2726 response: "<journal id='${jid}' name='${jname}' type='${jtype}' balance='${jbalance}'>" +
2727 // "<entries id='main.entries'>" +
2728 // "<entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
2729 // "<debit xmlns:account='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
2730 // "<credit xmlns:account='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
2747 response: "<journal id='${jid}' name='${jname}' type='${jtype}' balance='${jbalance}'>" +
2748 // "<entries id='main.entries'>" +
2749 // "<entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
2750 // "<debit xmlns:account='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
2751 // "<credit xmlns:account='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
2761 response: "<entry id='${eid}' entrynum='' state='' journalid='${jid}' date='' currency='${cc}'>" +
2762 "<debit xmlns:account='com/interrupt/bookkeeping/account' id='${edid}' amount='${edamount}' entryid='${eid}' accountid='${aid}' account='' currency='${cc}'/>" +
2763 "<credit xmlns:account='com/interrupt/bookkeeping/account' id='${ecid}' amount='${ecamount}' entryid='${eid}' accountid='${aid}' account='' currency='${cc}'/>" +
2778 response: "<entry id='${eid}' entrynum='' state='' journalid='${jid}' date='' currency='${cc}'>" +
2779 "<debit xmlns:account='com/interrupt/bookkeeping/account' id='${edid}' amount='${edamount}' entryid='${eid}' accountid='${aid}' account='' currency='${cc}'/>" +
2780 "<credit xmlns:account='com/interrupt/bookkeeping/account' id='${ecid}' amount='${ecamount}' entryid='${eid}' accountid='${aid}' account='' currency='${cc}'/>" +
2790 response: "<logs>" +
2793 "<logMessage>Failed to login.</logMessage>" +
2803 response: "<logs>" +
2806 "<logMessage>Failed to register.</logMessage>" +
2816 response: "<logs>" +
2819 "<logMessage>Failed to get user profile.</logMessage>" +
2829 response: "<logs>" +
2832 "<logMessage>Failed to update user profile.</logMessage>" +
2842 response: "<logs>" +
2845 "<logMessage>Failed to get accounts list.</logMessage>" +
2855 response: "<logs>" +
2858 "<logMessage>Failed to get journals list.</logMessage>" +
2868 response: "<logs>" +
2871 "<logMessage>Failed to get entries list.</logMessage>" +
2881 response: "<logs>" +
2884 "<logMessage>Failed to add account.</logMessage>" +
2894 response: "<logs>" +
2897 "<logMessage>Failed to remove account.</logMessage>" +
2907 response: "<logs>" +
2910 "<logMessage>Failed to edit account.</logMessage>" +
2920 response: "<logs>" +
2923 "<logMessage>Failed to add journal.</logMessage>" +
2933 response: "<logs>" +
2936 "<logMessage>Failed to remove journal.</logMessage>" +
2946 response: "<logs>" +
2949 "<logMessage>Failed to edit journal.</logMessage>" +
2959 response: "<logs>" +
2962 "<logMessage>Failed to add entry.</logMessage>" +
2972 response: "<logs>" +
2975 "<logMessage>Failed to remove entry.</logMessage>" +
2985 response: "<logs>" +
2988 "<logMessage>Failed to edit entry.</logMessage>" +
2998 var _TEST_AC = "<accounts id='main.accounts'>" +
2999 "<account id='05' name='bankaccount' type='asset' counterWeight='debit' currency=''/>" +
3000 "<account id='06' name='creditcard' type='liability' counterWeight='credit' currency=''/>" +
3002 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'>" +
3003 "<currency:currencies id='main.currencies' default='CDN'>" +
3004 "<currency:currency id='CDN' name='Canadian Dollar'/>" +
3005 "<currency:currency id='USD' name='US Dollar'/>" +
3006 "<currency:currency id='BP' name='British Pound'/>" +
3007 "<currency:currency id='EUR' name='Euoropean Euro'/>" +
3008 "<currency:currency id='JPN' name='Japanese Yen'/>" +
3009 "</currency:currencies>" +
3010 "<accounts xmlns='com/interrupt/bookkeeping/account' id='main.accounts'>" +
3011 "<account xmlns='com/interrupt/bookkeeping/account' type='asset' id='' name='' counterWeight='debit'/>" +
3013 "<journal:journals id='main.journals'>" +
3014 "<journal:journal id='generalledger' name='generalledger' type='' balance=''>" +
3015 "<journal:entries id='main.entries'>" +
3016 "<journal:entry id='qwertySTUB' entrynum='' state='' journalid='generalledger' date='' currency='CDN'>" +
3017 "<debit xmlns='com/interrupt/bookkeeping/account' id='dtS' amount='120.00' entryid='qwertySTUB' accountid='05' account='' currency='CDN'/>" +
3018 "<credit xmlns='com/interrupt/bookkeeping/account' id='crS' amount='120.00' entryid='qwertySTUB' accountid='06' account='' currency='CDN'/>" +
3019 "</journal:entry>" +
3020 "</journal:entries>" +
3021 "</journal:journal>" +
3022 "</journal:journals>" +