Projects/kmymoney basic viewer/dev/js/Copie de kmymoney.js
(Deskargatu)
/* Kmymoney basic viewer
- tfe < tfe@ptain.info>
*/
// Loadding google visualizacion apis
google.load('visualization', '1', {packages: ['annotatedtimeline']});
google.load('visualization', '1', {packages: ['areachart']});
google.load('visualization', '1', {packages: ['columnchart']});
google.load('visualization', '1', {packages: ['linechart']});
/* Main */
var kmymoney = {
// XML Vars
file : 'comptes.xml',
xml : false,
xmlDoc : false,
prefix : '/KMYMONEY-FILE',
// Google chart types
graph_types : [ 'AnnotatedTimeLine', 'AreaChart', 'ColumnChart', 'LineChart' ],
chart_width : '90%', // 90% of screen width
chart_height : '400px',
// Data
created_date : false,
// Default filter data
startdate : '',
enddate : '',
step_trans : 50,
// HTML Links
layout : false,
// Init: get xml file
init : function(layout)
{
// Init default values
this.layout = $(layout);
this.layout.update('');
this.startdate='20001230';
this.enddate=this.date_format(new Date());
// Loading advise after basic menu
this.layout.appendChild(new Element('p').update('Loading Kmymoney XML File '+this.file+'. Please wait...'));
// Request XML Kmymoney file
this.xml= new Ajax.Request(this.file,{
onSuccess: function()
{
// Not using "this" (?) , bug??
setTimeout(function() { kmymoney.firstanalyse();},100);
},
onFailure : function()
{
setTimeout(function() { kmymoney.failure();},100);
}
});
},
// On load xml failure
failure : function()
{
$(this.layout).update('');
if(this.xml.transport.status=='404')
{
$(this.layout).appendChild(new Element('p', { 'class':'error'}).update('The file '+this.file+' cannot be found. Please check the file and try again later.'));
}
else
{
$(this.layout).appendChild(new Element('p', { 'class':'error'}).update('An error occured downloading the file '+this.file+' : error '+this.xml.transport.status));
}
var p = new Element('p');
p.appendChild(new Element('a', { href:'#', onclick:'kmymoney.init(\'loading\'); return false;'}).update('Retry'));
$(this.layout).appendChild(p);
},
// XML FOrmat to user readable format
date_format_user : function(dateformat)
{
return dateformat.replace(/(\d{4})(\d{2})(\d{2})/,'$3/$2/$1');
},
// XML user format to xml format
undate_format_user : function(dateformat)
{
return dateformat.replace(/(\d{2})\/(\d{2})\/(\d{4})/,'$3$2$1');
},
// date object to xml format
date_format : function(dateformat)
{
var year = dateformat.getYear()+1900;
var month = dateformat.getMonth()+1;
var day = dateformat.getDate();
if(month<10) { month='0'+month; }
if(day<10) { day='0'+day; }
return year+''+month+''+day;
},
// xml format to date object
undate_format : function(date_format)
{
var reg= /(\d{4})(\d{2})(\d{2})/i;
var date_data = reg.exec(date_format);
if(!date_data)
{
return false;
}
return new Date(date_data[1], date_data[2]-1, date_data[3]);
},
// GetXPath
// Cross browser
xpath : function(path)
{
if(this.xmlDoc.evaluate)
{
return this.xmlDoc.evaluate(
this.prefix+path,
this.xmlDoc,
null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE ,
null
);
}
else
{
return this.xmlDoc.selectNodes(this.prefix+path);
}
},
// XPath result item
// Cross browser
xpathItem : function(elt,i)
{
if(elt.snapshotLength)
{
return elt.snapshotItem(i);
}
return elt[i];
},
// XPath result length
// Cross browser
xpathLength : function(elt)
{
if(elt.snapshotLength)
{
return elt.snapshotLength
}
return elt.length;
},
// First analysis
// Used to correct date-formats and create basic elements
// Generate menu
firstanalyse : function()
{
// Deleting loading advise
$(this.layout).update('');
$(this.layout).appendChild(new Element('p').update('XML Kmymoney file '+this.file+' loaded.'));
this.xmlDoc= this.xml.transport.responseXML;
// Correcting date format (numerics)
var trans = this.xpath('/TRANSACTIONS/TRANSACTION');
for(var i=0;i<this.xpathLength(trans); i++)
{
var item_modify = this.xpathItem(trans,i);
var current_date = item_modify.getAttribute('postdate');
item_modify.setAttribute('postdate', current_date.replace(/\D/g,''));
if(i==0)
{
this.startdate=item_modify.getAttribute('postdate');
}
else if(i==this.xpathLength(trans)-1)
{
this.enddate=item_modify.getAttribute('postdate');
}
}
// Endo date format correction
// Modify general startdate and enddate
// Get created_date DATE
var fileinfo = this.xpath('/FILEINFO/CREATION_DATE');
this.created_date= this.xpathItem(fileinfo,0).getAttribute('date');
// Creating menu
var div = new Element('div', { 'class':'menu'});
var ul = new Element('ul');
var li = new Element('li');
li.appendChild(new Element('a', { href:'#', onclick:'kmymoney.mouvements_list(); return false;'}).update('Categories'));
ul.appendChild(li);
li = new Element('li');
li.appendChild(new Element('a', { href:'#', onclick:'kmymoney.graphiques_start(); return false;'}).update('Charts'));
ul.appendChild(li);
div.appendChild(ul);
document.body.insertBefore(div,$(this.layout));
// Calculate graph values using filters
this.calculate_sums();
},
// Liste des comptes disponibles
mouvements_list : function()
{
$(this.layout).update('');
var div = new Element('div', { 'class' : 'tier'});
this.layout.appendChild(div);
div.appendChild(new Element('h2').update('Account list'));
this.mouvements_list_detail('AStd::Asset', div);
div = new Element('div', { 'class' : 'tier'});
this.layout.appendChild(div);
div.appendChild(new Element('h2').update('Income list'));
this.mouvements_list_detail('AStd::Income', div);
div = new Element('div', { 'class' : 'tier'});
this.layout.appendChild(div);
div.appendChild(new Element('h2').update('Expense list'));
this.mouvements_list_detail('AStd::Expense', div);
div = new Element('div', { 'class' : 'endtier'});
this.layout.appendChild(div);
},
mouvements_list_detail : function(account_id, elt, antifreeze)
{
if(!antifreeze)
{
antifreeze=new Array();
}
// Calculate totals for each categorie
var data = this.xpath('/ACCOUNTS/ACCOUNT[@parentaccount=\''+account_id+'\']');
var main_tag = elt.tagName=='UL' ? 'LI': 'DIV';
var main = new Element(main_tag);
var ul=new Element('ul');
if(this.xpathLength(data)>0)
{
for(i=0; i<this.xpathLength(data); i++)
{
var id= this.xpathItem(data,i).getAttribute('id');
var last_total= (parseInt(this.xpathItem(data, i).getAttribute('total'))/100).toFixed(2)+'&euro';
var li = new Element('li');
li.appendChild(new Element('a', { href:'#' , onclick:'kmymoney.mouvements(\''+id+'\'); return false;'}).update(this.xpathItem(data,i).getAttribute('name')));
li.appendChild(new Element('span').update(' ('+last_total+')'));
ul.appendChild(li);
if(antifreeze.indexOf(id)==-1)
{
antifreeze.push(id);
this.mouvements_list_detail(id, ul);
}
}
main.appendChild(ul);
elt.appendChild(main);
}
},
// Mouvements du compte
mouvements: function(account_id, endid)
{
if(!endid)
{
endid=0;
}
this.graphiques_start(account_id);
var data = this.xpath('/TRANSACTIONS/TRANSACTION/SPLITS/SPLIT[@account=\''+account_id+'\']');
// Show last 20
endid=parseInt(endid);
var rowstart= this.xpathLength(data)-(this.step_trans*endid)-1;
var rowend= this.xpathLength(data)-(this.step_trans*endid)-(this.step_trans+1);
if(rowend<0)
{
rowend=-1;
}
if(!$('table_'+account_id))
{
//$(this.layout).update();
var xml_cat = this.xpath('/ACCOUNTS/ACCOUNT[@id=\''+account_id+'\']');
$(this.layout).appendChild(new Element('h2').update(''+this.xpathItem(xml_cat,0).getAttribute('name')));
var span = new Element('span', { id:'last_trans_steps'}).update(this.step_trans);
$(this.layout).appendChild(new Element('p', {id:'last_trans_steps'}).update('Displaying '+this.step_trans+' transactions/update'));
var form= new Element('form');
var table= new Element('table', { id:'table_'+account_id, border:1});
form.appendChild(table);
var tr= new Element('tr');
var td= new Element('th').update('Date');
tr.appendChild(td);
td= new Element('th').update('Categorie');
tr.appendChild(td);
td= new Element('th').update('Payee');
tr.appendChild(td);
td= new Element('th').update('Amount');
tr.appendChild(td);
td= new Element('th').update('Account current value');
tr.appendChild(td);
td= new Element('th').update('Memo');
tr.appendChild(td);
table.appendChild(tr);
$(this.layout).appendChild(form);
}
else
{
table =$('table_'+account_id);
}
var p = new Element('p');
if(rowend>0)
{
p.appendChild(new Element('a', { id:'next_trans', href:'#', onclick:'this.parentNode.remove();kmymoney.mouvements(\''+account_id+'\',\''+(endid+1)+'\'); return false'}).update('Display more'));
p.appendChild(document.createTextNode(' - '));
$(this.layout).appendChild(p);
}
p.appendChild(new Element('a', {href:'#', onclick:'kmymoney.mouvements_list()'}).update('Go back'));
$(this.layout).appendChild(p);
var cats_name= new Hash();
var payee_name= new Hash();
/* Transaction loops */
for(i=rowstart; i>rowend; i--)
{
var trans = this.xpathItem(data,i).parentNode.parentNode;
var splits = this.xpathItem(data,i).parentNode;
var current = this.xpathItem(data,i).getAttribute('shares');
var current=current.replace(/\/10(0?)$/,'$1');
var categorie='';
/* Splits loop */
for(var j=0;j<splits.childNodes.length;j++)
{
if(splits.childNodes[j].tagName && splits.childNodes[j]!=this.xpathItem(data,i))
{
categorieId= splits.childNodes[j].getAttribute('account');
if(!cats_name.get(categorieId))
{
var xml_cat = this.xpath('/ACCOUNTS/ACCOUNT[@id=\''+categorieId+'\']');
cats_name.set(categorieId, this.xpathItem(xml_cat,0).getAttribute('name'));
}
categorie = cats_name.get(categorieId);
payee= splits.childNodes[j].getAttribute('payee');
if(payee!='')
{
if(!cats_name.get(payee))
{
var xml_cat = this.xpath('/PAYEES/PAYEE[@id=\''+payee+'\']');
payee_name.set(categorie, this.xpathItem(xml_cat,0).getAttribute('name'));
}
payee = payee_name.get(categorie);
}
}
}
tr= new Element('tr');
td= new Element('td').update(this.date_format_user(trans.getAttribute('postdate')));
tr.appendChild(td);
var a = new Element('a', { href:'#', onclick:'kmymoney.mouvements(\''+categorieId+'\'); return false;'}).update(categorie);
td= new Element('td').appendChild(a);
tr.appendChild(td);
td= new Element('td').update(payee);
tr.appendChild(td);
var num = (parseInt(this.xpathItem(data,i).getAttribute('shares'))/100).toFixed(2);
var className = num>0 ? 'ok' : 'ko';
td= new Element('td', { 'class':className+' euro'}).update(num+'€');
tr.appendChild(td);
num = (parseInt(this.xpathItem(data,i).getAttribute('total'))/100).toFixed(2);
var className = num>0 ? 'ok_total' : 'ko_total';
td= new Element('td', { 'class':className+' euro'}).update(num+'€');
tr.appendChild(td);
td= new Element('td').update(this.xpathItem(data,i).getAttribute('memo'));
tr.appendChild(td);
table.appendChild(tr);
}
},
// Chart part
// Calculats sums for each categories
calculate_sums : function()
{
/* Reset category totals */
var xml_trans = this.xpath('/ACCOUNTS/ACCOUNT');
for(i=0; i<this.xpathLength(xml_trans);i++)
{
this.xpathItem(xml_trans,i).setAttribute('total',0);
}
/* Reset transactions totals */
var xml_trans = this.xpath('/TRANSACTIONS/TRANSACTION/SPLITS/SPLIT');
for(i=0; i<this.xpathLength(xml_trans);i++)
{
this.xpathItem(xml_trans,i).setAttribute('total',0);
}
// Calculate totals for each categorie
//var xml_split = this.xpath('/TRANSACTIONS/TRANSACTION[@postdate>'+startdate+' and @postdate<'+enddate+']/SPLITS/SPLIT');
var xml_split = this.xpath('/TRANSACTIONS/TRANSACTION/SPLITS/SPLIT');
var reg = /(.*)\/(.*)/;
/* Loop - calculate totals */
for(i=0; i<this.xpathLength(xml_split); i++)
{
var trans = this.xpathItem(xml_split,i);
var current = trans.getAttribute('shares');
var account = trans.getAttribute('account');
var categorie = this.xpath('/ACCOUNTS/ACCOUNT[@id=\''+account+'\']');
var account_current_total = this.xpathItem(categorie, 0).getAttribute('total');
if(!account_current_total)
{
account_current_total=0;
}
// Calculate new total
var newtotal = parseInt(current)+parseInt(account_current_total);
// Xpath links
var modify_item = this.xpathItem(categorie, 0);
var modify_item2 = this.xpathItem(xml_split, i);
// Modify categorie (displayed in table)
// Sum of all transactions
modify_item.setAttribute('total',newtotal);
// Modify split total (displayed in char)
modify_item2.setAttribute('total',newtotal);
}
},
// Graphiques start
graphiques_start : function(accounts)
{
this.layout.update('');
var form=new Element('form');
var div=new Element('div', { id : 'div_charts', 'class':'category'});
// Creating menu
var submenu=new Element('div', { 'class':'submenu'} );
//var cat_name = this.xpath('/ACCOUNTS/ACCOUNT[@id=\''+parent_id+'\']');
//$(this.layout).appendChild(new Element('h3').update(this.xpathItem(cat_name,0).getAttribute('name')));
var p = new Element('p');
p.appendChild( new Element('label', { 'for': 'startdate'}).update('Display from'));
p.appendChild( new Element('input', { type:'text',id: 'startdate', value: this.date_format_user(this.startdate) }));
p.appendChild(document.createTextNode(' to '));
p.appendChild( new Element('input', { type:'text',id: 'enddate', value: this.date_format_user(this.enddate) }));
submenu.appendChild(p);
// Steps
p = new Element('p');
p.appendChild( new Element('label', { 'for': 'step_num'}).update('Interval'));
p.appendChild( new Element('input', { type:'text',id: 'step_num', value:1 }));
var select= new Element('select', { id:'step_type'});
select.appendChild(new Element('option', { value:'year'}).update('Years'));
select.appendChild(new Element('option', { selected:'selected', value:'months'}).update('Months'));
select.appendChild(new Element('option', { value:'days'}).update('Days'));
p.appendChild(select);
submenu.appendChild(p);
// Summarize
p = new Element('p');
var checked = accounts ? false : 'checked';
p.appendChild( new Element('label', { 'for': 'attribute'}).update('Summarize values/dates'));
p.appendChild( new Element('input', { type:'checkbox','checked':checked, 'id': 'attribute'}));
submenu.appendChild(p);
p = new Element('p');
p.appendChild( new Element('label', { 'for': 'summarize'}).update('Total of categories'));
p.appendChild( new Element('input', { type:'checkbox', 'id': 'summarize'}));
submenu.appendChild(p);
// Graph type
p = new Element('p');
p.appendChild( new Element('label', { 'for': 'graphtype'}).update('Chart type'));
var select= new Element('select', { id:'graphtype'});
select.appendChild(new Element('option', { value:0}).update(this.graph_types[0]));
select.appendChild(new Element('option', { value:1}).update(this.graph_types[1]));
select.appendChild(new Element('option', { selected:'selected', value:2}).update(this.graph_types[2]));
select.appendChild(new Element('option', { value:3}).update(this.graph_types[3]));
p.appendChild(select);
submenu.appendChild(p);
/* If accounts not set */
if(!accounts)
{
// Chart type data
p = new Element('p');
p.appendChild( new Element('label', { 'for': 'chart_type_data'}).update('Data type'));
var select=new Element('select', { id:'chart_type_data'});
select.appendChild(new Element('option', { value:'AStd::Asset,AStd::Liability'}).update('Accounts'));
select.appendChild(new Element('option', { value:'AStd::Income'}).update('Income'));
select.appendChild(new Element('option', { value:'AStd::Expense'}).update('Expense'));
select.appendChild(new Element('option', { value:'AStd::Income,AStd::Expense'}).update('Income - Expense'));
p.appendChild(select);
submenu.appendChild(p);
}
/* if accounts sets */
else
{
// Chart type data
p = new Element('p');
p.appendChild( new Element('input', { 'type': 'hidden', id:'chart_type_data', value:accounts}));
submenu.appendChild(p);
}
// Invert chart
p = new Element('p');
p.appendChild( new Element('label', { 'for': 'invert'}).update('Invert values'));
p.appendChild( new Element('input', { type:'checkbox', 'id': 'invert'}));
submenu.appendChild(p);
// Submit
p = new Element('p');
p.appendChild( new Element('input', { type:'submit', value: 'Update chart', onclick:'kmymoney.display_category_chart(); return false' }));
submenu.appendChild(p);
form.appendChild(submenu);
$(this.layout).appendChild(form);
$(this.layout).appendChild(div);
kmymoney.display_category_chart();
},
display_category_chart : function()
{
$('div_charts').update();
attribute= $('attribute').checked ? 'total' : 'shares';
display_subcats= $('summarize').checked ? true : false;
graph_type= $('graphtype').value;
// Empty layer
var cats_init= $F('chart_type_data').split(',');
var categories = new Array();
// Display expense categories
for(var i=0;i<cats_init.length;i++)
{
var cats_temp = this.get_categorie_list(cats_init[i], true, attribute, $('div_charts'));
for(var j=0;j<cats_temp.length;j++)
{
if(categories.indexOf(cats_temp[j])==-1)
{
categories.push(cats_temp[j]);
}
}
}
// Display shares column
// Get step data
var stepNum = parseInt($F('step_num'));
if(stepNum==0)
{
alert('fail');
}
var stepType = $F('step_type');
var days=0;
var months=0;
if(stepType=='days')
{
days=stepNum;
}
else if(stepType=='months')
{
months=stepNum;
}
else
{
months=stepNum*12;
}
this.chart(
this.undate_format_user($F('startdate')),
this.undate_format_user($F('enddate')),
categories,
attribute,
display_subcats,
graph_type,
$('div_charts'),
$F('invert'),
days,
months
);
},
// get categories tree (RECURSIVE)
get_categorie_list : function(parent_id, display_subcats, attribute, elt)
{
var cats = Array(parent_id);
if(!parent_id)
{
parent_id='';
}
// View categorie
var categorie = this.xpath('/ACCOUNTS/ACCOUNT[@parentaccount=\''+parent_id+'\']');
// Init loop categorie
if(this.xpathLength(categorie)>0)
{
var ul = new Element('ul');
for(var i=0;i<this.xpathLength(categorie);i++)
{
var current_categorie = this.xpathItem(categorie, i);
cats.push(current_categorie.getAttribute('id'));
if(display_subcats)
{
var new_cats = this.get_categorie_list(current_categorie.getAttribute('id'), display_subcats, attribute, p2);
for(var j=0;j< new_cats.length; j++)
{
cats.push(new_cats[j]);
}
}
}
}
// End account view
return cats;
},
// Display chart
chart : function(startdate, enddate, categories, attribute, display_subcats, graph_type, html_item, invert, daystep, monthstep)
{
monthstep = parseInt(monthstep);
daystep = parseInt(daystep);
var date_init = this.undate_format(startdate);
var date_end = this.undate_format(enddate);
var reg= /(\d{4})(\d{2})(\d{2})/i;
if(!date_init || !date_end || date_init>date_end)
{
return false;
}
if(categories.length==0)
{
html_item.appendChild(new Element('p').update('No data available'));
return false;
}
var data = new google.visualization.DataTable();
data.addColumn('date', 'Date');
if(display_subcats)
{
data.addColumn('number', 'Total');
}
// Sub query for categories
var xpath_sub= new Array();
if(categories.length==0)
{
xpath_sub.push('@account=0');
}
else
{
for(var i=0;i<categories.length;i++)
{
xpath_sub.push('@account=\''+categories[i]+'\'');
}
}
var columns_totals=new Hash();
var columns=Array();
date_end.setDate(date_end.getDate()+1);
if(attribute=='total')
{
var columns_parcial=new Hash();
}
/* Get Previous values of startdate to get correct total value*/
var xpath_query = '/TRANSACTIONS/TRANSACTION[@postdate<'+startdate+']/SPLITS/SPLIT';
xpath_query+='['+xpath_sub.join(' or ')+']';
var xml_data = this.xpath(xpath_query);
var columns_parcial=new Hash();
/* Loop to get previous values */
for(var i=0;i<this.xpathLength(xml_data);i++)
{
var transaction = this.xpathItem(xml_data, i).parentNode.parentNode;
var kmy_split = this.xpathItem(xml_data, i);
var account = ''+kmy_split.getAttribute('account');
// Date extract
var ar = reg.exec(''+transaction.getAttribute('postdate'));
var num = kmy_split.getAttribute(attribute);
num= num.replace(/\/100$/,'');
num= parseFloat(num.replace(/\/10$/,'0'));
columns_totals.set(account,num);
}
// Loop main dates
while(date_init<date_end)
{
var date_init_old = new Date();
date_init_old.setTime(date_init.valueOf());
date_init.setMonth(date_init.getMonth()+monthstep);
if(daystep==0)
{
date_init.setDate(1);
}
else
{
date_init.setDate(date_init.getDate()+daystep);
}
var addData = [ date_init_old ];
// Start construcion dada here
// Main query
var xpath_query = '/TRANSACTIONS/TRANSACTION[@postdate>='+this.date_format(date_init_old)+' and @postdate<'+this.date_format(date_init)+']/SPLITS/SPLIT';
xpath_query+='['+xpath_sub.join(' or ')+']';
var xml_data = this.xpath(xpath_query);
var columns_parcial=new Hash();
for(var i=0;i<this.xpathLength(xml_data);i++)
{
var transaction = this.xpathItem(xml_data, i).parentNode.parentNode;
var kmy_split = this.xpathItem(xml_data, i);
var account = ''+kmy_split.getAttribute('account');
// Date extract
var ar = reg.exec(''+transaction.getAttribute('postdate'));
var num = kmy_split.getAttribute(attribute);
num= num.replace(/\/100$/,'');
num= parseFloat(num.replace(/\/10$/,'0'));
// Adding column if no exists
if(!display_subcats)
{
if(columns.indexOf(kmy_split.getAttribute('account'))==-1)
{
// Searchin column name in accounts
var xml_column = this.xpath('/ACCOUNTS/ACCOUNT[@id=\''+kmy_split.getAttribute('account')+'\']');
data.addColumn('number', this.xpathItem(xml_column, 0).getAttribute('name'));
columns.push(kmy_split.getAttribute('account'));
}
}
columns_totals.set(account,num);
var old=0;
if(columns_parcial.get(account,num))
{
old=columns_parcial.get(account,num);
}
columns_parcial.set(account,num+old);
}
if(attribute=='total')
{
var cat_display= columns_totals;
}
else
{
var cat_display= columns_parcial;
}
// Display current categorie value
if(!display_subcats)
{
var total=0;
for(var i=0;i<columns.length;i++)
{
if(cat_display.get(columns[i]))
{
if(invert)
{
addData.push(-cat_display.get(columns[i])/100);
}
else
{
addData.push(cat_display.get(columns[i])/100);
}
}
else
{
addData.push(0);
}
}
}
// Display sum of all categories
else
{
var total=0;
cat_display.each(function(e) {
total+=e.value;
});
if(invert)
{
addData.push(-total/100);
}
else
{
addData.push(total/100);
}
}
data.addRow(addData);
}
var div_graph = new Element('div', { style: 'width:'+this.chart_width+'; margin:auto; height:'+this.chart_height });
html_item.appendChild(div_graph);
var annotatedtimeline = eval('new google.visualization.'+this.graph_types[graph_type]+'(div_graph)');
annotatedtimeline.draw(data,
{
displayLegendDots: true,
isStacked: true,
displayAnnotationsFilter : true,
fill:60,
numberFormats: '##############.##'
});
}
}