UNPKG

json-object-editor

Version:

JOE the Json Object Editor | Platform Edition

927 lines (858 loc) 46.3 kB
var schema = { title : function(item){if(item.name){ return item.name;} else{ return 'Ledger | ${start_date} - ${end_date}'; } }, info:"A ledger uses a tag to automatically track each transaction associated with that tag. A budget can be associated with your ledger using the same tag.", // Curated summary for agents summary:{ description:'Period view that aggregates transactions (by tag) and budgets into balances.', purpose:'Use ledgers to compute spending, remaining withdrawals, and balances over a time window driven by tags and optional paycheck references.', labelField:'name', defaultSort:{ field:'__sdate', dir:'desc' }, searchableFields:['name','_id'], allowedSorts:['__sdate','ending_balance','joeUpdated','created'], relationships:{ outbound:[ { field:'tags', targetSchema:'tag', cardinality:'many' }, { field:'paycheck', targetSchema:'transaction', cardinality:'one' } ], inbound:{ graphRef:'server/relationships.graph.json' } }, joeManagedFields:['created','joeUpdated'], fields:[ { name:'_id', type:'string', required:true }, { name:'itemtype', type:'string', required:true, const:'ledger' }, { name:'name', type:'string' }, { name:'start_date', type:'string', format:'date' }, { name:'end_date', type:'string', format:'date' }, { name:'starting_balance', type:'number' }, { name:'ending_balance', type:'number' }, { name:'tags', type:'string', isArray:true, isReference:true, targetSchema:'tag' }, { name:'paycheck', type:'string', isReference:true, targetSchema:'transaction' }, { name:'joeUpdated', type:'string', format:'date-time', required:true }, { name:'created', type:'string', format:'date-time', required:true } ] }, listView:{ title:function(item){ var balance = item.ending_balance-item.starting_balance; var paycheckname = _joe.getDataItemProp(item.paycheck,'transaction'); var temp = '<joe-full-right>'+ '<joe-subtext>${RUN[_joe.getDataItemProp;${tags};\'tag\']}</joe-subtext><b class="'+((balance>0)?'':'red-text')+'">'+ formatMoney(balance)+ '<span class="faded-text">&Delta;</span></b>' +'</joe-full-right>'+ '<joe-subtitle>'+(item.name && paycheckname || '')+'</joe-subtitle>'+ '<joe-title>'+(item.name && item.name ||paycheckname)+'</joe-title>'+ '<joe-subtitle>${start_date} - ${end_date}</joe-subtitle>' +'<br/><joe-subtitle>'+formatMoney(item.starting_balance)+' > '+formatMoney(item.ending_balance)+'</joe-subtitle>'; return temp; } , listWindowTitle: 'Ledgers' }, itemExpander:function(ledger){ var ending = _joe.schemas.ledger.methods.getEndingTotals(ledger); var html =''; try{ html = `<joe-metric><joe-subtext>Remaining Withdrawals</joe-subtext> <joe-title>${formatMoney(ending.pending + ending.unspent)}</joe-title></joe-metric>`; ['pending','unspent','positive','negative'].map(key=>{ html+= `<joe-metric><joe-subtext>${key}</joe-subtext> <joe-title>${formatMoney(ending[key])}</joe-title> </joe-metric> `; }) }catch(e){ return html; } return html; }, menuicon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="-8 -8 48 48"><path d="M16 2C14.7 2 13.8 2.9 13.4 4L11 4 6 4 5 4 5 5 5 29 5 30 6 30 19 30 19 31 19 32 20 32 31 32 32 32 32 31 32 16 32 15 31 15 27 15 27 5 27 4 26 4 22 4 21 4 18.6 4C18.2 2.9 17.3 2 16 2zM16 4C16.6 4 17 4.4 17 5L17 6 18 6 20 6 20 8 12 8 12 6 14 6 15 6 15 5C15 4.4 15.4 4 16 4zM7 6L10 6 10 9 10 10 11 10 21 10 22 10 22 9 22 6 25 6 25 15 20 15 19 15 19 16 19 28 7 28 7 6zM9 12L9 16 11 16 11 12 9 12zM15 13L15 16 17 16 17 13 15 13zM12 14L12 16 14 16 14 14 12 14zM21 17L30 17 30 30 21 30 21 17zM13 18C10.8 18 9 19.8 9 22 9 24.2 10.8 26 13 26 15.2 26 17 24.2 17 22 17 19.8 15.2 18 13 18zM23 19L23 21 28 21 28 19 23 19zM13 20L13 22 15 22C15 23.1 14.1 24 13 24 11.9 24 11 23.1 11 22 11 20.9 11.9 20 13 20zM23 23L23 25 25 25 25 23 23 23zM26 23L26 25 28 25 28 23 26 23zM23 26L23 28 25 28 25 26 23 26zM26 26L26 28 28 28 28 26 26 26z" /></svg>', stripeColor:function(item){ if(item.ending_balance == undefined){ return ''; } if(item.ending_balance > 0){ return {title:'end positive',color:'limegreen'}; } return {title:'end negative',color:'OrangeRed'}; }, bgColor:function(ledger){ if((new Date(ledger.start_date).toISOString() <= _joe.nowISO) && (new Date(ledger.end_date).toISOString() >= _joe.times.SOD)){ return 'yellow'; } return ''; }, sorter:[ {name:'date',field:'!__sdate'/*,primer:function(a){ return ($c.format(new Date(a),'Y/d/m')||a); }*/}, {name:'balance',field:'!ending_balance'} ], filters:function(){ var tags = _joe.Filter.Options.tags({group:true,untagged:true,collapsed:true}); var paychecks = _joe.Filter.Options.datasetProperty('transaction','paycheck',{group:'paychecks',query:{paycheck:true},collapsed:true}) var filters = paychecks.concat(tags); //filters = filters.concat(self.Filter.Options.dataset('transaction',{group:'paycheck',query:{paycheck:true},collapsed:true})); // _joe.Data.group.sortBy('name').map(function(g){ // subs.push({name:g.name,filter:{_id:{$in:g.members}}}); // }) return filters; }, subsets:function(){ function thisyear(ledger,year){ if(ledger.__years && ledger.__years.indexOf(year) != -1){ return true; } return false; } // function currentLedger(ledger){ // if((new Date(ledger.start_date).toISOString() <= _joe.nowISO) // && (new Date(ledger.end_date).toISOString() >= _joe.nowISO)){ // return 'yellow'; // } // return ''; // } var years = ['2021','2020','2019','2018','2017','2016']; var sets = [{name:'current',bgcolor:'yellow',filter:function(){ if((new Date(this.start_date).toISOString() <= _joe.nowISO) && (new Date(this.end_date).toISOString() >= _joe.times.SOD)){ return true; } return false; }}]; years.map(function(year){ var d=false; if(new Date().getFullYear() == year){ d=true; } sets.push({name:year,default:d,filter:function(){ return thisyear(this,year); }}) }); return sets; }, fields:[ {extend:'name',specs:{hidden:function(item){ if(_joe.isNewItem()){ return false; } return !(item.tags || (item.tags && !item.tags.length)) }}}, {name:'starting_balance',type:'number',display:'starting balance', onblur:'_joe.Fields.rerender(\'transactions,ending_balance\');', //rerender:'transactions,ending_balance' }, {section_start:'list'}, {name:'transactions',type:'content',passthrough:true, updateAmount:function(domval,transactionid){ _jco().transactions[transactionid].amount = Math.abs(domval); _joe.Fields.rerender('transactions,ending_balance'); }, updateBudgetAmount:function(domval,budgetid){ _jco().budgets[budgetid] = _jco().budgets[budgetid] || {}; _jco().budgets[budgetid].amount = Math.abs(domval); _joe.Fields.rerender('transactions,ending_balance'); }, setStatus:function(transactionid){ var newstatus; //var statuses = {'','paid','posted':}; var transaction = _jco().transactions[transactionid]; // if(transaction.autopay && transaction.status == ''){ // transaction.status 'paid'; // } if(transaction.paycheck){return;} switch(transaction.status){ case '': newstatus = 'paid'; color = 'rgba(230, 230, 0,.3)'; break; case 'paid': case 'autopay': newstatus = 'posted'; color = 'rgba(154, 205, 50,.5)'; break; case 'posted': newstatus = ''; color = ''; if(transaction.autopay){ newstatus='autopay'; color='rgba(230, 230, 0,.3)'; } break; } transaction.status = newstatus; transaction.color = color; $('tr[data-transid='+transactionid+']').css('background-color',color); //var ending = _joe.getField('transactions').getEndingTotals(); var ending = _joe.schemas.ledger.methods.getEndingTotals(); var remaining_withdrawals = _joe.Utils.toDollars(ending.pending+ending.unspent); $('td#pending').html('$'+_joe.Utils.toDollars(ending.pending)); $('td#unspent').html('$'+_joe.Utils.toDollars(ending.unspent)); $('td#total_spending').html('$'+remaining_withdrawals); // $('tr[data-transid='+transactionid+']')[0].style['background-color'] = color; }, comment:'click transaction title to change payment status', run:function(item,property,nohtml){ var ledger = item; var bm = new Benchmarker(); var height = 32; var balance = parseFloat(item.starting_balance); var amount; var html ='<table class="joe-table ledger-table fixed">'+ //'<colgroup>'+ //00ffbf '<col span="1" style="width: 4px;">'+ '<col span="1" style="width: 50px;">'+ '<col span="1" >'+ //'<col span="1" style="width: 40px;">'+ '<col span="1" style="width:calc(100%-250px);">\ <col span="1" style="width:100px;">\ <col span="1" style="width:100px;">' // +'</colgroup>' +'<tr>' //+'<th width=40></th><th width=40></th>' +'<th colspan="4" align="left">Starting Balance</th><td style="width:75px;">' +'</td><td style="width:60px;" align="right"><b>$'+balance+'</b></td></tr>'; var startDate = new Date(item.start_date); var endDate = new Date(item.end_date); item.transactions = item.transactions || {}; for(var t in item.transactions){ item.transactions[t].active = false; } item.budgets = item.budgets || {}; for(var b in item.budgets){ item.budgets[b].active = false; } var budgeted = {}; //_bmResponse(bm,'setup') //PRIMARY FILTER - IN DATE RANGE var pending = 0,unspent = 0; var tdate,bgcolor,transaction; var transactions = []; function transactionPassthrough(transaction,tdate){ item.transactions[transaction._id] = item.transactions[transaction._id] ||{status:'', amount:transaction.amount, color:'', type:transaction.transaction_type, budget:transaction.budget, }; if(item.transactions[transaction._id].active == true){ //logit(item.transactions[transaction._id]); } if(tdate){ item.transactions[transaction._id].tdate = tdate; } Object.assign(item.transactions[transaction._id],{ active:true, name:transaction.name, autopay:transaction.autopay, paycheck:transaction.paycheck, budget:transaction.budget, account_from:transaction.account_from, account_to:transaction.account_to }) // item.transactions[transaction._id].active = true; // item.transactions[transaction._id].autopay = transaction.autopay; // item.transactions[transaction._id].paycheck = transaction.paycheck; // item.transactions[transaction._id].budget = transaction.budget; if(!transaction.variable_amount){ item.transactions[transaction._id].amount = transaction.amount; } item.transactions[transaction._id].type = transaction.transaction_type; if(transaction.autopay && item.transactions[transaction._id].status == ''){ item.transactions[transaction._id].status = 'autopay'; item.transactions[transaction._id].color = 'rgba(230, 230, 0,.1)'; } return item.transactions[transaction._id]; } //TRANSACTIONS function prepTransaction(trans){ /* adds tdate, check if in range, set amount symbol */ tdate = _joe.schemas.ledger.methods.transactionInRange(trans,startDate,endDate,budgeted,prepTransaction); let transaction = {}; if(tdate && $.type(tdate) == "string") { //fix transations missing leading 0 if(tdate.indexOf('/') == 1){ tdate = '0'+tdate; } var pretrans = transactionPassthrough(trans,tdate); transaction.amount = (trans.transaction_type == 'credit') ? pretrans.amount : -pretrans.amount; // if(pretrans.status == 'paid' || pretrans.status == "autopay"){ // pending += transaction.amount; // } transaction.tdate = tdate; transactions.push($.extend({str_tdate:$c.format(new Date(tdate),'Y/m/d')},trans,pretrans,transaction)); }else if(tdate.isArray()){ tdate.map((ctd,i)=>{ let transaction = {}; //fix transations missing leading 0 if(ctd.indexOf('/') == 1){ ctd = '0'+ctd; } var pretrans = transactionPassthrough(trans,ctd); transaction.amount = (trans.transaction_type == 'credit') ? pretrans.amount : -pretrans.amount; // if(pretrans.status == 'paid' || pretrans.status == "autopay"){ // pending += transaction.amount; // } transaction.tdate = ctd; transactions.push($.extend({str_tdate:$c.format(new Date(ctd),'Y/m/d')},trans,pretrans,transaction)); }) } } var trans_query ={}; if(item.tags && item.tags.length){ trans_query.tags = {$in:item.tags} } _joe.Data.transaction.where({trans_query}).map(prepTransaction); //html = transactions.length+' transactions'+html; //transactions.sortBy(['tdate','!paycheck','name'],null,function(a,b,c){ // return ($c.format(new Date(a),'Y/m/d')||a); var budgetList = _joe.Data.budget.where({tags:{$in:item.tags}}).sortBy(['additive','name']); transactions.sortBy(['str_tdate','!paycheck','name'],null,function(a,b,c){ //return ($c.format(new Date(a),'Y/m/d')||a); return a; //$c.format(new Date(a),'Y/m/d') }).map(function(t,it){ if(t){ balance += parseFloat(t.amount); balance = parseFloat(balance.toFixed(2)); html += _joe.schemas.ledger.methods.renderTransactionRow(t,it,{balance},nohtml); } }); var budget_html= ''; //BUDGETS var it=0; var bst = 0;//budgeted spending total var budget_action; var abs_amount; var budget_lookup={};//id=>name budgetList.map(function(bud){ it++; budget_lookup[bud._id] = bud.name; if(item.budgets[bud._id]){ amount = -Math.abs(item.budgets[bud._id].amount); }else{ amount = -Math.abs(bud.amount); } item.budgets[bud._id] = item.budgets[bud._id] || {amount:amount}; item.budgets[bud._id].active = true; item.budgets[bud._id].name = bud.name; item.budgets[bud._id].transactions = []; var subhtml = '',sub_amount,tot_sub_amount = 0,budget_amount,remaining,remaining_str,bud_trans,pre_budtrans; var budgetedObj = budgeted[bud.name] if(budgetedObj){ budgetedObj.sortBy('transaction_date').map(function(bs){ sub_amount = -bs.amount; tot_sub_amount += sub_amount; //balance += parseFloat(sub_amount); pre_budtrans = transactionPassthrough(bs,bs.transaction_date); bud_trans = $.extend({},bs,pre_budtrans,{amount:sub_amount}); subhtml += _joe.schemas.ledger.methods.renderTransactionRow(bud_trans,it,{balance:false},nohtml); }) } abs_amount = Math.abs(amount); //balance to show in column remaining = Math.abs(tot_sub_amount) - abs_amount; if(remaining > 0){ balance += parseFloat(tot_sub_amount); bst += parseFloat(tot_sub_amount); } else{ balance += parseFloat(amount); bst += parseFloat(amount); } if(bud.additive){ item.budgets[bud._id].remaining = 0; }else{ if(abs_amount > tot_sub_amount){ item.budgets[bud._id].remaining = -remaining; }else{ item.budgets[bud._id].remaining = Math.abs(tot_sub_amount) - abs_amount; } } balance = parseFloat(balance.toFixed(2)); bst = parseFloat(bst.toFixed(2)); //balance = parseFloat(balance);//.toFixed(2); //amount to show in column if(nohtml){return '';} budget_action='_joe.getField(\'transactions\').updateBudgetAmount(this.value,\''+bud._id+'\')'; var input_str = '$<input class="joe-money-variable" type="number" step="any" onchange="'+budget_action+'" value='+(-tot_sub_amount)+'>'; var input_str_amount = '$<input class="joe-money-variable" type="number" step="any" onchange="'+budget_action+'" value='+(-amount)+'>'; budget_amount = (bud.additive)?input_str: //(remaining > 0)?input_str+' ('+amount+')':input_str_amount; input_str_amount; remaining_str = (bud.additive)?'': ' <joe-fright style="margin-top:5px">$'+(-(amount-remaining)).toFixed(2)+' /</joe-fright>'+ ' <joe-subtext>$'+(-remaining).toFixed(2)/*((remaining<0 && -remaining) || '0')*/+' remaining</joe-subtext>'; budget_html += '<tr data-budgetid="'+bud._id+'" class="row-divider">' + '<td colspan="5">'+bud.name+remaining_str+'</td>' //+ '<td>$' + budget_amount + '</td>' +'<td>' + budget_amount + '</td>' +'<td align="right" class="left-border" style="font-weight:500;">' +('$'+balance).replace('$-','-$') + '</td>' +'</tr>'; budget_html+= subhtml; }); //_bmResponse(bm,'budget loop') budget_html = '<tr class="budget-header"><th colspan="4" align="left">Budgeted Spending <br/><small>total</small></th>' +'<th colspan="2" align="right">'+('$'+bst).replace('$-','-$') +'</th>' //+'<td></td>' +'</tr>'+budget_html; //var ending = _joe.getField('transactions').getEndingTotals(); var ending = _joe.schemas.ledger.methods.getEndingTotals(ledger); ledger.ending_balance = balance; var remaining_withdrawals = _joe.Utils.toDollars(ending.pending+ending.unspent); html+=budget_html+ '<tr class="end-bal-header"><th colspan="6" align="left">Ending Balance</th><td align="right"><b>$'+balance+'</b></td></tr>' +'<tr><td colspan="4"></td><th colspan="2" align="left">Unspent</th><td id="unspent">$' +_joe.Utils.toDollars(ending.unspent)+'</td></tr>' +'<tr><td colspan="4"></td><th colspan="2" align="left">Pending</th><td id="pending">$' +_joe.Utils.toDollars(ending.pending)+'</td></tr>' +'<tr><td colspan="4"></td><th colspan="2" align="left">Remaining Withdrawals</th><td id="total_spending">$'+remaining_withdrawals+'</td></tr>' // +'<tr><td colspan="3"></td><th colspan="2" align="left">Total</th><td id="total_spending">$'+ending.total+'</td></tr>' +'</tbody></table>'; //_joe.current.object.ending_balance = balance; //console.log('setting ending balance to '+balance); if(_joe.current.object && _joe.current.object._id == ledger._id){ _joe.current.object.remaining_withdrawals = remaining_withdrawals; //_joe.Fields.rerender('remaining_withdrawals',{remaining_withdrawals:remaining_withdrawals}) _joe.current.object.ending_balance = balance; _joe.Fields.rerender(['ending_balance','remaining_withdrawals'],{ ending_balance:balance, remaining_withdrawals:remaining_withdrawals}) } //_bmResponse(bm,'full loop') html += '<joe-subtext>'+bm.stop()+' secs'+'</joe-subtext>'; return html; } }, {name:'budgets',passthrough:true,type:'content',hidden:true}, {section_end:'list'}, {section_start:'review'}, {name:'ending_balance',display:'ending balance',passthrough:true,locked:true}, {name:'remaining_withdrawals',display:'remaining withdrawals',passthrough:true,locked:true}, {name:'ledger_timeline',type:'content',run:function(){ var html ='<div id="chart"></div>' html+='<script>_joe.schemas.ledger.methods.renderTimeline();</script>'; return html; }}, {section_end:'review'}, {sidebar_start:'right'/*,collapsed:function(item){ return item.start_date && item.end_date; }*/}, // {section_start:'reports'}, 'updated', {name:'createTransaction',type:'create',schema:'transaction'}, 'reports', // {section_end:'reports'}, {section_start:'dates',collapsed:function(ledger){ return (ledger.start_date && ledger.end_date); }}, 'start_date:date:30%', 'end_date:date:30%', {name:'summary_ledger',display:'summary ledger',type:'boolean', label:'exclude from aggregator'}, {section_end:'dates'}, {section_start:'filters',collapsed:function(ledger){ return (ledger.paycheck && ledger.tags && ledger.tags.length); }}, {name:'paycheck',type:'select',idprop:'_id',width:'40%', values:function(item){ var checks = [{name:'none',_id:''}]; _joe.Data.transaction.where({paycheck:true}).sortBy('name').map(function(tr){ checks.push(tr); }); return checks; } }, 'tags', {section_end:'filters'}, {section_start:'accounts'}, {name:'plaid_accounts',label:false,type:'content',run:function(obj){ _joe.Components.load('account-info'); var accts = _joe.Data.financial_account.where({ledger_account:true}); var html = accts.map(acct=>{ return `<account-info joe-id="${acct._id}" acct-name="${acct.name}" plaid-id="${acct.plaid_id}" start-date="${obj.start_date}" end-date="${obj.end_date}" ${(acct.account_id && `acct-id="${acct.account_id}"`) || ''}>${acct.code}</account-info>`; }).join(''); return html; }}, {section_end:'accounts'}, {sidebar_end:'right'}, {section_start:'system',collapsed:true}, '_id','created','itemtype', {section_end:'system'} ], idprop : "_id", methods:{ transactionInRange:function(t,startDate,endDate,budgeted,prepTransaction){ function prettyDate(tdate){ return (tdate.getMonth()+1)+'/'+("0" + tdate.getDate()).slice(-2)+'/'+tdate.getFullYear(); } if(t.end_date && new Date(t.end_date) < startDate){//trans ends before ledger starts return false; } // if(t.start_date && ((t.start_date < item.start_date)||(t.start_date > item.end_date))){ // return false; // } //trans starts after ledger ends if(t.start_date && new Date(t.start_date) > endDate){ return false; } /* if(t.start_date){ if(t.start_date > item.end_date){// starts after end date return false } if( t.start_date < item.start_date){//starts before start date, no end date } // return false; }*/ switch(t.recurrence){ case 'onetime': var tdate = new Date(t.transaction_date); if(tdate >= startDate && tdate <= endDate){ //if(t.transaction_date >= item.start_date && t.transaction_date <= item.end_date){ if(t.budget){ if(budgeted){ budgeted[t.budget] = budgeted[t.budget] || []; budgeted[t.budget].push(t); } return false; } return t.transaction_date; } break; case 'irregular': var occ_trans,occ_date,occ_trans_date; if(t.occurences){ t.occurences.map(function(occurence,i){ occ_date = new Date(occurence.date); //if(occurence.date >= item.start_date && occurence.date <= item.end_date){ if(occ_date >= startDate && occ_date <= endDate){ occ_trans = { transaction_date:occurence.date, name:t.name, info:occurence.info, amount:occurence.amount||t.amount, _id:t._id+'_'+occurence.date.replace(/\//g,''), budget:t.budget, recurrence:t.recurrence, transaction_type:t.transaction_type } prepTransaction && prepTransaction(occ_trans); return false; }else{ occ_date; } }); } else{ occ_trans_date = new Date(t.transaction_date ) //if(t.transaction_date >= item.start_date && t.transaction_date <= item.end_date){ if(occ_trans_date >= startDate && occ_trans_date <= endDate){ if(t.budget){ if(budgeted){ budgeted[t.budget] = budgeted[t.budget] || []; budgeted[t.budget].push(t); } return false; } return t.transaction_date; } } break; case 'weekly': var next =''; var tdate = new Date(t.transaction_date); if(tdate >= startDate && tdate <= endDate){ return (tdate.getMonth()+1)+'/'+("0" + tdate.getDate()).slice(-2)+'/'+tdate.getFullYear(); //return t.transaction_date; }else{ while(tdate <= endDate) { tdate.setHours(24 * 7); if(tdate >= startDate && tdate <= endDate){ return (tdate.getMonth()+1)+'/'+("0" + tdate.getDate()).slice(-2)+'/'+tdate.getFullYear(); } } return true; } case 'biweekly': var next =''; var timesInRange = []; var tdate = new Date(t.transaction_date); if(tdate >= startDate && tdate <= endDate){ return (tdate.getMonth()+1)+'/'+("0" + tdate.getDate()).slice(-2)+'/'+tdate.getFullYear(); //return t.transaction_date; }else{ while(tdate <= endDate) { tdate.setHours(24 * 14); // if(tdate >= startDate && tdate <= endDate){ // return (tdate.getMonth()+1)+'/'+("0" + tdate.getDate()).slice(-2)+'/'+tdate.getFullYear(); // } if(tdate >= startDate && tdate <= endDate){ timesInRange.push( (tdate.getMonth()+1)+'/'+("0" + tdate.getDate()).slice(-2)+'/'+tdate.getFullYear()); } } if(timesInRange.length > 0){ return timesInRange; } return true; } case 'monthly': var monthint = (startDate.getDate() <= t.transaction_date)? (startDate.getMonth()+1): (startDate.getMonth()+2); if(monthint > 12){ monthint=monthint%12; } var dStr = monthint +'/'+(('0'+t.transaction_date).slice(-2))+'/'+startDate.getFullYear(); var checkDate = new Date(dStr); if(checkDate >= startDate && checkDate <= endDate){ return dStr; } //year switch bug fix if(startDate.getFullYear()!= endDate.getFullYear()){ dStr = monthint +'/'+(('0'+t.transaction_date).slice(-2))+'/'+endDate.getFullYear(); checkDate = new Date(dStr); if(checkDate >= startDate && checkDate <= endDate){ return dStr; } } break; case 'yearly': var next =''; var tdate = new Date(t.transaction_date); if(tdate >= startDate && tdate <= endDate){ return prettyDate(tdate); //return t.transaction_date; }else{ while(tdate <= endDate) { tdate.setFullYear(tdate.getFullYear()+1); if(tdate >= startDate && tdate <= endDate){ var f = (tdate.getMonth()+1)+'/'+("0" + tdate.getDate()).slice(-2)+'/'+tdate.getFullYear() ; return f; } } // return true; } break; default: return false; } return false; }, renderTransactionRow : function(t,index,specs,nohtml){ if(nohtml){ return ''; } var fullCurrentYear = new Date().getFullYear(); var id = (t._id.contains('_'))?(t._id).substr(0,t._id.indexOf('_')):t._id; var specs = specs || {}; var bgcolor = (index%2==0)?'#f4f4f4':'transparent'; var stripeColor = 'transparent'; if(t.paycheck){ stripeColor = "#00ffbf"; }else{ stripeColor = (t.transaction_type =="credit")?'#00cc0044':'#cc000044'; } var action = '_joe.getField(\'transactions\').setStatus(\''+t._id+'\')'; var trans_action = 'goJoe(_joe.search(\''+id+'\')[0],{schema:\'transaction\'})'; var input_action='_joe.getField(\'transactions\').updateAmount(this.value,\''+t._id+'\')'; var trans_date = t.tdate || t.transaction_date; var colspan = 3 var clickable = (t.paycheck)?'':'clickable'; var onclick = (t.paycheck)?'':'onclick="'+action+'"'; var fromToLine =(t.account_from && t.account_to) ?`<joe-subtext>${_joe.Indexes['_id'][t.account_from].name} > ${_joe.Indexes['_id'][t.account_to].name}</joe-subtext>`:''; var tname = '<td title="'+(t.info||'')+'" class="transaction-title '+clickable+'" '+onclick+' colspan="'+colspan+'" style="padding-left:10px;">'+ ((trans_date && ('<joe-subtext>'+ (trans_date.replace('/'+fullCurrentYear,'')) +((t.recurrence == 'irregular' && t.info)?' '+t.name:'')+'</joe-subtext>'))||'') +((t.recurrence == 'irregular' && t.info)?t.info:t.name) +fromToLine +'</td>'; var html = '<tr data-transid="'+t._id+'" class="'+(specs.trcss||'')+'" style="background-color:'+(t.color||'')+';">' +`<td class="trans-stripe" style="background-color:${stripeColor};"></td>` +'<td onclick="'+trans_action+'" class="joe-table-button joe-view-button" title="view">&nbsp;</td>' //+((t.paycheck)?'': '<td onclick="'+action+'" class="joe-table-button joe-submit-button" title="status">&nbsp;</td>') +tname +'<td>' + ((t.variable_amount)? '$<input class="joe-money-variable" type="number" step="any" onchange="'+input_action+'" value='+t.amount+'>' :('$'+t.amount).replace('$-','-$') )+ '</td>' +(( specs.balance !== false && '<td align="right" class="left-border">' + ('$'+(specs.balance||balance)).replace('$-','-$') + '</td>')||'<td ></td>') +'</tr>'; return html; }, update:function(item){ _joe.schemas.ledger.fields[3].run(item,null,true); }, getEndingTotals:function(ledgerItem){ var currentLedger = ledgerItem || _jco(); var pending = 0,unspent=0,total=0; var positive = 0,negative = 0,budgeted = 0; var list = currentLedger.transactions; var trans; var d; for(var t in list){ trans = list[t]; d = parseFloat(trans.amount); switch(trans.type){ case "debit": negative += d; break; case "credit": positive += d; break; } if(trans.type=="debit" && trans.active){ //if(!trans.budget){//transactions, not budgeted spending //d = parseFloat(trans.amount); if(trans.status == 'paid' || trans.status == "autopay"){ pending += d; }else if(trans.status == ''){ unspent += d; } total += d; //} } } var budgets = currentLedger.budgets,budg; for(var b in budgets){ budg = budgets[b]; if(budg.active){ unspent += parseFloat(budg.remaining); budgeted += parseFloat(budg.remaining); } } return {pending:pending,unspent:unspent,total:total, positive:positive, negative:negative, budgeted:budgeted }; }, renderTimeline:function(){ var ledger = _jco(); //console.log(ledger); var transactions = _jco().transactions; var balance = +parseFloat(ledger.starting_balance).toFixed(2); var inital_balance = balance; var startDate = new Date(ledger.start_date); var endDate = new Date(ledger.end_date); var currentDate = startDate; var currentDateString; var columns = [ ['Dates'],//,$c.toDateTime(currentDate,{format:'Y-m-d'})], ['balance'], ['tcount'], ['change']//,balance] ]; var dates = {},tran,datetran,tranamount; for(var t in transactions){ tran = transactions[t]; dates[tran.tdate] = dates[tran.tdate] ||{trans:[],total:0}; dates[tran.tdate].trans.push(t); tranamount = +parseFloat(tran.amount).toFixed(2); if(tran.type == "debit"){ // if(tran.budget){ // dates[tran.tdate].total += tranamount; // }else{ dates[tran.tdate].total -= tranamount; // } }else if(tran.type == "credit"){ // if(tran.budget){ // dates[tran.tdate].total -= tranamount; // }else{ dates[tran.tdate].total += tranamount; // } } } var cdateobj; while(currentDate <= endDate){ currentDateString = $c.toDateTime(currentDate,{format:'m/d/Y'}); cdateobj = dates[currentDateString] || {totel:0,trans:[]}; //columns[0].push($c.toDateTime(currentDate,{format:'m/d/Y'})); //dates = columns[0]; //balances = columns[1]; columns[0].push($c.toDateTime(currentDate,{format:'Y-m-d'})); //dates[currentDateString] balance += (dates[currentDateString] && dates[currentDateString].total)||0; columns[1].push(balance.toFixed(2)); columns[2].push((dates[currentDateString] && dates[currentDateString].trans.length)||0); columns[3].push((dates[currentDateString] && dates[currentDateString].total.toFixed(2))||0); currentDate.setDate(currentDate.getDate() + 1); } console.log(columns); setTimeout(function(){ var chart = c3.generate({ bindto: '#chart', size: { }, data: { x:'Dates', columns: columns/*[ ['x'], ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ]*/ }, axis: { x: { type: 'timeseries' } } }); },10); } }, onload:function(ledger_arr){ ledger_arr.map(function(ledger){ ledger.__sdate = $c.format(new Date(ledger.start_date),'Ymd'); ledger.__edate = $c.format(new Date(ledger.end_date),'Ymd'); ledger.__years = ledger.__sdate.substr(0,4)+ledger.__edate.substr(0,4); }) }, aggregator:function(list){ var totals = { balance:0, count:0, transactions:0 } list.map(item=>{ if(item.summary_ledger){return false;} totals.count++; totals.balance += (item.ending_balance-item.starting_balance); totals.transactions += item.transactions; }) var html= ` <joe-menu-label>Aggregating ${totals.count} ledgers</joe-menu-label> <joe-content-section> balance change <b class="${((totals.balance>0)?'':'red-text')}">${formatMoney(totals.balance)}<span class="faded-text">&Delta;</span></b> </joe-content-section>`; return html; }, report:{ name:'Standard Ledger', id:'standard', template:function(item,template_data){ var temp = "<report-section></report-section>"; //TODO: highlight associated paycheck //show spending breakdown (pie chart) //show daily dollars graph return temp; }, name:'Literal Ledger', id:'literal', template:function(item,template_data){ var temp = `<report-section></report-section>`; //TODO: highlight associated paycheck //show spending breakdown (pie chart) //show daily dollars graph return temp; } } }; module.exports = schema;